subq $40 %rsp 与 AS 崩溃但 GCC 没有

subq $40 %rsp crash with AS but GCC not

遇到一个奇怪的现象,记录下代码。 我的测试平台是 x86_64 并且 gcc 是 5.3.0 当我在堆栈中为本地值保留一些 space 时,有时它会崩溃。

                   | AS and LD | gcc    |
--------------------------------------------
 40 bytes in stack |  crash    | ok     |
--------------------------------------------
 32 bytes in stack |   ok      | crash  |
--------------------------------------------


.section .data

fmt:
    .ascii "0x%lx\n[=10=]"

.section .text
.global _start

_start:
    subq , %rsp   # subq , %rsp is OK
                     # I want to reserve some place for local value.

    movq , %rsi
    movq $fmt, %rdi
    call printf      #print something

    addq , %rsp
    movq , %rax
    int [=10=]x80

    as tsp.s -o tsp.o 
    ld -lc -I /lib64/ld-linux-x86-64.so.2 tsp.o -o tsp
    ./tsp
    Segmentation fault (core dumped)

这次使用gcc编译link。 没关系,当我在堆栈中保留 40 个字节时。 当我在堆栈中保留 32 个字节时,它崩溃了。

.section .data

fmt:
    .ascii "0x%lx\n[=11=]"

.section .text
.global main 

main:
    subq , %rsp   # if subq , %rsp, it would crash.

    movq , %rsi
    movq $fmt, %rdi
    call printf

    addq , %rsp   
    movq , %rax
    int [=11=]x80

    gcc tsp.s -o tsp
    ./tsp
    0x8

当我测试你的代码时 printf 在访问 xmm 寄存器时崩溃了。有两个原因。当你让 gcc 进行编译和链接时,它实际上会在 main 之前有额外的代码。该代码将正确对齐堆栈,然后调用 main.

由于 main 像普通函数一样被调用,由于调用指令,堆栈将在 8 mod 16 处对齐,但是在调用函数时,堆栈必须正确对齐(0 mod 16).对齐要求的原因是因为 xmm 寄存器(以及其他)。

现在,为什么 printf 首先接触 xmm 寄存器?因为你打错了printf。 amd64 的 ABI 表示:

When a function taking variable-arguments is called, %rax must be set to the total number of floating point parameters passed to the function in SSE registers.

您的 rax 中可能有一些非零值。

所以,有两件事可以解决您的问题。 xorl %eax, %eax 在调用 printf 之前归零 %rax。并注意您是如何被调用的以及如何对齐堆栈。如果您已作为普通函数被调用,则需要在调用之前从堆栈指针中减去 8+n*16n 可以为 0)。如果你被调用为入口点是安全的,你需要正确对齐你的堆栈指针,因为我不确定内核是否总是保证你的堆栈指针将被对齐。