]> The Tcpdump Group git mirrors - libpcap/commitdiff
Pick up unaligned big-endian fetch macros/routines from tcpdump.
authorGuy Harris <[email protected]>
Sat, 4 Aug 2018 09:26:53 +0000 (02:26 -0700)
committerGuy Harris <[email protected]>
Sat, 4 Aug 2018 09:26:53 +0000 (02:26 -0700)
This should squelch some undefined behavior warnings, because, on
platforms where you *can* do unaligned fetches, we just suppress that
warning.

While we're at it, use C99-style integer types, which we now require;
pcap/pcap-inttypes.h ensures they're defined on Windows, and we simply
assume <inttypes.h> defines them on UN*X (if it doesn't, we don't
support your UN*X any more).

bpf_filter.c

index 615ed3fc3d0f54f21456a32bb36e8eb4f2b1f135..f04746b3aeb9f284a9d25111bd9215c5fabd278d 100644 (file)
 
 #include <stdlib.h>
 
-#define int32 bpf_int32
-#define u_int32 bpf_u_int32
-
-#ifndef LBL_ALIGN
 /*
- * XXX - IA-64?  If not, this probably won't work on Win64 IA-64
- * systems, unless LBL_ALIGN is defined elsewhere for them.
- * XXX - SuperH?  If not, this probably won't work on WinCE SuperH
- * systems, unless LBL_ALIGN is defined elsewhere for them.
+ * If we have versions of GCC or Clang that support an __attribute__
+ * to say "if we're building with unsigned behavior sanitization,
+ * don't complain about undefined behavior in this function", we
+ * label these functions with that attribute - we *know* it's undefined
+ * in the C standard, but we *also* know it does what we want with
+ * the ISA we're targeting and the compiler we're using.
+ *
+ * For GCC 4.9.0 and later, we use __attribute__((no_sanitize_undefined));
+ * pre-5.0 GCC doesn't have __has_attribute, and I'm not sure whether
+ * GCC or Clang first had __attribute__((no_sanitize(XXX)).
+ *
+ * For Clang, we check for __attribute__((no_sanitize(XXX)) with
+ * __has_attribute, as there are versions of Clang that support
+ * __attribute__((no_sanitize("undefined")) but don't support
+ * __attribute__((no_sanitize_undefined)).
+ *
+ * We define this here, rather than in funcattrs.h, because we
+ * only want it used here, we don't want it to be broadly used.
+ * (Any printer will get this defined, but this should at least
+ * make it harder for people to find.)
  */
-#if defined(sparc) || defined(__sparc__) || defined(mips) || \
-    defined(ibm032) || defined(__alpha) || defined(__hpux) || \
-    defined(__arm__)
-#define LBL_ALIGN
-#endif
+#if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 409)
+#define UNALIGNED_OK   __attribute__((no_sanitize_undefined))
+#elif __has_attribute(no_sanitize)
+#define UNALIGNED_OK   __attribute__((no_sanitize("undefined")))
+#else
+#define UNALIGNED_OK
 #endif
 
-#ifndef LBL_ALIGN
-#ifndef _WIN32
-#include <netinet/in.h>
-#endif
+#if (defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(__x86_64__) || defined(_M_X64)) || \
+    (defined(__arm__) || defined(_M_ARM) || defined(__aarch64__)) || \
+    (defined(__m68k__) && (!defined(__mc68000__) && !defined(__mc68010__))) || \
+    (defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PPC64)) || \
+    (defined(__s390__) || defined(__s390x__) || defined(__zarch__))
+/*
+ * The processor natively handles unaligned loads, so we can just
+ * cast the pointer and fetch through it.
+ *
+ * XXX - are those all the x86 tests we need?
+ * XXX - do we need to worry about ARMv1 through ARMv5, which didn't
+ * support unaligned loads, and, if so, do we need to worry about all
+ * of them, or just some of them, e.g. ARMv5?
+ * XXX - are those the only 68k tests we need not to generated
+ * unaligned accesses if the target is the 68000 or 68010?
+ * XXX - are there any tests we don't need, because some definitions are for
+ * compilers that also predefine the GCC symbols?
+ * XXX - do we need to test for both 32-bit and 64-bit versions of those
+ * architectures in all cases?
+ */
+UNALIGNED_OK static inline uint16_t
+EXTRACT_SHORT(const void *p)
+{
+       return ((uint16_t)ntohs(*(const uint16_t *)(p)));
+}
 
-#define EXTRACT_SHORT(p)       ((u_short)ntohs(*(u_short *)p))
-#define EXTRACT_LONG(p)                (ntohl(*(u_int32 *)p))
+UNALIGNED_OK static inline uint32_t
+EXTRACT_LONG(const void *p)
+{
+       return ((uint32_t)ntohl(*(const uint32_t *)(p)));
+}
+
+#elif PCAP_IS_AT_LEAST_GNUC_VERSION(2,0) && \
+    (defined(__alpha) || defined(__alpha__) || \
+     defined(__mips) || defined(__mips__))
+/*
+ * This is MIPS or Alpha, which don't natively handle unaligned loads,
+ * but which have instructions that can help when doing unaligned
+ * loads, and this is GCC 2.0 or later or a compiler that claims to
+ * be GCC 2.0 or later, which we assume that mean we have
+ * __attribute__((packed)), which we can use to convince the compiler
+ * to generate those instructions.
+ *
+ * Declare packed structures containing a uint16_t and a uint32_t,
+ * cast the pointer to point to one of those, and fetch through it;
+ * the GCC manual doesn't appear to explicitly say that
+ * __attribute__((packed)) causes the compiler to generate unaligned-safe
+ * code, but it apppears to do so.
+ *
+ * We do this in case the compiler can generate code using those
+ * instructions to do an unaligned load and pass stuff to "ntohs()" or
+ * "ntohl()", which might be better than than the code to fetch the
+ * bytes one at a time and assemble them.  (That might not be the
+ * case on a little-endian platform, such as DEC's MIPS machines and
+ * Alpha machines, where "ntohs()" and "ntohl()" might not be done
+ * inline.)
+ *
+ * We do this only for specific architectures because, for example,
+ * at least some versions of GCC, when compiling for 64-bit SPARC,
+ * generate code that assumes alignment if we do this.
+ *
+ * XXX - add other architectures and compilers as possible and
+ * appropriate.
+ *
+ * HP's C compiler, indicated by __HP_cc being defined, supports
+ * "#pragma unaligned N" in version A.05.50 and later, where "N"
+ * specifies a number of bytes at which the typedef on the next
+ * line is aligned, e.g.
+ *
+ *     #pragma unalign 1
+ *     typedef uint16_t unaligned_uint16_t;
+ *
+ * to define unaligned_uint16_t as a 16-bit unaligned data type.
+ * This could be presumably used, in sufficiently recent versions of
+ * the compiler, with macros similar to those below.  This would be
+ * useful only if that compiler could generate better code for PA-RISC
+ * or Itanium than would be generated by a bunch of shifts-and-ORs.
+ *
+ * DEC C, indicated by __DECC being defined, has, at least on Alpha,
+ * an __unaligned qualifier that can be applied to pointers to get the
+ * compiler to generate code that does unaligned loads and stores when
+ * dereferencing the pointer in question.
+ *
+ * XXX - what if the native C compiler doesn't support
+ * __attribute__((packed))?  How can we get it to generate unaligned
+ * accesses for *specific* items?
+ */
+typedef struct {
+       uint16_t        val;
+} __attribute__((packed)) unaligned_uint16_t;
+
+typedef struct {
+       uint32_t        val;
+} __attribute__((packed)) unaligned_uint32_t;
+
+UNALIGNED_OK static inline uint16_t
+EXTRACT_SHORT(const void *p)
+{
+       return ((uint16_t)ntohs(((const unaligned_uint16_t *)(p))->val));
+}
+
+UNALIGNED_OK static inline uint32_t
+EXTRACT_LONG(const void *p)
+{
+       return ((uint32_t)ntohl(((const unaligned_uint32_t *)(p))->val));
+}
 #else
-#define EXTRACT_SHORT(p)\
-       ((u_short)\
-               ((u_short)*((u_char *)p+0)<<8|\
-                (u_short)*((u_char *)p+1)<<0))
-#define EXTRACT_LONG(p)\
-               ((u_int32)*((u_char *)p+0)<<24|\
-                (u_int32)*((u_char *)p+1)<<16|\
-                (u_int32)*((u_char *)p+2)<<8|\
-                (u_int32)*((u_char *)p+3)<<0)
-#endif
+/*
+ * This architecture doesn't natively support unaligned loads, and either
+ * this isn't a GCC-compatible compiler, we don't have __attribute__,
+ * or we do but we don't know of any better way with this instruction
+ * set to do unaligned loads, so do unaligned loads of big-endian
+ * quantities the hard way - fetch the bytes one at a time and
+ * assemble them.
+ */
+#define EXTRACT_SHORT(p) \
+       ((uint16_t)(((uint16_t)(*((const uint8_t *)(p) + 0)) << 8) | \
+                   ((uint16_t)(*((const uint8_t *)(p) + 1)) << 0)))
+#define EXTRACT_LONG(p) \
+       ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 24) | \
+                   ((uint32_t)(*((const uint8_t *)(p) + 1)) << 16) | \
+                   ((uint32_t)(*((const uint8_t *)(p) + 2)) << 8) | \
+                   ((uint32_t)(*((const uint8_t *)(p) + 3)) << 0)))
+#endif /* unaligned access checks */
 
 #ifdef __linux__
 #include <linux/types.h>
@@ -119,9 +238,9 @@ u_int
 bpf_filter_with_aux_data(const struct bpf_insn *pc, const u_char *p,
     u_int wirelen, u_int buflen, const struct bpf_aux_data *aux_data)
 {
-       register u_int32 A, X;
+       register u_int32_t A, X;
        register bpf_u_int32 k;
-       u_int32 mem[BPF_MEMWORDS];
+       u_int32_t mem[BPF_MEMWORDS];
 
        if (pc == 0)
                /*
@@ -381,7 +500,7 @@ bpf_filter_with_aux_data(const struct bpf_insn *pc, const u_char *p,
                         * can't be unsigned; throw some casts to
                         * specify what we're trying to do.
                         */
-                       A = (u_int32)(-(int32)A);
+                       A = (u_int32_t)(-(int32_t)A);
                        continue;
 
                case BPF_MISC|BPF_TAX: