0

最近、WindowsでGCCを使用してx86アセンブリを練習しています。現在、テスト目的でアセンブリと C コードを混在させています。現在の知識では説明できない奇妙なことに遭遇したので、SOに助けを求めました。

最小限の例を見てみましょう。C コードにtest_func()関数があり、これをアセンブリに変換してから、後で C コードから呼び出します。ただし、この関数はプロジェクトから他の関数​​を呼び出しており、そこに問題があります。アセンブリ内から他の C コード関数を呼び出すと、セグメンテーション エラーが発生します。

アセンブリで見たい関数:

#include "rf_string.h"
void test_func()
{//nonsense function, just calling another C function of the project
    RF_String s;
    rfString_Init(&s,"anything");
}

このためのアセンブリ コードを吐き出すように gcc に要求すると、次のようになります。

    .file   "to_asm.c"
    .section .rdata,"dr"
    .align 4
LC0:
    .ascii "anything\0"
    .text
    .globl  _test_func
    .def    _test_func; .scl    2;  .type   32; .endef
_test_func:
LFB6:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $40, %esp
    movl    $LC0, 4(%esp)
    leal    -16(%ebp), %eax
    movl    %eax, (%esp)
    movl    _rfString_Init, %eax
    call    *%eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE6:

次に、これを「asm_test.S」というファイルに保存し、それをプロジェクトのビルド プロセスに含めることで、正常にビルドおよびリンクされます。そのため、関数を呼び出す簡単なプログラムを作成してみます。

int main()
{
    test_func();
    return 0;
}

問題はデバッグ時に発生します。アセンブリ コード内から他の C 関数を呼び出すと、セグメンテーション エラーが発生します。これは、アセンブリ コードから呼び出そうとする他の関数で発生します。使用した関数とは特に関係ありません。

ここでの関数呼び出しに関係していると思います:

 movl    %eax, (%esp)
 movl    _rfString_Init, %eax
 call    *%eax

なぜこれを行うのか、これはどういう意味ですか? 具体的には、単に関数を呼び出さないのはなぜですか? また、%eax レジスタの前の * は何ですか? また、最後の 2 行を以下のような関数への単純な呼び出しに置き換えると、すべてが完全に正常に機能しているように見えて、本当に困惑することを追加する必要があります。

 call     _rfString_Init

もう 1 つの小さな質問は、これらすべての cfi_directives に関するものです。SOで尋ねられた質問への回答で判断する場合、それらは例外処理の目的で生成されているようです。問題は、コードの機能に関する限り、それらを安全に無視できるかどうかです。

4

1 に答える 1

1

ここ:

 movl    _rfString_Init, %eax
 call    *%eax

$以前はないことに注意してください_rfString_Init(とは異なりmovl $LC0, 4(%esp)ます)。これは_rfString_Init、即時定数(たとえば、関数またはオブジェクトの定数アドレス、あるいは単なる定数)ではなく、メモリ変数であることを意味します。

したがって、という名前の変数の内容をmovlロードします。eax_rfString_Init

次にcall、に含まれるアドレスへの呼び出しを実行しますeax。星は構文上の糖衣構文であり、参照/ポインターによって制御が間接的に渡されることを示します。

つまり、rfString_Init実際には関数へのポインタです。あなたのCコードでそれを調べてください!

そしてもちろん、ポインタに含まれるバイトに制御を移そうとしても、コードとして解釈されることを期待されていないデータであるため、何も起こりません。さらに、基盤となるメモリは実行不可能として構成されている可能性があります。

私はそれらの指令についてあまり知りません。デバッグ用にも使用できます。とにかく、彼らはあなたが彼らを見るところにコードを挿入しません。

于 2012-09-17T06:03:17.890 に答える