Search code examples
cstrict-aliasing

How to workaround strict aliasing in C?


My aim is to make an generic arena allocator with an buffer stored in the .bss section of the executable to avoid any allocations in the actual program, however this has problems in C with strict aliasing. If I define the buffer as static char buffer[BUFFER_SIZE];, then by strict aliasing I can't use pointers to it that are of another type without causing undefined behavior. As I want this allocator to be generic, so I need to be able to allocate any type in it.

I can't use memcpy() because the allocator needs to return a pointer that can be used in any way, not an copy of the value. I also cannot use union-based type punning because, if I'm not mistaken, it doesn't work through pointers (see the second code block here).

The only way that I know to work around this in C is to disable strict aliasing entirely with the -fno-strict-aliasing flag. In this blog post the author attempts to do the same thing, but the solution he arrives is to use inline assembly to "launder" the pointer so that the optimizer can't apply the strict aliasing optimization, but this seems very fragile.

Is there any better way to do something like this? Ideally there would be an builtin that allows to clearly mark a cast or region of memory to be ignored by strict aliasing.


Solution

  • Put the allocation code in a separate module or modules from the code using it (the client code). Then, as long as you are building with traditional compilers and linkers that do not perform cross-module optimization, aliasing necessarily works due to the separate-module nature of the build tools rather than due to the requirements of the C standard. (The allocation code itself needs to observe the aliasing rules.)

    The reason this works is that when compiling some client code of the allocation routines, the compiler sees only the client code and not the allocation code. It cannot know the declared type used by the allocation code. After you compile the client code, you could link it with your allocation routines or you could, hypothetically, link it with some other code in which all the addresses returned by the allocation code have declared types matching their uses or with some other code in which all the addresses returned by the allocation code are provided by the malloc defined by the C standard rather than your own code. Since the client code could be linked with this other code, the compiler must generate an object module that works with it as specified by the C standard. However, the behavior of your allocation code and the behavior of this other code are identical in the view of the interfaces between them: The client code calls an allocation routine and receives a pointer or it calls a release routine and provides a pointer, and so on. Therefore, when the compiler generates code that works with the other standard-conforming code, it must also work with your actual allocation code.