我正在试图弄清楚以下汇编代码究竟发生了什么。有人可以一行一行地解释发生了什么吗?我输入了我认为正在发生的事情(见评论),但需要澄清。
.file "testcalc.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "x=%d, y=%d, z=%d, result=%d\n"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx // establish stack frame
andl $-16, %esp // decrement %esp by 16, align stack
pushl -4(%ecx) // push original stack pointer
pushl %ebp // save base pointer
movl %esp, %ebp // establish stack frame
pushl %ecx // save to ecx
subl $36, %esp // alloc 36 bytes for local vars
movl $11, 8(%esp) // store 11 in z
movl $6, 4(%esp) // store 6 in y
movl $2, (%esp) // store 2 in x
call calc // function call to calc
movl %eax, 20(%esp) // %esp + 20 into %eax
movl $11, 16(%esp) // WHAT
movl $6, 12(%esp) // WHAT
movl $2, 8(%esp) // WHAT
movl $.LC0, 4(%esp) // WHAT?!?!
movl $1, (%esp) // move result into address of %esp
call __printf_chk // call printf function
addl $36, %esp // WHAT?
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
原始代码:
#include <stdio.h>
int calc(int x, int y, int z);
int main()
{
int x = 2;
int y = 6;
int z = 11;
int result;
result = calc(x,y,z);
printf("x=%d, y=%d, z=%d, result=%d\n",x,y,z,result);
}
答案 0 :(得分:11)
您没有显示编译命令,这可能很有用,但似乎您已启用优化,因此实际上没有局部变量的空间,它们已经过优化:
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
以上所有代码都设置了堆栈帧。由于它是main
,它与标准堆栈帧略有不同:它确保堆栈与andl $-16, %esp
对齐,以防万一。
pushl %ecx
它会在对齐校正之前保存esp
的原始值,以便在结束时将其恢复。
subl $36, %esp
它分配36个字节的堆栈空间,不是用于局部变量,而是用于调用参数。
movl $11, 8(%esp)
movl $6, 4(%esp)
movl $2, (%esp)
它设置从右到左调用calc
的参数,即常量(2, 6, 11)
。
call calc // function call to calc
它使用calc
指向的参数调用函数esp
。
movl %eax, 20(%esp)
movl $11, 16(%esp)
movl $6, 12(%esp)
movl $2, 8(%esp)
movl $.LC0, 4(%esp)
movl $1, (%esp)
这些是从右到左调用__printf_chk
的参数:(1, .LC0, 2, 6, 11, %eax)
,其中%eax
是calc()
的返回值(请记住,没有局部变量!)并且.LC0
是文字字符串的地址,请查看程序集顶部的这些行:
.LC0:
.string "x=%d, y=%d, z=%d, result=%d\n"
但那个神秘的1
呢?好吧,在Ubuntu中,标准编译选项(-D_FORTIFY_SOURCE
)将使printf
内联函数转发到__printf_chk(1, ...)
或类似的东西,对参数进行额外检查。
call __printf_chk
这是对printf
替代函数的调用。
addl $36, %esp
这将删除使用subl $36, %esp
添加到堆栈的36个字节。
popl %ecx
这会将可能未对齐的堆栈指针恢复为ecx
。
popl %ebp
leal -4(%ecx), %esp
这将恢复先前的堆栈帧。
ret
这会返回没有值,因为你没有为main
写回报。