当动态链接二进制与库时,重定位信息用于绑定不同 ELF 对象的变量/函数。然而 DWARF 不受重定位的影响:调试器应该如何解析全局变量?
假设我有 liba.so (ac) 定义一个全局变量(使用 GNU/Linux 和 GCC 或 Clang):
#include <stdio.h>
int foo = 10;
int test(void) {
printf("&foo=%p\n", &foo);
}
和一个与 liba.so (bc) 链接的程序 b:
#include <stdio.h>
extern int foo;
int main(int argc, char** argv) {
test();
printf("&foo=%p\n", &foo);
return 0;
}
我希望“foo”将在 liba.so 中实例化,但实际上它在 liba.so 和 b 中都实例化:
$ ./b
&foo=0x600c68 # <- b .bss
&foo=0x600c68 # <- b .bss
使用的 foo 变量(b 和 lib.so 都使用)在 b 的 .bss 中,而不是在 liba.so 中:
[...]
0x0000000000600c68 - 0x0000000000600c70 is .bss
[...]
0x00007ffff7dda9c8 - 0x00007ffff7dda9d4 is .data in /home/foo/bar/liba.so
0x00007ffff7dda9d4 - 0x00007ffff7dda9d8 is .bss in /home/foo/bar/liba.so
foo 变量被实例化了两次:
在 liba.so 中一次(与程序 b 链接时不使用此实例)
一次在 b 中(这个实例是 b 中另一个的实例)。
(我真的不明白为什么在可执行文件中实例化了变量。)
DWARF 信息中的 b 中只有一个声明(如预期的那样):
$ readelf -wi b
[...]
<1><ca>: Abbrev Number: 9 (DW_TAG_variable)
<cb> DW_AT_name : foo
<cf> DW_AT_decl_file : 1
<d0> DW_AT_decl_line : 3
<d1> DW_AT_type : <0x57>
<d5> DW_AT_external : 1
<d5> DW_AT_declaration : 1
[...]
在 liba.so 中找到一个位置:
$ readelf -wi liba.so
[...]
<1><90>: Abbrev Number: 5 (DW_TAG_variable)
<91> DW_AT_name : foo
<95> DW_AT_decl_file : 1
<96> DW_AT_decl_line : 3
<97> DW_AT_type : <0x57>
<9b> DW_AT_external : 1
<9b> DW_AT_location : 9 bloc d'octets: 3 d0 9 20 0 0 0 0 0 (DW_OP_addr: 2009d0)
[...]
此地址是 liba.so (.data) 中 foo 的(未使用的)实例的位置。
- 我最终得到了 foo 全局变量的 2 个实例(在 liba.so 中,一个在 b 中);
- DWARF 只能看到第一个;
- 只使用第二个。
调试器应该如何解析 foo 全局变量?