【问题标题】:What is *ABS* section and when to use?什么是 *ABS* 部分以及何时使用?
【发布时间】:2019-05-08 07:23:29
【问题描述】:
// 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 展示了一种有趣的方法,可以通过--defsym*ABS* 中传递一些额外的符号。但我认为传递宏会更容易。

那么*ABS* 部分是什么?什么时候有人会使用它?

编辑

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

我写了一个demo,但是绝对符号的地址好像可以修改。

// 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>

【问题讨论】:

  • /a.out 0x556e06629eef -- 很好的观察。我也很好奇;现在调查它,到目前为止我已经发现了两件事。 1)你得到的不同结果是内核完成ASLR的结果。 2) 如果您静态链接程序 (gcc -static ...),您将在输出中看到未修改的 0xbeef。所以 ISTM 动态加载器仍然通过基地址移动所有内容,包括绝对符号......

标签: c gcc binary linker elf


【解决方案1】:

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

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

至于您的问题 w.r.t 没有存储在 .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-相对寻址用于获取foo 的地址。

113d:       48 8d 35 ab ad 00 00    lea    rsi,[rip+0xadab]        # beef <foo>
                    1140: R_X86_64_PC32     foo-0x4

编译器不知道它会是一个绝对符号,所以它会生成它所做的代码(与普通符号一样)。 rip是指令指针,所以它依赖于程序被ld.so映射到内存后包含.text的段的基地址。

我发现这个answer 揭示了绝对符号的正确用例。

【讨论】:

  • 谢谢!但是我仍然对绝对符号的地址感到困惑。由于很难融入 cmets 我编辑了这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
  • 2011-04-07
  • 1970-01-01
  • 2019-05-16
  • 2016-11-21
  • 2018-09-17
相关资源
最近更新 更多