为啥需要重定位同一文件中的全局符号?
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_global
或 jmp 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 也可能缺少可能的优化'不要使用那个。以上是关于为啥需要重定位同一文件中的全局符号?的主要内容,如果未能解决你的问题,请参考以下文章