Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Computing the address of the memory position before an external symbol in C

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?


1 Answers

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;
}
like image 94
Lundin Avatar answered Feb 05 '26 05:02

Lundin