3

当动态链接二进制与库时,重定位信息用于绑定不同 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 全局变量?

4

2 回答 2

1

我真的不明白为什么变量在可执行文件中被实例化。

你可以在这里找到答案。

调试器应该如何解析 foo 全局变量

调试器读取符号表(除了调试信息),并且foo 确实在 main 可执行文件b和以下文件中定义liba.so

nm b | grep foo
0000000000600c68 B foo
于 2014-02-16T20:24:03.207 回答
0

(我阅读了@Employed Russian 提供的 Oracle 文档。)

对非 PIC 代码进行全局变量重新实例化,以便以非 PIC 方式取消引用变量,而无需修补非 PIC 代码:

  • 为非 PIC 代码完成了变量的副本;
  • 该变量在可执行文件中实例化;
  • 复制重定位指令用于在动态链接时从源共享对象复制数据;
  • 未使用共享对象中的实例(在完成重定位复制之后)。

复制搬迁说明:

$readelf -r b

Relocation section '.rela.dyn' at offset 0x638 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000600c58  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000600ca8  001200000005 R_X86_64_COPY     0000000000600ca8 foo + 0

对于函数,GOT+PLT 技术的使用方式与它们在 PIC 代码中的使用方式相同。

于 2014-02-17T08:29:15.297 回答