hochl
hochl

Reputation: 12960

Macro to handle unaligned 32 bit memory access

I have inherited a very ancient (start of 2000s) Code base that consists of a driver and a kernel module and it crashes for some combinations of compiler flags/architectures. At some point an offset is incorrect for an unknown reason and when I skimmed the code base I found a lot of unaligned access and wanted to clean up this part to remove these potential issues. Example:

uint32_t* p = ....; // <-- odd address on right side.
uint32_t b = *p + 1;

I know that current processors handle such cases without noticeable delays, and I had problems finding hardware that would trigger a BUS error (I once had a RPi 1 that would produce bus errors when setting /proc/cpu/alignment to 4). I wanted to make sure the compiler generates code that correctly access the memory. My first draft for reading a 32 bit integer and converting it from network to host byte order (there was already a macro for that) for an unaligned address was thus (dest and src are pointers to some memory):

#define SWAP32(dest, src) \
    do { \
        uint32_t tmp_ = 0; \
        memcpy(&tmp_, (const char*)(src), sizeof(tmp_)); \
        tmp_ = bswap_32(tmp_); \
        memcpy((char*)(dest), &tmp_, sizeof(tmp_)); \
    } while(0)

I then wanted to create a macro to wrap pointer accesses for other cases that were not yet wrapped in a macro and came up with the following solution (sample contains code to make it compile):

#include <stdint.h>
#include <stdio.h>

struct __attribute__((packed)) unaligned_fix_32 { uint32_t n; };

#define GET32(x)    ((struct unaligned_fix_32*)(x))->n

int main(int argc, char* argv[])
{
    char mem[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
    uint32_t n = GET32(&mem[1]);
    printf("0x%8.8x\n", n);
}

My goal is to wrap pointer accesses inside this macro everywhere. The idea is that, knowing the struct is packed, the compiler will generate code that accesses memory in a way that avoids a potential bus error on architectures that cannot access unaligned memory.

EDIT: I changed my code to the following new version:

static inline uint32_t get_be_unaligned_u32(const void* p)
{
    uint32_t u32;
    memcpy(&u32, (const char*)p, sizeof(u32));
    return bswap_32(u32);
}
#define GET32(src)   get_be_unaligned_u32(src)

Please answer questions also for the new version:

  1. Is this a workable solution on real hardware? Can anyone check this? Using -fsanitize=alignment produces the proper exceptions without the attribute packed, but the generated assembler simply checks addresses with & 0x3 and raises a trap if it is not zero.
  2. Is this undefined behavior?
  3. Are there better/other methods to detect/fix/simulate unaligned access inside a C/C++ program?
  4. Can I somehow do this using QEmu?
  5. Is the SWAP32 macro UB?

Upvotes: 0

Views: 415

Answers (0)

Related Questions