この質問の適切な件名が何であるかはわかりませんが、ここで説明します。
コードのクリティカルセクションのコードの局所性/コンパクト性を強制するためにR_X86_64_JUMP_SLOT
、呼び出し時に直接「ジャンプスロット」(ELF再配置)を介して外部(動的にロードされた)ライブラリの関数を呼び出す方法を探していますサイト-リンカーが通常PLT/GOTに配置するものですが、これらは呼び出しサイトにインライン化されます。
次のように呼び出しをエミュレートすると、次のようになります。
#include <stdio.h>
int main(int argc, char **argv)
{
asm ("push $1f\n\t"
"jmp *0f\n\t"
"0: .quad %P0\n"
"1:\n\t"
: : "i"(printf), "D"("Hello, World!\n"));
return 0;
}
64ビットワード用のスペースを確保するために、呼び出し自体が機能します(これは、特定のABIルールに違反するため、幸運な偶然の一致についてのコメントはありません。これらはすべて、この質問の対象ではありません。
私の場合、他の方法で回避/対処するために、この例を簡潔にしようとしています)。
次のアセンブリを作成します。
0000000000000000 <メイン>: 0:bf 00 00 00 00 mov $ 0x0、%edi 1:R_X86_64_32 .rodata.str1.1 5:68 00 00 00 00 pushq $ 0x0 6:R_X86_64_32 .text + 0x19 a:ff 24 25 00 00 00 00 jmpq * 0x0 d:R_X86_64_32S .text + 0x11 ..。 11:R_X86_64_64 printf 19:31 c0 xor%eax、%eax 1b:c3 retq
しかし(printf
イミディエートとして使用しているため、私は推測します...?)ここでのターゲットアドレスはまだPLTフックのアドレスです-同じR_X86_64_64
再配置です。libcに対するオブジェクトファイルを実際の実行可能ファイルにリンクすると、次のようになります。
0000000000400428 <printf @ plt>: 400428:ff 25 92 04 10 00 jmpq * 1049746(%rip)#5008c0 <_GLOBAL_OFFSET_TABLE_ + 0x20> [...] 0000000000400500 <メイン>: 400500:bf 0c 06 40 00 mov $ 0x40060c、%edi 400505:68 19 05 40 00 pushq $ 0x400519 40050a:ff 24 25 11 05 40 00 jmpq * 0x400511 400511:[.quad 400428] 400519:31 c0 xorl%eax、%eax 40051b:c3 retq [...] 動的再配置記録 オフセットタイプ値 [...] 00000000005008c0 R_X86_64_JUMP_SLOT printf
つまり、これでも2段階のリダイレクトが行われ、最初に実行がPLTフックに転送され、次にライブラリのエントリポイントにジャンプします。
コンパイラ/アセンブラ/リンカに、この例ではアドレスのジャンプスロットターゲットを「インライン化」するように指示する方法はあります0x400511
か?
つまり、「ローカル」(プログラムのリンク時にld
)R_X86_64_64
relocを「リモート」(プログラムのロード時にld.so
)に置き換えますR_X86_64_JUMP_SLOT
(コードのこのセクションでは非遅延ロードを強制します)?たぶんリンカーマップファイルはこれを可能にするかもしれません-もしそうなら、どのように?
編集:
これを明確にするために、問題は、動的にリンクされた実行可能ファイル/動的ライブラリでのみ使用可能な外部関数でこれを実現する方法についてです。はい、それは本当の静的リンクがこれをより簡単な方法で解決しますが、:
- 静的ライブラリが通常ベンダーから出荷されていないシステム(Solarisなど)があります
- ソースコードまたは静的バージョンとして利用できないライブラリがあります
したがって、静的リンクはここでは役に立ちません:(
Edit2:
一部のアーキテクチャ(SPARC、特に、GNUでのSPARCの再配置に関するセクションを手動で参照)では、GNUは修飾子を使用してリンカーの特定のタイプの再配置参照をインプレースで作成できることがわかりました。引用されたSPARCは%gdop(symbolname)
、アセンブラに「ここにその再配置を作成する」という指示をリンカーに送信させるために使用します。Itanium上のIntelのアセンブラは、同じ種類のものの@fptr(symbol)
リンク再配置演算子を知っています( Itanium psABIのセクション4も参照してください)。しかし、同等のメカニズム(コード内の特定の位置に特定のリンカー再配置タイプを発行するようにアセンブラーに指示するもの)がx86_64に存在しますか?
また、GNUアセンブラには、.reloc
この目的で使用されると思われるディレクティブがあることもわかりました。それでも、私が試してみると:
#include <stdio.h>
int main(int argc, char **argv)
{
asm ("push %%rax\n\t"
"lea 1f(%%rip), %%rax\n\t"
"xchg %%rax, (%rsp)\n\t"
"jmp *0f\n\t"
".reloc 0f, R_X86_64_JUMP_SLOT, printf\n\t"
"0: .quad 0\n"
"1:\n\t"
: : "D"("Hello, World!\n"));
return 0;
}
リンカからエラーが発生します(注意してください7 == R_X86_64_JUMP_SLOT
):
エラー:/tmp/cc6BUEZh.o:オブジェクトファイル内の予期しない再配置7アセンブラは、次のようなオブジェクトファイルを作成します
readelf
。オフセット0x5e8の再配置セクション'.rela.text.startup'には、次の2つのエントリが含まれています。 オフセット情報タイプシンボルの値シンボルの名前+加数 0000000000000001 000000050000000a R_X86_64_32 0000000000000000 .rodata.str1.1 + 0 0000000000000017 0000000b00000007 R_X86_64_JUMP_SLOT 0000000000000000 printf + 0
これは私が欲しいものです-しかし、リンカーはそれを取りません。
リンカはR_X86_64_64
、上記の代わりに使用するだけで受け入れます。これを行うと、最初の場合と同じ種類のバイナリが作成されます... printf@plt
「解決された」バイナリではなく、にリダイレクトされます。