最近、これを 64 ビット コードで実行できることに気付きました。
const size_t kLowStackSize = 1024UL * 1024UL * 4UL;
void *low_stack = mmap(NULL, kLowStackSize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
struct __attribute__((packed, aligned(16))) {
int32_t address;
int16_t segment;
} target = {(uint32_t) (uint64_t) code, 0x23};
asm volatile(
"mov %%rsp, %%r8\n"
"mov %[stack], %%rsp\n"
"push %%r8\n"
"lcall *(%[target])\n"
"pop %%rsp"
:
: [stack] "r" (low_stack + kLowStackSize), [target] "r" (&target)
: "r8");
ここcode
で、アドレス空間の下位 4GiB の実行可能ページにある 32 ビット コードの一部を指し、Linux の x86 ヘッダーのセグメント セレクター0x23
の値です。__USER32_CS
ジャンプ対象に属性が必要かどうかはわかりませんが、念のため追加しました。もちろん、far return を可能にするには、この呼び出しコード自体を仮想アドレス空間の下位 4 GiB のどこかに配置する必要があります。に入れるだけmain
で十分であることがわかりました。
これはほとんど役に立たず (32 ビット ライブラリがロードされていない、呼び出し規約が異なるなど)、破損しやすい (の値は__USER32_CS
Linux のユーザー空間向け API の一部ではない) ことを理解しています。
私の質問:呼び出しのターゲットが実際に 32 ビット モードで実行されていることを示す簡単な方法はありますか? この種の呼び出しの実用的な用途 (それを利用するライブラリの既存のソフトウェア、または少なくともそれほど非現実的ではない可能性) はありますか?