为什么需要重新定位同一文件中的全局符号?
我有测试C程序:AC为什么需要重新定位同一文件中的全局符号?
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();
}
这是它的重定位表生成后:
[[email protected] link_symbol_test]$ gcc -c a.c
[[email protected] 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
搬迁项是funcation调用fa_global()在试验(+),其中有偏移量00000000003e。
[[email protected] 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日更新:优化后汇编代码
[[email protected] relocation_test]$ gcc -fno-inline -O2 -c a.c
[[email protected] 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
fa_local
是一个函数。编译器可以确定它与调用点的偏移量。它为呼叫指令使用PC相对寻址模式,因此它不需要绝对地址并可以直接发送代码。
相反,a
符号位于内存的不同部分,即在编译时无法确定偏移量的可写段。链接器在重定位阶段执行此操作。
就在这里该函数在同一个文件中,但是非静态的,它也可以从稍后编译的其他文件调用。
编译器无法知道这是否会发生,所以它必须“为最坏的情况做好准备”。
为什么外部程序需要重定位表链接到fa_global?我认为他们可以通过符号表链接到fa_global? 我试过“objdump -dS”,找出重定位入口是程序集callq指令将跳转到的地址。 (我更新了这个帖子) – Freeman
尝试使用优化进行编译。 – o11c
@ o11c,似乎它的工作原因是fa_global有点像被内联到test(),这意味着test()实际上不会调用fa_global()。但当然,它仍然可以通过外部访问。 – Freeman
我甚至用'__attribute __((noinline))'尝试过,优化仍然会消除重定位。 – o11c