2017-09-08 177 views
0

我有測試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

+0

嘗試使用優化進行編譯。 – o11c

+0

@ o11c,似乎它的工作原因是fa_global有點像被內聯到test(),這意味着test()實際上不會調用fa_global()。但當然,它仍然可以通過外部訪問。 – Freeman

+0

我甚至用'__attribute __((noinline))'嘗試過,優化仍然會消除重定位。 – o11c

回答

1

fa_local是一個函數。編譯器可以確定它與調用點的偏移量。它爲呼叫指令使用PC相對尋址模式,因此它不需要絕對地址並可以直接發送代碼。

相反,a符號位於內存的不同部分,即在編譯時無法確定偏移量的可寫段。鏈接器在重定位階段執行此操作。

+0

嗨@chqrlie,其實我的問題是fa_global函數 – Freeman

+0

'fa_global'有外部鏈接,這可以解釋爲什麼編譯器會生成一個重定位條目。有可能對'fa_local'的調用內嵌擴展,所以沒有重定位項,因爲沒有調用操作碼。你能發佈爲'fa_global'生成的代碼嗎? – chqrlie

+0

當-Ox未設置時,它似乎不會默認內聯。 man gcc,-fno-inline:「不要將任何內聯函數擴展爲標記爲」always_inline「屬性的內聯函數,這是未優化時的默認值。」 – Freeman

0

就在這裏該函數在同一個文件中,但是非靜態的,它也可以從稍後編譯的其他文件調用。

編譯器無法知道這是否會發生,所以它必須「爲最壞的情況做好準備」。

+0

爲什麼外部程序需要重定位錶鏈接到fa_global?我認爲他們可以通過符號表鏈接到fa_global? 我試過「objdump -dS」,找出重定位入口是程序​​集callq指令將跳轉到的地址。 (我更新了這個帖子) – Freeman

相關問題