2017-09-08 17 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

関連する問題