汇编输出+关于堆栈的问题

assembly output + questions about stacks

当我 运行 进入一个我似乎无法解决的特定练习时,我正在学习我的一门课程......这是非常基础的,因为我对装配非常陌生。那么让我们开始吧。

我有一个 C 函数

unsigned int func(int *ptr, unsigned int j) {
    unsigned int res = j;   
    int i = ptr[j+1];

    for(; i<8; ++i) {
        res >>= 1;
    }
    return res;
}

我运行将其与 gcc 一起编译为程序集

.file   "func.c"
.intel_syntax noprefix
.text
.globl  func
.type   func, @function
func:
.LFB0:
.cfi_startproc
push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov rbp, rsp
    .cfi_def_cfa_register 6
    mov QWORD PTR [rbp-24], rdi
    mov DWORD PTR [rbp-28], esi
    mov eax, DWORD PTR [rbp-28]
    mov DWORD PTR [rbp-8], eax
    mov eax, DWORD PTR [rbp-28]
    add eax, 1
    mov eax, eax
    lea rdx, [0+rax*4]
    mov rax, QWORD PTR [rbp-24]
    add rax, rdx
    mov eax, DWORD PTR [rax]
    mov DWORD PTR [rbp-4], eax
    jmp .L2
.L3:
    shr DWORD PTR [rbp-8]
    add DWORD PTR [rbp-4], 1
.L2:
    cmp DWORD PTR [rbp-4], 7
    jle .L3
    mov eax, DWORD PTR [rbp-8]
    pop rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   func, .-func
    .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
    .section    .note.GNU-stack,"",@progbits

问题如下。将 j(c 函数中的变量)放在堆栈顶部的命令是什么?

实在想不通请赐教XD

考虑到这些说明

mov eax, DWORD PTR [rbp-28]
add eax, 1

似乎 j 存储在地址 rbp-28ptr 存储在地址 rbp-24.

这些是将值存储在堆栈中的指令

mov QWORD PTR [rbp-24], rdi
mov DWORD PTR [rbp-28], esi

似乎参数是使用寄存器 rdiesi 传递给函数的。

编译器可以优化它们的函数调用并使用寄存器而不是堆栈来将小尺寸参数传递给函数。在函数中,他们可以使用堆栈来临时存储通过寄存器传递的参数。

只是一个建议,供您自己进一步探索。使用 gcc -O0 -g2 f.c -Wa,-adhln。它将关闭优化并生成与源代码混合的汇编代码。它可能会让您更好地了解它的作用。

作为替代方案,您可以在输出“.o”或可执行文件上使用 objdump -Sd f.o。只需确保添加调试信息并在编译时关闭优化。

变量jfunc的第二个参数;它存储在 x86-64 System V ABI 调用约定中的寄存器 esi 中。这条指令mov DWORD PTR [rbp-28], esij入栈。

你可以通过编写一个简单的函数调用 "func" 并用 -O0 编译它(或者用 -O2 并将其标记为 noinline 来非常清楚地看到它,或者只提供一个原型,这样编译器就不需要内联任何东西了)。

unsigned int func(int *ptr, unsigned int j) {
    unsigned int res = j;   
    int i = ptr[j+1];

    for(; i<8; ++i) {
        res >>= 1;
    }
    return res;
}

int main()
{
    int a = 1;
    int array[10];    
    func (array, a);
    return 0;
}

使用 Godbolt 编译器资源管理器,we can easily get gcc -O0 -fverbose-asm assembly output。 请重点关注以下说明:

# in main:
...
mov DWORD PTR [rbp-4], 1
mov edx, DWORD PTR [rbp-4]
...
mov esi, edx
...


func(int*, unsigned int):
...
mov DWORD PTR [rbp-28], esi   # j, j
...

j, jgcc -fverbose-asm添加的注释,告诉你源操作数和目标操作数都是该指令中的C变量j

完整的组装说明:

func(int*, unsigned int):
  push rbp
  mov rbp, rsp
  mov QWORD PTR [rbp-24], rdi
  mov DWORD PTR [rbp-28], esi
  mov eax, DWORD PTR [rbp-28]
  mov DWORD PTR [rbp-4], eax
  mov eax, DWORD PTR [rbp-28]
  add eax, 1
  mov eax, eax
  lea rdx, [0+rax*4]
  mov rax, QWORD PTR [rbp-24]
  add rax, rdx
  mov eax, DWORD PTR [rax]
  mov DWORD PTR [rbp-8], eax
  jmp .L2
.L3:
  shr DWORD PTR [rbp-4]
  add DWORD PTR [rbp-8], 1
.L2:
  cmp DWORD PTR [rbp-8], 7
  jle .L3
  mov eax, DWORD PTR [rbp-4]
  pop rbp
  ret
main:
  push rbp
  mov rbp, rsp
  sub rsp, 48
  mov DWORD PTR [rbp-4], 1
  mov edx, DWORD PTR [rbp-4]
  lea rax, [rbp-48]
  mov esi, edx
  mov rdi, rax
  call func(int*, unsigned int)
  mov eax, 0
  leave
  ret