【问题标题】:Code segfaults unless esp incremented除非 esp 增加,否则代码段错误
【发布时间】:2013-09-06 19:01:38
【问题描述】:

我有以下代码,它所要做的就是使用 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

运行时它可以工作,但是我找不到它的原因。对于单参数函数,我必须从 esp 中减去 12。不应该是 8,不是 push 已经处理增加参数的堆栈吗?

【问题讨论】:

  • 你不应该给 esp 添加 12 以便从堆栈中删除最后三个参数吗? (在调用 _CFStringCreateWithCString 之后)
  • CFStringCreateWithCString 在将这些参数从堆栈中弹出时不这样做吗?
  • 参数永远不会被调用的函数从堆栈中弹出,否则它们会弄乱堆栈帧。
  • @BlackBear:这完全取决于calling convention
  • @JimMischel:当然。我假设默认的 cdecl

标签: macos assembly x86 cfstring


【解决方案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 可以省略。

【讨论】:

  • Mac OS X 要求堆栈是 16 字节对齐的,这让我很头疼。不过我确实让它工作了,谢谢
  • @Zimm3r 好的。您可以将该原则应用于上述情况。我很高兴你能成功。
  • 16 字节堆栈对齐在 call 之前(不在函数入口处),因此堆栈参数是对齐的。
猜你喜欢
  • 1970-01-01
  • 2019-06-27
  • 1970-01-01
  • 1970-01-01
  • 2013-03-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多