我对组装中如何使用堆栈有几个疑问。据我所知,%rsp寄存器用作堆栈指针。要在汇编代码中在堆栈上分配新的内存,只需从%rsp中减去所需的数量,然后将其向后移动。然后,您可以通过向%rsp添加特定值来访问新分配的内存。
但是,当我将带有GCC的C代码编译为Assembly时,有时会得到奇怪的结果。
当我执行这样的功能时:
int fun(int arg1, int arg2)
{
return arg2;
}
我希望这样:
fun: pushq %rbp
movq %rsp, %rbp
sub $8, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
addq $8, %rsp
popq %rbp
ret
相反,我得到了:
fun:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl 16(%rbp), %eax
popq %rbp
ret
实际上并没有移动堆栈指针,只是使用了它后面的空间。当我传递7个参数时,它变得更加奇怪:
int fun(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7)
{
return arg7;
}
现在的汇编代码:
fun:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl %edx, -12(%rbp)
movl %ecx, -16(%rbp)
movl %r8d, -20(%rbp)
movl %r9d, -24(%rbp)
movl 16(%rbp), %eax
popq %rbp
ret
在上一个示例之后,我期望代码不会从%rsp中减去任何内容。使用较小的地址是完全可以的,那里什么也没有。但是16(%rsp)呢?它应该指向已在堆栈中分配的空间,它不会覆盖某些内容吗?
还有最后但并非最不重要的一点,如果我编写了这个简单的函数:
void fun(int arr[], int n)
{
int i = 0;
while(i < n)
{
++arr[i++];
}
}
汇编代码:
fun:
.LFB0:
pushq %rbp
movq %rsp, %rbp
movq %rdi, -24(%rbp)
movl %esi, -28(%rbp)
movl $0, -4(%rbp)
jmp .L2
.L3:
movl -4(%rbp), %eax
leal 1(%rax), %edx
movl %edx, -4(%rbp)
cltq
leaq 0(,%rax,4), %rdx
movq -24(%rbp), %rax
addq %rdx, %rax
movl (%rax), %edx
addl $1, %edx
movl %edx, (%rax)
.L2:
movl -4(%rbp), %eax
cmpl -28(%rbp), %eax
jl .L3
nop
popq %rbp
ret
如您所见,指向arr的指针存储在-24(%rsp)到-16(%rsp)中。整数n存储在-28(%rsp)至-24(%rsp)中,而整数i存储在-4(%rsp)至(%rsp)中。 -16(%rsp)到-4(%rsp)之间的间隔呢?为什么未使用?其背后是否有某些特殊原因?