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

时间:2017-10-18 23:31:00

标签: c gcc assembly intel disassembly

当我遇到一个我似乎无法解决的特定练习时,我正在学习我的一门课程......这是非常基础的,因为我对装配很新。让我们开始吧。

我有一个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。

3 个答案:

答案 0 :(得分:1)

变量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, j是由gcc -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

答案 1 :(得分:0)

考虑到这些说明

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

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

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

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

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

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

答案 2 :(得分:0)

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

作为替代方案,您可以在输出中使用objdump -Sd f.o&#39; .o&#39;或可执行的。只需确保添加调试信息并在编译时关闭优化。