3 ms·
The way I usually deal with this nonsense is to write a function in assembly that returns its pointer argument: stop_messing_with_my_code: mov rax, rdi
by laanaah 3y ago
The way I usually deal with this nonsense is to write a function in assembly that returns its pointer argument:
stop_messing_with_my_code:
mov rax, rdi
ret
extern "C" void *stop_messing_with_my_code(void *);
void *p = stop_messing_with_my_code(malloc(100));
// compiler cant know what my function does, it might have initialized it,
// aligned it properly, returned the correct, type, etc.
Also useful for getting around the idiotic type-based aliasing so you can write custom allocators (i.e a byte pool allocated statically/globally, then allocate data from that which is common in embedded, which is technically not possible in standard C or C++...)
- gpderetta 3y agoLaundering your pointers through no-op inline asm also helps.
- thrtythreeforty 3y agoThis is an extremely useful trick for foiling the optimizer. To make it explicit, it goes like this: asm volatile("" : "+r"(my_val) ::); This means "an empty assembly statement that you must assume contains side effects, and takes my_val as an input register and modifies it somehow." Note that because there is no assembly, this is perfectly portable (across architectures, maybe not across compilers, since only GCC and Clang know this syntax). I've used it to get the compiler to forget that function pointers cannot be null, for example. Of course you are still in "implementation defined behavior" land re: the standard. But it's often pretty easy to reason like the compiler does once you know your target architecture, and once your values are all unconstrained integers.
- Joker_vD 3y ago> a byte pool allocated statically/globally, then allocate data from that which is common in embedded, which is technically not possible Is it? I thought that char-pointers-into-char-arrays are specifically allowed to alias with whatever type. But yeah, it's a shame we don't have an official "launder" function a-la C++: the malloc/calloc/realloc get to launder the pointers they return simply because the standard declares those functions special.
- jilller 3y ago>Is it? I thought that char-pointers-into-char-arrays are specifically allowed to alias with whatever type. I think they mean something like this: char storage[1024]; struct pool pool_of_foos = { storage, sizeof(storage) }; // use storage as my memory struct foo *p = pool_allocate(&pool_of_foos, sizeof(struct foo)); // pool_allocate cannot be implemented in conforming C or C++. // i.e you cannot place a 'struct foo object' in the memory location containing 'char objects' How can malloc() work then you might ask? Well it has defined semantics in the standard, so its essentially a magical special exception, you cannot write such a function yourself in standard C, you must use compiler extensions or assembly. Which is quite insane that it has come to this, I doubt K&R had that as their intention for the language.
- Joker_vD 3y agoAfter a closer re-reading of C's rules on effective type, I think you're actually right, you return pointers-to-structs that point inside a (static or automatic) char array, you have to slice the storage returned from malloc/calloc/realloc. But C++ has std::launder that allows you to do that, see the very last example in [0]: it does placement-new on a char array, then launders the pointer to that array recasted as a pointer-to-struct, and it's supposed to not be UB. [0] https://en.cppreference.com/w/cpp/utility/launder https://en.cppreference.com/w/cpp/utility/launder
- gpderetta 3y agoYou do not need launder for this. Placement new is enough as long as you refer to the newed object via the pointer returned by new. My understanding is that launder exists if you need to recover the pointer to the allocated object directly from the pool pointer. Re C, I'm far from expert, but I think that the issue exists primarily if you try to allocated within an existing named object (like in the parent examu). If you carve out your allocations from a larger chunk of anonymous memory it is supposedly conforming [A lot of "I think", "supposedly"... All object lifetimes and pointer derivation stuff is underspecified in the standards]