あなたはsymbol-interpositionを構築していないが(-fPIC
の副作用を)有効になって、そのcall
宛先アドレスが潜在的に静的に同じ実行可能ファイルにリンクされている別のオブジェクトファイル内のアドレスへのリンク時に解決することができます。 (例えば、gcc foo.o bar.o
)。
シンボルが唯一あなたが(gcc foo.o -lbar
)へのリンク動的しているライブラリで発見された場合は、call
はサポートにPLTを通じてindirectedする必要があります。
は、今これはトリッキーな部分である:without -fPIC
or -fPIE
、GCCはまだ直接関数を呼び出しASM発する:
int puts(const char*); // puts exists in libc, so we can link this example
void call_puts(void) { puts("foo"); }
# gcc 5.3 -O3 (without -fPIC)
movl $.LC0, %edi # absolute 32bit addressing: slightly smaller code, because static data is known to be in the low 2GB, in the default "small" code model
jmp puts # tail-call optimization. Same as call puts/ret, except for stack alignment
をしかし、あなたがリンクされたバイナリを見れば: (this Godbolt compiler explorer linkに、「バイナリ」ボタンをクリックしてくださいリンクの際
# disassembled linker output
mov $0x400654,%edi
jmpq 400490 <[email protected]>
)
gcc -S
ASM出力と
objdump -dr
分解の間を切り替えるには、
puts
への呼び出しは、「魔法」を通じて間接に交換しました
[email protected]
、および
[email protected]
定義がリンクされた実行可能ファイルに存在します。
これはどのように動作するのか分かりませんが、リンク時に共有ライブラリにリンクしています。重要なことは、関数プロトタイプを共有ライブラリにあるものとしてマークするためにヘッダファイルに何も必要としないことです。 puts
を自分で宣言するのと同じように、<stdio.h>
を含めると同じ結果になります。 (これは非常にお勧めしません。それが唯一のヘッダに宣言で正しく動作するCの実装のために、おそらく法的だしかし、Linux上で動作するように起こる。。)
位置に依存しない実行ファイルをコンパイルする場合(-fPIE
で)、リンクされたバイナリは、-fPIC
とまったく同じように、PLTを介してputs
にジャンプします。しかし、コンパイラのアセンブラ出力が異なる(上記godboltリンク上でそれを自分で試してください):
call_puts: # compiled with -fPIE
leaq .LC0(%rip), %rdi # RIP-relative addressing for static data
jmp [email protected]
関数への呼び出しのためのPLTによるコンパイラ軍の間接それがために定義を参照することはできません。なぜか分からない。 PIEモードでは、共有ライブラリではなく実行可能ファイルのコードをコンパイルしています。リンカは、複数のオブジェクトファイルを、実行可能ファイルに定義されている関数間の直接呼び出しで、位置に依存しない実行可能ファイルにリンクできなければなりません。私はLinux(私のデスクトップとゴッドボルト)でテストしています。OS Xではなく、gcc -fPIE
がデフォルトであると仮定しています。これはIDKとは異なる設定になっている可能性があります。 -fPIC
代わりの-fPIE
で
は、物事はさらに悪化している。でも、PLTを通過する必要があり、同じコンパイル単位内で定義されたグローバル関数の呼び出し、symbol interpositionをサポートします。 (例えばLD_PRELOAD=intercept_some_functions.so ./a.out
)-fPIC
と-fPIE
間
違いはPIEは、同じコンパイル単位で機能のためのシンボルの介在をとることができないが、PICはないことを主としています。 OS Xは、位置に依存しない実行可能ファイルと共有ライブラリを必要としますが、ライブラリのコードを作成するときとコンパイラが実行可能ファイルのコードを作るときとで異なることがあります。
このGodbolt exampleには、PICモードとPIEモードに関するものを示すいくつかの機能があります。 call_puts()
は、PICモードの別の関数にインラインで挿入することはできず、PIEのみをインライン化することはできません。
も参照してください。Shared object in Linux without symbol interposition, -fno-semantic-interposition error。あなたがアドレスが移転に基づいて、リンク時にリンカによって置き換えられます0をプレースホルダている.o
から解体出力を見ているmov $0, %edi
によって困惑
ELFオブジェクトファイル内の情報だから@Leandrosはobjdump -r
を提案した。
同様に、call
マシンコード内の相対変位は、リンカーがまだ入力していないため、すべてゼロです。
再配置エントリを表示するには、 'objdump -dr'を使用します。 – Jester
FYI、 'clang -S'は、あなたのmov 0x0/callqのペアの代わりにこれを出します:' leaq L_.str(%rip)、%rdi; callq _give_me_a_ptr'です。 –
PLTについては、PICをコンパイルする場合にのみ使用され、 '-fPIC'フラグを使用します。 – Jester