In my C code I have an external symbol, some_symbol. I need to get the address of the memory position just preceding that symbol (&some_symbol-1). This used to work fine in older versions of gcc, but on gcc 12.2.0 with -O2 enabled I get an array-bounds warning:
#include <stdio.h>
extern void *some_symbol;
int main (void) {
printf ("%p\n",&some_symbol-1);
return 0;
}
$ cc -Wall -O2 -c x.c -o x.o
x.c: In function ‘main’:
x.c:6:9: warning: array subscript -1 is outside array bounds of ‘void[8]’ [-Warray-bounds]
6 | printf ("%p\n",&some_symbol-1);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
x.c:3:14: note: at offset -8 into object ‘some_symbol’ of size 8
3 | extern void *some_symbol;
| ^~~~~~~~~~~
I understand why this is dangerous. But in this case, I'm referring to a symbol from the run time system of another language, and the RTS documents that there is space above this symbol, which in some cases needs to be referenced. Here is an example of what the RTS does:
.data
some_symbol_name:
.string "some_symbol\0"
.text
.quad some_symbol_name
.globl some_symbol
some_symbol:
# ...
I can circumvent the problem using uintptr_t:
printf ("%s\n",*(char**)(&some_symbol-1));
printf ("%s\n",*(char**)((intptr_t)&some_symbol-sizeof(void*)));
Both of these correctly print some_symbol, but the first gives a compilation warning similar to the one above.
The API of the external system guarantees that there is readable data above some_symbol, but how do I tell this to gcc? Using uintptr_t everywhere is unwieldy.
I know that I can disable the warning (locally), but would prefer not to.
Is there a way to specify in the extern declaration of the symbol that there is space before the symbol that can be referenced?
Pointer arithmetic is only well-defined within the bounds of an array. In case of single variables, they are to be regarded as an array of 1 item.
In this case you attempt pointer arithmetic on a single void** so &some_symbol-1 invokes undefined behavior.
Casting to uintptr_t is the only sensible solution. That is: (uintptr_t)&some_symbol - 1. Or if that's for some reason too unwieldy, perhaps you could cook up a function-like macro?
#define get_offset(ptr, n) ((uintptr_t)(ptr) + sizeof(*ptr)*(n))
Usage:
#include <stdio.h>
#include <inttypes.h>
#define get_offset(ptr, n) ((uintptr_t)(ptr) + sizeof(*ptr)*(n))
extern void *some_symbol;
int main (void) {
printf("%"PRIuPTR "\n", (uintptr_t)&some_symbol);
printf("%"PRIuPTR "\n", get_offset(&some_symbol, -1));
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With