我遇到一个奇怪的现象,我在下面记录代码。 我的试验台是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
答案 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*16
(n
可以为0)。如果您已被称为安全的入口点,则需要正确对齐堆栈指针,因为我不确定内核是否始终保证堆栈指针将对齐。