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 确定的地址,就像您习惯的那样.
在以前的 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 确定的地址,就像您习惯的那样.