1

次のコードは、CoreFoundation 関数を使用して Hello World を出力するだけです。ただし、適切に整列されたスタックがあるように見えるときはいつでも、機能せず、セグフォルトが発生します。しかし、最終的に機能するようになったとき、スタックが整列していません?!?!?!

global  _main

align 4, db 0x90

extern _CFStringCreateWithCString
extern _CFShow

section .data
    hw:  db 'Hello World!' ,0xA,0

section .text
    _main: ; entering a new function stack must be balanced right?

    push ebp ; saving ebp  (esp + 4)
    mov  ebp, esp ; moving registers around 
    ; align stack as calling pushed a 4 byte address on to the stack
    sub  esp, 12 ; balancing the stack back to mod 16 (4 + 12 = 16)


    push 8   ; 4 bytes
    push hw  ; 4 bytes
    push 0   ; 4 bytes
    call _CFStringCreateWithCString ; 4 bytes

    ; stack still balanced

    sub  esp, 12 ; 12 bytes
    push eax     ; 4 bytes
    call _CFShow ; 4 bytes

    ; that is 20 bytes?!?!? yet when I change the 12 to an 8 it doesn't run and instead segfaults! When I have the stack balanced!


    mov eax, 99 ; return value

    mov esp, ebp ; restore stack for function that called us
    pop ebp
    ret          ; return

実行すると動作しますが、動作する理由がわかりません。引数が 1 つの関数の場合、esp から 12 を引く必要があります。8 であるべきではありません。push は、引数のスタックのインクリメントを既に処理していませんか?

4

1 に答える 1

1

スタックに割り当てられたスペースを使用せずに、元の関数がスタックから追加の減算を行っている理由がわかりません。スタックは x86 で成長します。このコンテキストでは、次のようにします。

sub esp, NUMBER

NUMBER何らかの目的で使用するために、スタックにバイトを割り当てます (利用可能にします) 。

私は、ライブラリが C 呼び出し規則に従っていると仮定しています。

1) Push the parameters (in reverse order) onto the stack
2) Call the function
3) Restore the stack based upon the amount of space used by the prior pushes.

これらのことを念頭に置いて、関数を次のように記述します。

global  _main

align   4, db 0x90

extern _CFStringCreateWithCString
extern _CFShow

section .data
hw: db 'Hello World!' ,0xA,0

section .text
_main: ; entering a new function stack must be balanced right?

    push   ebp         ; saving ebp  (esp + 4)
    mov    ebp, esp    ; set stack frame pointer 

    push   8           ; String encoding - 4 bytes
    push   hw          ; String pointer - 4 bytes
    push   0           ; Allocator [0 for default] - 4 bytes
    call   _CFStringCreateWithCString
    add    esp, 12     ; restore the stack [pop the 12 bytes back off]

    push   eax         ; Address of string to show (returned by prior call) - 4 bytes
    call   _CFShow
    add    esp, 4      ; restore the stack [pop the 4 bytes back off] NOT NEEDED with 

    mov    eax, 99     ; return value

    mov    esp, ebp    ; restore stack for function that called us
    pop    ebp
    ret

mov最後の命令はスタックを復元するため、最後の命令はadd esp,4省略できることに注意してください。


MacOS は、関数呼び出しのスタック ポインターの 16 バイト アラインメントを要求/保証します。これをする:

global  _main

align   4, db 0x90

extern _CFStringCreateWithCString
extern _CFShow

section .data
hw: db 'Hello World!' ,0xA,0

section .text
_main:
 ; ESP was aligned before the call instruction pushed a return address
 ; now the nearest alignment boundaries are ESP+4 and ESP-12

    push   ebp         ; saving ebp  (esp + 4)
    mov    ebp, esp    ; set stack frame pointer 

 ; ESP-8 is 16-byte aligned; not enough room for 12 bytes of args
    sub    esp,12      ; So we have to go past that to aim for the *next* alignment boundary
    push   8           ; String encoding - 4 bytes
    push   hw          ; String pointer - 4 bytes
    push   0           ; Allocator [0 for default] - 4 bytes
    call   _CFStringCreateWithCString

    ;add    esp, 12+12 - 4    ; pop the padding and args, then sub 4 for 16-byte alignment on next call (after push)
    ;push   eax         ; Address of string to show (returned by prior call) - 4 bytes

    mov    [esp], eax   ; reuse the stack reservation; ESP is still aligned
    call   _CFShow
    add    esp, 12+12   ; restore the stack [pop the args + padding back off]

    mov    eax, 99     ; return value

    mov    esp, ebp    ; restore stack for function that called us
    pop    ebp
    ret

最初のケースと同様に、最後のケースはadd esp,24省略できます。

于 2013-09-07T01:44:59.307 に答える