Search code examples
cunixgccobjdump

Symbol offsets reported by objdump no longer match run-time offsets


In previous versions of GCC, the symbol offsets reported by objdump matched the ones used during the actual execution of the code. For example:

$ cat example.c
#include <stdio.h>

int g_someGlobal = 0;

int main()
{
    printf("%d\n", g_someGlobal);
    return 0;
}

$ gcc-6 -v
...
gcc version 6.1.1 20160802 (Debian 6.1.1-11)


$ gcc-6 -O0 -g -o example example.c

$ objdump -x example | grep Global
...
080496f4 g     O .bss       00000004              g_someGlobal
...

And indeed, when running the binary, the actual address of the symbol used during execution is the same one as that reported by objdump:

$ gdb ./example
...
(gdb) start
Temporary breakpoint 1, main () at example.c:10
10          printf("%d\n", g_someGlobal);

(gdb) p/x &g_someGlobal
$1 = 0x80496f4

Unfortunately, repeating the same sequence of commands in the recently released Debian Stretch, this happens instead:

$ gcc-6 -v
...
gcc version 6.3.0 20170415 (Debian 6.3.0-14)


$ gcc-6 -O0 -g -o example example.c

$ objdump -x example | grep Global
00002020 g     O .bss   00000004              g_someGlobal

The symbol offset now seems to be a much smaller value - which...

$ gdb ./example
...
(gdb) start
...
Temporary breakpoint 1, main () at example.c:7
7               printf("%d\n", g_someGlobal);
(gdb) p/x &g_someGlobal
$1 = 0x80002020

...no longer matches with the one used at runtime.

Am I making a mistake here? Has the usage of the tools changed in the meantime? If not, what is the reasoning behind this change?

Regardless - in theory there must be a way to get the "expected run-time offset" of the .bss segment that hosts the variable (objdump does report which section it will be placed in, so the final run-time position could be calculated by adding the .bss offset). In my preliminary attempts to do so, I have not found a way to get this, though:

$ readelf --sections example | grep bss
[26] .bss         NOBITS     0000201c 00101c 000008 00  WA  0   0  4

This doesn't seem to report the "shift" of 0x80000000 that seems to happen to .bss-hosted variables in this example.

(Even if that is a "magic constant" for this new execution environment, does that apply to the .data variables, too? And to be honest, I hate magic values - previously, whatever came out of objdump -x was accurate, regardless of where the symbols resided...)

Any information to resolve this most welcome. Ideally, I'd like to reproduce the old behavior of objdump -x - i.e. to statically (NOT at run-time) get the value of the run-time address of a symbol, from the ELF that hosts it.

UPDATE: I did a custom compile (from sources) of GCC7.1.0, and this is no longer reproducible. Perhaps this was a regression in GCC 6.3 (the version packaged in Debian Stretch)...


Solution

  • The reason is that Debian's gcc package is built with --enable-default-pie. In a PIE executable, the ELF segments can be loaded at an arbitrary (as long as it's properly aligned) base address, usually chosen randomly by the loader. The symbol addresses you see in the ELF file are offsets relative to the base address it's loaded at, rather than absolute virtual addresses.

    If you don't want/need PIE, you can add -no-pie to link command line to get link-time determined addresses like you're used to.