1

关于如何在 Assembly 中使用堆栈,我几乎没有疑问。据我所知,%rsp 寄存器用作堆栈指针。要在汇编代码中在堆栈上分配新内存,只需从 %rsp 中减去所需的数量,然后将其向后移动。然后,您可以通过向 %rsp 添加特定值来访问新分配的内存。

但是,当我使用 GCC 将 C 代码编译为汇编时,有时会得到奇怪的结果。

当我做一些这样的功能时:

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) 之间的空间呢?为什么不用?这背后有什么特别的原因吗?

4

0 回答 0