objdump 报告的符号偏移量不再匹配 运行-time 偏移量

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

在以前的 GCC 版本中,objdump 报告的符号偏移量与代码实际执行期间使用的偏移量相匹配。例如:

$ 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
...

确实,当运行编译二进制文件时,执行期间使用的符号的实际地址与objdump报告的地址相同:

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

(gdb) p/x &g_someGlobal
 = 0x80496f4

不幸的是,在最近发布的 Debian Stretch 中重复相同的命令序列,却发生了这种情况:

$ 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

符号偏移量现在似乎是一个小得多的值 - 这...

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

...不再与 运行 时使用的匹配。

我在这里犯错了吗?工具的使用是否在此期间发生了变化?如果不是,此更改背后的原因是什么?

无论如何 - 理论上必须有一种方法来获取承载变量的 .bss 段的 "expected run-time offset"(objdump 确实报告它将被放置在哪个部分,所以最后运行-time 位置可以通过添加 .bss 偏移来计算)。在我初步尝试这样做时,我还没有找到一种方法来获得它,但是:

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

这似乎没有报告 0x80000000 的 "shift",在本例中似乎发生在 .bss 托管变量上。

(即使对于这个新的执行环境这是 "magic constant",那是否也适用于 .data 变量?说实话,我讨厌魔法值- 以前,无论符号位于何处,objdump -x 的结果都是准确的...)

欢迎提供解决此问题的任何信息。理想情况下,我想重现 objdump -x 的旧行为 - 即静态地(不是在 运行 时间)获取符号的 运行 时间地址的值,从托管它的 ELF。

更新:我对 GCC7.1.0 进行了自定义编译(从源代码),这不再可重现。也许这是 GCC 6.3(Debian Stretch 中打包的版本)的回归...

原因是 Debian 的 gcc 包是用 --enable-default-pie 构建的。在 PIE 可执行文件中,ELF 段可以加载到任意(只要正确对齐)基地址,通常由加载程序随机选择。您在 ELF 文件中看到的符号地址是相对于其加载的基地址的偏移量,而不是绝对虚拟地址。

如果您没有 want/need PIE,您可以将 -no-pie 添加到 link 命令行以获取 link-time 确定的地址,就像您习惯的那样.