我正在努力学习基本装配。我用C编写了一个简单的程序来翻译成汇编:
void myFunc(int x, int y) {
int z;
}
int main() {
myFunc(20, 10);
return 0;
}
这就是我认为函数的正确翻译:
.text
.globl _start
.type myFunc, @function
myFunc:
pushl %ebp #Push old ebp register on to stack
movl %esp, %ebp #Move esp into ebp so we can reference vars
sub $4, %esp #Subtract 4 bytes from esp to make room for 'z' var
movl $2, -4(%ebp) #Move value 2 into 'z'
movl %ebp, %esp #Restore esp
popl %ebp #Set ebp to 0?
ret #Restore eip and jump to next instruction
_start:
pushl $10 #Push 10 onto stack for 'y' var
pushl $20 #Push 20 onto stack for 'x' var
call myFunc #Jump to myFunc (this pushes ret onto stack)
add $8, %esp #Restore esp to where it was before
movl $1, %eax #Exit syscall
movl $0, %ebx #Return 0
int $0x80 #Interrupt
只是仔细检查一下我在gdb中运行它并且对结果感到困惑:
(gdb) disas myFunc
Dump of assembler code for function myFunc:
0x08048374 <myFunc+0>: push ebp
0x08048375 <myFunc+1>: mov ebp,esp
0x08048377 <myFunc+3>: sub esp,0x10
0x0804837a <myFunc+6>: leave
0x0804837b <myFunc+7>: ret
End of assembler dump.
当整数长度为4个字节时,为什么在0x08048377时gcc从堆栈中减去0x10(16个字节)?
另外,休假指令是否等同于以下内容?
movl %ebp, %esp #Restore esp
popl %ebp #Set ebp to 0?
使用:
gcc version 4.3.2 (Debian 4.3.2-1.1)
GNU gdb 6.8-debian
答案 0 :(得分:7)
根据平台的不同,GCC可能会选择不同的堆栈对齐方式;这可以被覆盖,但这样做会使程序变慢或崩溃。默认-mpreferred-stack-boundary=4
使堆栈与16字节地址保持一致。假设堆栈指针已经在函数的开头适当地对齐,它将在sub %esp, $10
之后保持对齐。
leave
是一个x86宏指令,相当于mov %ebp, %esp; pop %ebp
。
答案 1 :(得分:4)
你的GDB被配置为打印出英特尔而不是AT&amp; T汇编语法 - 在它比你已经困惑之前把它关闭。
要求堆栈指针(%esp
)始终与16字节边界对齐。这可能是sub esp,0x10
来自的地方。 (这是不必要的,但GCC历来一直不注意堆栈调整是不必要的。)此外,你的功能没有做任何有趣的事情,所以身体已经优化了。你应该编译这段代码:
int myFunc(int x, int y)
{
return x + y;
}
int main(void)
{
return myFunc(20, 30);
}
这将生成更容易映射回原始C语言的汇编语言.GCC 仍然可以生成
main:
movl $50,%eax
ret
除了使用-O3 -fwhole-program
; - )之外,其他任何事情都不会发生