进行顺序 C 调用时,在 MacOS 上的 x86 程序集中出现堆栈对齐错误?

Getting stack alignment error in x86 Assembly on MacOS when making sequential C calls?

我目前正在学习 MacOS 上的 x86 汇编,每当进行两次 C 调用时,我都会收到堆栈未对齐错误。

我已经尝试过使用适当的堆栈填充,确保堆栈大小为 12 字节以满足 MacOS 进行额外推送时的 16 字节堆栈对齐。

我在 mac x86 汇编中用谷歌搜索了 C 调用,但我似乎无法找到不仅仅是调用的示例,然后退出。我相信在使堆栈未对齐的调用之后我没有正确执行一些堆栈清理,但我似乎无法弄清楚。

    _main:

        # First open the file?
        # int open(const char *path, int flags, ...)
        subl [=10=]x04, %esp            # Pad the stack
        pushl [=10=]x0                  # This is the flag for O_RDONLY
        pushl $path                 # Then add the path

        call _open                  # Make the call to open
        movl %eax, fd               # Put the file descriptor into the fd variable

        # Let's just close the file
        # int close(int fd);
        subl [=10=]x08, %esp            # Stack padding
        pushl $fd                   # Push the file descriptor onto the stack
        call _close                 # Close that bitch

        # Display a nice message and leave
        subl [=10=]x08, %esp            # Buffer da stackaroo
        pushl $end_message          # Put our message on da stack
        call _printf                # Print the message

        # Goodbye :)
        call _exit

理论上这应该只是打开和关闭一个文件描述符,当我打开它时就可以工作,但是一旦我添加关闭它的代码(或者只是 printf 代码,它就会失败)。

这是我从 lldb:

得到的完整错误
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0xa7aa6bd0 libdyld.dylib`misaligned_stack_error_

有人知道我到底做错了什么吗?

问题是您假设堆栈在调用 C 函数后再次按 16 字节对齐 returns。不是。它与您的 call 指令之前相同,少了 4 个字节。

_main:
# on entry, %esp is 0x...0 (last digit is 0, 16-byte aligned)
    subl [=10=]x04, %esp            # Pad the stack
# %esp is 0x...c
    pushl [=10=]x0                  # This is the flag for O_RDONLY
# %esp is 0x...8
    pushl $path                 # Then add the path
# %esp is 0x...4
# call will push 4-byte return address, making %esp on entry to open() 0x...0
    call _open                  # Make the call to open
# open()'s ret will pop the 4-byte return address, putting %esp back to 0x...4
    movl %eax, fd               # Put the file descriptor into the fd variable
    subl [=10=]x08, %esp            # Stack padding
# %esp is 0x...c
    pushl $fd                   # Push the file descriptor onto the stack
# %esp is 0x...8
# call will push 4-byte return address, making %esp on entry to close() 0x...4
# that's mis-aligned
    call _close                 # Close that bitch
    ...