【示例代码1】
(1)C代码(test.c)
1 #include
2
3 void func()
4 {
5 return;
6 }
7
8 int main(void)
9 {
10 return 0;
11 }
(2)编译命令:gcc -S test.c
(3)汇编代码(test.s)
1 .file "test.c"
2 .text
3 .globl func
4 .type func, @function
5 func:
6 pushl %ebp ;EBP寄存器压栈
7 movl %esp, %ebp ;将ESP值赋给EBP
8 popl %ebp ;弹栈赋给EBP
9 ret
10 .size func, .-func
11 .globl main
12 .type main, @function
13 main:
14 leal 4(%esp), %ecx ;ESP值加4赋给ECX
15 andl $-16, %esp ;ESP末四位清零,目的是为了字节对齐
16 pushl -4(%ecx) ;将ECX-4值压栈(即ESP所指位置),这就是EIP
17 pushl %ebp ;EBP压栈,以便在函数退出时恢复EBP
18 movl %esp, %ebp ;将ESP寄存器的值赋给EBP
19 pushl %ecx ;ECX值压栈,在函数退出时,通过此值恢复ESP
20 movl $0, %eax ;函数返回值赋给EAX
21 popl %ecx ;ECX弹栈
22 popl %ebp ;EBP弹栈,恢复EBP
23 leal -4(%ecx), %esp ;恢复ESP
24 ret
25 .size main, .-main
26 .ident "GCC: (GNU) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"
27 .section .note.GNU-stack,"",@progbits
(4)汇编代码详解
第1行,文件名称。
第2行,代码段标识符。
第3 - 4行,func函数标识。若该函数为static类型,则没有第三行声明。
第5行,func函数入口标识。
第6 - 9行,func函数主体。
第10行,func函数的代码段大小。
第11 - 12行,main函数标识。
第13行,main函数入口标识。
第14 - 24行,main函数主体部分。
第25行,main函数的代码段大小。
第26 - 27行,GCC编译器嵌入的标记。
【分析总结】
(1)一个代码段(函数)运行时使用的栈内存空间是连续的。EBP指向当前运行代码段的堆栈空间的第一个位置,也就是基地址;代码段在存取自身所使用的数据时使用EBP索引,在获取局部变量参数时使用ESP索引。
(2)ESP是栈顶指针,POP、PUSH会自动改变ESP的值;PUSH操作,先移动指针后向内存写数据;POP操作,先从读取内存数据后移动指针。
(3)堆栈是动态内存空间,在程序运行过程时,其中保持的主要内容有:局部变量(非静态变量),函数调用的现场保护数据(主要是相关寄存器的值)。
(4)在调用某函数时,必须先保存EBP寄存器的值,然后将EBP指向自己的真实地址;在退出函数执行时,必须恢复EBP的值,使调用者能够正常运行。
(5)函数的返回值,在默认的情况下,存放在EAX寄存器中。
阅读(6895) | 评论(0) | 转发(2) |