1996-08-19 20:36:34 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994, 1995, 1996
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that: (1) source code distributions
|
|
|
|
* retain the above copyright notice and this paragraph in its entirety, (2)
|
|
|
|
* distributions including binary code include the above copyright notice and
|
|
|
|
* this paragraph in its entirety in the documentation or other materials
|
|
|
|
* provided with the distribution, and (3) all advertising materials mentioning
|
|
|
|
* features or use of this software display the following acknowledgement:
|
|
|
|
* ``This product includes software developed by the University of California,
|
|
|
|
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
|
|
|
|
* the University nor the names of its contributors may be used to endorse
|
|
|
|
* or promote products derived from this software without specific prior
|
|
|
|
* written permission.
|
|
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
|
|
|
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
*
|
2018-05-18 12:21:19 +00:00
|
|
|
* Optimization module for BPF code intermediate representation.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
2001-04-03 04:18:09 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
2018-05-18 12:21:19 +00:00
|
|
|
#include <config.h>
|
1996-08-19 20:36:34 +00:00
|
|
|
#endif
|
|
|
|
|
2018-05-18 12:21:19 +00:00
|
|
|
#include <pcap-types.h>
|
Update libpcap to 1.1.1.
Changes:
Thu. April 1, 2010. guy@alum.mit.edu.
Summary for 1.1.1 libpcap release
Update CHANGES to reflect more of the changes in 1.1.0.
Fix build on RHEL5.
Fix shared library build on AIX.
Thu. March 11, 2010. ken@netfunctional.ca/guy@alum.mit.edu.
Summary for 1.1.0 libpcap release
Add SocketCAN capture support
Add Myricom SNF API support
Update Endace DAG and ERF support
Add support for shared libraries on Solaris, HP-UX, and AIX
Build, install, and un-install shared libraries by default;
don't build/install shared libraries on platforms we don't support
Fix building from a directory other than the source directory
Fix compiler warnings and builds on some platforms
Update config.guess and config.sub
Support monitor mode on mac80211 devices on Linux
Fix USB memory-mapped capturing on Linux; it requires a new DLT_
value
On Linux, scan /sys/class/net for devices if we have it; scan
it, or /proc/net/dev if we don't have /sys/class/net, even if
we have getifaddrs(), as it'll find interfaces with no
addresses
Add limited support for reading pcap-ng files
Fix BPF driver-loading error handling on AIX
Support getting the full-length interface description on FreeBSD
In the lexical analyzer, free up any addrinfo structure we got back
from getaddrinfo().
Add support for BPF and libdlpi in OpenSolaris (and SXCE)
Hyphenate "link-layer" everywhere
Add /sys/kernel/debug/usb/usbmon to the list of usbmon locations
In pcap_read_linux_mmap(), if there are no frames available, call
poll() even if we're in non-blocking mode, so we pick up
errors, and check for the errors in question.
Note that poll() works on BPF devices is Snow Leopard
If an ENXIO or ENETDOWN is received, it may mean the device has
gone away. Deal with it.
For BPF, raise the default capture buffer size to from 32k to 512k
Support ps_ifdrop on Linux
Added a bunch of #ifdef directives to make wpcap.dll (WinPcap) compile
under cygwin.
Changes to Linux mmapped captures.
Fix bug where create_ring would fail for particular snaplen and
buffer size combinations
Update pcap-config so that it handles libpcap requiring
additional libraries
Add workaround for threadsafeness on Windows
Add missing mapping for DLT_ENC <-> LINKTYPE_ENC
DLT: Add DLT_CAN_SOCKETCAN
DLT: Add Solaris ipnet
Don't check for DLT_IPNET if it's not defined
Add link-layer types for Fibre Channel FC-2
Add link-layer types for Wireless HART
Add link-layer types for AOS
Add link-layer types for DECT
Autoconf fixes (AIX, HP-UX, OSF/1, Tru64 cleanups)
Install headers unconditionally, and include vlan.h/bluetooth.h if
enabled
Autoconf fixes+cleanup
Support enabling/disabling bluetooth (--{en,dis}able-bluetooth)
Support disabling SITA support (--without-sita)
Return -1 on failure to create packet ring (if supported but
creation failed)
Fix handling of 'any' device, so that it can be opened, and no longer
attempt to open it in Monitor mode
Add support for snapshot length for USB Memory-Mapped Interface
Fix configure and build on recent Linux kernels
Fix memory-mapped Linux capture to support pcap_next() and
pcap_next_ex()
Fixes for Linux USB capture
DLT: Add DLT_LINUX_EVDEV
DLT: Add DLT_GSMTAP_UM
DLT: Add DLT_GSMTAP_ABIS
2010-10-28 16:22:13 +00:00
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <memory.h>
|
2019-10-06 04:26:37 +00:00
|
|
|
#include <setjmp.h>
|
2005-05-29 17:46:52 +00:00
|
|
|
#include <string.h>
|
2023-03-27 18:45:17 +00:00
|
|
|
#include <limits.h> /* for SIZE_MAX */
|
2001-04-03 04:18:09 +00:00
|
|
|
#include <errno.h>
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
#include "pcap-int.h"
|
|
|
|
|
|
|
|
#include "gencode.h"
|
2018-05-18 12:21:19 +00:00
|
|
|
#include "optimize.h"
|
2023-03-27 18:45:17 +00:00
|
|
|
#include "diag-control.h"
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_OS_PROTO_H
|
|
|
|
#include "os-proto.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef BDEBUG
|
2018-05-18 12:21:19 +00:00
|
|
|
/*
|
|
|
|
* The internal "debug printout" flag for the filter expression optimizer.
|
|
|
|
* The code to print that stuff is present only if BDEBUG is defined, so
|
|
|
|
* the flag, and the routine to set it, are defined only if BDEBUG is
|
|
|
|
* defined.
|
|
|
|
*/
|
|
|
|
static int pcap_optimizer_debug;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2018-05-18 12:21:19 +00:00
|
|
|
/*
|
|
|
|
* Routine to set that flag.
|
|
|
|
*
|
|
|
|
* This is intended for libpcap developers, not for general use.
|
|
|
|
* If you want to set these in a program, you'll have to declare this
|
|
|
|
* routine yourself, with the appropriate DLL import attribute on Windows;
|
|
|
|
* it's not declared in any header file, and won't be declared in any
|
|
|
|
* header file provided by libpcap.
|
|
|
|
*/
|
|
|
|
PCAP_API void pcap_set_optimizer_debug(int value);
|
|
|
|
|
|
|
|
PCAP_API_DEF void
|
|
|
|
pcap_set_optimizer_debug(int value)
|
|
|
|
{
|
|
|
|
pcap_optimizer_debug = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The internal "print dot graph" flag for the filter expression optimizer.
|
|
|
|
* The code to print that stuff is present only if BDEBUG is defined, so
|
|
|
|
* the flag, and the routine to set it, are defined only if BDEBUG is
|
|
|
|
* defined.
|
|
|
|
*/
|
|
|
|
static int pcap_print_dot_graph;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
/*
|
2018-05-18 12:21:19 +00:00
|
|
|
* Routine to set that flag.
|
|
|
|
*
|
|
|
|
* This is intended for libpcap developers, not for general use.
|
|
|
|
* If you want to set these in a program, you'll have to declare this
|
|
|
|
* routine yourself, with the appropriate DLL import attribute on Windows;
|
|
|
|
* it's not declared in any header file, and won't be declared in any
|
|
|
|
* header file provided by libpcap.
|
2017-02-12 07:04:44 +00:00
|
|
|
*/
|
2018-05-18 12:21:19 +00:00
|
|
|
PCAP_API void pcap_set_print_dot_graph(int value);
|
|
|
|
|
|
|
|
PCAP_API_DEF void
|
|
|
|
pcap_set_print_dot_graph(int value)
|
|
|
|
{
|
|
|
|
pcap_print_dot_graph = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
/*
|
2018-05-18 12:21:19 +00:00
|
|
|
* lowest_set_bit().
|
|
|
|
*
|
|
|
|
* Takes a 32-bit integer as an argument.
|
2017-02-12 07:04:44 +00:00
|
|
|
*
|
2018-05-18 12:21:19 +00:00
|
|
|
* If handed a non-zero value, returns the index of the lowest set bit,
|
2023-03-27 18:45:17 +00:00
|
|
|
* counting upwards from zero.
|
2018-05-18 12:21:19 +00:00
|
|
|
*
|
|
|
|
* If handed zero, the results are platform- and compiler-dependent.
|
|
|
|
* Keep it out of the light, don't give it any water, don't feed it
|
|
|
|
* after midnight, and don't pass zero to it.
|
|
|
|
*
|
|
|
|
* This is the same as the count of trailing zeroes in the word.
|
|
|
|
*/
|
|
|
|
#if PCAP_IS_AT_LEAST_GNUC_VERSION(3,4)
|
|
|
|
/*
|
|
|
|
* GCC 3.4 and later; we have __builtin_ctz().
|
|
|
|
*/
|
2023-03-27 18:45:17 +00:00
|
|
|
#define lowest_set_bit(mask) ((u_int)__builtin_ctz(mask))
|
2018-05-18 12:21:19 +00:00
|
|
|
#elif defined(_MSC_VER)
|
|
|
|
/*
|
|
|
|
* Visual Studio; we support only 2005 and later, so use
|
|
|
|
* _BitScanForward().
|
|
|
|
*/
|
|
|
|
#include <intrin.h>
|
|
|
|
|
|
|
|
#ifndef __clang__
|
|
|
|
#pragma intrinsic(_BitScanForward)
|
|
|
|
#endif
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
static __forceinline u_int
|
2018-05-18 12:21:19 +00:00
|
|
|
lowest_set_bit(int mask)
|
|
|
|
{
|
|
|
|
unsigned long bit;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't sign-extend mask if long is longer than int.
|
|
|
|
* (It's currently not, in MSVC, even on 64-bit platforms, but....)
|
|
|
|
*/
|
|
|
|
if (_BitScanForward(&bit, (unsigned int)mask) == 0)
|
2023-03-27 18:45:17 +00:00
|
|
|
abort(); /* mask is zero */
|
|
|
|
return (u_int)bit;
|
2018-05-18 12:21:19 +00:00
|
|
|
}
|
|
|
|
#elif defined(MSDOS) && defined(__DJGPP__)
|
|
|
|
/*
|
|
|
|
* MS-DOS with DJGPP, which declares ffs() in <string.h>, which
|
|
|
|
* we've already included.
|
|
|
|
*/
|
2023-03-27 18:45:17 +00:00
|
|
|
#define lowest_set_bit(mask) ((u_int)(ffs((mask)) - 1))
|
2018-05-18 12:21:19 +00:00
|
|
|
#elif (defined(MSDOS) && defined(__WATCOMC__)) || defined(STRINGS_H_DECLARES_FFS)
|
|
|
|
/*
|
|
|
|
* MS-DOS with Watcom C, which has <strings.h> and declares ffs() there,
|
|
|
|
* or some other platform (UN*X conforming to a sufficient recent version
|
|
|
|
* of the Single UNIX Specification).
|
|
|
|
*/
|
|
|
|
#include <strings.h>
|
2023-03-27 18:45:17 +00:00
|
|
|
#define lowest_set_bit(mask) (u_int)((ffs((mask)) - 1))
|
2018-05-18 12:21:19 +00:00
|
|
|
#else
|
|
|
|
/*
|
|
|
|
* None of the above.
|
|
|
|
* Use a perfect-hash-function-based function.
|
2017-02-12 07:04:44 +00:00
|
|
|
*/
|
2023-03-27 18:45:17 +00:00
|
|
|
static u_int
|
2018-05-18 12:21:19 +00:00
|
|
|
lowest_set_bit(int mask)
|
2017-02-12 07:04:44 +00:00
|
|
|
{
|
2018-05-18 12:21:19 +00:00
|
|
|
unsigned int v = (unsigned int)mask;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
static const u_int MultiplyDeBruijnBitPosition[32] = {
|
2018-05-18 12:21:19 +00:00
|
|
|
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
|
|
|
|
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
|
|
|
|
};
|
2017-02-12 07:04:44 +00:00
|
|
|
|
2018-05-18 12:21:19 +00:00
|
|
|
/*
|
|
|
|
* We strip off all but the lowermost set bit (v & ~v),
|
|
|
|
* and perform a minimal perfect hash on it to look up the
|
|
|
|
* number of low-order zero bits in a table.
|
|
|
|
*
|
|
|
|
* See:
|
|
|
|
*
|
|
|
|
* http://7ooo.mooo.com/text/ComputingTrailingZerosHOWTO.pdf
|
|
|
|
*
|
|
|
|
* http://supertech.csail.mit.edu/papers/debruijn.pdf
|
|
|
|
*/
|
|
|
|
return (MultiplyDeBruijnBitPosition[((v & -v) * 0x077CB531U) >> 27]);
|
2017-02-12 07:04:44 +00:00
|
|
|
}
|
2009-03-21 20:43:56 +00:00
|
|
|
#endif
|
|
|
|
|
2005-05-29 17:46:52 +00:00
|
|
|
/*
|
|
|
|
* Represents a deleted instruction.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
#define NOP -1
|
|
|
|
|
2005-05-29 17:46:52 +00:00
|
|
|
/*
|
|
|
|
* Register numbers for use-def values.
|
|
|
|
* 0 through BPF_MEMWORDS-1 represent the corresponding scratch memory
|
|
|
|
* location. A_ATOM is the accumulator and X_ATOM is the index
|
|
|
|
* register.
|
|
|
|
*/
|
|
|
|
#define A_ATOM BPF_MEMWORDS
|
|
|
|
#define X_ATOM (BPF_MEMWORDS+1)
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
/*
|
|
|
|
* This define is used to represent *both* the accumulator and
|
|
|
|
* x register in use-def computations.
|
|
|
|
* Currently, the use-def code assumes only one definition per instruction.
|
|
|
|
*/
|
|
|
|
#define AX_ATOM N_ATOMS
|
|
|
|
|
|
|
|
/*
|
2017-02-12 07:04:44 +00:00
|
|
|
* These data structures are used in a Cocke and Shwarz style
|
|
|
|
* value numbering scheme. Since the flowgraph is acyclic,
|
|
|
|
* exit values can be propagated from a node's predecessors
|
|
|
|
* provided it is uniquely defined.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
struct valnode {
|
|
|
|
int code;
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 v0, v1;
|
|
|
|
int val; /* the value number */
|
2017-02-12 07:04:44 +00:00
|
|
|
struct valnode *next;
|
|
|
|
};
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
/* Integer constants mapped with the load immediate opcode. */
|
2023-03-27 18:45:17 +00:00
|
|
|
#define K(i) F(opt_state, BPF_LD|BPF_IMM|BPF_W, i, 0U)
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
struct vmapinfo {
|
|
|
|
int is_const;
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 const_val;
|
2017-02-12 07:04:44 +00:00
|
|
|
};
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2018-05-18 12:21:19 +00:00
|
|
|
typedef struct {
|
2019-10-06 04:26:37 +00:00
|
|
|
/*
|
|
|
|
* Place to longjmp to on an error.
|
|
|
|
*/
|
|
|
|
jmp_buf top_ctx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The buffer into which to put error message.
|
|
|
|
*/
|
|
|
|
char *errbuf;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
/*
|
|
|
|
* A flag to indicate that further optimization is needed.
|
|
|
|
* Iterative passes are continued until a given pass yields no
|
2023-03-27 18:45:17 +00:00
|
|
|
* code simplification or branch movement.
|
2017-02-12 07:04:44 +00:00
|
|
|
*/
|
|
|
|
int done;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - detect loops that do nothing but repeated AND/OR pullups
|
|
|
|
* and edge moves.
|
|
|
|
* If 100 passes in a row do nothing but that, treat that as a
|
|
|
|
* sign that we're in a loop that just shuffles in a cycle in
|
|
|
|
* which each pass just shuffles the code and we eventually
|
|
|
|
* get back to the original configuration.
|
|
|
|
*
|
|
|
|
* XXX - we need a non-heuristic way of detecting, or preventing,
|
|
|
|
* such a cycle.
|
|
|
|
*/
|
|
|
|
int non_branch_movement_performed;
|
|
|
|
|
|
|
|
u_int n_blocks; /* number of blocks in the CFG; guaranteed to be > 0, as it's a RET instruction at a minimum */
|
2017-02-12 07:04:44 +00:00
|
|
|
struct block **blocks;
|
2023-03-27 18:45:17 +00:00
|
|
|
u_int n_edges; /* twice n_blocks, so guaranteed to be > 0 */
|
2017-02-12 07:04:44 +00:00
|
|
|
struct edge **edges;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
/*
|
|
|
|
* A bit vector set representation of the dominators.
|
|
|
|
* We round up the set size to the next power of two.
|
|
|
|
*/
|
2023-03-27 18:45:17 +00:00
|
|
|
u_int nodewords; /* number of 32-bit words for a bit vector of "number of nodes" bits; guaranteed to be > 0 */
|
|
|
|
u_int edgewords; /* number of 32-bit words for a bit vector of "number of edges" bits; guaranteed to be > 0 */
|
2017-02-12 07:04:44 +00:00
|
|
|
struct block **levels;
|
|
|
|
bpf_u_int32 *space;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
#define BITS_PER_WORD (8*sizeof(bpf_u_int32))
|
|
|
|
/*
|
|
|
|
* True if a is in uset {p}
|
|
|
|
*/
|
|
|
|
#define SET_MEMBER(p, a) \
|
2019-10-06 04:26:37 +00:00
|
|
|
((p)[(unsigned)(a) / BITS_PER_WORD] & ((bpf_u_int32)1 << ((unsigned)(a) % BITS_PER_WORD)))
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Add 'a' to uset p.
|
|
|
|
*/
|
|
|
|
#define SET_INSERT(p, a) \
|
2019-10-06 04:26:37 +00:00
|
|
|
(p)[(unsigned)(a) / BITS_PER_WORD] |= ((bpf_u_int32)1 << ((unsigned)(a) % BITS_PER_WORD))
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete 'a' from uset p.
|
|
|
|
*/
|
|
|
|
#define SET_DELETE(p, a) \
|
2019-10-06 04:26:37 +00:00
|
|
|
(p)[(unsigned)(a) / BITS_PER_WORD] &= ~((bpf_u_int32)1 << ((unsigned)(a) % BITS_PER_WORD))
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* a := a intersect b
|
2023-03-27 18:45:17 +00:00
|
|
|
* n must be guaranteed to be > 0
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
#define SET_INTERSECT(a, b, n)\
|
|
|
|
{\
|
|
|
|
register bpf_u_int32 *_x = a, *_y = b;\
|
2023-03-27 18:45:17 +00:00
|
|
|
register u_int _n = n;\
|
|
|
|
do *_x++ &= *_y++; while (--_n != 0);\
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* a := a - b
|
2023-03-27 18:45:17 +00:00
|
|
|
* n must be guaranteed to be > 0
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
#define SET_SUBTRACT(a, b, n)\
|
|
|
|
{\
|
|
|
|
register bpf_u_int32 *_x = a, *_y = b;\
|
2023-03-27 18:45:17 +00:00
|
|
|
register u_int _n = n;\
|
|
|
|
do *_x++ &=~ *_y++; while (--_n != 0);\
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* a := a union b
|
2023-03-27 18:45:17 +00:00
|
|
|
* n must be guaranteed to be > 0
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
#define SET_UNION(a, b, n)\
|
|
|
|
{\
|
|
|
|
register bpf_u_int32 *_x = a, *_y = b;\
|
2023-03-27 18:45:17 +00:00
|
|
|
register u_int _n = n;\
|
|
|
|
do *_x++ |= *_y++; while (--_n != 0);\
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
uset all_dom_sets;
|
|
|
|
uset all_closure_sets;
|
|
|
|
uset all_edge_sets;
|
|
|
|
|
|
|
|
#define MODULUS 213
|
|
|
|
struct valnode *hashtbl[MODULUS];
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 curval;
|
|
|
|
bpf_u_int32 maxval;
|
2017-02-12 07:04:44 +00:00
|
|
|
|
|
|
|
struct vmapinfo *vmap;
|
|
|
|
struct valnode *vnode_base;
|
|
|
|
struct valnode *next_vnode;
|
2018-05-18 12:21:19 +00:00
|
|
|
} opt_state_t;
|
2017-02-12 07:04:44 +00:00
|
|
|
|
|
|
|
typedef struct {
|
2019-10-06 04:26:37 +00:00
|
|
|
/*
|
|
|
|
* Place to longjmp to on an error.
|
|
|
|
*/
|
|
|
|
jmp_buf top_ctx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The buffer into which to put error message.
|
|
|
|
*/
|
|
|
|
char *errbuf;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
/*
|
|
|
|
* Some pointers used to convert the basic block form of the code,
|
|
|
|
* into the array form that BPF requires. 'fstart' will point to
|
|
|
|
* the malloc'd array while 'ftail' is used during the recursive
|
|
|
|
* traversal.
|
|
|
|
*/
|
|
|
|
struct bpf_insn *fstart;
|
|
|
|
struct bpf_insn *ftail;
|
|
|
|
} conv_state_t;
|
|
|
|
|
2019-10-06 04:26:37 +00:00
|
|
|
static void opt_init(opt_state_t *, struct icode *);
|
2017-02-12 07:04:44 +00:00
|
|
|
static void opt_cleanup(opt_state_t *);
|
2019-10-06 04:26:37 +00:00
|
|
|
static void PCAP_NORETURN opt_error(opt_state_t *, const char *, ...)
|
|
|
|
PCAP_PRINTFLIKE(2, 3);
|
2017-02-12 07:04:44 +00:00
|
|
|
|
|
|
|
static void intern_blocks(opt_state_t *, struct icode *);
|
|
|
|
|
|
|
|
static void find_inedges(opt_state_t *, struct block *);
|
|
|
|
#ifdef BDEBUG
|
2019-10-06 04:26:37 +00:00
|
|
|
static void opt_dump(opt_state_t *, struct icode *);
|
2017-02-12 07:04:44 +00:00
|
|
|
#endif
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
#ifndef MAX
|
|
|
|
#define MAX(a,b) ((a)>(b)?(a):(b))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
find_levels_r(opt_state_t *opt_state, struct icode *ic, struct block *b)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
int level;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
if (isMarked(ic, b))
|
1996-08-19 20:36:34 +00:00
|
|
|
return;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
Mark(ic, b);
|
1996-08-19 20:36:34 +00:00
|
|
|
b->link = 0;
|
|
|
|
|
|
|
|
if (JT(b)) {
|
2017-02-12 07:04:44 +00:00
|
|
|
find_levels_r(opt_state, ic, JT(b));
|
|
|
|
find_levels_r(opt_state, ic, JF(b));
|
1996-08-19 20:36:34 +00:00
|
|
|
level = MAX(JT(b)->level, JF(b)->level) + 1;
|
|
|
|
} else
|
|
|
|
level = 0;
|
|
|
|
b->level = level;
|
2017-02-12 07:04:44 +00:00
|
|
|
b->link = opt_state->levels[level];
|
|
|
|
opt_state->levels[level] = b;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Level graph. The levels go from 0 at the leaves to
|
2017-02-12 07:04:44 +00:00
|
|
|
* N_LEVELS at the root. The opt_state->levels[] array points to the
|
1996-08-19 20:36:34 +00:00
|
|
|
* first node of the level list, whose elements are linked
|
|
|
|
* with the 'link' field of the struct block.
|
|
|
|
*/
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
find_levels(opt_state_t *opt_state, struct icode *ic)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2017-02-12 07:04:44 +00:00
|
|
|
memset((char *)opt_state->levels, 0, opt_state->n_blocks * sizeof(*opt_state->levels));
|
|
|
|
unMarkAll(ic);
|
|
|
|
find_levels_r(opt_state, ic, ic->root);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find dominator relationships.
|
|
|
|
* Assumes graph has been leveled.
|
|
|
|
*/
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
find_dom(opt_state_t *opt_state, struct block *root)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2023-03-27 18:45:17 +00:00
|
|
|
u_int i;
|
|
|
|
int level;
|
1996-08-19 20:36:34 +00:00
|
|
|
struct block *b;
|
|
|
|
bpf_u_int32 *x;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize sets to contain all nodes.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
x = opt_state->all_dom_sets;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* In opt_init(), we've made sure the product doesn't overflow.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
i = opt_state->n_blocks * opt_state->nodewords;
|
2023-03-27 18:45:17 +00:00
|
|
|
while (i != 0) {
|
|
|
|
--i;
|
2018-05-18 12:21:19 +00:00
|
|
|
*x++ = 0xFFFFFFFFU;
|
2023-03-27 18:45:17 +00:00
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
/* Root starts off empty. */
|
2023-03-27 18:45:17 +00:00
|
|
|
for (i = opt_state->nodewords; i != 0;) {
|
|
|
|
--i;
|
1996-08-19 20:36:34 +00:00
|
|
|
root->dom[i] = 0;
|
2023-03-27 18:45:17 +00:00
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/* root->level is the highest level no found. */
|
2023-03-27 18:45:17 +00:00
|
|
|
for (level = root->level; level >= 0; --level) {
|
|
|
|
for (b = opt_state->levels[level]; b; b = b->link) {
|
1996-08-19 20:36:34 +00:00
|
|
|
SET_INSERT(b->dom, b->id);
|
|
|
|
if (JT(b) == 0)
|
|
|
|
continue;
|
2017-02-12 07:04:44 +00:00
|
|
|
SET_INTERSECT(JT(b)->dom, b->dom, opt_state->nodewords);
|
|
|
|
SET_INTERSECT(JF(b)->dom, b->dom, opt_state->nodewords);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
propedom(opt_state_t *opt_state, struct edge *ep)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
SET_INSERT(ep->edom, ep->id);
|
|
|
|
if (ep->succ) {
|
2017-02-12 07:04:44 +00:00
|
|
|
SET_INTERSECT(ep->succ->et.edom, ep->edom, opt_state->edgewords);
|
|
|
|
SET_INTERSECT(ep->succ->ef.edom, ep->edom, opt_state->edgewords);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute edge dominators.
|
|
|
|
* Assumes graph has been leveled and predecessors established.
|
|
|
|
*/
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
find_edom(opt_state_t *opt_state, struct block *root)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2023-03-27 18:45:17 +00:00
|
|
|
u_int i;
|
1996-08-19 20:36:34 +00:00
|
|
|
uset x;
|
2023-03-27 18:45:17 +00:00
|
|
|
int level;
|
1996-08-19 20:36:34 +00:00
|
|
|
struct block *b;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
x = opt_state->all_edge_sets;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* In opt_init(), we've made sure the product doesn't overflow.
|
|
|
|
*/
|
|
|
|
for (i = opt_state->n_edges * opt_state->edgewords; i != 0; ) {
|
|
|
|
--i;
|
2018-05-18 12:21:19 +00:00
|
|
|
x[i] = 0xFFFFFFFFU;
|
2023-03-27 18:45:17 +00:00
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/* root->level is the highest level no found. */
|
2017-02-12 07:04:44 +00:00
|
|
|
memset(root->et.edom, 0, opt_state->edgewords * sizeof(*(uset)0));
|
|
|
|
memset(root->ef.edom, 0, opt_state->edgewords * sizeof(*(uset)0));
|
2023-03-27 18:45:17 +00:00
|
|
|
for (level = root->level; level >= 0; --level) {
|
|
|
|
for (b = opt_state->levels[level]; b != 0; b = b->link) {
|
2017-02-12 07:04:44 +00:00
|
|
|
propedom(opt_state, &b->et);
|
|
|
|
propedom(opt_state, &b->ef);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the backwards transitive closure of the flow graph. These sets
|
|
|
|
* are backwards in the sense that we find the set of nodes that reach
|
|
|
|
* a given node, not the set of nodes that can be reached by a node.
|
|
|
|
*
|
|
|
|
* Assumes graph has been leveled.
|
|
|
|
*/
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
find_closure(opt_state_t *opt_state, struct block *root)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2023-03-27 18:45:17 +00:00
|
|
|
int level;
|
1996-08-19 20:36:34 +00:00
|
|
|
struct block *b;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize sets to contain no nodes.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
memset((char *)opt_state->all_closure_sets, 0,
|
|
|
|
opt_state->n_blocks * opt_state->nodewords * sizeof(*opt_state->all_closure_sets));
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/* root->level is the highest level no found. */
|
2023-03-27 18:45:17 +00:00
|
|
|
for (level = root->level; level >= 0; --level) {
|
|
|
|
for (b = opt_state->levels[level]; b; b = b->link) {
|
1996-08-19 20:36:34 +00:00
|
|
|
SET_INSERT(b->closure, b->id);
|
|
|
|
if (JT(b) == 0)
|
|
|
|
continue;
|
2017-02-12 07:04:44 +00:00
|
|
|
SET_UNION(JT(b)->closure, b->closure, opt_state->nodewords);
|
|
|
|
SET_UNION(JF(b)->closure, b->closure, opt_state->nodewords);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2023-03-27 18:45:17 +00:00
|
|
|
* Return the register number that is used by s.
|
|
|
|
*
|
|
|
|
* Returns ATOM_A if A is used, ATOM_X if X is used, AX_ATOM if both A and X
|
|
|
|
* are used, the scratch memory location's number if a scratch memory
|
|
|
|
* location is used (e.g., 0 for M[0]), or -1 if none of those are used.
|
1996-08-19 20:36:34 +00:00
|
|
|
*
|
|
|
|
* The implementation should probably change to an array access.
|
|
|
|
*/
|
|
|
|
static int
|
2013-05-30 06:41:26 +00:00
|
|
|
atomuse(struct stmt *s)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
register int c = s->code;
|
|
|
|
|
|
|
|
if (c == NOP)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
switch (BPF_CLASS(c)) {
|
|
|
|
|
|
|
|
case BPF_RET:
|
|
|
|
return (BPF_RVAL(c) == BPF_A) ? A_ATOM :
|
|
|
|
(BPF_RVAL(c) == BPF_X) ? X_ATOM : -1;
|
|
|
|
|
|
|
|
case BPF_LD:
|
|
|
|
case BPF_LDX:
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* As there are fewer than 2^31 memory locations,
|
|
|
|
* s->k should be convertible to int without problems.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
return (BPF_MODE(c) == BPF_IND) ? X_ATOM :
|
2023-03-27 18:45:17 +00:00
|
|
|
(BPF_MODE(c) == BPF_MEM) ? (int)s->k : -1;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
case BPF_ST:
|
|
|
|
return A_ATOM;
|
|
|
|
|
|
|
|
case BPF_STX:
|
|
|
|
return X_ATOM;
|
|
|
|
|
|
|
|
case BPF_JMP:
|
|
|
|
case BPF_ALU:
|
|
|
|
if (BPF_SRC(c) == BPF_X)
|
|
|
|
return AX_ATOM;
|
|
|
|
return A_ATOM;
|
|
|
|
|
|
|
|
case BPF_MISC:
|
|
|
|
return BPF_MISCOP(c) == BPF_TXA ? X_ATOM : A_ATOM;
|
|
|
|
}
|
|
|
|
abort();
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the register number that is defined by 's'. We assume that
|
|
|
|
* a single stmt cannot define more than one register. If no register
|
|
|
|
* is defined, return -1.
|
|
|
|
*
|
|
|
|
* The implementation should probably change to an array access.
|
|
|
|
*/
|
|
|
|
static int
|
2013-05-30 06:41:26 +00:00
|
|
|
atomdef(struct stmt *s)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
if (s->code == NOP)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
switch (BPF_CLASS(s->code)) {
|
|
|
|
|
|
|
|
case BPF_LD:
|
|
|
|
case BPF_ALU:
|
|
|
|
return A_ATOM;
|
|
|
|
|
|
|
|
case BPF_LDX:
|
|
|
|
return X_ATOM;
|
|
|
|
|
|
|
|
case BPF_ST:
|
|
|
|
case BPF_STX:
|
|
|
|
return s->k;
|
|
|
|
|
|
|
|
case BPF_MISC:
|
|
|
|
return BPF_MISCOP(s->code) == BPF_TAX ? X_ATOM : A_ATOM;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-05-29 17:46:52 +00:00
|
|
|
/*
|
|
|
|
* Compute the sets of registers used, defined, and killed by 'b'.
|
|
|
|
*
|
|
|
|
* "Used" means that a statement in 'b' uses the register before any
|
|
|
|
* statement in 'b' defines it, i.e. it uses the value left in
|
|
|
|
* that register by a predecessor block of this block.
|
|
|
|
* "Defined" means that a statement in 'b' defines it.
|
|
|
|
* "Killed" means that a statement in 'b' defines it before any
|
|
|
|
* statement in 'b' uses it, i.e. it kills the value left in that
|
|
|
|
* register by a predecessor block of this block.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
static void
|
2013-05-30 06:41:26 +00:00
|
|
|
compute_local_ud(struct block *b)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
struct slist *s;
|
2017-02-12 07:04:44 +00:00
|
|
|
atomset def = 0, use = 0, killed = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
int atom;
|
|
|
|
|
|
|
|
for (s = b->stmts; s; s = s->next) {
|
|
|
|
if (s->s.code == NOP)
|
|
|
|
continue;
|
|
|
|
atom = atomuse(&s->s);
|
|
|
|
if (atom >= 0) {
|
|
|
|
if (atom == AX_ATOM) {
|
|
|
|
if (!ATOMELEM(def, X_ATOM))
|
|
|
|
use |= ATOMMASK(X_ATOM);
|
|
|
|
if (!ATOMELEM(def, A_ATOM))
|
|
|
|
use |= ATOMMASK(A_ATOM);
|
|
|
|
}
|
|
|
|
else if (atom < N_ATOMS) {
|
|
|
|
if (!ATOMELEM(def, atom))
|
|
|
|
use |= ATOMMASK(atom);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
atom = atomdef(&s->s);
|
|
|
|
if (atom >= 0) {
|
|
|
|
if (!ATOMELEM(use, atom))
|
2017-02-12 07:04:44 +00:00
|
|
|
killed |= ATOMMASK(atom);
|
1996-08-19 20:36:34 +00:00
|
|
|
def |= ATOMMASK(atom);
|
|
|
|
}
|
|
|
|
}
|
2005-05-29 17:46:52 +00:00
|
|
|
if (BPF_CLASS(b->s.code) == BPF_JMP) {
|
|
|
|
/*
|
|
|
|
* XXX - what about RET?
|
|
|
|
*/
|
|
|
|
atom = atomuse(&b->s);
|
|
|
|
if (atom >= 0) {
|
|
|
|
if (atom == AX_ATOM) {
|
|
|
|
if (!ATOMELEM(def, X_ATOM))
|
|
|
|
use |= ATOMMASK(X_ATOM);
|
|
|
|
if (!ATOMELEM(def, A_ATOM))
|
|
|
|
use |= ATOMMASK(A_ATOM);
|
|
|
|
}
|
|
|
|
else if (atom < N_ATOMS) {
|
|
|
|
if (!ATOMELEM(def, atom))
|
|
|
|
use |= ATOMMASK(atom);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
b->def = def;
|
2017-02-12 07:04:44 +00:00
|
|
|
b->kill = killed;
|
1996-08-19 20:36:34 +00:00
|
|
|
b->in_use = use;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Assume graph is already leveled.
|
|
|
|
*/
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
find_ud(opt_state_t *opt_state, struct block *root)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
int i, maxlevel;
|
|
|
|
struct block *p;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* root->level is the highest level no found;
|
|
|
|
* count down from there.
|
|
|
|
*/
|
|
|
|
maxlevel = root->level;
|
|
|
|
for (i = maxlevel; i >= 0; --i)
|
2017-02-12 07:04:44 +00:00
|
|
|
for (p = opt_state->levels[i]; p; p = p->link) {
|
1996-08-19 20:36:34 +00:00
|
|
|
compute_local_ud(p);
|
|
|
|
p->out_use = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 1; i <= maxlevel; ++i) {
|
2017-02-12 07:04:44 +00:00
|
|
|
for (p = opt_state->levels[i]; p; p = p->link) {
|
1996-08-19 20:36:34 +00:00
|
|
|
p->out_use |= JT(p)->in_use | JF(p)->in_use;
|
|
|
|
p->in_use |= p->out_use &~ p->kill;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
init_val(opt_state_t *opt_state)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->curval = 0;
|
|
|
|
opt_state->next_vnode = opt_state->vnode_base;
|
|
|
|
memset((char *)opt_state->vmap, 0, opt_state->maxval * sizeof(*opt_state->vmap));
|
|
|
|
memset((char *)opt_state->hashtbl, 0, sizeof opt_state->hashtbl);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Because we really don't have an IR, this stuff is a little messy.
|
|
|
|
*
|
|
|
|
* This routine looks in the table of existing value number for a value
|
|
|
|
* with generated from an operation with the specified opcode and
|
|
|
|
* the specified values. If it finds it, it returns its value number,
|
|
|
|
* otherwise it makes a new entry in the table and returns the
|
|
|
|
* value number of that entry.
|
|
|
|
*/
|
|
|
|
static bpf_u_int32
|
|
|
|
F(opt_state_t *opt_state, int code, bpf_u_int32 v0, bpf_u_int32 v1)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
u_int hash;
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 val;
|
1996-08-19 20:36:34 +00:00
|
|
|
struct valnode *p;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
hash = (u_int)code ^ (v0 << 4) ^ (v1 << 8);
|
1996-08-19 20:36:34 +00:00
|
|
|
hash %= MODULUS;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
for (p = opt_state->hashtbl[hash]; p; p = p->next)
|
1996-08-19 20:36:34 +00:00
|
|
|
if (p->code == code && p->v0 == v0 && p->v1 == v1)
|
|
|
|
return p->val;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Not found. Allocate a new value, and assign it a new
|
|
|
|
* value number.
|
|
|
|
*
|
|
|
|
* opt_state->curval starts out as 0, which means VAL_UNKNOWN; we
|
|
|
|
* increment it before using it as the new value number, which
|
|
|
|
* means we never assign VAL_UNKNOWN.
|
|
|
|
*
|
|
|
|
* XXX - unless we overflow, but we probably won't have 2^32-1
|
|
|
|
* values; we treat 32 bits as effectively infinite.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
val = ++opt_state->curval;
|
1996-08-19 20:36:34 +00:00
|
|
|
if (BPF_MODE(code) == BPF_IMM &&
|
|
|
|
(BPF_CLASS(code) == BPF_LD || BPF_CLASS(code) == BPF_LDX)) {
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->vmap[val].const_val = v0;
|
|
|
|
opt_state->vmap[val].is_const = 1;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
p = opt_state->next_vnode++;
|
1996-08-19 20:36:34 +00:00
|
|
|
p->val = val;
|
|
|
|
p->code = code;
|
|
|
|
p->v0 = v0;
|
|
|
|
p->v1 = v1;
|
2017-02-12 07:04:44 +00:00
|
|
|
p->next = opt_state->hashtbl[hash];
|
|
|
|
opt_state->hashtbl[hash] = p;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
2023-03-27 18:45:17 +00:00
|
|
|
vstore(struct stmt *s, bpf_u_int32 *valp, bpf_u_int32 newval, int alter)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2018-05-18 12:21:19 +00:00
|
|
|
if (alter && newval != VAL_UNKNOWN && *valp == newval)
|
1996-08-19 20:36:34 +00:00
|
|
|
s->code = NOP;
|
|
|
|
else
|
|
|
|
*valp = newval;
|
|
|
|
}
|
|
|
|
|
2015-01-06 18:58:31 +00:00
|
|
|
/*
|
|
|
|
* Do constant-folding on binary operators.
|
|
|
|
* (Unary operators are handled elsewhere.)
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
static void
|
2023-03-27 18:45:17 +00:00
|
|
|
fold_op(opt_state_t *opt_state, struct stmt *s, bpf_u_int32 v0, bpf_u_int32 v1)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2007-10-16 02:02:02 +00:00
|
|
|
bpf_u_int32 a, b;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
a = opt_state->vmap[v0].const_val;
|
|
|
|
b = opt_state->vmap[v1].const_val;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
switch (BPF_OP(s->code)) {
|
|
|
|
case BPF_ADD:
|
|
|
|
a += b;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_SUB:
|
|
|
|
a -= b;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_MUL:
|
|
|
|
a *= b;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_DIV:
|
|
|
|
if (b == 0)
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_error(opt_state, "division by zero");
|
1996-08-19 20:36:34 +00:00
|
|
|
a /= b;
|
|
|
|
break;
|
|
|
|
|
2015-01-06 18:58:31 +00:00
|
|
|
case BPF_MOD:
|
|
|
|
if (b == 0)
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_error(opt_state, "modulus by zero");
|
2015-01-06 18:58:31 +00:00
|
|
|
a %= b;
|
|
|
|
break;
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
case BPF_AND:
|
|
|
|
a &= b;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_OR:
|
|
|
|
a |= b;
|
|
|
|
break;
|
|
|
|
|
2015-01-06 18:58:31 +00:00
|
|
|
case BPF_XOR:
|
|
|
|
a ^= b;
|
|
|
|
break;
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
case BPF_LSH:
|
2019-10-06 04:26:37 +00:00
|
|
|
/*
|
|
|
|
* A left shift of more than the width of the type
|
|
|
|
* is undefined in C; we'll just treat it as shifting
|
|
|
|
* all the bits out.
|
|
|
|
*
|
|
|
|
* XXX - the BPF interpreter doesn't check for this,
|
|
|
|
* so its behavior is dependent on the behavior of
|
|
|
|
* the processor on which it's running. There are
|
|
|
|
* processors on which it shifts all the bits out
|
|
|
|
* and processors on which it does no shift.
|
|
|
|
*/
|
|
|
|
if (b < 32)
|
|
|
|
a <<= b;
|
|
|
|
else
|
|
|
|
a = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_RSH:
|
2019-10-06 04:26:37 +00:00
|
|
|
/*
|
|
|
|
* A right shift of more than the width of the type
|
|
|
|
* is undefined in C; we'll just treat it as shifting
|
|
|
|
* all the bits out.
|
|
|
|
*
|
|
|
|
* XXX - the BPF interpreter doesn't check for this,
|
|
|
|
* so its behavior is dependent on the behavior of
|
|
|
|
* the processor on which it's running. There are
|
|
|
|
* processors on which it shifts all the bits out
|
|
|
|
* and processors on which it does no shift.
|
|
|
|
*/
|
|
|
|
if (b < 32)
|
|
|
|
a >>= b;
|
|
|
|
else
|
|
|
|
a = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
s->k = a;
|
|
|
|
s->code = BPF_LD|BPF_IMM;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct slist *
|
2013-05-30 06:41:26 +00:00
|
|
|
this_op(struct slist *s)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
while (s != 0 && s->s.code == NOP)
|
|
|
|
s = s->next;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-05-30 06:41:26 +00:00
|
|
|
opt_not(struct block *b)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
struct block *tmp = JT(b);
|
|
|
|
|
|
|
|
JT(b) = JF(b);
|
|
|
|
JF(b) = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_peep(opt_state_t *opt_state, struct block *b)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
struct slist *s;
|
|
|
|
struct slist *next, *last;
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 val;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
s = b->stmts;
|
|
|
|
if (s == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
last = s;
|
2005-05-29 17:46:52 +00:00
|
|
|
for (/*empty*/; /*empty*/; s = next) {
|
|
|
|
/*
|
|
|
|
* Skip over nops.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
s = this_op(s);
|
|
|
|
if (s == 0)
|
2005-05-29 17:46:52 +00:00
|
|
|
break; /* nothing left in the block */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the next real instruction after that one
|
|
|
|
* (skipping nops).
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
next = this_op(s->next);
|
|
|
|
if (next == 0)
|
2005-05-29 17:46:52 +00:00
|
|
|
break; /* no next instruction */
|
1996-08-19 20:36:34 +00:00
|
|
|
last = next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* st M[k] --> st M[k]
|
|
|
|
* ldx M[k] tax
|
|
|
|
*/
|
|
|
|
if (s->s.code == BPF_ST &&
|
|
|
|
next->s.code == (BPF_LDX|BPF_MEM) &&
|
|
|
|
s->s.k == next->s.k) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
next->s.code = BPF_MISC|BPF_TAX;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* ld #k --> ldx #k
|
|
|
|
* tax txa
|
|
|
|
*/
|
|
|
|
if (s->s.code == (BPF_LD|BPF_IMM) &&
|
|
|
|
next->s.code == (BPF_MISC|BPF_TAX)) {
|
|
|
|
s->s.code = BPF_LDX|BPF_IMM;
|
|
|
|
next->s.code = BPF_MISC|BPF_TXA;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* This is an ugly special case, but it happens
|
|
|
|
* when you say tcp[k] or udp[k] where k is a constant.
|
|
|
|
*/
|
|
|
|
if (s->s.code == (BPF_LD|BPF_IMM)) {
|
|
|
|
struct slist *add, *tax, *ild;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that X isn't used on exit from this
|
|
|
|
* block (which the optimizer might cause).
|
|
|
|
* We know the code generator won't generate
|
|
|
|
* any local dependencies.
|
|
|
|
*/
|
|
|
|
if (ATOMELEM(b->out_use, X_ATOM))
|
2005-05-29 17:46:52 +00:00
|
|
|
continue;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2005-05-29 17:46:52 +00:00
|
|
|
/*
|
|
|
|
* Check that the instruction following the ldi
|
|
|
|
* is an addx, or it's an ldxms with an addx
|
|
|
|
* following it (with 0 or more nops between the
|
|
|
|
* ldxms and addx).
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (next->s.code != (BPF_LDX|BPF_MSH|BPF_B))
|
|
|
|
add = next;
|
|
|
|
else
|
|
|
|
add = this_op(next->next);
|
|
|
|
if (add == 0 || add->s.code != (BPF_ALU|BPF_ADD|BPF_X))
|
2005-05-29 17:46:52 +00:00
|
|
|
continue;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2005-05-29 17:46:52 +00:00
|
|
|
/*
|
|
|
|
* Check that a tax follows that (with 0 or more
|
|
|
|
* nops between them).
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
tax = this_op(add->next);
|
|
|
|
if (tax == 0 || tax->s.code != (BPF_MISC|BPF_TAX))
|
2005-05-29 17:46:52 +00:00
|
|
|
continue;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2005-05-29 17:46:52 +00:00
|
|
|
/*
|
|
|
|
* Check that an ild follows that (with 0 or more
|
|
|
|
* nops between them).
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
ild = this_op(tax->next);
|
|
|
|
if (ild == 0 || BPF_CLASS(ild->s.code) != BPF_LD ||
|
|
|
|
BPF_MODE(ild->s.code) != BPF_IND)
|
2005-05-29 17:46:52 +00:00
|
|
|
continue;
|
1996-08-19 20:36:34 +00:00
|
|
|
/*
|
|
|
|
* We want to turn this sequence:
|
|
|
|
*
|
|
|
|
* (004) ldi #0x2 {s}
|
|
|
|
* (005) ldxms [14] {next} -- optional
|
|
|
|
* (006) addx {add}
|
|
|
|
* (007) tax {tax}
|
|
|
|
* (008) ild [x+0] {ild}
|
|
|
|
*
|
|
|
|
* into this sequence:
|
|
|
|
*
|
|
|
|
* (004) nop
|
|
|
|
* (005) ldxms [14]
|
|
|
|
* (006) nop
|
|
|
|
* (007) nop
|
|
|
|
* (008) ild [x+2]
|
|
|
|
*
|
2005-05-29 17:46:52 +00:00
|
|
|
* XXX We need to check that X is not
|
|
|
|
* subsequently used, because we want to change
|
|
|
|
* what'll be in it after this sequence.
|
|
|
|
*
|
|
|
|
* We know we can eliminate the accumulator
|
|
|
|
* modifications earlier in the sequence since
|
|
|
|
* it is defined by the last stmt of this sequence
|
|
|
|
* (i.e., the last statement of the sequence loads
|
|
|
|
* a value into the accumulator, so we can eliminate
|
|
|
|
* earlier operations on the accumulator).
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
ild->s.k += s->s.k;
|
|
|
|
s->s.code = NOP;
|
|
|
|
add->s.code = NOP;
|
|
|
|
tax->s.code = NOP;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
2005-05-29 17:46:52 +00:00
|
|
|
* If the comparison at the end of a block is an equality
|
|
|
|
* comparison against a constant, and nobody uses the value
|
|
|
|
* we leave in the A register at the end of a block, and
|
|
|
|
* the operation preceding the comparison is an arithmetic
|
|
|
|
* operation, we can sometime optimize it away.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
2005-05-29 17:46:52 +00:00
|
|
|
if (b->s.code == (BPF_JMP|BPF_JEQ|BPF_K) &&
|
|
|
|
!ATOMELEM(b->out_use, A_ATOM)) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* We can optimize away certain subtractions of the
|
|
|
|
* X register.
|
|
|
|
*/
|
2005-05-29 17:46:52 +00:00
|
|
|
if (last->s.code == (BPF_ALU|BPF_SUB|BPF_X)) {
|
2004-03-31 09:07:39 +00:00
|
|
|
val = b->val[X_ATOM];
|
2017-02-12 07:04:44 +00:00
|
|
|
if (opt_state->vmap[val].is_const) {
|
2004-03-31 09:07:39 +00:00
|
|
|
/*
|
2005-05-29 17:46:52 +00:00
|
|
|
* If we have a subtract to do a comparison,
|
|
|
|
* and the X register is a known constant,
|
|
|
|
* we can merge this value into the
|
|
|
|
* comparison:
|
|
|
|
*
|
2004-03-31 09:07:39 +00:00
|
|
|
* sub x -> nop
|
|
|
|
* jeq #y jeq #(x+y)
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
b->s.k += opt_state->vmap[val].const_val;
|
2004-03-31 09:07:39 +00:00
|
|
|
last->s.code = NOP;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
2004-03-31 09:07:39 +00:00
|
|
|
} else if (b->s.k == 0) {
|
|
|
|
/*
|
2005-05-29 17:46:52 +00:00
|
|
|
* If the X register isn't a constant,
|
|
|
|
* and the comparison in the test is
|
|
|
|
* against 0, we can compare with the
|
|
|
|
* X register, instead:
|
|
|
|
*
|
|
|
|
* sub x -> nop
|
|
|
|
* jeq #0 jeq x
|
2004-03-31 09:07:39 +00:00
|
|
|
*/
|
|
|
|
last->s.code = NOP;
|
2005-05-29 17:46:52 +00:00
|
|
|
b->s.code = BPF_JMP|BPF_JEQ|BPF_X;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
2004-03-31 09:07:39 +00:00
|
|
|
/*
|
2005-05-29 17:46:52 +00:00
|
|
|
* Likewise, a constant subtract can be simplified:
|
|
|
|
*
|
|
|
|
* sub #x -> nop
|
|
|
|
* jeq #y -> jeq #(x+y)
|
2004-03-31 09:07:39 +00:00
|
|
|
*/
|
2005-05-29 17:46:52 +00:00
|
|
|
else if (last->s.code == (BPF_ALU|BPF_SUB|BPF_K)) {
|
2004-03-31 09:07:39 +00:00
|
|
|
last->s.code = NOP;
|
|
|
|
b->s.k += last->s.k;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
2005-05-29 17:46:52 +00:00
|
|
|
/*
|
|
|
|
* And, similarly, a constant AND can be simplified
|
|
|
|
* if we're testing against 0, i.e.:
|
|
|
|
*
|
|
|
|
* and #k nop
|
|
|
|
* jeq #0 -> jset #k
|
|
|
|
*/
|
|
|
|
else if (last->s.code == (BPF_ALU|BPF_AND|BPF_K) &&
|
|
|
|
b->s.k == 0) {
|
|
|
|
b->s.k = last->s.k;
|
|
|
|
b->s.code = BPF_JMP|BPF_K|BPF_JSET;
|
|
|
|
last->s.code = NOP;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
2005-05-29 17:46:52 +00:00
|
|
|
opt_not(b);
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
2004-03-31 09:07:39 +00:00
|
|
|
/*
|
|
|
|
* jset #0 -> never
|
|
|
|
* jset #ffffffff -> always
|
|
|
|
*/
|
|
|
|
if (b->s.code == (BPF_JMP|BPF_K|BPF_JSET)) {
|
|
|
|
if (b->s.k == 0)
|
|
|
|
JT(b) = JF(b);
|
2023-03-27 18:45:17 +00:00
|
|
|
if (b->s.k == 0xffffffffU)
|
2004-03-31 09:07:39 +00:00
|
|
|
JF(b) = JT(b);
|
|
|
|
}
|
2009-03-21 20:43:56 +00:00
|
|
|
/*
|
|
|
|
* If we're comparing against the index register, and the index
|
|
|
|
* register is a known constant, we can just compare against that
|
|
|
|
* constant.
|
|
|
|
*/
|
|
|
|
val = b->val[X_ATOM];
|
2017-02-12 07:04:44 +00:00
|
|
|
if (opt_state->vmap[val].is_const && BPF_SRC(b->s.code) == BPF_X) {
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 v = opt_state->vmap[val].const_val;
|
2009-03-21 20:43:56 +00:00
|
|
|
b->s.code &= ~BPF_X;
|
|
|
|
b->s.k = v;
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
/*
|
|
|
|
* If the accumulator is a known constant, we can compute the
|
|
|
|
* comparison result.
|
|
|
|
*/
|
|
|
|
val = b->val[A_ATOM];
|
2017-02-12 07:04:44 +00:00
|
|
|
if (opt_state->vmap[val].is_const && BPF_SRC(b->s.code) == BPF_K) {
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 v = opt_state->vmap[val].const_val;
|
1996-08-19 20:36:34 +00:00
|
|
|
switch (BPF_OP(b->s.code)) {
|
|
|
|
|
|
|
|
case BPF_JEQ:
|
|
|
|
v = v == b->s.k;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_JGT:
|
2023-03-27 18:45:17 +00:00
|
|
|
v = v > b->s.k;
|
1996-08-19 20:36:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_JGE:
|
2023-03-27 18:45:17 +00:00
|
|
|
v = v >= b->s.k;
|
1996-08-19 20:36:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_JSET:
|
|
|
|
v &= b->s.k;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
2023-03-27 18:45:17 +00:00
|
|
|
if (JF(b) != JT(b)) {
|
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
2023-03-27 18:45:17 +00:00
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
if (v)
|
|
|
|
JF(b) = JT(b);
|
|
|
|
else
|
|
|
|
JT(b) = JF(b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute the symbolic value of expression of 's', and update
|
|
|
|
* anything it defines in the value table 'val'. If 'alter' is true,
|
|
|
|
* do various optimizations. This code would be cleaner if symbolic
|
|
|
|
* evaluation and code transformations weren't folded together.
|
|
|
|
*/
|
|
|
|
static void
|
2023-03-27 18:45:17 +00:00
|
|
|
opt_stmt(opt_state_t *opt_state, struct stmt *s, bpf_u_int32 val[], int alter)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
int op;
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 v;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
switch (s->code) {
|
|
|
|
|
|
|
|
case BPF_LD|BPF_ABS|BPF_W:
|
|
|
|
case BPF_LD|BPF_ABS|BPF_H:
|
|
|
|
case BPF_LD|BPF_ABS|BPF_B:
|
2017-02-12 07:04:44 +00:00
|
|
|
v = F(opt_state, s->code, s->k, 0L);
|
1996-08-19 20:36:34 +00:00
|
|
|
vstore(s, &val[A_ATOM], v, alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_LD|BPF_IND|BPF_W:
|
|
|
|
case BPF_LD|BPF_IND|BPF_H:
|
|
|
|
case BPF_LD|BPF_IND|BPF_B:
|
|
|
|
v = val[X_ATOM];
|
2017-02-12 07:04:44 +00:00
|
|
|
if (alter && opt_state->vmap[v].is_const) {
|
1996-08-19 20:36:34 +00:00
|
|
|
s->code = BPF_LD|BPF_ABS|BPF_SIZE(s->code);
|
2017-02-12 07:04:44 +00:00
|
|
|
s->k += opt_state->vmap[v].const_val;
|
|
|
|
v = F(opt_state, s->code, s->k, 0L);
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
else
|
2017-02-12 07:04:44 +00:00
|
|
|
v = F(opt_state, s->code, s->k, v);
|
1996-08-19 20:36:34 +00:00
|
|
|
vstore(s, &val[A_ATOM], v, alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_LD|BPF_LEN:
|
2017-02-12 07:04:44 +00:00
|
|
|
v = F(opt_state, s->code, 0L, 0L);
|
1996-08-19 20:36:34 +00:00
|
|
|
vstore(s, &val[A_ATOM], v, alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_LD|BPF_IMM:
|
|
|
|
v = K(s->k);
|
|
|
|
vstore(s, &val[A_ATOM], v, alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_LDX|BPF_IMM:
|
|
|
|
v = K(s->k);
|
|
|
|
vstore(s, &val[X_ATOM], v, alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_LDX|BPF_MSH|BPF_B:
|
2017-02-12 07:04:44 +00:00
|
|
|
v = F(opt_state, s->code, s->k, 0L);
|
1996-08-19 20:36:34 +00:00
|
|
|
vstore(s, &val[X_ATOM], v, alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_ALU|BPF_NEG:
|
2017-02-12 07:04:44 +00:00
|
|
|
if (alter && opt_state->vmap[val[A_ATOM]].is_const) {
|
1996-08-19 20:36:34 +00:00
|
|
|
s->code = BPF_LD|BPF_IMM;
|
2019-10-06 04:26:37 +00:00
|
|
|
/*
|
|
|
|
* Do this negation as unsigned arithmetic; that's
|
|
|
|
* what modern BPF engines do, and it guarantees
|
|
|
|
* that all possible values can be negated. (Yeah,
|
|
|
|
* negating 0x80000000, the minimum signed 32-bit
|
|
|
|
* two's-complement value, results in 0x80000000,
|
|
|
|
* so it's still negative, but we *should* be doing
|
|
|
|
* all unsigned arithmetic here, to match what
|
|
|
|
* modern BPF engines do.)
|
|
|
|
*
|
|
|
|
* Express it as 0U - (unsigned value) so that we
|
|
|
|
* don't get compiler warnings about negating an
|
|
|
|
* unsigned value and don't get UBSan warnings
|
|
|
|
* about the result of negating 0x80000000 being
|
|
|
|
* undefined.
|
|
|
|
*/
|
2023-03-27 18:45:17 +00:00
|
|
|
s->k = 0U - opt_state->vmap[val[A_ATOM]].const_val;
|
1996-08-19 20:36:34 +00:00
|
|
|
val[A_ATOM] = K(s->k);
|
|
|
|
}
|
|
|
|
else
|
2017-02-12 07:04:44 +00:00
|
|
|
val[A_ATOM] = F(opt_state, s->code, val[A_ATOM], 0L);
|
1996-08-19 20:36:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_ALU|BPF_ADD|BPF_K:
|
|
|
|
case BPF_ALU|BPF_SUB|BPF_K:
|
|
|
|
case BPF_ALU|BPF_MUL|BPF_K:
|
|
|
|
case BPF_ALU|BPF_DIV|BPF_K:
|
2015-01-06 18:58:31 +00:00
|
|
|
case BPF_ALU|BPF_MOD|BPF_K:
|
1996-08-19 20:36:34 +00:00
|
|
|
case BPF_ALU|BPF_AND|BPF_K:
|
|
|
|
case BPF_ALU|BPF_OR|BPF_K:
|
2015-01-06 18:58:31 +00:00
|
|
|
case BPF_ALU|BPF_XOR|BPF_K:
|
1996-08-19 20:36:34 +00:00
|
|
|
case BPF_ALU|BPF_LSH|BPF_K:
|
|
|
|
case BPF_ALU|BPF_RSH|BPF_K:
|
|
|
|
op = BPF_OP(s->code);
|
|
|
|
if (alter) {
|
|
|
|
if (s->k == 0) {
|
2019-10-06 04:26:37 +00:00
|
|
|
/*
|
|
|
|
* Optimize operations where the constant
|
|
|
|
* is zero.
|
|
|
|
*
|
|
|
|
* Don't optimize away "sub #0"
|
2002-06-21 01:36:27 +00:00
|
|
|
* as it may be needed later to
|
2019-10-06 04:26:37 +00:00
|
|
|
* fixup the generated math code.
|
|
|
|
*
|
|
|
|
* Fail if we're dividing by zero or taking
|
|
|
|
* a modulus by zero.
|
|
|
|
*/
|
2002-06-21 01:36:27 +00:00
|
|
|
if (op == BPF_ADD ||
|
1996-08-19 20:36:34 +00:00
|
|
|
op == BPF_LSH || op == BPF_RSH ||
|
2015-01-06 18:58:31 +00:00
|
|
|
op == BPF_OR || op == BPF_XOR) {
|
1996-08-19 20:36:34 +00:00
|
|
|
s->code = NOP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (op == BPF_MUL || op == BPF_AND) {
|
|
|
|
s->code = BPF_LD|BPF_IMM;
|
|
|
|
val[A_ATOM] = K(s->k);
|
|
|
|
break;
|
|
|
|
}
|
2019-10-06 04:26:37 +00:00
|
|
|
if (op == BPF_DIV)
|
|
|
|
opt_error(opt_state,
|
|
|
|
"division by zero");
|
|
|
|
if (op == BPF_MOD)
|
|
|
|
opt_error(opt_state,
|
|
|
|
"modulus by zero");
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
if (opt_state->vmap[val[A_ATOM]].is_const) {
|
2019-10-06 04:26:37 +00:00
|
|
|
fold_op(opt_state, s, val[A_ATOM], K(s->k));
|
1996-08-19 20:36:34 +00:00
|
|
|
val[A_ATOM] = K(s->k);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
val[A_ATOM] = F(opt_state, s->code, val[A_ATOM], K(s->k));
|
1996-08-19 20:36:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_ALU|BPF_ADD|BPF_X:
|
|
|
|
case BPF_ALU|BPF_SUB|BPF_X:
|
|
|
|
case BPF_ALU|BPF_MUL|BPF_X:
|
|
|
|
case BPF_ALU|BPF_DIV|BPF_X:
|
2015-01-06 18:58:31 +00:00
|
|
|
case BPF_ALU|BPF_MOD|BPF_X:
|
1996-08-19 20:36:34 +00:00
|
|
|
case BPF_ALU|BPF_AND|BPF_X:
|
|
|
|
case BPF_ALU|BPF_OR|BPF_X:
|
2015-01-06 18:58:31 +00:00
|
|
|
case BPF_ALU|BPF_XOR|BPF_X:
|
1996-08-19 20:36:34 +00:00
|
|
|
case BPF_ALU|BPF_LSH|BPF_X:
|
|
|
|
case BPF_ALU|BPF_RSH|BPF_X:
|
|
|
|
op = BPF_OP(s->code);
|
2017-02-12 07:04:44 +00:00
|
|
|
if (alter && opt_state->vmap[val[X_ATOM]].is_const) {
|
|
|
|
if (opt_state->vmap[val[A_ATOM]].is_const) {
|
2019-10-06 04:26:37 +00:00
|
|
|
fold_op(opt_state, s, val[A_ATOM], val[X_ATOM]);
|
1996-08-19 20:36:34 +00:00
|
|
|
val[A_ATOM] = K(s->k);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
s->code = BPF_ALU|BPF_K|op;
|
2017-02-12 07:04:44 +00:00
|
|
|
s->k = opt_state->vmap[val[X_ATOM]].const_val;
|
2019-10-06 04:26:37 +00:00
|
|
|
if ((op == BPF_LSH || op == BPF_RSH) &&
|
2023-03-27 18:45:17 +00:00
|
|
|
s->k > 31)
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_error(opt_state,
|
|
|
|
"shift by more than 31 bits");
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
val[A_ATOM] =
|
2017-02-12 07:04:44 +00:00
|
|
|
F(opt_state, s->code, val[A_ATOM], K(s->k));
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Check if we're doing something to an accumulator
|
|
|
|
* that is 0, and simplify. This may not seem like
|
|
|
|
* much of a simplification but it could open up further
|
|
|
|
* optimizations.
|
2004-03-31 09:07:39 +00:00
|
|
|
* XXX We could also check for mul by 1, etc.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
if (alter && opt_state->vmap[val[A_ATOM]].is_const
|
|
|
|
&& opt_state->vmap[val[A_ATOM]].const_val == 0) {
|
2015-01-06 18:58:31 +00:00
|
|
|
if (op == BPF_ADD || op == BPF_OR || op == BPF_XOR) {
|
1996-08-19 20:36:34 +00:00
|
|
|
s->code = BPF_MISC|BPF_TXA;
|
|
|
|
vstore(s, &val[A_ATOM], val[X_ATOM], alter);
|
|
|
|
break;
|
|
|
|
}
|
2015-01-06 18:58:31 +00:00
|
|
|
else if (op == BPF_MUL || op == BPF_DIV || op == BPF_MOD ||
|
2004-03-31 09:07:39 +00:00
|
|
|
op == BPF_AND || op == BPF_LSH || op == BPF_RSH) {
|
1996-08-19 20:36:34 +00:00
|
|
|
s->code = BPF_LD|BPF_IMM;
|
|
|
|
s->k = 0;
|
|
|
|
vstore(s, &val[A_ATOM], K(s->k), alter);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (op == BPF_NEG) {
|
|
|
|
s->code = NOP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
val[A_ATOM] = F(opt_state, s->code, val[A_ATOM], val[X_ATOM]);
|
1996-08-19 20:36:34 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_MISC|BPF_TXA:
|
|
|
|
vstore(s, &val[A_ATOM], val[X_ATOM], alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_LD|BPF_MEM:
|
|
|
|
v = val[s->k];
|
2017-02-12 07:04:44 +00:00
|
|
|
if (alter && opt_state->vmap[v].is_const) {
|
1996-08-19 20:36:34 +00:00
|
|
|
s->code = BPF_LD|BPF_IMM;
|
2017-02-12 07:04:44 +00:00
|
|
|
s->k = opt_state->vmap[v].const_val;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
vstore(s, &val[A_ATOM], v, alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_MISC|BPF_TAX:
|
|
|
|
vstore(s, &val[X_ATOM], val[A_ATOM], alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_LDX|BPF_MEM:
|
|
|
|
v = val[s->k];
|
2017-02-12 07:04:44 +00:00
|
|
|
if (alter && opt_state->vmap[v].is_const) {
|
1996-08-19 20:36:34 +00:00
|
|
|
s->code = BPF_LDX|BPF_IMM;
|
2017-02-12 07:04:44 +00:00
|
|
|
s->k = opt_state->vmap[v].const_val;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
vstore(s, &val[X_ATOM], v, alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_ST:
|
|
|
|
vstore(s, &val[s->k], val[A_ATOM], alter);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BPF_STX:
|
|
|
|
vstore(s, &val[s->k], val[X_ATOM], alter);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
deadstmt(opt_state_t *opt_state, register struct stmt *s, register struct stmt *last[])
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
register int atom;
|
|
|
|
|
|
|
|
atom = atomuse(s);
|
|
|
|
if (atom >= 0) {
|
|
|
|
if (atom == AX_ATOM) {
|
|
|
|
last[X_ATOM] = 0;
|
|
|
|
last[A_ATOM] = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
last[atom] = 0;
|
|
|
|
}
|
|
|
|
atom = atomdef(s);
|
|
|
|
if (atom >= 0) {
|
|
|
|
if (last[atom]) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
last[atom]->code = NOP;
|
|
|
|
}
|
|
|
|
last[atom] = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_deadstores(opt_state_t *opt_state, register struct block *b)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
register struct slist *s;
|
|
|
|
register int atom;
|
|
|
|
struct stmt *last[N_ATOMS];
|
|
|
|
|
|
|
|
memset((char *)last, 0, sizeof last);
|
|
|
|
|
|
|
|
for (s = b->stmts; s != 0; s = s->next)
|
2017-02-12 07:04:44 +00:00
|
|
|
deadstmt(opt_state, &s->s, last);
|
|
|
|
deadstmt(opt_state, &b->s, last);
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
for (atom = 0; atom < N_ATOMS; ++atom)
|
|
|
|
if (last[atom] && !ATOMELEM(b->out_use, atom)) {
|
|
|
|
last[atom]->code = NOP;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_blk(opt_state_t *opt_state, struct block *b, int do_stmts)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
struct slist *s;
|
|
|
|
struct edge *p;
|
|
|
|
int i;
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 aval, xval;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2000-01-30 00:32:56 +00:00
|
|
|
#if 0
|
|
|
|
for (s = b->stmts; s && s->next; s = s->next)
|
|
|
|
if (BPF_CLASS(s->s.code) == BPF_JMP) {
|
|
|
|
do_stmts = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
/*
|
|
|
|
* Initialize the atom values.
|
|
|
|
*/
|
|
|
|
p = b->in_edges;
|
2005-05-29 17:46:52 +00:00
|
|
|
if (p == 0) {
|
|
|
|
/*
|
|
|
|
* We have no predecessors, so everything is undefined
|
|
|
|
* upon entry to this block.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
memset((char *)b->val, 0, sizeof(b->val));
|
2005-05-29 17:46:52 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Inherit values from our predecessors.
|
|
|
|
*
|
|
|
|
* First, get the values from the predecessor along the
|
|
|
|
* first edge leading to this node.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
memcpy((char *)b->val, (char *)p->pred->val, sizeof(b->val));
|
2005-05-29 17:46:52 +00:00
|
|
|
/*
|
|
|
|
* Now look at all the other nodes leading to this node.
|
|
|
|
* If, for the predecessor along that edge, a register
|
|
|
|
* has a different value from the one we have (i.e.,
|
|
|
|
* control paths are merging, and the merging paths
|
|
|
|
* assign different values to that register), give the
|
|
|
|
* register the undefined value of 0.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
while ((p = p->next) != NULL) {
|
|
|
|
for (i = 0; i < N_ATOMS; ++i)
|
|
|
|
if (b->val[i] != p->pred->val[i])
|
|
|
|
b->val[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
aval = b->val[A_ATOM];
|
2005-05-29 17:46:52 +00:00
|
|
|
xval = b->val[X_ATOM];
|
1996-08-19 20:36:34 +00:00
|
|
|
for (s = b->stmts; s; s = s->next)
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_stmt(opt_state, &s->s, b->val, do_stmts);
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a special case: if we don't use anything from this
|
2005-05-29 17:46:52 +00:00
|
|
|
* block, and we load the accumulator or index register with a
|
|
|
|
* value that is already there, or if this block is a return,
|
1996-08-19 20:36:34 +00:00
|
|
|
* eliminate all the statements.
|
2005-05-29 17:46:52 +00:00
|
|
|
*
|
2023-03-27 18:45:17 +00:00
|
|
|
* XXX - what if it does a store? Presumably that falls under
|
|
|
|
* the heading of "if we don't use anything from this block",
|
|
|
|
* i.e., if we use any memory location set to a different
|
|
|
|
* value by this block, then we use something from this block.
|
2005-05-29 17:46:52 +00:00
|
|
|
*
|
|
|
|
* XXX - why does it matter whether we use anything from this
|
|
|
|
* block? If the accumulator or index register doesn't change
|
|
|
|
* its value, isn't that OK even if we use that value?
|
|
|
|
*
|
|
|
|
* XXX - if we load the accumulator with a different value,
|
|
|
|
* and the block ends with a conditional branch, we obviously
|
|
|
|
* can't eliminate it, as the branch depends on that value.
|
|
|
|
* For the index register, the conditional branch only depends
|
|
|
|
* on the index register value if the test is against the index
|
|
|
|
* register value rather than a constant; if nothing uses the
|
|
|
|
* value we put into the index register, and we're not testing
|
|
|
|
* against the index register's value, and there aren't any
|
|
|
|
* other problems that would keep us from eliminating this
|
|
|
|
* block, can we eliminate it?
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
2004-03-31 09:07:39 +00:00
|
|
|
if (do_stmts &&
|
2018-05-18 12:21:19 +00:00
|
|
|
((b->out_use == 0 &&
|
|
|
|
aval != VAL_UNKNOWN && b->val[A_ATOM] == aval &&
|
|
|
|
xval != VAL_UNKNOWN && b->val[X_ATOM] == xval) ||
|
1996-08-19 20:36:34 +00:00
|
|
|
BPF_CLASS(b->s.code) == BPF_RET)) {
|
|
|
|
if (b->stmts != 0) {
|
|
|
|
b->stmts = 0;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
} else {
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_peep(opt_state, b);
|
|
|
|
opt_deadstores(opt_state, b);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Set up values for branch optimizer.
|
|
|
|
*/
|
|
|
|
if (BPF_SRC(b->s.code) == BPF_K)
|
|
|
|
b->oval = K(b->s.k);
|
|
|
|
else
|
|
|
|
b->oval = b->val[X_ATOM];
|
|
|
|
b->et.code = b->s.code;
|
|
|
|
b->ef.code = -b->s.code;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if any register that is used on exit from 'succ', has
|
|
|
|
* an exit value that is different from the corresponding exit value
|
|
|
|
* from 'b'.
|
|
|
|
*/
|
|
|
|
static int
|
2013-05-30 06:41:26 +00:00
|
|
|
use_conflict(struct block *b, struct block *succ)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
int atom;
|
|
|
|
atomset use = succ->out_use;
|
|
|
|
|
|
|
|
if (use == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (atom = 0; atom < N_ATOMS; ++atom)
|
|
|
|
if (ATOMELEM(use, atom))
|
|
|
|
if (b->val[atom] != succ->val[atom])
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Given a block that is the successor of an edge, and an edge that
|
|
|
|
* dominates that edge, return either a pointer to a child of that
|
|
|
|
* block (a block to which that block jumps) if that block is a
|
|
|
|
* candidate to replace the successor of the latter edge or NULL
|
|
|
|
* if neither of the children of the first block are candidates.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
static struct block *
|
2013-05-30 06:41:26 +00:00
|
|
|
fold_edge(struct block *child, struct edge *ep)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
int sense;
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 aval0, aval1, oval0, oval1;
|
1996-08-19 20:36:34 +00:00
|
|
|
int code = ep->code;
|
|
|
|
|
|
|
|
if (code < 0) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* This edge is a "branch if false" edge.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
code = -code;
|
|
|
|
sense = 0;
|
2023-03-27 18:45:17 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This edge is a "branch if true" edge.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
sense = 1;
|
2023-03-27 18:45:17 +00:00
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* If the opcode for the branch at the end of the block we
|
|
|
|
* were handed isn't the same as the opcode for the branch
|
|
|
|
* to which the edge we were handed corresponds, the tests
|
|
|
|
* for those branches aren't testing the same conditions,
|
|
|
|
* so the blocks to which the first block branches aren't
|
|
|
|
* candidates to replace the successor of the edge.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (child->s.code != code)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
aval0 = child->val[A_ATOM];
|
|
|
|
oval0 = child->oval;
|
|
|
|
aval1 = ep->pred->val[A_ATOM];
|
|
|
|
oval1 = ep->pred->oval;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* If the A register value on exit from the successor block
|
|
|
|
* isn't the same as the A register value on exit from the
|
|
|
|
* predecessor of the edge, the blocks to which the first
|
|
|
|
* block branches aren't candidates to replace the successor
|
|
|
|
* of the edge.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (aval0 != aval1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (oval0 == oval1)
|
|
|
|
/*
|
2005-05-29 17:46:52 +00:00
|
|
|
* The operands of the branch instructions are
|
2023-03-27 18:45:17 +00:00
|
|
|
* identical, so the branches are testing the
|
|
|
|
* same condition, and the result is true if a true
|
2005-05-29 17:46:52 +00:00
|
|
|
* branch was taken to get here, otherwise false.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
return sense ? JT(child) : JF(child);
|
|
|
|
|
|
|
|
if (sense && code == (BPF_JMP|BPF_JEQ|BPF_K))
|
|
|
|
/*
|
|
|
|
* At this point, we only know the comparison if we
|
|
|
|
* came down the true branch, and it was an equality
|
2005-05-29 17:46:52 +00:00
|
|
|
* comparison with a constant.
|
|
|
|
*
|
|
|
|
* I.e., if we came down the true branch, and the branch
|
|
|
|
* was an equality comparison with a constant, we know the
|
|
|
|
* accumulator contains that constant. If we came down
|
|
|
|
* the false branch, or the comparison wasn't with a
|
|
|
|
* constant, we don't know what was in the accumulator.
|
|
|
|
*
|
|
|
|
* We rely on the fact that distinct constants have distinct
|
|
|
|
* value numbers.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
return JF(child);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* If we can make this edge go directly to a child of the edge's current
|
|
|
|
* successor, do so.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_j(opt_state_t *opt_state, struct edge *ep)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2023-03-27 18:45:17 +00:00
|
|
|
register u_int i, k;
|
1996-08-19 20:36:34 +00:00
|
|
|
register struct block *target;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Does this edge go to a block where, if the test
|
|
|
|
* at the end of it succeeds, it goes to a block
|
|
|
|
* that's a leaf node of the DAG, i.e. a return
|
|
|
|
* statement?
|
|
|
|
* If so, there's nothing to optimize.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (JT(ep->succ) == 0)
|
|
|
|
return;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Does this edge go to a block that goes, in turn, to
|
|
|
|
* the same block regardless of whether the test at the
|
|
|
|
* end succeeds or fails?
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (JT(ep->succ) == JF(ep->succ)) {
|
|
|
|
/*
|
|
|
|
* Common branch targets can be eliminated, provided
|
|
|
|
* there is no data dependency.
|
2023-03-27 18:45:17 +00:00
|
|
|
*
|
|
|
|
* Check whether any register used on exit from the
|
|
|
|
* block to which the successor of this edge goes
|
|
|
|
* has a value at that point that's different from
|
|
|
|
* the value it has on exit from the predecessor of
|
|
|
|
* this edge. If not, the predecessor of this edge
|
|
|
|
* can just go to the block to which the successor
|
|
|
|
* of this edge goes, bypassing the successor of this
|
|
|
|
* edge, as the successor of this edge isn't doing
|
|
|
|
* any calculations whose results are different
|
|
|
|
* from what the blocks before it did and isn't
|
|
|
|
* doing any tests the results of which matter.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
2023-03-27 18:45:17 +00:00
|
|
|
if (!use_conflict(ep->pred, JT(ep->succ))) {
|
|
|
|
/*
|
|
|
|
* No, there isn't.
|
|
|
|
* Make this edge go to the block to
|
|
|
|
* which the successor of that edge
|
|
|
|
* goes.
|
|
|
|
*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
ep->succ = JT(ep->succ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* For each edge dominator that matches the successor of this
|
|
|
|
* edge, promote the edge successor to the its grandchild.
|
|
|
|
*
|
|
|
|
* XXX We violate the set abstraction here in favor a reasonably
|
|
|
|
* efficient loop.
|
|
|
|
*/
|
|
|
|
top:
|
2017-02-12 07:04:44 +00:00
|
|
|
for (i = 0; i < opt_state->edgewords; ++i) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/* i'th word in the bitset of dominators */
|
1996-08-19 20:36:34 +00:00
|
|
|
register bpf_u_int32 x = ep->edom[i];
|
|
|
|
|
|
|
|
while (x != 0) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/* Find the next dominator in that word and mark it as found */
|
2018-05-18 12:21:19 +00:00
|
|
|
k = lowest_set_bit(x);
|
2019-10-06 04:26:37 +00:00
|
|
|
x &=~ ((bpf_u_int32)1 << k);
|
1996-08-19 20:36:34 +00:00
|
|
|
k += i * BITS_PER_WORD;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
target = fold_edge(ep->succ, opt_state->edges[k]);
|
1996-08-19 20:36:34 +00:00
|
|
|
/*
|
2023-03-27 18:45:17 +00:00
|
|
|
* We have a candidate to replace the successor
|
|
|
|
* of ep.
|
|
|
|
*
|
1996-08-19 20:36:34 +00:00
|
|
|
* Check that there is no data dependency between
|
2023-03-27 18:45:17 +00:00
|
|
|
* nodes that will be violated if we move the edge;
|
|
|
|
* i.e., if any register used on exit from the
|
|
|
|
* candidate has a value at that point different
|
|
|
|
* from the value it has when we exit the
|
|
|
|
* predecessor of that edge, there's a data
|
|
|
|
* dependency that will be violated.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
if (target != 0 && !use_conflict(ep->pred, target)) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* It's safe to replace the successor of
|
|
|
|
* ep; do so, and note that we've made
|
|
|
|
* at least one change.
|
|
|
|
*
|
|
|
|
* XXX - this is one of the operations that
|
|
|
|
* happens when the optimizer gets into
|
|
|
|
* one of those infinite loops.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
ep->succ = target;
|
|
|
|
if (JT(target) != 0)
|
|
|
|
/*
|
|
|
|
* Start over unless we hit a leaf.
|
|
|
|
*/
|
|
|
|
goto top;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - is this, and and_pullup(), what's described in section 6.1.2
|
|
|
|
* "Predicate Assertion Propagation" in the BPF+ paper?
|
|
|
|
*
|
|
|
|
* Note that this looks at block dominators, not edge dominators.
|
|
|
|
* Don't think so.
|
|
|
|
*
|
|
|
|
* "A or B" compiles into
|
|
|
|
*
|
|
|
|
* A
|
|
|
|
* t / \ f
|
|
|
|
* / B
|
|
|
|
* / t / \ f
|
|
|
|
* \ /
|
|
|
|
* \ /
|
|
|
|
* X
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
or_pullup(opt_state_t *opt_state, struct block *b)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 val;
|
|
|
|
int at_top;
|
1996-08-19 20:36:34 +00:00
|
|
|
struct block *pull;
|
|
|
|
struct block **diffp, **samep;
|
|
|
|
struct edge *ep;
|
|
|
|
|
|
|
|
ep = b->in_edges;
|
|
|
|
if (ep == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure each predecessor loads the same value.
|
|
|
|
* XXX why?
|
|
|
|
*/
|
|
|
|
val = ep->pred->val[A_ATOM];
|
|
|
|
for (ep = ep->next; ep != 0; ep = ep->next)
|
|
|
|
if (val != ep->pred->val[A_ATOM])
|
|
|
|
return;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* For the first edge in the list of edges coming into this block,
|
|
|
|
* see whether the predecessor of that edge comes here via a true
|
|
|
|
* branch or a false branch.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (JT(b->in_edges->pred) == b)
|
2023-03-27 18:45:17 +00:00
|
|
|
diffp = &JT(b->in_edges->pred); /* jt */
|
1996-08-19 20:36:34 +00:00
|
|
|
else
|
2023-03-27 18:45:17 +00:00
|
|
|
diffp = &JF(b->in_edges->pred); /* jf */
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* diffp is a pointer to a pointer to the block.
|
|
|
|
*
|
|
|
|
* Go down the false chain looking as far as you can,
|
|
|
|
* making sure that each jump-compare is doing the
|
|
|
|
* same as the original block.
|
|
|
|
*
|
|
|
|
* If you reach the bottom before you reach a
|
|
|
|
* different jump-compare, just exit. There's nothing
|
|
|
|
* to do here. XXX - no, this version is checking for
|
|
|
|
* the value leaving the block; that's from the BPF+
|
|
|
|
* pullup routine.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
at_top = 1;
|
2018-05-18 12:21:19 +00:00
|
|
|
for (;;) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Done if that's not going anywhere XXX
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (*diffp == 0)
|
|
|
|
return;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Done if that predecessor blah blah blah isn't
|
|
|
|
* going the same place we're going XXX
|
|
|
|
*
|
|
|
|
* Does the true edge of this block point to the same
|
|
|
|
* location as the true edge of b?
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (JT(*diffp) != JT(b))
|
|
|
|
return;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Done if this node isn't a dominator of that
|
|
|
|
* node blah blah blah XXX
|
|
|
|
*
|
|
|
|
* Does b dominate diffp?
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (!SET_MEMBER((*diffp)->dom, b->id))
|
|
|
|
return;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Break out of the loop if that node's value of A
|
|
|
|
* isn't the value of A above XXX
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if ((*diffp)->val[A_ATOM] != val)
|
|
|
|
break;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Get the JF for that node XXX
|
|
|
|
* Go down the false path.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
diffp = &JF(*diffp);
|
|
|
|
at_top = 0;
|
|
|
|
}
|
2023-03-27 18:45:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that we've found a different jump-compare in a chain
|
|
|
|
* below b, search further down until we find another
|
|
|
|
* jump-compare that looks at the original value. This
|
|
|
|
* jump-compare should get pulled up. XXX again we're
|
|
|
|
* comparing values not jump-compares.
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
samep = &JF(*diffp);
|
2018-05-18 12:21:19 +00:00
|
|
|
for (;;) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Done if that's not going anywhere XXX
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (*samep == 0)
|
|
|
|
return;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Done if that predecessor blah blah blah isn't
|
|
|
|
* going the same place we're going XXX
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (JT(*samep) != JT(b))
|
|
|
|
return;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Done if this node isn't a dominator of that
|
|
|
|
* node blah blah blah XXX
|
|
|
|
*
|
|
|
|
* Does b dominate samep?
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if (!SET_MEMBER((*samep)->dom, b->id))
|
|
|
|
return;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Break out of the loop if that node's value of A
|
|
|
|
* is the value of A above XXX
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
if ((*samep)->val[A_ATOM] == val)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* XXX Need to check that there are no data dependencies
|
|
|
|
between dp0 and dp1. Currently, the code generator
|
|
|
|
will not produce such dependencies. */
|
|
|
|
samep = &JF(*samep);
|
|
|
|
}
|
|
|
|
#ifdef notdef
|
|
|
|
/* XXX This doesn't cover everything. */
|
|
|
|
for (i = 0; i < N_ATOMS; ++i)
|
|
|
|
if ((*samep)->val[i] != pred->val[i])
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
/* Pull up the node. */
|
|
|
|
pull = *samep;
|
|
|
|
*samep = JF(pull);
|
|
|
|
JF(pull) = *diffp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At the top of the chain, each predecessor needs to point at the
|
|
|
|
* pulled up node. Inside the chain, there is only one predecessor
|
|
|
|
* to worry about.
|
|
|
|
*/
|
|
|
|
if (at_top) {
|
|
|
|
for (ep = b->in_edges; ep != 0; ep = ep->next) {
|
|
|
|
if (JT(ep->pred) == b)
|
|
|
|
JT(ep->pred) = pull;
|
|
|
|
else
|
|
|
|
JF(ep->pred) = pull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*diffp = pull;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - this is one of the operations that happens when the
|
|
|
|
* optimizer gets into one of those infinite loops.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
and_pullup(opt_state_t *opt_state, struct block *b)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2023-03-27 18:45:17 +00:00
|
|
|
bpf_u_int32 val;
|
|
|
|
int at_top;
|
1996-08-19 20:36:34 +00:00
|
|
|
struct block *pull;
|
|
|
|
struct block **diffp, **samep;
|
|
|
|
struct edge *ep;
|
|
|
|
|
|
|
|
ep = b->in_edges;
|
|
|
|
if (ep == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure each predecessor loads the same value.
|
|
|
|
*/
|
|
|
|
val = ep->pred->val[A_ATOM];
|
|
|
|
for (ep = ep->next; ep != 0; ep = ep->next)
|
|
|
|
if (val != ep->pred->val[A_ATOM])
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (JT(b->in_edges->pred) == b)
|
|
|
|
diffp = &JT(b->in_edges->pred);
|
|
|
|
else
|
|
|
|
diffp = &JF(b->in_edges->pred);
|
|
|
|
|
|
|
|
at_top = 1;
|
2018-05-18 12:21:19 +00:00
|
|
|
for (;;) {
|
1996-08-19 20:36:34 +00:00
|
|
|
if (*diffp == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (JF(*diffp) != JF(b))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!SET_MEMBER((*diffp)->dom, b->id))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((*diffp)->val[A_ATOM] != val)
|
|
|
|
break;
|
|
|
|
|
|
|
|
diffp = &JT(*diffp);
|
|
|
|
at_top = 0;
|
|
|
|
}
|
|
|
|
samep = &JT(*diffp);
|
2018-05-18 12:21:19 +00:00
|
|
|
for (;;) {
|
1996-08-19 20:36:34 +00:00
|
|
|
if (*samep == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (JF(*samep) != JF(b))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!SET_MEMBER((*samep)->dom, b->id))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((*samep)->val[A_ATOM] == val)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* XXX Need to check that there are no data dependencies
|
|
|
|
between diffp and samep. Currently, the code generator
|
|
|
|
will not produce such dependencies. */
|
|
|
|
samep = &JT(*samep);
|
|
|
|
}
|
|
|
|
#ifdef notdef
|
|
|
|
/* XXX This doesn't cover everything. */
|
|
|
|
for (i = 0; i < N_ATOMS; ++i)
|
|
|
|
if ((*samep)->val[i] != pred->val[i])
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
/* Pull up the node. */
|
|
|
|
pull = *samep;
|
|
|
|
*samep = JT(pull);
|
|
|
|
JT(pull) = *diffp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At the top of the chain, each predecessor needs to point at the
|
|
|
|
* pulled up node. Inside the chain, there is only one predecessor
|
|
|
|
* to worry about.
|
|
|
|
*/
|
|
|
|
if (at_top) {
|
|
|
|
for (ep = b->in_edges; ep != 0; ep = ep->next) {
|
|
|
|
if (JT(ep->pred) == b)
|
|
|
|
JT(ep->pred) = pull;
|
|
|
|
else
|
|
|
|
JF(ep->pred) = pull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*diffp = pull;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - this is one of the operations that happens when the
|
|
|
|
* optimizer gets into one of those infinite loops.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_blks(opt_state_t *opt_state, struct icode *ic, int do_stmts)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
int i, maxlevel;
|
|
|
|
struct block *p;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
init_val(opt_state);
|
|
|
|
maxlevel = ic->root->level;
|
2001-04-03 04:18:09 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
find_inedges(opt_state, ic->root);
|
1996-08-19 20:36:34 +00:00
|
|
|
for (i = maxlevel; i >= 0; --i)
|
2017-02-12 07:04:44 +00:00
|
|
|
for (p = opt_state->levels[i]; p; p = p->link)
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_blk(opt_state, p, do_stmts);
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
if (do_stmts)
|
|
|
|
/*
|
|
|
|
* No point trying to move branches; it can't possibly
|
|
|
|
* make a difference at this point.
|
2023-03-27 18:45:17 +00:00
|
|
|
*
|
|
|
|
* XXX - this might be after we detect a loop where
|
|
|
|
* we were just looping infinitely moving branches
|
|
|
|
* in such a fashion that we went through two or more
|
|
|
|
* versions of the machine code, eventually returning
|
|
|
|
* to the first version. (We're really not doing a
|
|
|
|
* full loop detection, we're just testing for two
|
|
|
|
* passes in a row where we do nothing but
|
|
|
|
* move branches.)
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
return;
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* Is this what the BPF+ paper describes in sections 6.1.1,
|
|
|
|
* 6.1.2, and 6.1.3?
|
|
|
|
*/
|
1996-08-19 20:36:34 +00:00
|
|
|
for (i = 1; i <= maxlevel; ++i) {
|
2017-02-12 07:04:44 +00:00
|
|
|
for (p = opt_state->levels[i]; p; p = p->link) {
|
|
|
|
opt_j(opt_state, &p->et);
|
|
|
|
opt_j(opt_state, &p->ef);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
2001-04-03 04:18:09 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
find_inedges(opt_state, ic->root);
|
1996-08-19 20:36:34 +00:00
|
|
|
for (i = 1; i <= maxlevel; ++i) {
|
2017-02-12 07:04:44 +00:00
|
|
|
for (p = opt_state->levels[i]; p; p = p->link) {
|
|
|
|
or_pullup(opt_state, p);
|
|
|
|
and_pullup(opt_state, p);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
2013-05-30 06:41:26 +00:00
|
|
|
link_inedge(struct edge *parent, struct block *child)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
parent->next = child->in_edges;
|
|
|
|
child->in_edges = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
find_inedges(opt_state_t *opt_state, struct block *root)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2023-03-27 18:45:17 +00:00
|
|
|
u_int i;
|
|
|
|
int level;
|
1996-08-19 20:36:34 +00:00
|
|
|
struct block *b;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
for (i = 0; i < opt_state->n_blocks; ++i)
|
|
|
|
opt_state->blocks[i]->in_edges = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Traverse the graph, adding each edge to the predecessor
|
|
|
|
* list of its successors. Skip the leaves (i.e. level 0).
|
|
|
|
*/
|
2023-03-27 18:45:17 +00:00
|
|
|
for (level = root->level; level > 0; --level) {
|
|
|
|
for (b = opt_state->levels[level]; b != 0; b = b->link) {
|
1996-08-19 20:36:34 +00:00
|
|
|
link_inedge(&b->et, JT(b));
|
|
|
|
link_inedge(&b->ef, JF(b));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-05-30 06:41:26 +00:00
|
|
|
opt_root(struct block **b)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
struct slist *tmp, *s;
|
|
|
|
|
|
|
|
s = (*b)->stmts;
|
|
|
|
(*b)->stmts = 0;
|
|
|
|
while (BPF_CLASS((*b)->s.code) == BPF_JMP && JT(*b) == JF(*b))
|
|
|
|
*b = JT(*b);
|
|
|
|
|
|
|
|
tmp = (*b)->stmts;
|
|
|
|
if (tmp != 0)
|
|
|
|
sappend(s, tmp);
|
|
|
|
(*b)->stmts = s;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the root node is a return, then there is no
|
|
|
|
* point executing any statements (since the bpf machine
|
|
|
|
* has no side effects).
|
|
|
|
*/
|
|
|
|
if (BPF_CLASS((*b)->s.code) == BPF_RET)
|
|
|
|
(*b)->stmts = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_loop(opt_state_t *opt_state, struct icode *ic, int do_stmts)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
#ifdef BDEBUG
|
2018-05-18 12:21:19 +00:00
|
|
|
if (pcap_optimizer_debug > 1 || pcap_print_dot_graph) {
|
2002-06-21 01:36:27 +00:00
|
|
|
printf("opt_loop(root, %d) begin\n", do_stmts);
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_dump(opt_state, ic);
|
2002-06-21 01:36:27 +00:00
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
#endif
|
2023-03-27 18:45:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
int loop_count = 0;
|
|
|
|
for (;;) {
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->done = 1;
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* XXX - optimizer loop detection.
|
|
|
|
*/
|
|
|
|
opt_state->non_branch_movement_performed = 0;
|
2017-02-12 07:04:44 +00:00
|
|
|
find_levels(opt_state, ic);
|
|
|
|
find_dom(opt_state, ic->root);
|
|
|
|
find_closure(opt_state, ic->root);
|
|
|
|
find_ud(opt_state, ic->root);
|
|
|
|
find_edom(opt_state, ic->root);
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_blks(opt_state, ic, do_stmts);
|
1996-08-19 20:36:34 +00:00
|
|
|
#ifdef BDEBUG
|
2018-05-18 12:21:19 +00:00
|
|
|
if (pcap_optimizer_debug > 1 || pcap_print_dot_graph) {
|
2017-02-12 07:04:44 +00:00
|
|
|
printf("opt_loop(root, %d) bottom, done=%d\n", do_stmts, opt_state->done);
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_dump(opt_state, ic);
|
2002-06-21 01:36:27 +00:00
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
#endif
|
2023-03-27 18:45:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Was anything done in this optimizer pass?
|
|
|
|
*/
|
|
|
|
if (opt_state->done) {
|
|
|
|
/*
|
|
|
|
* No, so we've reached a fixed point.
|
|
|
|
* We're done.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX - was anything done other than branch movement
|
|
|
|
* in this pass?
|
|
|
|
*/
|
|
|
|
if (opt_state->non_branch_movement_performed) {
|
|
|
|
/*
|
|
|
|
* Yes. Clear any loop-detection counter;
|
|
|
|
* we're making some form of progress (assuming
|
|
|
|
* we can't get into a cycle doing *other*
|
|
|
|
* optimizations...).
|
|
|
|
*/
|
|
|
|
loop_count = 0;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* No - increment the counter, and quit if
|
|
|
|
* it's up to 100.
|
|
|
|
*/
|
|
|
|
loop_count++;
|
|
|
|
if (loop_count >= 100) {
|
|
|
|
/*
|
|
|
|
* We've done nothing but branch movement
|
|
|
|
* for 100 passes; we're probably
|
|
|
|
* in a cycle and will never reach a
|
|
|
|
* fixed point.
|
|
|
|
*
|
|
|
|
* XXX - yes, we really need a non-
|
|
|
|
* heuristic way of detecting a cycle.
|
|
|
|
*/
|
|
|
|
opt_state->done = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Optimize the filter code in its dag representation.
|
2019-10-06 04:26:37 +00:00
|
|
|
* Return 0 on success, -1 on error.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
2019-10-06 04:26:37 +00:00
|
|
|
int
|
|
|
|
bpf_optimize(struct icode *ic, char *errbuf)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state_t opt_state;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2019-10-06 04:26:37 +00:00
|
|
|
memset(&opt_state, 0, sizeof(opt_state));
|
|
|
|
opt_state.errbuf = errbuf;
|
2023-03-27 18:45:17 +00:00
|
|
|
opt_state.non_branch_movement_performed = 0;
|
2019-10-06 04:26:37 +00:00
|
|
|
if (setjmp(opt_state.top_ctx)) {
|
|
|
|
opt_cleanup(&opt_state);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
opt_init(&opt_state, ic);
|
|
|
|
opt_loop(&opt_state, ic, 0);
|
|
|
|
opt_loop(&opt_state, ic, 1);
|
2017-02-12 07:04:44 +00:00
|
|
|
intern_blocks(&opt_state, ic);
|
2002-06-21 01:36:27 +00:00
|
|
|
#ifdef BDEBUG
|
2018-05-18 12:21:19 +00:00
|
|
|
if (pcap_optimizer_debug > 1 || pcap_print_dot_graph) {
|
2002-06-21 01:36:27 +00:00
|
|
|
printf("after intern_blocks()\n");
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_dump(&opt_state, ic);
|
2002-06-21 01:36:27 +00:00
|
|
|
}
|
|
|
|
#endif
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_root(&ic->root);
|
2002-06-21 01:36:27 +00:00
|
|
|
#ifdef BDEBUG
|
2018-05-18 12:21:19 +00:00
|
|
|
if (pcap_optimizer_debug > 1 || pcap_print_dot_graph) {
|
2002-06-21 01:36:27 +00:00
|
|
|
printf("after opt_root()\n");
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_dump(&opt_state, ic);
|
2002-06-21 01:36:27 +00:00
|
|
|
}
|
|
|
|
#endif
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_cleanup(&opt_state);
|
2019-10-06 04:26:37 +00:00
|
|
|
return 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
make_marks(struct icode *ic, struct block *p)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2017-02-12 07:04:44 +00:00
|
|
|
if (!isMarked(ic, p)) {
|
|
|
|
Mark(ic, p);
|
1996-08-19 20:36:34 +00:00
|
|
|
if (BPF_CLASS(p->s.code) != BPF_RET) {
|
2017-02-12 07:04:44 +00:00
|
|
|
make_marks(ic, JT(p));
|
|
|
|
make_marks(ic, JF(p));
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-02-12 07:04:44 +00:00
|
|
|
* Mark code array such that isMarked(ic->cur_mark, i) is true
|
1996-08-19 20:36:34 +00:00
|
|
|
* only for nodes that are alive.
|
|
|
|
*/
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
mark_code(struct icode *ic)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2017-02-12 07:04:44 +00:00
|
|
|
ic->cur_mark += 1;
|
|
|
|
make_marks(ic, ic->root);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* True iff the two stmt lists load the same value from the packet into
|
|
|
|
* the accumulator.
|
|
|
|
*/
|
|
|
|
static int
|
2013-05-30 06:41:26 +00:00
|
|
|
eq_slist(struct slist *x, struct slist *y)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2018-05-18 12:21:19 +00:00
|
|
|
for (;;) {
|
1996-08-19 20:36:34 +00:00
|
|
|
while (x && x->s.code == NOP)
|
|
|
|
x = x->next;
|
|
|
|
while (y && y->s.code == NOP)
|
|
|
|
y = y->next;
|
|
|
|
if (x == 0)
|
|
|
|
return y == 0;
|
|
|
|
if (y == 0)
|
|
|
|
return x == 0;
|
|
|
|
if (x->s.code != y->s.code || x->s.k != y->s.k)
|
|
|
|
return 0;
|
|
|
|
x = x->next;
|
|
|
|
y = y->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
2013-05-30 06:41:26 +00:00
|
|
|
eq_blk(struct block *b0, struct block *b1)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
if (b0->s.code == b1->s.code &&
|
|
|
|
b0->s.k == b1->s.k &&
|
|
|
|
b0->et.succ == b1->et.succ &&
|
|
|
|
b0->ef.succ == b1->ef.succ)
|
|
|
|
return eq_slist(b0->stmts, b1->stmts);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
intern_blocks(opt_state_t *opt_state, struct icode *ic)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
struct block *p;
|
2023-03-27 18:45:17 +00:00
|
|
|
u_int i, j;
|
2007-10-16 02:02:02 +00:00
|
|
|
int done1; /* don't shadow global */
|
1996-08-19 20:36:34 +00:00
|
|
|
top:
|
2007-10-16 02:02:02 +00:00
|
|
|
done1 = 1;
|
2017-02-12 07:04:44 +00:00
|
|
|
for (i = 0; i < opt_state->n_blocks; ++i)
|
|
|
|
opt_state->blocks[i]->link = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
mark_code(ic);
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
for (i = opt_state->n_blocks - 1; i != 0; ) {
|
|
|
|
--i;
|
2017-02-12 07:04:44 +00:00
|
|
|
if (!isMarked(ic, opt_state->blocks[i]))
|
1996-08-19 20:36:34 +00:00
|
|
|
continue;
|
2017-02-12 07:04:44 +00:00
|
|
|
for (j = i + 1; j < opt_state->n_blocks; ++j) {
|
|
|
|
if (!isMarked(ic, opt_state->blocks[j]))
|
1996-08-19 20:36:34 +00:00
|
|
|
continue;
|
2017-02-12 07:04:44 +00:00
|
|
|
if (eq_blk(opt_state->blocks[i], opt_state->blocks[j])) {
|
|
|
|
opt_state->blocks[i]->link = opt_state->blocks[j]->link ?
|
|
|
|
opt_state->blocks[j]->link : opt_state->blocks[j];
|
1996-08-19 20:36:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
for (i = 0; i < opt_state->n_blocks; ++i) {
|
|
|
|
p = opt_state->blocks[i];
|
1996-08-19 20:36:34 +00:00
|
|
|
if (JT(p) == 0)
|
|
|
|
continue;
|
|
|
|
if (JT(p)->link) {
|
2007-10-16 02:02:02 +00:00
|
|
|
done1 = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
JT(p) = JT(p)->link;
|
|
|
|
}
|
|
|
|
if (JF(p)->link) {
|
2007-10-16 02:02:02 +00:00
|
|
|
done1 = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
JF(p) = JF(p)->link;
|
|
|
|
}
|
|
|
|
}
|
2007-10-16 02:02:02 +00:00
|
|
|
if (!done1)
|
1996-08-19 20:36:34 +00:00
|
|
|
goto top;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_cleanup(opt_state_t *opt_state)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2017-02-12 07:04:44 +00:00
|
|
|
free((void *)opt_state->vnode_base);
|
|
|
|
free((void *)opt_state->vmap);
|
|
|
|
free((void *)opt_state->edges);
|
|
|
|
free((void *)opt_state->space);
|
|
|
|
free((void *)opt_state->levels);
|
|
|
|
free((void *)opt_state->blocks);
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
2019-10-06 04:26:37 +00:00
|
|
|
/*
|
|
|
|
* For optimizer errors.
|
|
|
|
*/
|
|
|
|
static void PCAP_NORETURN
|
|
|
|
opt_error(opt_state_t *opt_state, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
if (opt_state->errbuf != NULL) {
|
|
|
|
va_start(ap, fmt);
|
2023-03-27 18:45:17 +00:00
|
|
|
(void)vsnprintf(opt_state->errbuf,
|
2019-10-06 04:26:37 +00:00
|
|
|
PCAP_ERRBUF_SIZE, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
longjmp(opt_state->top_ctx, 1);
|
|
|
|
/* NOTREACHED */
|
2023-03-27 18:45:17 +00:00
|
|
|
#ifdef _AIX
|
|
|
|
PCAP_UNREACHABLE
|
|
|
|
#endif /* _AIX */
|
2019-10-06 04:26:37 +00:00
|
|
|
}
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
/*
|
|
|
|
* Return the number of stmts in 's'.
|
|
|
|
*/
|
2012-10-04 21:07:56 +00:00
|
|
|
static u_int
|
2013-05-30 06:41:26 +00:00
|
|
|
slength(struct slist *s)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2012-10-04 21:07:56 +00:00
|
|
|
u_int n = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
for (; s; s = s->next)
|
|
|
|
if (s->s.code != NOP)
|
|
|
|
++n;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the number of nodes reachable by 'p'.
|
|
|
|
* All nodes should be initially unmarked.
|
|
|
|
*/
|
|
|
|
static int
|
2017-02-12 07:04:44 +00:00
|
|
|
count_blocks(struct icode *ic, struct block *p)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2017-02-12 07:04:44 +00:00
|
|
|
if (p == 0 || isMarked(ic, p))
|
1996-08-19 20:36:34 +00:00
|
|
|
return 0;
|
2017-02-12 07:04:44 +00:00
|
|
|
Mark(ic, p);
|
|
|
|
return count_blocks(ic, JT(p)) + count_blocks(ic, JF(p)) + 1;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do a depth first search on the flow graph, numbering the
|
|
|
|
* the basic blocks, and entering them into the 'blocks' array.`
|
|
|
|
*/
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
number_blks_r(opt_state_t *opt_state, struct icode *ic, struct block *p)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2023-03-27 18:45:17 +00:00
|
|
|
u_int n;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
if (p == 0 || isMarked(ic, p))
|
1996-08-19 20:36:34 +00:00
|
|
|
return;
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
Mark(ic, p);
|
|
|
|
n = opt_state->n_blocks++;
|
2023-03-27 18:45:17 +00:00
|
|
|
if (opt_state->n_blocks == 0) {
|
|
|
|
/*
|
|
|
|
* Overflow.
|
|
|
|
*/
|
|
|
|
opt_error(opt_state, "filter is too complex to optimize");
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
p->id = n;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->blocks[n] = p;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
number_blks_r(opt_state, ic, JT(p));
|
|
|
|
number_blks_r(opt_state, ic, JF(p));
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the number of stmts in the flowgraph reachable by 'p'.
|
|
|
|
* The nodes should be unmarked before calling.
|
2001-04-03 04:18:09 +00:00
|
|
|
*
|
|
|
|
* Note that "stmts" means "instructions", and that this includes
|
|
|
|
*
|
|
|
|
* side-effect statements in 'p' (slength(p->stmts));
|
|
|
|
*
|
|
|
|
* statements in the true branch from 'p' (count_stmts(JT(p)));
|
|
|
|
*
|
|
|
|
* statements in the false branch from 'p' (count_stmts(JF(p)));
|
|
|
|
*
|
|
|
|
* the conditional jump itself (1);
|
|
|
|
*
|
|
|
|
* an extra long jump if the true branch requires it (p->longjt);
|
|
|
|
*
|
|
|
|
* an extra long jump if the false branch requires it (p->longjf).
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
2012-10-04 21:07:56 +00:00
|
|
|
static u_int
|
2017-02-12 07:04:44 +00:00
|
|
|
count_stmts(struct icode *ic, struct block *p)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2012-10-04 21:07:56 +00:00
|
|
|
u_int n;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
if (p == 0 || isMarked(ic, p))
|
1996-08-19 20:36:34 +00:00
|
|
|
return 0;
|
2017-02-12 07:04:44 +00:00
|
|
|
Mark(ic, p);
|
|
|
|
n = count_stmts(ic, JT(p)) + count_stmts(ic, JF(p));
|
2001-04-03 04:18:09 +00:00
|
|
|
return slength(p->stmts) + n + 1 + p->longjt + p->longjf;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate memory. All allocation is done before optimization
|
|
|
|
* is begun. A linear bound on the size of all data structures is computed
|
|
|
|
* from the total number of blocks and/or statements.
|
|
|
|
*/
|
|
|
|
static void
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_init(opt_state_t *opt_state, struct icode *ic)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
bpf_u_int32 *p;
|
|
|
|
int i, n, max_stmts;
|
2023-03-27 18:45:17 +00:00
|
|
|
u_int product;
|
|
|
|
size_t block_memsize, edge_memsize;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* First, count the blocks, so we can malloc an array to map
|
|
|
|
* block number to block. Then, put the blocks into the array.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
unMarkAll(ic);
|
|
|
|
n = count_blocks(ic, ic->root);
|
|
|
|
opt_state->blocks = (struct block **)calloc(n, sizeof(*opt_state->blocks));
|
|
|
|
if (opt_state->blocks == NULL)
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_error(opt_state, "malloc");
|
2017-02-12 07:04:44 +00:00
|
|
|
unMarkAll(ic);
|
|
|
|
opt_state->n_blocks = 0;
|
|
|
|
number_blks_r(opt_state, ic, ic->root);
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
/*
|
|
|
|
* This "should not happen".
|
|
|
|
*/
|
|
|
|
if (opt_state->n_blocks == 0)
|
|
|
|
opt_error(opt_state, "filter has no instructions; please report this as a libpcap issue");
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->n_edges = 2 * opt_state->n_blocks;
|
2023-03-27 18:45:17 +00:00
|
|
|
if ((opt_state->n_edges / 2) != opt_state->n_blocks) {
|
|
|
|
/*
|
|
|
|
* Overflow.
|
|
|
|
*/
|
|
|
|
opt_error(opt_state, "filter is too complex to optimize");
|
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->edges = (struct edge **)calloc(opt_state->n_edges, sizeof(*opt_state->edges));
|
2019-10-06 04:26:37 +00:00
|
|
|
if (opt_state->edges == NULL) {
|
|
|
|
opt_error(opt_state, "malloc");
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The number of levels is bounded by the number of nodes.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->levels = (struct block **)calloc(opt_state->n_blocks, sizeof(*opt_state->levels));
|
2019-10-06 04:26:37 +00:00
|
|
|
if (opt_state->levels == NULL) {
|
|
|
|
opt_error(opt_state, "malloc");
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
opt_state->edgewords = opt_state->n_edges / BITS_PER_WORD + 1;
|
|
|
|
opt_state->nodewords = opt_state->n_blocks / BITS_PER_WORD + 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure opt_state->n_blocks * opt_state->nodewords fits
|
|
|
|
* in a u_int; we use it as a u_int number-of-iterations
|
|
|
|
* value.
|
|
|
|
*/
|
|
|
|
product = opt_state->n_blocks * opt_state->nodewords;
|
|
|
|
if ((product / opt_state->n_blocks) != opt_state->nodewords) {
|
|
|
|
/*
|
|
|
|
* XXX - just punt and don't try to optimize?
|
|
|
|
* In practice, this is unlikely to happen with
|
|
|
|
* a normal filter.
|
|
|
|
*/
|
|
|
|
opt_error(opt_state, "filter is too complex to optimize");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the total memory required for that doesn't
|
|
|
|
* overflow.
|
|
|
|
*/
|
|
|
|
block_memsize = (size_t)2 * product * sizeof(*opt_state->space);
|
|
|
|
if ((block_memsize / product) != 2 * sizeof(*opt_state->space)) {
|
|
|
|
opt_error(opt_state, "filter is too complex to optimize");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure opt_state->n_edges * opt_state->edgewords fits
|
|
|
|
* in a u_int; we use it as a u_int number-of-iterations
|
|
|
|
* value.
|
|
|
|
*/
|
|
|
|
product = opt_state->n_edges * opt_state->edgewords;
|
|
|
|
if ((product / opt_state->n_edges) != opt_state->edgewords) {
|
|
|
|
opt_error(opt_state, "filter is too complex to optimize");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the total memory required for that doesn't
|
|
|
|
* overflow.
|
|
|
|
*/
|
|
|
|
edge_memsize = (size_t)product * sizeof(*opt_state->space);
|
|
|
|
if (edge_memsize / product != sizeof(*opt_state->space)) {
|
|
|
|
opt_error(opt_state, "filter is too complex to optimize");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the total memory required for both of them doesn't
|
|
|
|
* overflow.
|
|
|
|
*/
|
|
|
|
if (block_memsize > SIZE_MAX - edge_memsize) {
|
|
|
|
opt_error(opt_state, "filter is too complex to optimize");
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
/* XXX */
|
2023-03-27 18:45:17 +00:00
|
|
|
opt_state->space = (bpf_u_int32 *)malloc(block_memsize + edge_memsize);
|
2019-10-06 04:26:37 +00:00
|
|
|
if (opt_state->space == NULL) {
|
|
|
|
opt_error(opt_state, "malloc");
|
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
p = opt_state->space;
|
|
|
|
opt_state->all_dom_sets = p;
|
1996-08-19 20:36:34 +00:00
|
|
|
for (i = 0; i < n; ++i) {
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->blocks[i]->dom = p;
|
|
|
|
p += opt_state->nodewords;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->all_closure_sets = p;
|
1996-08-19 20:36:34 +00:00
|
|
|
for (i = 0; i < n; ++i) {
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->blocks[i]->closure = p;
|
|
|
|
p += opt_state->nodewords;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->all_edge_sets = p;
|
1996-08-19 20:36:34 +00:00
|
|
|
for (i = 0; i < n; ++i) {
|
2017-02-12 07:04:44 +00:00
|
|
|
register struct block *b = opt_state->blocks[i];
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
b->et.edom = p;
|
2017-02-12 07:04:44 +00:00
|
|
|
p += opt_state->edgewords;
|
1996-08-19 20:36:34 +00:00
|
|
|
b->ef.edom = p;
|
2017-02-12 07:04:44 +00:00
|
|
|
p += opt_state->edgewords;
|
1996-08-19 20:36:34 +00:00
|
|
|
b->et.id = i;
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->edges[i] = &b->et;
|
|
|
|
b->ef.id = opt_state->n_blocks + i;
|
|
|
|
opt_state->edges[opt_state->n_blocks + i] = &b->ef;
|
1996-08-19 20:36:34 +00:00
|
|
|
b->et.pred = b;
|
|
|
|
b->ef.pred = b;
|
|
|
|
}
|
|
|
|
max_stmts = 0;
|
|
|
|
for (i = 0; i < n; ++i)
|
2017-02-12 07:04:44 +00:00
|
|
|
max_stmts += slength(opt_state->blocks[i]->stmts) + 1;
|
1996-08-19 20:36:34 +00:00
|
|
|
/*
|
|
|
|
* We allocate at most 3 value numbers per statement,
|
|
|
|
* so this is an upper bound on the number of valnodes
|
|
|
|
* we'll need.
|
|
|
|
*/
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->maxval = 3 * max_stmts;
|
|
|
|
opt_state->vmap = (struct vmapinfo *)calloc(opt_state->maxval, sizeof(*opt_state->vmap));
|
2019-10-06 04:26:37 +00:00
|
|
|
if (opt_state->vmap == NULL) {
|
|
|
|
opt_error(opt_state, "malloc");
|
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
opt_state->vnode_base = (struct valnode *)calloc(opt_state->maxval, sizeof(*opt_state->vnode_base));
|
2019-10-06 04:26:37 +00:00
|
|
|
if (opt_state->vnode_base == NULL) {
|
|
|
|
opt_error(opt_state, "malloc");
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-02-12 07:04:44 +00:00
|
|
|
* This is only used when supporting optimizer debugging. It is
|
|
|
|
* global state, so do *not* do more than one compile in parallel
|
|
|
|
* and expect it to provide meaningful information.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
#ifdef BDEBUG
|
2018-05-18 12:21:19 +00:00
|
|
|
int bids[NBIDS];
|
1996-08-19 20:36:34 +00:00
|
|
|
#endif
|
|
|
|
|
2019-10-06 04:26:37 +00:00
|
|
|
static void PCAP_NORETURN conv_error(conv_state_t *, const char *, ...)
|
|
|
|
PCAP_PRINTFLIKE(2, 3);
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
/*
|
|
|
|
* Returns true if successful. Returns false if a branch has
|
|
|
|
* an offset that is too large. If so, we have marked that
|
|
|
|
* branch so that on a subsequent iteration, it will be treated
|
|
|
|
* properly.
|
|
|
|
*/
|
|
|
|
static int
|
2019-10-06 04:26:37 +00:00
|
|
|
convert_code_r(conv_state_t *conv_state, struct icode *ic, struct block *p)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
struct bpf_insn *dst;
|
|
|
|
struct slist *src;
|
2017-02-12 07:04:44 +00:00
|
|
|
u_int slen;
|
1996-08-19 20:36:34 +00:00
|
|
|
u_int off;
|
2000-01-30 00:32:56 +00:00
|
|
|
struct slist **offset = NULL;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
if (p == 0 || isMarked(ic, p))
|
1996-08-19 20:36:34 +00:00
|
|
|
return (1);
|
2017-02-12 07:04:44 +00:00
|
|
|
Mark(ic, p);
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2019-10-06 04:26:37 +00:00
|
|
|
if (convert_code_r(conv_state, ic, JF(p)) == 0)
|
1996-08-19 20:36:34 +00:00
|
|
|
return (0);
|
2019-10-06 04:26:37 +00:00
|
|
|
if (convert_code_r(conv_state, ic, JT(p)) == 0)
|
1996-08-19 20:36:34 +00:00
|
|
|
return (0);
|
|
|
|
|
|
|
|
slen = slength(p->stmts);
|
2017-02-12 07:04:44 +00:00
|
|
|
dst = conv_state->ftail -= (slen + 1 + p->longjt + p->longjf);
|
1996-08-19 20:36:34 +00:00
|
|
|
/* inflate length by any extra jumps */
|
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
p->offset = (int)(dst - conv_state->fstart);
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2000-01-30 00:32:56 +00:00
|
|
|
/* generate offset[] for convenience */
|
|
|
|
if (slen) {
|
2004-03-31 09:07:39 +00:00
|
|
|
offset = (struct slist **)calloc(slen, sizeof(struct slist *));
|
2000-01-30 00:32:56 +00:00
|
|
|
if (!offset) {
|
2019-10-06 04:26:37 +00:00
|
|
|
conv_error(conv_state, "not enough core");
|
2000-01-30 00:32:56 +00:00
|
|
|
/*NOTREACHED*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
src = p->stmts;
|
|
|
|
for (off = 0; off < slen && src; off++) {
|
|
|
|
#if 0
|
|
|
|
printf("off=%d src=%x\n", off, src);
|
|
|
|
#endif
|
|
|
|
offset[off] = src;
|
|
|
|
src = src->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
off = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
for (src = p->stmts; src; src = src->next) {
|
|
|
|
if (src->s.code == NOP)
|
|
|
|
continue;
|
|
|
|
dst->code = (u_short)src->s.code;
|
|
|
|
dst->k = src->s.k;
|
2000-01-30 00:32:56 +00:00
|
|
|
|
|
|
|
/* fill block-local relative jump */
|
2001-04-03 04:18:09 +00:00
|
|
|
if (BPF_CLASS(src->s.code) != BPF_JMP || src->s.code == (BPF_JMP|BPF_JA)) {
|
2000-01-30 00:32:56 +00:00
|
|
|
#if 0
|
|
|
|
if (src->s.jt || src->s.jf) {
|
2019-10-06 04:26:37 +00:00
|
|
|
free(offset);
|
|
|
|
conv_error(conv_state, "illegal jmp destination");
|
2000-01-30 00:32:56 +00:00
|
|
|
/*NOTREACHED*/
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
goto filled;
|
|
|
|
}
|
|
|
|
if (off == slen - 2) /*???*/
|
|
|
|
goto filled;
|
|
|
|
|
|
|
|
{
|
2017-02-12 07:04:44 +00:00
|
|
|
u_int i;
|
2000-01-30 00:32:56 +00:00
|
|
|
int jt, jf;
|
2018-05-18 12:21:19 +00:00
|
|
|
const char ljerr[] = "%s for block-local relative jump: off=%d";
|
2000-01-30 00:32:56 +00:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
printf("code=%x off=%d %x %x\n", src->s.code,
|
|
|
|
off, src->s.jt, src->s.jf);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!src->s.jt || !src->s.jf) {
|
2019-10-06 04:26:37 +00:00
|
|
|
free(offset);
|
|
|
|
conv_error(conv_state, ljerr, "no jmp destination", off);
|
2000-01-30 00:32:56 +00:00
|
|
|
/*NOTREACHED*/
|
|
|
|
}
|
|
|
|
|
|
|
|
jt = jf = 0;
|
|
|
|
for (i = 0; i < slen; i++) {
|
|
|
|
if (offset[i] == src->s.jt) {
|
|
|
|
if (jt) {
|
2019-10-06 04:26:37 +00:00
|
|
|
free(offset);
|
|
|
|
conv_error(conv_state, ljerr, "multiple matches", off);
|
2000-01-30 00:32:56 +00:00
|
|
|
/*NOTREACHED*/
|
|
|
|
}
|
|
|
|
|
2018-05-18 12:21:19 +00:00
|
|
|
if (i - off - 1 >= 256) {
|
2019-10-06 04:26:37 +00:00
|
|
|
free(offset);
|
|
|
|
conv_error(conv_state, ljerr, "out-of-range jump", off);
|
2018-05-18 12:21:19 +00:00
|
|
|
/*NOTREACHED*/
|
|
|
|
}
|
|
|
|
dst->jt = (u_char)(i - off - 1);
|
2000-01-30 00:32:56 +00:00
|
|
|
jt++;
|
|
|
|
}
|
|
|
|
if (offset[i] == src->s.jf) {
|
|
|
|
if (jf) {
|
2019-10-06 04:26:37 +00:00
|
|
|
free(offset);
|
|
|
|
conv_error(conv_state, ljerr, "multiple matches", off);
|
2000-01-30 00:32:56 +00:00
|
|
|
/*NOTREACHED*/
|
|
|
|
}
|
2018-05-18 12:21:19 +00:00
|
|
|
if (i - off - 1 >= 256) {
|
2019-10-06 04:26:37 +00:00
|
|
|
free(offset);
|
|
|
|
conv_error(conv_state, ljerr, "out-of-range jump", off);
|
2018-05-18 12:21:19 +00:00
|
|
|
/*NOTREACHED*/
|
|
|
|
}
|
|
|
|
dst->jf = (u_char)(i - off - 1);
|
2000-01-30 00:32:56 +00:00
|
|
|
jf++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!jt || !jf) {
|
2019-10-06 04:26:37 +00:00
|
|
|
free(offset);
|
|
|
|
conv_error(conv_state, ljerr, "no destination found", off);
|
2000-01-30 00:32:56 +00:00
|
|
|
/*NOTREACHED*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
filled:
|
1996-08-19 20:36:34 +00:00
|
|
|
++dst;
|
2000-01-30 00:32:56 +00:00
|
|
|
++off;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
2000-01-30 00:32:56 +00:00
|
|
|
if (offset)
|
|
|
|
free(offset);
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
#ifdef BDEBUG
|
2018-05-18 12:21:19 +00:00
|
|
|
if (dst - conv_state->fstart < NBIDS)
|
|
|
|
bids[dst - conv_state->fstart] = p->id + 1;
|
1996-08-19 20:36:34 +00:00
|
|
|
#endif
|
|
|
|
dst->code = (u_short)p->s.code;
|
|
|
|
dst->k = p->s.k;
|
|
|
|
if (JT(p)) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/* number of extra jumps inserted */
|
|
|
|
u_char extrajmps = 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
off = JT(p)->offset - (p->offset + slen) - 1;
|
|
|
|
if (off >= 256) {
|
|
|
|
/* offset too large for branch, must add a jump */
|
|
|
|
if (p->longjt == 0) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/* mark this instruction and retry */
|
1996-08-19 20:36:34 +00:00
|
|
|
p->longjt++;
|
|
|
|
return(0);
|
|
|
|
}
|
2023-03-27 18:45:17 +00:00
|
|
|
dst->jt = extrajmps;
|
1996-08-19 20:36:34 +00:00
|
|
|
extrajmps++;
|
|
|
|
dst[extrajmps].code = BPF_JMP|BPF_JA;
|
|
|
|
dst[extrajmps].k = off - extrajmps;
|
|
|
|
}
|
|
|
|
else
|
2018-05-18 12:21:19 +00:00
|
|
|
dst->jt = (u_char)off;
|
1996-08-19 20:36:34 +00:00
|
|
|
off = JF(p)->offset - (p->offset + slen) - 1;
|
|
|
|
if (off >= 256) {
|
|
|
|
/* offset too large for branch, must add a jump */
|
|
|
|
if (p->longjf == 0) {
|
2023-03-27 18:45:17 +00:00
|
|
|
/* mark this instruction and retry */
|
1996-08-19 20:36:34 +00:00
|
|
|
p->longjf++;
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
/* branch if F to following jump */
|
|
|
|
/* if two jumps are inserted, F goes to second one */
|
2023-03-27 18:45:17 +00:00
|
|
|
dst->jf = extrajmps;
|
1996-08-19 20:36:34 +00:00
|
|
|
extrajmps++;
|
|
|
|
dst[extrajmps].code = BPF_JMP|BPF_JA;
|
|
|
|
dst[extrajmps].k = off - extrajmps;
|
|
|
|
}
|
|
|
|
else
|
2018-05-18 12:21:19 +00:00
|
|
|
dst->jf = (u_char)off;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert flowgraph intermediate representation to the
|
|
|
|
* BPF array representation. Set *lenp to the number of instructions.
|
2007-10-16 02:02:02 +00:00
|
|
|
*
|
|
|
|
* This routine does *NOT* leak the memory pointed to by fp. It *must
|
|
|
|
* not* do free(fp) before returning fp; doing so would make no sense,
|
|
|
|
* as the BPF array pointed to by the return value of icode_to_fcode()
|
|
|
|
* must be valid - it's being returned for use in a bpf_program structure.
|
|
|
|
*
|
|
|
|
* If it appears that icode_to_fcode() is leaking, the problem is that
|
|
|
|
* the program using pcap_compile() is failing to free the memory in
|
|
|
|
* the BPF program when it's done - the leak is in the program, not in
|
|
|
|
* the routine that happens to be allocating the memory. (By analogy, if
|
|
|
|
* a program calls fopen() without ever calling fclose() on the FILE *,
|
|
|
|
* it will leak the FILE structure; the leak is not in fopen(), it's in
|
|
|
|
* the program.) Change the program to use pcap_freecode() when it's
|
|
|
|
* done with the filter program. See the pcap man page.
|
1996-08-19 20:36:34 +00:00
|
|
|
*/
|
|
|
|
struct bpf_insn *
|
2023-03-27 18:45:17 +00:00
|
|
|
icode_to_fcode(struct icode *ic, struct block *root, u_int *lenp,
|
2019-10-06 04:26:37 +00:00
|
|
|
char *errbuf)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
2012-10-04 21:07:56 +00:00
|
|
|
u_int n;
|
1996-08-19 20:36:34 +00:00
|
|
|
struct bpf_insn *fp;
|
2017-02-12 07:04:44 +00:00
|
|
|
conv_state_t conv_state;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
2019-10-06 04:26:37 +00:00
|
|
|
conv_state.fstart = NULL;
|
|
|
|
conv_state.errbuf = errbuf;
|
|
|
|
if (setjmp(conv_state.top_ctx) != 0) {
|
|
|
|
free(conv_state.fstart);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
/*
|
2002-06-21 01:36:27 +00:00
|
|
|
* Loop doing convert_code_r() until no branches remain
|
1996-08-19 20:36:34 +00:00
|
|
|
* with too-large offsets.
|
|
|
|
*/
|
2018-05-18 12:21:19 +00:00
|
|
|
for (;;) {
|
2017-02-12 07:04:44 +00:00
|
|
|
unMarkAll(ic);
|
|
|
|
n = *lenp = count_stmts(ic, root);
|
2004-03-31 09:07:39 +00:00
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
fp = (struct bpf_insn *)malloc(sizeof(*fp) * n);
|
2019-10-06 04:26:37 +00:00
|
|
|
if (fp == NULL) {
|
2023-03-27 18:45:17 +00:00
|
|
|
(void)snprintf(errbuf, PCAP_ERRBUF_SIZE,
|
2019-10-06 04:26:37 +00:00
|
|
|
"malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
memset((char *)fp, 0, sizeof(*fp) * n);
|
2017-02-12 07:04:44 +00:00
|
|
|
conv_state.fstart = fp;
|
|
|
|
conv_state.ftail = fp + n;
|
2004-03-31 09:07:39 +00:00
|
|
|
|
2017-02-12 07:04:44 +00:00
|
|
|
unMarkAll(ic);
|
2019-10-06 04:26:37 +00:00
|
|
|
if (convert_code_r(&conv_state, ic, root))
|
1996-08-19 20:36:34 +00:00
|
|
|
break;
|
|
|
|
free(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
|
2019-10-06 04:26:37 +00:00
|
|
|
/*
|
|
|
|
* For iconv_to_fconv() errors.
|
|
|
|
*/
|
|
|
|
static void PCAP_NORETURN
|
|
|
|
conv_error(conv_state_t *conv_state, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
2023-03-27 18:45:17 +00:00
|
|
|
(void)vsnprintf(conv_state->errbuf,
|
2019-10-06 04:26:37 +00:00
|
|
|
PCAP_ERRBUF_SIZE, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
longjmp(conv_state->top_ctx, 1);
|
|
|
|
/* NOTREACHED */
|
2023-03-27 18:45:17 +00:00
|
|
|
#ifdef _AIX
|
|
|
|
PCAP_UNREACHABLE
|
|
|
|
#endif /* _AIX */
|
2019-10-06 04:26:37 +00:00
|
|
|
}
|
|
|
|
|
2001-04-03 04:18:09 +00:00
|
|
|
/*
|
|
|
|
* Make a copy of a BPF program and put it in the "fcode" member of
|
|
|
|
* a "pcap_t".
|
|
|
|
*
|
|
|
|
* If we fail to allocate memory for the copy, fill in the "errbuf"
|
|
|
|
* member of the "pcap_t" with an error message, and return -1;
|
|
|
|
* otherwise, return 0.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
install_bpf_program(pcap_t *p, struct bpf_program *fp)
|
|
|
|
{
|
|
|
|
size_t prog_size;
|
|
|
|
|
2009-03-21 20:43:56 +00:00
|
|
|
/*
|
|
|
|
* Validate the program.
|
|
|
|
*/
|
2023-03-27 18:45:17 +00:00
|
|
|
if (!pcap_validate_filter(fp->bf_insns, fp->bf_len)) {
|
|
|
|
snprintf(p->errbuf, sizeof(p->errbuf),
|
2009-03-21 20:43:56 +00:00
|
|
|
"BPF program is not valid");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2001-04-03 04:18:09 +00:00
|
|
|
/*
|
|
|
|
* Free up any already installed program.
|
|
|
|
*/
|
|
|
|
pcap_freecode(&p->fcode);
|
|
|
|
|
|
|
|
prog_size = sizeof(*fp->bf_insns) * fp->bf_len;
|
|
|
|
p->fcode.bf_len = fp->bf_len;
|
|
|
|
p->fcode.bf_insns = (struct bpf_insn *)malloc(prog_size);
|
|
|
|
if (p->fcode.bf_insns == NULL) {
|
2018-05-18 12:21:19 +00:00
|
|
|
pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf),
|
|
|
|
errno, "malloc");
|
2001-04-03 04:18:09 +00:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
memcpy(p->fcode.bf_insns, fp->bf_insns, prog_size);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1996-08-19 20:36:34 +00:00
|
|
|
#ifdef BDEBUG
|
|
|
|
static void
|
2017-02-12 07:04:44 +00:00
|
|
|
dot_dump_node(struct icode *ic, struct block *block, struct bpf_program *prog,
|
|
|
|
FILE *out)
|
|
|
|
{
|
|
|
|
int icount, noffset;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (block == NULL || isMarked(ic, block))
|
|
|
|
return;
|
|
|
|
Mark(ic, block);
|
|
|
|
|
|
|
|
icount = slength(block->stmts) + 1 + block->longjt + block->longjf;
|
|
|
|
noffset = min(block->offset + icount, (int)prog->bf_len);
|
|
|
|
|
2023-03-27 18:45:17 +00:00
|
|
|
fprintf(out, "\tblock%u [shape=ellipse, id=\"block-%u\" label=\"BLOCK%u\\n", block->id, block->id, block->id);
|
2017-02-12 07:04:44 +00:00
|
|
|
for (i = block->offset; i < noffset; i++) {
|
|
|
|
fprintf(out, "\\n%s", bpf_image(prog->bf_insns + i, i));
|
|
|
|
}
|
|
|
|
fprintf(out, "\" tooltip=\"");
|
|
|
|
for (i = 0; i < BPF_MEMWORDS; i++)
|
2018-05-18 12:21:19 +00:00
|
|
|
if (block->val[i] != VAL_UNKNOWN)
|
2017-02-12 07:04:44 +00:00
|
|
|
fprintf(out, "val[%d]=%d ", i, block->val[i]);
|
|
|
|
fprintf(out, "val[A]=%d ", block->val[A_ATOM]);
|
|
|
|
fprintf(out, "val[X]=%d", block->val[X_ATOM]);
|
|
|
|
fprintf(out, "\"");
|
|
|
|
if (JT(block) == NULL)
|
|
|
|
fprintf(out, ", peripheries=2");
|
|
|
|
fprintf(out, "];\n");
|
|
|
|
|
|
|
|
dot_dump_node(ic, JT(block), prog, out);
|
|
|
|
dot_dump_node(ic, JF(block), prog, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dot_dump_edge(struct icode *ic, struct block *block, FILE *out)
|
|
|
|
{
|
|
|
|
if (block == NULL || isMarked(ic, block))
|
|
|
|
return;
|
|
|
|
Mark(ic, block);
|
|
|
|
|
|
|
|
if (JT(block)) {
|
2023-03-27 18:45:17 +00:00
|
|
|
fprintf(out, "\t\"block%u\":se -> \"block%u\":n [label=\"T\"]; \n",
|
2017-02-12 07:04:44 +00:00
|
|
|
block->id, JT(block)->id);
|
2023-03-27 18:45:17 +00:00
|
|
|
fprintf(out, "\t\"block%u\":sw -> \"block%u\":n [label=\"F\"]; \n",
|
2017-02-12 07:04:44 +00:00
|
|
|
block->id, JF(block)->id);
|
|
|
|
}
|
|
|
|
dot_dump_edge(ic, JT(block), out);
|
|
|
|
dot_dump_edge(ic, JF(block), out);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Output the block CFG using graphviz/DOT language
|
|
|
|
* In the CFG, block's code, value index for each registers at EXIT,
|
|
|
|
* and the jump relationship is show.
|
|
|
|
*
|
|
|
|
* example DOT for BPF `ip src host 1.1.1.1' is:
|
|
|
|
digraph BPF {
|
|
|
|
block0 [shape=ellipse, id="block-0" label="BLOCK0\n\n(000) ldh [12]\n(001) jeq #0x800 jt 2 jf 5" tooltip="val[A]=0 val[X]=0"];
|
|
|
|
block1 [shape=ellipse, id="block-1" label="BLOCK1\n\n(002) ld [26]\n(003) jeq #0x1010101 jt 4 jf 5" tooltip="val[A]=0 val[X]=0"];
|
|
|
|
block2 [shape=ellipse, id="block-2" label="BLOCK2\n\n(004) ret #68" tooltip="val[A]=0 val[X]=0", peripheries=2];
|
|
|
|
block3 [shape=ellipse, id="block-3" label="BLOCK3\n\n(005) ret #0" tooltip="val[A]=0 val[X]=0", peripheries=2];
|
|
|
|
"block0":se -> "block1":n [label="T"];
|
|
|
|
"block0":sw -> "block3":n [label="F"];
|
|
|
|
"block1":se -> "block2":n [label="T"];
|
|
|
|
"block1":sw -> "block3":n [label="F"];
|
|
|
|
}
|
|
|
|
*
|
2023-03-27 18:45:17 +00:00
|
|
|
* After install graphviz on https://www.graphviz.org/, save it as bpf.dot
|
2017-02-12 07:04:44 +00:00
|
|
|
* and run `dot -Tpng -O bpf.dot' to draw the graph.
|
|
|
|
*/
|
2019-10-06 04:26:37 +00:00
|
|
|
static int
|
|
|
|
dot_dump(struct icode *ic, char *errbuf)
|
1996-08-19 20:36:34 +00:00
|
|
|
{
|
|
|
|
struct bpf_program f;
|
2017-02-12 07:04:44 +00:00
|
|
|
FILE *out = stdout;
|
1996-08-19 20:36:34 +00:00
|
|
|
|
|
|
|
memset(bids, 0, sizeof bids);
|
2019-10-06 04:26:37 +00:00
|
|
|
f.bf_insns = icode_to_fcode(ic, ic->root, &f.bf_len, errbuf);
|
|
|
|
if (f.bf_insns == NULL)
|
|
|
|
return -1;
|
2017-02-12 07:04:44 +00:00
|
|
|
|
|
|
|
fprintf(out, "digraph BPF {\n");
|
|
|
|
unMarkAll(ic);
|
|
|
|
dot_dump_node(ic, ic->root, &f, out);
|
|
|
|
unMarkAll(ic);
|
|
|
|
dot_dump_edge(ic, ic->root, out);
|
|
|
|
fprintf(out, "}\n");
|
|
|
|
|
|
|
|
free((char *)f.bf_insns);
|
2019-10-06 04:26:37 +00:00
|
|
|
return 0;
|
2017-02-12 07:04:44 +00:00
|
|
|
}
|
|
|
|
|
2019-10-06 04:26:37 +00:00
|
|
|
static int
|
|
|
|
plain_dump(struct icode *ic, char *errbuf)
|
2017-02-12 07:04:44 +00:00
|
|
|
{
|
|
|
|
struct bpf_program f;
|
|
|
|
|
|
|
|
memset(bids, 0, sizeof bids);
|
2019-10-06 04:26:37 +00:00
|
|
|
f.bf_insns = icode_to_fcode(ic, ic->root, &f.bf_len, errbuf);
|
|
|
|
if (f.bf_insns == NULL)
|
|
|
|
return -1;
|
1996-08-19 20:36:34 +00:00
|
|
|
bpf_dump(&f, 1);
|
|
|
|
putchar('\n');
|
|
|
|
free((char *)f.bf_insns);
|
2019-10-06 04:26:37 +00:00
|
|
|
return 0;
|
1996-08-19 20:36:34 +00:00
|
|
|
}
|
2017-02-12 07:04:44 +00:00
|
|
|
|
|
|
|
static void
|
2019-10-06 04:26:37 +00:00
|
|
|
opt_dump(opt_state_t *opt_state, struct icode *ic)
|
2017-02-12 07:04:44 +00:00
|
|
|
{
|
2019-10-06 04:26:37 +00:00
|
|
|
int status;
|
|
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
|
|
|
2018-05-18 12:21:19 +00:00
|
|
|
/*
|
|
|
|
* If the CFG, in DOT format, is requested, output it rather than
|
|
|
|
* the code that would be generated from that graph.
|
2017-02-12 07:04:44 +00:00
|
|
|
*/
|
2018-05-18 12:21:19 +00:00
|
|
|
if (pcap_print_dot_graph)
|
2019-10-06 04:26:37 +00:00
|
|
|
status = dot_dump(ic, errbuf);
|
2017-02-12 07:04:44 +00:00
|
|
|
else
|
2019-10-06 04:26:37 +00:00
|
|
|
status = plain_dump(ic, errbuf);
|
|
|
|
if (status == -1)
|
|
|
|
opt_error(opt_state, "opt_dump: icode_to_fcode failed: %s", errbuf);
|
2017-02-12 07:04:44 +00:00
|
|
|
}
|
1996-08-19 20:36:34 +00:00
|
|
|
#endif
|