2017-04-23 10 views
1

これで、少し面白いためにアセンブリをやりとりした後、私は今、呼び出しプロシージャに固執しています。スタックを介してアセンブリを渡すアセンブリ

... 
_start: 
    push dword len 
    push dword msg 

    call print 

    mov eax, SYS_EXIT 
    mov ebx, 0 
    int 80h 

print: ; *char (message), int (len) -> push len, then message 
    mov eax, SYS_WRITE 
    mov ebx, STDOUT 
    pop ecx 
    pop edx 
    int 80h 
    ret 

私は、アセンブリのこの部分を実行すると

nasm -f elf program.asm && ld -m elf_i386 -o program program.o && ./program 

私は印刷機能の内容と「コール・プリント」を交換する場合一方でそれは、すべて、プログラムの内容ワンセグ障害を出力します、それは正常に動作します。

+0

あるさ呼び出しがスタックに値をプッシュすることも認識していますか? – rcd

答えて

1

最初のPOPが戻りアドレスをポップし、2番目のPOPがアドレスmsgをポップします。 int 80hコールを駄目にしないと、関数が復帰しようとすると少なくともセグメンテーションフォルトが発生します。

関連する値は、リターンアドレスの後ろにあります。ここではesp + 4とesp + 8です。このアドレスにはESP+xxで直接アクセスできます。あなたはより洗練された手順を構築するとき、あなたはEBPに回避したいが、一瞬ESPでそれを行う可能性があります:命令ポップECXの代わりにRET命令の `EAXのSYS_EXIT`のアドレスを受信するので、それは、あなたが

SYS_EXIT equ 1 
SYS_WRITE equ 4 
STDOUT equ 1 

segment .data 

    msg db `Hello world!\n`  ; Backspaces for C-like escape-sequences ("\n") 
    len equ $- msg 

section .text 

    global _start 

_start: 
    push dword len 
    push dword msg 

    call print 
    ; and the stack? 

    mov eax, SYS_EXIT 
    mov ebx, 0 
    int 80h 

print: ; *char (message), int (len) -> push len, then message 
    mov eax, SYS_WRITE 
    mov ebx, STDOUT 
    mov ecx, [esp+4] 
    mov edx, [esp+8] 
    int 80h 
    ret 
+0

コードを修正するときに、 'call print'の後にスタックをクリーンアップしなかったのはなぜですか?そこにコメントがあります。それはどういう意味なのか分かりませんし、尋問者もそうでないと思っています。 –

+0

@CodyGray:次のSYS_EXITはスタックも "クリーンアップ"します。したがって、それが清掃されているかどうかは問題ではありません。なぜOPがこの時点でスタックをクリーンアップしていないのか分かりません。おそらくここに掲載されていない目的のために必要です。 – rkhb

+0

返信アドレスをポップアップして保存しておき、残りの部分をポップしたら、返信アドレスを戻して戻すことができますか? – Jackywathy

2

次のコードは、あなたがそれを書くべき方法である:

_start: 
    push dword len 
    push dword msg 
    call print 
    add esp, 8 ; equivalent to 2 32-bit POP instructions 
       ; (effectively "undoes" the above PUSH instructions 
       ; to restore the stack to its original state) 

    mov eax, SYS_EXIT 
    mov ebx, 0 
    int 80h 

print: ; *char (message), int (len) -> push len, then message 
    mov eax, SYS_WRITE 
    mov ebx, STDOUT 
    mov ecx, [esp+4] ; load "msg" from stack using an offset from ESP 
    mov edx, [esp+8] ; load "length" from stack using an offset from ESP 
    int 80h 
    ret 

問題がどこにあるべきスタックはを指していなかったということでした。スタックの先入れ先出しの性質を覚えておかなければなりません。また、callret命令がスタックポインタに影響すると考えてください。 call関数では、戻りアドレスがスタックにプッシュされるので、popprintの内側に置くと、実際にスタックから戻り値がポップされてしまい、間違った値を与えるだけでなく、あなたの能力はret urn後で。

スタック上の関数に渡されるパラメータを取得する正しい方法は、スタックポインタ(ESP)からのオフセットによるものです。最初のパラメータはESP + 4にあります(4バイトの戻りアドレスの後にスタックにプッシュされますcall)。詳細は、Cコードでよく使用されるSTDCALLとCDECLの呼び出し規約を参照してください。

+0

編集のためにコディーグレイに感謝します:)。 – rcd

関連する問題