2

関数ポインターをロードし (それがどのように機能するかは問題ではありません)、いくつかの引数をいくつかのインライン アセンブリでスタックにプッシュし、その関数を呼び出します。コードは次のようになります。

unsigned long stack[] = { 1, 23, 33, 43 };

/* save all the registers and the stack pointer */
unsigned long esp;
asm __volatile__ ( "pusha" );
asm __volatile__ ( "mov %%esp, %0" :"=m" (esp));

for( i = 0; i < sizeof(stack); i++ ){
    unsigned long val = stack[i];
    asm __volatile__ ( "push %0" :: "m"(val) );
}

unsigned long ret = function_pointer();

/* restore registers and stack pointer */
asm __volatile__ ( "mov %0, %%esp" :: "m" (esp) );
asm __volatile__ ( "popa" );

何らかの形で追加したい

#ifdef _LP64
   // 64bit inline assembly
#else
   // 32bit version as above example
#endif

しかし、私は64ビットマシンのインラインアセンブリを知りません.誰かが私を助けることができますか?

ありがとう

4

3 に答える 3

4

インライン アセンブリで適切な引数を使用して関数ポインターを呼び出すことはそれほど問題にはならないはずですが、使用される呼び出し規約がおそらく異なるため (デフォルト32 ビットと 64 ビットの Linux では明らかに異なります)。詳細はこちらをご覧ください。したがって、この場合、インライン アセンブリなしで回避できれば (他の回答を参照)、移植が容易になると思います。

編集:OK、アセンブリを使用する必要があるかもしれません。ここにいくつかの指針があります。

Agner Fog のドキュメントによると、Linux x64 はパラメータ転送に RDI、RSI、RDX、RCX、R8、R9、および XMM0-XMM7 を使用します。これは、(浮動小数点の使用を無視して)目的を達成するために、関数が次のことを行う必要があることを意味します。

(1) 退避が必要なすべてのレジスター (RBX、RBP、R12-R15) を退避: スタック上のスペースを確保し、これらのレジスターをそこに移動します。これは (Intel 構文) の行に沿ったものになります。

sub rsp, 0xSomeNumber1
mov [rsp+i*8], r# ; insert appropriate i for each register r# to be moved

(2) ターゲット関数にスタックで渡さなければならない引数の数を評価します。sub rsp, 0xSomeNumber2これを使用して、スタック ( ) に必要なスペースを確保し、スタック0xSomeNumber1が最後に 16 バイトにアラインされるようにします。つまりrsp、16 の倍数にする必要がありますrsp。戻ってきた。

(3) 関数の引数をスタック (必要な場合) およびパラメーター転送に使用されるレジスターにロードします。私の見解では、スタック パラメーターから始めて、最後にレジスター パラメーターをロードするのが最も簡単です。

;loop over stack parameters - something like this
mov rax, qword ptr [AddrOfFirstStackParam + 8*NumberOfStackParam]
mov [rsp + OffsetToFirstStackParam + 8*NumberOfStackParam], rax

ルーチンの設定方法によっては、最初のスタック パラメータなどへのオフセットが不要になる場合があります。次に、レジスタに渡される引数の数を設定します (必要のない引数はスキップします)。

mov r9, Param6
mov r8, Param5
mov rcx, Param4
mov rdx, Param3
mov rsi, Param2
mov rdi, Param1

(4) 上記とは別のレジスタを使用して対象関数を呼び出します。

call qword ptr [r#] ; assuming register r# contains the address of the target function

(5) 保存されたレジスタを復元rspし、関数へのエントリ時の値に復元します。必要に応じて、呼び出された関数の戻り値を必要な場所にコピーします。それで全部です。

: 上記のスケッチは、XMM レジスタで渡される浮動小数点値を考慮していませんが、同じ原則が適用されます。 免責事項: Win64 で同様のことを行ったことはありますが、Linux では行ったことがないため、見落としている詳細がいくつかある可能性があります。よく読んで、コードを注意深く書き、よくテストしてください。

于 2010-03-25T19:29:06.787 に答える
2

setcontext実際にはあなたの質問に答えていませんが、 (または)を使用することで、プラットフォームに依存しない方法でこれを達成できる可能性があると思いますmakecontext

于 2010-03-25T18:47:01.820 に答える
1

主な問題:

  • x64 には pushad/popad がありません。保存したい個々のレジスタをプッシュする必要があります
  • rsp (64 ビット スタック ポインタ) を適切な 64 ビット レジスタ (rax?、r8? など) に保存する必要があります。
  • 呼び出し規約はほぼ確実に 32 ビットから 64 ビットに変更されました

x86 から x64 への変更の概要:

  • E で始まるレジスターには、R で始まる 64 ビットの同等のものがあります。 RAX、RBX、RCX、RDX、RDI、RSI、RIP、RSP、RBP。
  • 新しいレジスタ: R8 ... R15
  • プッシュパッドなし、ポップパッドなし

いくつかのインライン x86 コードを Windows 上の x64 に移植しました。x64 命令セットを読み、オペレーティング システムの呼び出し規約を読むのに時間がかかることは間違いありません。Windows での変更は急進的で、新しい呼び出し規約はよりシンプルになりました。GNU/Linux の変更も異なると思いますが、同じであるとは絶対に思いません。

ネイティブにコーディングする以外の方法を使用できる場合は、そうするという以前の回答に同意します。私の場合は避けられませんでした。

于 2010-03-25T19:49:43.440 に答える