2017-07-22 48 views
0

なぜこのコードがセグメンテーションフォルトを与えるのですか? (これはIntelアセンブラの構文でコード化されています)なぜこのアセンブルコードはセグメンテーションフォルトが発生するのですか?

結果は次のとおりです。

return value : 80 






[Dump] 
eax : 0x00000012 
ebx : 0x00000004 
ecx : 0x00000400 
edx : 0x4010a980 
Segmentation fault (core dumped) 

私はセグメンテーションフォルトを防ぐのに十分なコードがあると思います。 すべての関数には、スタックメモリを維持するためのプロローグとエピローグがあります。

ただし、セグメンテーションフォルトエラーが発生します。

[追加の注釈] コード 'mov ebx、4'を削除​​すると、セグメント化エラーが削除されました。私は、コード「のMOV EBX、4」を削除した場合

extern printf 

segment .data 
dumpmsg db  10,10,10,10,10,10,'[Dump]',10,'eax : 0x%0.8x',10,'ebx : 0x%0.8x',10,'ecx : 0x%0.8x',10,'edx : 0x%0.8x',10,00 
msg  db  'return value : %d', 10, 00 

segment .bss 

segment .text 
global main 
main: 
     push ebp 
     mov  ebp, esp 

     push 20 
     call pig 
     add  esp, 4 

     push eax 
     push msg 
     call printf 

     call print_x 
     leave 
     ret 

pig: 
     push ebp 
     mov  ebp, esp 

     mov  eax, [ebp+8] 
     mov  ebx, 4 
     mul  ebx 

     leave 
     ret 

print_x: 
     push ebp 
     mov  ebp, esp 

     push edx 
     push ecx 
     push ebx 
     push eax 
     push dumpmsg 
     call printf 

     leave 
     ret 
+0

あなたの投稿には詳細がありません。あなたが実行しているマシン、使用しているアセンブラやプラットフォームなどに関する情報を私たちに与えていないのです。 – Dai

+0

@Dai私はredhat 6.4でNasmを使用します。 – YouHoGeon

+0

@lurker printfを使用した後にスタックを復元しようとしましたが、結果は同じでした(セグメンテーションフォルト)。 – YouHoGeon

答えて

3

(しかし、結果は私の意図に合わない)、その後、セグメンテーションフォールトエラーが削除されていました。 (しかし結果は私の意図に合わない)

EBXレジスタはほぼすべてのx86呼び出し規約でcallee-saveです。つまり、リーフ関数がその値を壊すことはできません。 EBXを使用する必要がある場合は、元の値を関数の先頭に保存し、最後に復元する必要があります。これは通常、レジスタの値をスタックに保存するために命令PUSH + POPで実行されます。

あなたpig機能がmov ebx, 4命令でEBXを追い払ってくれるが、は元の値を保存し、復元するために世話をしていません。これは呼び出し規約に違反します。これはCコードと相互運用するため重要です。

修正は簡単です:

pig: 
    push ebp 
    mov  ebp, esp 
    push ebx 

    mov  eax, [ebp+12] 
    mov  ebx, 4 
    mul  ebx 

    pop  ebx 
    leave 
    ret 

それとも、この1つは、呼び出し元保存(および壊しすること自由)であることから、単に、除数を保持するためにECXレジスタを使用します。

pig: 
    push ebp 
    mov  ebp, esp 

    mov  eax, [ebp+12] 
    mov  ecx, 4 
    mul  ecx 

    leave 
    ret 

すべての関数には、スタックメモリを維持するためのプロローグとエピローグがあります。

これは実際には必要ありません。 、ベースポインタ(EBP)の使用は、同様に他の関数から省略される可能性が

pig: 
    mov  eax, [esp+4] 
    mov  ecx, 4 
    mul  ecx 
    ret 

:パラメータを取得する以外pig機能は、まったくスタックを使用していないので、あなたは、単にそれを書くことができますスタックポインタ(ESP)を直接使用することで、スタックを使用するものでもあります。コンパイラが追加のレジスタとしてEBPを解放し、プロローグ/エピローグの拡張を保存するのと同じ最適化です。しかし、あなたがこのようにするのがより快適ならば、パフォーマンス以外の何かを傷つけることはありません。

の場合は、呼び出し規約に従う必要があります。EAX,EDX、およびECXのレジスタは、リーフ関数の内部で自由にクローバーできます。残りのすべてを明示的に保存して元の値に戻す必要があります。スタックに領域を割り当てるときは、空き領域を確保する必要があります。あなたがそうする方法は、sub esp, xx ... add esp, xxかプロローグ...エピローグを持っているかどうかにかかわらず、柔軟です。

+0

あなたの種類の答えをありがとう。あなたの答えは私たちが問題を解決するのに役立ちました。 :> – YouHoGeon

関連する問題