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*16
(n
可以为 0)。如果你被调用为入口点是安全的,你需要正确对齐你的堆栈指针,因为我不确定内核是否总是保证你的堆栈指针将被对齐。
遇到一个奇怪的现象,记录下代码。 我的测试平台是 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*16
(n
可以为 0)。如果你被调用为入口点是安全的,你需要正确对齐你的堆栈指针,因为我不确定内核是否总是保证你的堆栈指针将被对齐。