程序集奇怪的堆栈用法

时间:2019-03-17 14:05:55

标签: c assembly stack x86-64 red-zone

我对组装中如何使用堆栈有几个疑问。据我所知,%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)之间的间隔呢?为什么未使用?其背后是否有某些特殊原因?

0 个答案:

没有答案