[英]assembly code of the c function
我正在尝试理解C函数的汇编代码。 我无法理解为什么andl -16
在主要部分完成。 是为局部变量分配空间。 如果是这样,为什么subl 32
是为main做的。
我无法理解func1
的反汇编。 随着读取,堆栈从高阶地址增长到8086处理器的低阶地址。 那么为什么在ebp的正侧(对于参数偏移)进行访问以及为什么不在ebp的负侧进行访问。 func1中的局部变量是3 +返回地址+保存的寄存器 - 所以它必须是20,但为什么它是24? ( subl $24,esp
)
#include<stdio.h>
int add(int a, int b){
int res = 0;
res = a + b;
return res;
}
int func1(int a){
int s1,s2,s3;
s1 = add(a,a);
s2 = add(s1,a);
s3 = add(s1,s2);
return s3;
}
int main(){
int a,b;
a = 1;b = 2;
b = func1(a);
printf("\n a : %d b : %d \n",a,b);
return 0;
}
汇编代码:
.file "sample.c"
.text
.globl add
.type add, @function
add:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $0, -4(%ebp)
movl 12(%ebp), %eax
movl 8(%ebp), %edx
leal (%edx,%eax), %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
leave
ret
.size add, .-add
.globl func1
.type func1, @function
func1:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl 8(%ebp), %eax
movl %eax, 4(%esp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call add
movl %eax, -4(%ebp)
movl 8(%ebp), %eax
movl %eax, 4(%esp)
movl -4(%ebp), %eax
movl %eax, (%esp)
call add
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, 4(%esp)
movl -4(%ebp), %eax
movl %eax, (%esp)
call add
movl %eax, -12(%ebp)
movl -12(%ebp), %eax
leave
ret
.size func1, .-func1
.section .rodata
.LC0:
.string "\n a : %d b : %d \n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
movl $1, 28(%esp)
movl $2, 24(%esp)
movl 28(%esp), %eax
movl %eax, (%esp)
call func1
movl %eax, 24(%esp)
movl $.LC0, %eax
movl 24(%esp), %edx
movl %edx, 8(%esp)
movl 28(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
.section .note.GNU-stack,"",@progbits
andl $-16, %esp
通过清除低4位将堆栈指针与16字节的倍数对齐。
正向偏移与(%ebp)
一起使用的唯一地方是参数访问。
您没有说明您的目标平台是什么或者您用于编译的开关。 汇编代码显示已插入一些Ubuntu标识符,但我不熟悉它使用的ABI,除此之外它可能类似于通常与Intel x86架构一起使用的ABI。 所以我猜测ABI在例程调用时需要8字节对齐,因此编译器使func1
的堆栈帧为24字节而不是20字节,以便保持8字节对齐。
我将进一步猜测,编译器将堆栈在main
的开头与16字节对齐,作为编译器中的一种“首选项”,以防它使用喜欢16字节对齐的SSE指令或其他更喜欢16字节的操作对准。
所以,我们有:
在main
, andl $-16, %esp
将堆栈与16个字节的倍数对齐,作为编译器首选项。 在main
内部, 28(%esp)
和24(%esp)
是指编译器在堆栈上保存的临时值,而8(%esp)
, 4(%esp)
和(%esp)
用于将参数传递给func1
和printf
。 我们从汇编代码调用printf
的事实中看到,但是在代码中注释掉了你已粘贴的C源代码与用于生成汇编代码的C源代码不同:这不是正确的汇编代码生成的C源代码。
在func1
,在堆栈上分配24个字节而不是20个字节以保持8字节对齐。 在func1
内部,通过8(%ebp)
和4(%ebp)
访问参数。 从-12(%ebp)
到-4(%ebp)
位置用于保存变量的值。 4(%esp)
和(%esp)
用于传递要add
参数。
这是func1
的堆栈框架:
- 4(%ebp) = 20(%esp): s1. - 8(%ebp) = 16(%esp): s2. -12(%ebp) = 12(%esp): s3. -16(%ebp) = 8(%esp): Unused padding. -20(%ebp) = 4(%esp): Passes second parameter of add. -24(%ebp) = 0(%esp): Passes first parameter of add.
我建议用objdump -S
的输出来解决这个问题,这会让你与C源交互。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.