コードは機能的には同じですがreturn *address
、このwrtと完全に同等ではありません。生成されたバイナリ/オブジェクトファイルに。
ELFでは、前方参照を使用すると(つまりmov $1f, ...
、アセンブリローカルラベルのアドレスを取得するために)、いわゆる再配置が作成されます。再配置とは、リンク/ロード時にのみ認識される値を挿入するための、リンカーへの(実行可能ファイルの作成時または後で実行可能ファイル/ライブラリのロード時の動的リンカーへの)命令です。オブジェクトコードでは、これは次のようになります。
セクション.textの逆アセンブル:
0000000000000000:
0:b8 00 00 00 00 mov $ 0x0、%eax
5:0f b6 07 movzbl(%rdi)、%eax
8:c3 retq
.text
実際には正しくありませんが、ここでは値(セクションのオフセット1)がゼロであることに注意してください。これは、実行中のコードのどこで関数が終了するかによって異なります。最終的にこれを知ることができるのは(ダイナミック)リンカだけであり、このメモリがロードされるときに更新する必要があるという情報は、実際にはオブジェクトファイルに配置されます。
$ readelf -a xqf.o
ELFヘッダー:
[...]
セクションヘッダー:
[Nr]名前タイプアドレスオフセット
サイズEntSizeフラグリンク情報整列
[0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[1] .text PROGBITS 0000000000000000 00000040
0000000000000009 0000000000000000 AX 0 0 16
[2] .rela.text RELA 0000000000000000 000004e0
0000000000000018 0000000000000018 10 1 8
[...]
オフセット0x4e0の再配置セクション'.rela.text'には、次の1つのエントリが含まれています。
オフセット情報タイプSym。値記号 名前+加数
000000000001 00020000000a R_X86_64_32 0000000000000000 .text + 8
[...]
このELFセクションのエントリには次のように書かれています。
- セクションへのオフセット
1
を見てください.text
- 64ビットに符号拡張される32ビット値があります(
R_X86_64_32
)
- (リンカーとして)配置する必要のある値は、
.text + 8
(リンク/ロード時に計算する必要があります)の値です。
このエントリは、指示のおかげで作成されmov $1f, %0
ます。それを省く(または単に書くreturn *address
)と、そこにはありません。
static
修飾子を削除して、上記のコード生成を強制しました。そうしないと、単純なコンパイルでは実際にはコードがまったく作成されません(static
使用しない場合はコードが削除され、使用する場合は多くの場合インライン化されます)。
前述のように、関数はstatic
、通常、コンパイラによって呼び出しサイトでインライン化されるためです。したがって、それが使用される情報は、それをインストルメント化するデバッガーの機能と同様に、通常は失われます。ただし、ここに示すトリックは、関数の使用ごとに1つの再配置エントリが作成されるため、これを(間接的に)回復できます。それに加えて、このようなメソッドを使用して、バイナリ内にインストルメンテーションポイントを確立できます。よく知られている/厳密に定義されているが機能的に意味のない小さなアセンブリステートメントをオブジェクトファイル形式で回復可能な場所に挿入し、必要に応じてデバッガ/トレースユーティリティでそれらを「より便利な」ものに置き換えます。