什么是* ABS *部分以及何时使用?

时间:2019-05-08 07:23:29

标签: c gcc binary linker elf

// foo.c
int main() { return 0; }

当我编译上面的代码时,我注意到*ABS*中有一些符号:

$ gcc foo.c
$ objdump -t a.out | grep ABS
0000000000000000 l    df *ABS*  0000000000000000              crtstuff.c
0000000000000000 l    df *ABS*  0000000000000000              foo.c
0000000000000000 l    df *ABS*  0000000000000000              crtstuff.c
0000000000000000 l    df *ABS*  0000000000000000              

看起来它们是一些调试符号,但是调试信息不​​是存储在.debug_info节之类的地方吗?

根据man objdump

  

* ABS *,如果该部分是绝对的(即未与任何部分连接)

我不明白,因为这里没有给出示例。

问题here显示了一种有趣的方式,可以通过*ABS*--defsym中传递一些额外的符号。但是我认为通过传递宏可能会更容易。

那么*ABS*部分是什么,有人何时使用它?

编辑

  

绝对符号不会被重定位,它们的虚拟地址(在您给出的示例中为0000000000000000)是固定的。

我写了一个演示,但似乎可以修改绝对符号的地址。

// foo.c

#include <stdio.h>

extern char foo;

int main()
{
  printf("%p\n", &foo);
  return 0;
}
$ gcc foo.c -Wl,--defsym,foo=0xbeef -g

$ objdump -t a.out | grep ABS
0000000000000000 l    df *ABS*  0000000000000000              crtstuff.c
0000000000000000 l    df *ABS*  0000000000000000              foo.c
0000000000000000 l    df *ABS*  0000000000000000              crtstuff.c
0000000000000000 l    df *ABS*  0000000000000000
000000000000beef g       *ABS*  0000000000000000              foo

# the addresses are not fixed
$ ./a.out
0x556e06629eef
$ ./a.out
0x564f0d7aeeef
$ ./a.out
0x55c2608dceef

# gdb shows that before entering main(), &foo == 0xbeef
$ gdb a.out
(gdb) p &foo
$1 = 0xbeef <error: Cannot access memory at address 0xbeef>
(gdb) br main
Breakpoint 1 at 0x6b4: file foo.c, line 7.
(gdb) r
Starting program: /home/user/a.out

Breakpoint 1, main () at foo.c:7
7         printf("%p", &foo);
(gdb) p &foo
$2 = 0x55555555feef <error: Cannot access memory at address 0x55555555feef>

1 个答案:

答案 0 :(得分:1)

如果您查看其他符号,可能会找到一个索引(如果读者为您进行映射,则可能会找到部分名称)来代替*ABS*。这是节标题表中的节索引。它指向在其中定义符号的节的节标题(如果您正在查看的对象中未定义,则指向SHN_UNDEF(零)。因此,符号的值(虚拟地址)将使用相同的值进行调整,该值在加载过程中会调整其包含部分。 (此过程称为 relocation 。)对于绝对符号(具有特殊值SHN_ABS作为其st_shndx)不是这样。绝对符号不会重定位,它们的虚拟地址(在您给出的示例中为0000000000000000)是固定的。

这种绝对符号有时用于存储一些元信息。特别是,编译器可以创建符号名称与其编译的翻译单元名称相同的符号。此类符号不是链接或运行程序所必需的,它们仅适用于人类和二进制处理工具。

对于您的问题,没有将其存储在.debug_info节中的原因(以及即使未指定调试开关也发出此信息的原因),答案是这是一回事;它只是符号表(.symtab)。当然,调试也需要它,但是它的主要目的是链接对象(.o)文件。默认情况下,它保留在链接的可执行文件/库中。您可以使用strip摆脱它。

我在这里写的大部分内容都在man 5 elf中。


我认为不支持/不应将您正在做的事情(使用--defsym用于动态链接。查看编译器输出(gcc -S -masm=intel),我看到了

lea     rsi, foo[rip]

或者,如果我们查看objdump -M intel -rD a.out(与-q链接以保留重定位),则会看到同一件事:rip相对寻址用于获取{{ 1}}。

foo

编译器不知道它将成为一个绝对符号,因此它会生成它所执行的代码(与普通符号一样)。 113d: 48 8d 35 ab ad 00 00 lea rsi,[rip+0xadab] # beef <foo> 1140: R_X86_64_PC32 foo-0x4 是指令指针,因此在rip将程序映射到内存之后,它取决于包含.text的段的基址。

我发现answer可以说明绝对符号的正确用例。