我试图弄清楚以下汇编代码到底发生了什么.有人可以一行一行地解释发生了什么吗?我输入了我认为正在发生的事情(见评论)但需要澄清.
.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
Run Code Online (Sandbox Code Playgroud)
原始代码:
#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);
}
Run Code Online (Sandbox Code Playgroud)
rod*_*igo 11
你没有表现出编译命令,这可能是有用的,但它似乎已启用的优化,所以实际上有局部变量没有空间,它们会被优化了:
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
Run Code Online (Sandbox Code Playgroud)
上面的所有代码都设置了堆栈帧.因为它与main标准堆栈框架略有不同:它确保了堆栈的对齐andl $-16, %esp,以防万一.
pushl %ecx
Run Code Online (Sandbox Code Playgroud)
它保存esp对齐校正前的原始值,以便在最后恢复它.
subl $36, %esp
Run Code Online (Sandbox Code Playgroud)
它分配36个字节的堆栈空间,不是用于局部变量,而是用于调用参数.
movl $11, 8(%esp)
movl $6, 4(%esp)
movl $2, (%esp)
Run Code Online (Sandbox Code Playgroud)
它设置calc从右到左调用的参数,即常量,(2, 6, 11).
call calc // function call to calc
Run Code Online (Sandbox Code Playgroud)
它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)
Run Code Online (Sandbox Code Playgroud)
这些是调用参数__printf_chk,从右到左:(1, .LC0, 2, 6, 11, %eax),其中%eax是的返回值calc()(记住,任何局部变量!),并.LC0为文字字符串的地址,看看这些线在组装的顶部:
.LC0:
.string "x=%d, y=%d, z=%d, result=%d\n"
Run Code Online (Sandbox Code Playgroud)
但那神秘1呢?好吧,在Ubuntu中,标准的编译选项(-D_FORTIFY_SOURCE)将创建printf一个转发到__printf_chk(1, ...)或类似的内联函数,它对参数进行额外的检查.
call __printf_chk
Run Code Online (Sandbox Code Playgroud)
这是对printf替代函数的调用.
addl $36, %esp
Run Code Online (Sandbox Code Playgroud)
这将删除添加到堆栈的36个字节subl $36, %esp.
popl %ecx
Run Code Online (Sandbox Code Playgroud)
这会将可能未对齐的堆栈指针恢复为ecx.
popl %ebp
leal -4(%ecx), %esp
Run Code Online (Sandbox Code Playgroud)
这将恢复先前的堆栈帧.
ret
Run Code Online (Sandbox Code Playgroud)
并且返回时没有值,因为您没有写入返回值main.