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

时间:2018-02-12 10:04:03

标签: c gcc x86-64 disassembly

我遇到一个奇怪的现象,我在下面记录代码。 我的试验台是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%lx\n\0"

.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%lx\n\0"

.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

1 个答案:

答案 0 :(得分:3)

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

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

现在,为什么printf首先触摸xmm寄存器?因为您错误地调用了printf。 amd64的ABI说:

  

当调用带有变量参数的函数时,%rax必须设置为传递给SSE寄存器中函数的浮点参数总数。

您的rax可能包含一些非零值。

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