为啥需要重定位同一文件中的全局符号?

Posted

技术标签:

【中文标题】为啥需要重定位同一文件中的全局符号?【英文标题】:Why does global symbol in the same file needed to be relocated?为什么需要重定位同一文件中的全局符号? 【发布时间】:2018-02-17 03:53:47 【问题描述】:

我有一个用于测试的 C 程序:a.c

int a = 0;

static int fa_local()

    a = 78; 
    int b;
    int c;


int fa_global()

    a = 7777;
    fa_local();


int test()

    a = 6666;
    fa_global();


这是它构建后的重定位表:

[freeman@centos-7 link_symbol_test]$ gcc -c a.c
[freeman@centos-7 link_symbol_test]$ readelf -r a.o

Relocation section '.rela.text' at offset 0x5d0 contains 4 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000006  000900000002 R_X86_64_PC32     0000000000000000 a - 8
000000000016  000900000002 R_X86_64_PC32     0000000000000000 a - 8
000000000030  000900000002 R_X86_64_PC32     0000000000000000 a - 8
00000000003e  000a00000002 R_X86_64_PC32     0000000000000010 fa_global - 4

重定位入口是test()中的函数调用fa_global(),偏移量为00000000003e。

[freeman@centos-7 link_symbol_test]$ objdump -dS a.o:

0000000000000010 <fa_global>:

int fa_global()

  10:   55                      push   %rbp
  11:   48 89 e5                mov    %rsp,%rbp
    a = 7777;
  14:   c7 05 00 00 00 00 61    movl   $0x1e61,0x0(%rip)        # 1e <fa_global+0xe>
  1b:   1e 00 00 
    fa_local();
  1e:   b8 00 00 00 00          mov    $0x0,%eax
  23:   e8 d8 ff ff ff          callq  0 <fa_local>

  28:   5d                      pop    %rbp
  29:   c3                      retq   

000000000000002a <test>:

int test()

  2a:   55                      push   %rbp
  2b:   48 89 e5                mov    %rsp,%rbp
    a = 6666;
  2e:   c7 05 00 00 00 00 0a    movl   $0x1a0a,0x0(%rip)        # 38 <test+0xe>
  35:   1a 00 00 
    fa_global();
  38:   b8 00 00 00 00          mov    $0x0,%eax
  3d:   e8 00 00 00 00          callq  42 <test+0x18>

  42:   5d                      pop    %rbp
  43:   c3                      retq

对于 fa_global(),它在同一个文件中。

为什么这个符号需要重新定位, 而静态符号 fa_local() 没有?


2017.9.12更新:优化后的汇编代码

[freeman@centos-7 relocation_test]$ gcc -fno-inline -O2 -c a.c
[freeman@centos-7 relocation_test]$ objdump -dS a.o

a.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <fa_local>:
   0:   c7 05 00 00 00 00 4e    movl   $0x4e,0x0(%rip)        # a <fa_local+0xa>
   7:   00 00 00 
   a:   c3                      retq   
   b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000000010 <fa_global>:
  10:   31 c0                   xor    %eax,%eax
  12:   c7 05 00 00 00 00 61    movl   $0x1e61,0x0(%rip)        # 1c <fa_global+0xc>
  19:   1e 00 00 
  1c:   eb e2                   jmp    0 <fa_local>
  1e:   66 90                   xchg   %ax,%ax

0000000000000020 <test>:
  20:   31 c0                   xor    %eax,%eax
  22:   c7 05 00 00 00 00 0a    movl   $0x1a0a,0x0(%rip)        # 2c <test+0xc>
  29:   1a 00 00 
  2c:   e9 00 00 00 00          jmpq   31 <test+0x11>

但我仍然看到重定位条目:

000000000000002d R_X86_64_PC32 fa_global-0x0000000000000004

【问题讨论】:

尝试优化编译。 @o11c,似乎因为 fa_global 有点像被内联到 test(),这意味着 test() 实际上并没有调用 fa_global()。当然,它仍然可以被外部访问。 我尝试了__attribute__((noinline)),优化仍然删除了重定位。 @o11c,请在最后部分查看我的更新。您是否在编译后但在链接之前检查目标文件? 使用objdump -drwC 在反汇编中包含重定位注释。 (-r 选项)。我也喜欢-Mintel。这被重新询问为 Why function that refers to a global function in the same section can only be solved at link time while local functions will be solve at compile time? - 仍然没有答案为什么 GAS 或 LLVM 在组装时不能完全解析 call fa_globaljmp fa_global。 (GAS 对 jmp fa_global 有效,使用 2 字节 jmp rel8,但 clang 没有) 【参考方案1】:

fa_local 是一个函数。编译器可以确定它与调用点的偏移量。 call指令采用PC相对寻址方式,不需要绝对地址,直接发出代码。

相反,a 符号位于内存的不同部分,即在编译时无法确定其偏移量的可写段。链接器在重定位阶段执行此操作。

【讨论】:

嗨@chqrlie,实际上我的问题是关于 fa_global 函数 fa_global 具有外部链接,这可以解释为什么编译器会生成重定位条目。对fa_local 的调用有可能被内联扩展,因此没有重定位条目,因为没有调用操作码。您可以发布为fa_global 生成的代码吗? 似乎在未设置 -Ox 时默认不会内联。 man gcc, -fno-inline: "除了那些标有 "always_inline" 属性的函数之外,不要展开任何内联函数。这是不优化时的默认值。"【参考方案2】:

就在这里该函数在同一个文件中,但它是非静态的,也可以从稍后编译的其他文件中调用。

编译器不知道这是否会发生,所以它必须“为最坏的情况做准备”。

【讨论】:

为什么外部程序需要重定位表才能链接到fa_global?我认为他们可以通过符号表链接到 fa_global 吗?我尝试了“objdump -dS”,发现重定位条目是汇编 callq 指令将跳转到的地址。 (我已将此更新到帖子中) 如果您尝试链接另一个也定义了fa_global.o,您会收到一个错误:multiple definitions of 'fa_global'。所以这并不能真正解释这一点。可能函数 可以 被标记为忽​​略多个相同的定义(以支持 C 和/o C++ inline 关键字链接语义),并且即使可见定义 没有,GAS 也可能缺少可能的优化'不要使用那个。

以上是关于为啥需要重定位同一文件中的全局符号?的主要内容,如果未能解决你的问题,请参考以下文章

图片+代码:GCC 链接过程中的重定位过程分析.md

图片+代码:GCC 链接过程中的重定位过程分析.md

学习笔记

GCC中可执行文件的非GOT样式重定位

符号解析

elf文件中的.plt .rel.dyn .rel.plt .got .got.plt的关系