【问题标题】:Trivial macOS assembly 64-bit program has incorrect stack alignmentTrivial macOS 汇编 64 位程序的堆栈对齐不正确
【发布时间】:2017-09-19 20:13:54
【问题描述】:

我正在尝试使用参数创建一个简单的控制台汇编程序。代码如下:

.section __TEXT,__text

.globl _main

_main:

    movl    $0, %edi
    callq   _exit

这是编译和链接脚本:

as test.s -o test.o
ld test.o -e _main -o test -lc 

现在程序要么因分段错误而失败,要么执行时没有错误,具体取决于参数计数:

$ ./test
Segmentation fault: 11
$ ./test 1
$ ./test 1 2
$ ./test 1 2 3
Segmentation fault: 11
$ ./test 1 2 3 4
$ ./test 1 2 3 4 5
Segmentation fault: 11

等等。

在 LLDB 下,我看到了更多信息错误:

Process 16318 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x00007fffad14b2fa libdyld.dylib`stack_not_16_byte_aligned_error
libdyld.dylib`stack_not_16_byte_aligned_error:
->  0x7fffad14b2fa <+0>: movdqa %xmm0, (%rsp)
    0x7fffad14b2ff <+5>: int3   

libdyld.dylib`_dyld_func_lookup:
    0x7fffad14b300 <+0>: pushq  %rbp
    0x7fffad14b301 <+1>: movq   %rsp, %rbp

确实,如果我在程序的第一行停止执行,我会看到堆栈对于某些参数计数不是 16 字节对齐的,而对于另一个参数计数是对齐的。虽然System V ABI for AMD64 声明:

堆栈指针保存地址最低的字节的地址 这是堆栈的一部分。它保证在 16 字节对齐 进程入口。

我错过了什么?

【问题讨论】:

  • 看起来 OS X 在某些长度的参数上违反了 ABI。 (希望他们从未声称完全遵循该 ABI?不过我认为他们也这样做了。)您是否尝试过不使用 libc,只是在进程进入静态链接二进制文件时自己(使用调试器)检查堆栈对齐,该二进制文件以直接syscall? (或设置退出状态 = rsp &amp; 0xf./test 1 2 3 ; echo $? 以查看堆栈对齐)。

标签: macos assembly


【解决方案1】:

我猜在 OS X 上,内核不保证在进入 main 时堆栈对齐。您必须手动对齐堆栈。幸运的是,这相当简单,只需将堆栈指针的至少四位清零即可。如果您需要获取参数向量或其他数据,请确保将原始堆栈指针存储在某处:

_main:
    mov %rsp,%rax    # copy original stack pointer
    and $-16,%rsp    # align stack to 16 bytes
    push %rax        # save original stack pointer
    push %rbp        # establish...
    mov %rsp,%rbp    # ...stack frame
    ...              # business logic here
    leave            # tear down stack frame
    pop %rsp         # restore original stack pointer
    ...              # exit process

您还需要在心理上跟踪您的堆栈对齐方式。让main 只做堆栈对齐然后调用您的实际主函数可能会更容易,这样您就可以在其中使用正常的堆栈帧:

_main:
    mov %rsp,%rbx    # save original stack pointer
    and $-16,%rsp    # align stack to 16 bytes
    call _my_main    # call actual main function
    mov %rbx,%rsp    # restore original stack pointer
    ...              # exit process

对于您的特定示例程序,您可以只使用这个最少的代码:

_main:
    and $-16,%rsp    # align stack to 16 bytes
    xor %edi,%edi    # set exit status to zero
    call _exit       # exit program

【讨论】:

  • 看起来是这样。我的代码基于第二个 sn-p,现在手动对齐有效。谢谢!
  • System V Application Binary Interface AMD64 Architecture Processor Supplement (With LP64 and ILP32 Programming Models) Draft Version 0.99.8: %rsp 堆栈指针保存最低地址字节的地址,即堆栈的一部分。它保证在进程入口处是 16 字节对齐的。 值得注意的是,这与普通函数的 (%rsp+8)%16 == 0 不同。
  • @EOF:这正是你想要进行函数调用的。在正常功能的开始,您需要一个虚拟的pushsub $8, %rsp,在call 之前。或jmp exit 尾随。因为在运行 call 指令之前堆栈应该是 16B 对齐的,所以 args 是对齐的。另外,请注意 OP 的错误消息:0x...a is not 0 or 8.
  • @Fuz:OP 使用_main 作为_start 的替代名称;注意 ld test.o -e _main -o test -lc 将 ELF 入口点设置为 _main。所以没有什么可回头的。名称选择非常糟糕,IMO,但堆栈的状态是内核启动状态(或动态链接器状态?),而不是 C main() 的对齐。
  • @PeterCordes 哦,确实!对于 OP,请注意,从没有通过 C 编译器链接来初始化 C 运行时环境的程序调用任何 libc 函数是一个非常糟糕的主意。不要那样做!
猜你喜欢
  • 1970-01-01
  • 2011-01-19
  • 2023-01-03
  • 2012-05-29
  • 1970-01-01
  • 2021-03-03
  • 1970-01-01
  • 2015-06-14
  • 2012-06-19
相关资源
最近更新 更多