Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix unaligned access issues in host-network byte I/O on ARM
Per report in #216 and https://bugs.debian.org/872408, the current implementation may raise SIGBUS on ARM platforms that do not support unaligned memory access. Fix this by using a stack variable and memcpy, which gives the compiler more opportunity to generate correct code. While at it, implement hton* and ntoh* in terms of byteswap intrinsics with an open-coded fallback. This drops the dependency on the platform's implementation, which might not necessarily be consistently fast. Smoke-tested locally on cross-compiled armv5tel-softfloat-linux-gnueabi and armv6j-hardfloat-linux-gnueabi with QEMU TCG. Fixes: #216.
- Loading branch information
Showing
5 changed files
with
218 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
#include <stdint.h> | ||
|
||
#if defined(__linux__) || defined(__CYGWIN__) | ||
#include <endian.h> | ||
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) | ||
#include <sys/endian.h> | ||
#elif defined(__DragonFly__) | ||
#include <sys/endian.h> | ||
#elif defined(__APPLE__) | ||
#include <libkern/OSByteOrder.h> | ||
#define __BYTE_ORDER BYTE_ORDER | ||
#define __BIG_ENDIAN BIG_ENDIAN | ||
#define __LITTLE_ENDIAN LITTLE_ENDIAN | ||
#elif defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__) | ||
/* Assume Windows is always LE. There seems to be no reliable way | ||
to detect endianness there */ | ||
#define __LITTLE_ENDIAN 1234 | ||
#define __BIG_ENDIAN 4321 | ||
#define __BYTE_ORDER __LITTLE_ENDIAN | ||
#else | ||
#error Cannot determine platform byte order. | ||
#endif | ||
|
||
|
||
#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) | ||
|
||
#define apg_bswap16(x) __builtin_bswap16(x) | ||
#define apg_bswap32(x) __builtin_bswap32(x) | ||
#define apg_bswap64(x) __builtin_bswap64(x) | ||
|
||
#elif defined(_MSC_VER) | ||
|
||
#define apg_bswap16(x) _byteswap_ushort(x) | ||
#define apg_bswap32(x) _byteswap_ulong(x) | ||
#define apg_bswap64(x) _byteswap_uint64(x) | ||
|
||
#else | ||
|
||
static inline uint16_t | ||
apg_bswap16(uint16_t) | ||
{ | ||
return ((x << 8) & 0xff00) | (x >> 8) & 0x00ff)); | ||
} | ||
|
||
static inline uint32_t | ||
apg_bswap32(uint32_t x) | ||
{ | ||
return ( | ||
((x << 24) & 0xff000000) | ((x << 8) & 0x00ff0000) | | ||
((x >> 8) & 0x0000ff00) | ((x >> 24) & 0x000000ff) | ||
); | ||
} | ||
|
||
static inline uint64_t | ||
apg_bswap64(uint64_t x) | ||
{ | ||
return ( | ||
((x << 56) & 0xff00000000000000ULL) | | ||
((x << 40) & 0x00ff000000000000ULL) | | ||
((x << 24) & 0x0000ff0000000000ULL) | | ||
((x << 8) & 0x000000ff00000000ULL) | | ||
((x >> 8) & 0x00000000ff000000ULL) | | ||
((x >> 24) & 0x0000000000ff0000ULL) | | ||
((x >> 40) & 0x000000000000ff00ULL) | | ||
((x >> 56) & 0x00000000000000ffULL); | ||
); | ||
} | ||
|
||
#endif | ||
|
||
#if __BYTE_ORDER == __BIG_ENDIAN | ||
|
||
#define apg_hton16(x) (x) | ||
#define apg_hton32(x) (x) | ||
#define apg_hton64(x) (x) | ||
|
||
#define apg_ntoh16(x) (x) | ||
#define apg_ntoh32(x) (x) | ||
#define apg_ntoh64(x) (x) | ||
|
||
#elif __BYTE_ORDER == __LITTLE_ENDIAN | ||
|
||
#define apg_hton16(x) apg_bswap16(x) | ||
#define apg_hton32(x) apg_bswap32(x) | ||
#define apg_hton64(x) apg_bswap64(x) | ||
|
||
#define apg_ntoh16(x) apg_bswap16(x) | ||
#define apg_ntoh32(x) apg_bswap32(x) | ||
#define apg_ntoh64(x) apg_bswap64(x) | ||
|
||
#else | ||
|
||
#error Unsupported byte order. | ||
|
||
#endif | ||
|
||
|
||
static inline void | ||
pack_int16(char *buf, int16_t x) | ||
{ | ||
uint16_t nx = apg_hton16((uint16_t)x); | ||
/* NOTE: the memcpy below is _important_ to support systems | ||
which disallow unaligned access. On systems, which do | ||
allow unaligned access it will be optimized away by the | ||
compiler | ||
*/ | ||
memcpy(buf, &nx, sizeof(uint16_t)); | ||
} | ||
|
||
|
||
static inline void | ||
pack_int32(char *buf, int64_t x) | ||
{ | ||
uint32_t nx = apg_hton32((uint32_t)x); | ||
memcpy(buf, &nx, sizeof(uint32_t)); | ||
} | ||
|
||
|
||
static inline void | ||
pack_int64(char *buf, int64_t x) | ||
{ | ||
uint64_t nx = apg_hton64((uint64_t)x); | ||
memcpy(buf, &nx, sizeof(uint64_t)); | ||
} | ||
|
||
|
||
static inline int16_t | ||
unpack_int16(const char *buf) | ||
{ | ||
uint16_t nx; | ||
memcpy((char *)&nx, buf, sizeof(uint16_t)); | ||
return (int16_t)apg_ntoh16(nx); | ||
} | ||
|
||
|
||
static inline int32_t | ||
unpack_int32(const char *buf) | ||
{ | ||
uint32_t nx; | ||
memcpy((char *)&nx, buf, sizeof(uint32_t)); | ||
return (int32_t)apg_ntoh32(nx); | ||
} | ||
|
||
|
||
static inline int64_t | ||
unpack_int64(const char *buf) | ||
{ | ||
uint64_t nx; | ||
memcpy((char *)&nx, buf, sizeof(uint64_t)); | ||
return (int64_t)apg_ntoh64(nx); | ||
} | ||
|
||
|
||
union _apg_floatconv { | ||
uint32_t i; | ||
float f; | ||
}; | ||
|
||
|
||
union _apg_doubleconv { | ||
uint64_t i; | ||
double f; | ||
}; | ||
|
||
|
||
static inline void | ||
pack_float(char *buf, float f) | ||
{ | ||
union _apg_floatconv v; | ||
v.f = f; | ||
pack_int32(buf, (int32_t)v.i); | ||
} | ||
|
||
|
||
static inline void | ||
pack_double(char *buf, double f) | ||
{ | ||
union _apg_doubleconv v; | ||
v.f = f; | ||
pack_int64(buf, (int64_t)v.i); | ||
} | ||
|
||
|
||
static inline float | ||
unpack_float(const char *buf) | ||
{ | ||
union _apg_floatconv v; | ||
v.i = (uint32_t)unpack_int32(buf); | ||
return v.f; | ||
} | ||
|
||
|
||
static inline double | ||
unpack_double(const char *buf) | ||
{ | ||
union _apg_doubleconv v; | ||
v.i = (uint64_t)unpack_int64(buf); | ||
return v.f; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters