C 为什么需要重新定位同一文件中的全局符号?

C 为什么需要重新定位同一文件中的全局符号?,c,gcc,symbols,object-files,relocation,C,Gcc,Symbols,Object Files,Relocation,我有一个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 [f

我有一个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(),其偏移量为0000000000 3e

[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
[freeman@centos-7链接符号测试]$objdump-dS a.o:
0000000000000010 :
int fa_global()
{
10:55%rbp
11:48 89 e5 mov%rsp,%rbp
a=7777;
14:c7 05 00 00 61动产$0x1e61,0x0(%rip)#1e
1b:1e 00 00
fa_local();
1e:b8 00 mov$0x0,%eax
23:e8 d8 ff ff callq 0
}
28:5d流行百分比rbp
29:c3 retq
00000000000000 2A:
int测试()
{
2a:55%的rbp
2b:48 89 e5 mov%rsp,%rbp
a=6666;
2e:c7 05 00 0a movl$0x1a0a,0x0(%rip)#38
35:1a 00
fa_global();
38:b8 00 mov$0x0,%eax
3d:e8 00呼叫42
}
42:5d流行百分比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>
[freeman@centos-7重新定位测试]$gcc-fno内联-O2-c a.c
[freeman@centos-7重新定位测试]$objdump-dS a.o
a、 o:文件格式elf64-x86-64
第节的分解。正文:
0000000000000000 :
0:c7 05 00 4e移动$0x4e,0x0(%rip)#a
7:   00 00 00 
a:c3-retq
b:0f 1f 44 00 nopl 0x0(%rax,%rax,1)
0000000000000010 :
10:31 c0异或%eax,%eax
12:c7 05 00 00 61动产$0x1e61,0x0(%rip)#1c
19:1E00
1c:eb e2 jmp 0
1e:66 90 xchg%ax,%ax
0000000000000020 :
20:31 c0异或%eax,%eax
22:c7 05 00 0a movl$0x1a0a,0x0(%rip)#2c
29:1a 00
2c:e9 00 jmpq 31
但我仍然看到搬迁入口:


00000000000000 2D R\U X86\U 64\U PC32 fa\U global-0x0000000000000004
fa\U local
是一个函数。编译器可以确定它与调用点的偏移量。它对调用指令使用PC相对寻址模式,因此不需要绝对地址,可以直接发出代码

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

此处的函数位于同一文件中,但它是非静态的,也可以从以后编译的其他文件中调用


编译器无法知道是否会发生这种情况,因此它必须“做好最坏的准备。”

Hi@chqrlie,实际上我的问题是fa_全局函数
fa_global
具有外部链接,这可能解释了编译器生成重定位项的原因。对
fa_local
的调用可能是内联展开的,因此没有重新定位条目,因为没有调用操作码。您可以发布为
fa_global
生成的代码吗?如果未设置-Ox,则默认情况下它不会内联。man gcc,-fno inline:“除了标记有“always_inline”属性的函数外,不要扩展任何内联函数。这是不优化时的默认值。”为什么外部程序需要重新定位表来链接到fa_global?我想他们可以通过符号表链接到fa_global?我尝试了“objdump-dS”,发现重新定位条目是汇编callq指令将跳转到的地址。(我已将此更新到帖子中)尝试使用优化进行编译。@o11c,似乎可以工作,因为fa_global在某种程度上类似于内联到test(),这意味着test()实际上不调用fa_global()。当然,它仍然可以被外部访问。我甚至尝试了
\uuuu属性(noinline))
,优化仍然会删除重新定位。@o11c,请参阅最后一部分的更新。是否在编译后但链接之前检查对象文件?