6

AT&Tx86アセンブリ言語を学んでいます。整数nを取り、結果(n / 2 + n / 3 + n / 4)を返すアセンブリプログラムを作成しようとしています。これが私がしたことです:

.text
.global _start
_start:
    pushl $24
    call profit
    movl %eax, %ebx
    movl $1, %eax
    int $0x80

profit:
    popl %ebx
    popl %eax
    mov $0, %esi
    movl $4, %ebp
    div %ebp
    addl %eax, %esi
    movl %ecx, %eax
    movl $3, %ebp
    div %ebp
    addl %eax, %esi
    movl %ecx, %eax
    movl $2, %ebp
    div %ebp
    addl %eax, %esi
    movl %esi, %eax
    cmpl %ecx, %esi
    jg end
    pushl %ebx
    ret

end:
    mov %ecx, %eax
    ret

問題は、セグメンテーション違反が発生していることです。問題はどこだ?

4

5 に答える 5

8

コードはここで失敗すると思います:

_start:
    pushl $24
    call profit
    movl %eax, %ebx
    movl $1, %eax
    int $0x80

profit:
    popl %ebx
    popl %eax

したがって、あなたpush $24(4 バイト) とcall profitがプッシュeipされて にジャンプしprofitます。次に、 の値を にポップし、値を にeipポップebx$24ますeax

そして、最終的に にjg end分岐するend:と、スタックは有効な戻りアドレスを保持できず、ret失敗します。pushl %ebxおそらくそこも必要です。

    cmpl %ecx, %esi
    jg end
    pushl %ebx
    ret

end:
    mov %ecx, %eax
    ; `pushl %ebx` is needed here!
    ret
于 2012-09-26T15:17:19.547 に答える
2

関数呼び出しを正しく行っていないようです。x86 ABI ( 32-bit64-bit )、特に「呼び出し規約」セクションを読んで理解する必要があります。

また、これは差し迫った問題ではありませんが、次のよう_startに記述しないでくださいmain。これを C プログラムであるかのように記述してください。もっと複雑なことを始めるときは、C ライブラリを利用できるようにしたいと思うでしょう。つまり、それ自体を初期化する必要があります。これに関連して、独自のシステム コールを作成しないでください。C ライブラリのラッパーを呼び出します。これにより、カーネル インターフェイスの低レベルの変更から隔離され、確実にerrno使用できるようになります。

于 2012-09-26T15:33:07.273 に答える
2
  1. 明示的に初期化せずに使用しますecx(Linuxがプロセスの開始時の状態を保証するかどうかはわかりません-ルールではないにしても、実際にはそうでecxあるように見えます)0
  2. プログラムがjg endプロシージャの終わり近くでジャンプすると、戻りアドレスはスタック上にないため、ret制御をガベージアドレスに移します。
于 2012-09-26T15:17:02.093 に答える
2

あなたの問題は、リターンアドレスをスタックからポップし、分岐して終了するときに復元しないことです。簡単な修正は、push %ebxそこにも追加することです。

あなたがすべきことは、呼び出し規約を正しく使用するようにプロシージャを変更することです。Linux では、呼び出し元関数がスタックから引数を消去することが期待されるため、プロシージャは引数をそのままにしておく必要があります。

これを実行して引数を取得し、後で戻りアドレスを復元する代わりに

popl %ebx
popl %eax

これを行い、戻りアドレスと引数をそのままにしておく必要があります

movl 4(%esp), %eax

戻りアドレスをスタックにプッシュするコードを取り除きます。次に、追加する必要があります

subl $4, %esp

スタックから引数を削除するプロシージャへの呼び出しの後。他の言語からアセンブリ プロシージャを呼び出せるようにする場合は、この規則に正しく従うことが重要です。

于 2012-09-26T16:21:31.330 に答える
1

利益を呼び出す前に 1 つの pushl があるように見えます。利益が最初に行うことは、2 つの popl 命令を実行することです。これにより、スタックにプッシュされた値と戻りコードがポップされ、ret が機能しないことが期待されます。

プッシュとポップは同じ回数にする必要があります。

call は戻りアドレスをスタックにプッシュします。

于 2012-09-26T15:17:37.383 に答える