我遇到了一个奇怪的现象,我记录了代码。我的测试床是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(。如果您被称为安全点以确保安全,则需要正确对齐堆栈指针,因为我不确定内核是否总是保证您的堆栈指针会对齐。