C- subq $ 40%的RSP崩溃,但GCC却没有



我遇到了一个奇怪的现象,我记录了代码。我的测试床是X86_64,GCC为5.3.0当我在堆栈中保留一些本地值时,有时会崩溃。

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

.section .data
fmt:
    .ascii "0x%lxn"
.section .text
.global _start
_start:
    subq $40, %rsp   # subq $32, %rsp is OK
                     # I want to reserve some place for local value.
    movq $8, %rsi
    movq $fmt, %rdi
    call printf      #print something
    addq $40, %rsp
    movq $1, %rax
    int $0x80
    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编译和链接。没关系,当我在堆栈中保留40个字节时。它崩溃了,当我在堆栈中保留32个字节时。

.section .data
fmt:
    .ascii "0x%lxn"
.section .text
.global main 
main:
    subq $40, %rsp   # if subq $32, %rsp, it would crash.
    movq $8, %rsi
    movq $fmt, %rdi
    call printf
    addq $40, %rsp   
    movq $1, %rax
    int $0x80
    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说:

当调用采用变量 - arguments的功能时,必须将%rax设置为传递给SSE寄存器函数的浮点参数的总数。

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

所以,解决问题的两件事。在致电printf之前,xorl %eax, %eax至零%rax。并注意您如何被调用以及如何对齐堆栈。如果您被称为正常函数,则需要在呼叫之前从堆栈指针中减去8+n*16(n为0(。如果您被称为安全点以确保安全,则需要正确对齐堆栈指针,因为我不确定内核是否总是保证您的堆栈指针会对齐。

最新更新