摘要:即将数据压栈,指令相当于执行之后,指向下一条指令,变为执行,,之后指向下一条指令,变为执行执行等价于每进入一个函数都会执行这两个汇编指令这两步操作是个规范化步骤叫做前序。
作者 云青
原创作品转载请注明出处
基本的汇编知识:
movl,subl,pushl,topl,ret,addl,leave,enter
参考资料下载地址。http://pan.baidu.com/s/1cdISDC
课程地址:《Linux内核分析》MOOC课程http://mooc.study.163.com/cou...
创建文件,并进行编译
int g(int x) { return x + 3; } int f(int x) { return g(x); } int main(void) { return f(8) + 1; }
编译命令:
gcc -S -o demo1.s demo1.c -m32
运行完毕后生成如下文件:
.file "demo1.c" .text .globl g .type g, @function g: .LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 movl 8(%ebp), %eax addl $3, %eax popl %ebp .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size g, .-g .globl f .type f, @function f: .LFB1: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 subl $4, %esp movl 8(%ebp), %eax movl %eax, (%esp) call g leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE1: .size f, .-f .globl main .type main, @function main: .LFB2: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 subl $4, %esp movl $8, (%esp) call f addl $1, %eax leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE2: .size main, .-main .ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4" .section .note.GNU-stack,"",@progbits
我们将命令简化得到如下汇编指令:
g: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax addl $3, %eax popl %ebp ret f: pushl %ebp movl %esp, %ebp subl $4, %esp movl 8(%ebp), %eax movl %eax, (%esp) call g leave ret main: pushl %ebp movl %esp, %ebp subl $4, %esp movl $8, (%esp) call f addl $1, %eax leave ret
备注:
%开头表示寄存器
$开头表示立即数
()表示间接寻址,样例(GAS = C语言):(%eax)= *eax [[2]](http://www.cnblogs.com/lxgeek...
Imm(Ea) ,变址寻址,样例(GAS = C语言):4(%eax) = *(4+eax)
我们以简化版为例:
C语言的都是从mian函数开始运行的,同理,汇编也是从main函数开始运行的,下面我们来从main函数入手。
首先执行的是18行pushl指令,
先画出内存的栈此时的情况:esp和ebp此时都指向栈底。
pushl %ebx ,即将数据压栈,指令相当于
subl $4, %esp
movl %ebx,(%esp)
执行pushl %ebp之后,eip指向下一条指令,变为:
2、执行movl %esp,%ebp,之后eip指向下一条指令,变为
3、执行 subl $4, %esp
4、执行movl $8, (%esp)
5 、call f
call f等价于
pushl %eip
movl f %eip
6 、pushl %ebp
7、movl %esp, %ebp
8、 subl $4, %esp
9、movl 8(%ebp), %eax
10、movl %eax, (%esp)
11、call g
12、
pushl %ebp
movl %esp,%ebp
每进入一个函数都会执行这两个汇编指令
这两步操作是个规范化步骤, 叫做前序(prologue) [1]。
执行函数时,汇编会生成一个堆栈调用框架:
具体如下:
//建立被调用者函数的堆栈框架 pushl %ebp movl %esp,%ebp //被调用者函数体 //do something //拆除被调用者的函数的堆栈框架 movl %ebp,%esp popl %ebp ret
13、 movl 8(%ebp), %eax
14、addl $3,%eax
15、popl %ebp
popl %ebp 相当于
movl (%esp),%ebp
addl $4,%esb
16、ret
等价于 popl %eip
17、leave 等价于
movl %ebp,%esp
popl %ebp
18、ret
19、addl $1,%eax
20 leave
21、ret
main函数运行结束,将%eax的值返回,即12
参考文章:
[1] X86汇编调用框架浅析与CFI简介
[2] http://www.cnblogs.com/lxgeek...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/33216.html
摘要:程序预处理本章节研究的是,源代码文件是如何一步步得到一个可执行程序的。如的语句被称为预处理指令,还有注释文本的删除,都在此阶段完成替换。目的是能够将所有文件中的代码组合到一起成一个完整的程序。终止程序可以正常也可以意外终止程序。 ...
摘要:假定我们有一个程序名为的语言源代码文件,要生成一个可执行文件,最简单的办法就是一步即可完成这时,预编译编译连接一次完成,生成一个系统预设的名为的可执行文件。 GCC简介 Linux系统下的gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比...
摘要:限制协程应用的最大技术条件是上下文切换。既然本系列讲的是基于汇编的协程,那么这篇文章我们就来讲讲使用汇编来进行上下文切换的原理。切换上下文也称为保存现场和恢复现场。协程结束后,会返回到函数中。 在前一篇文章《基于汇编的 C/C++ 协程 - 背景知识》中提到一个用于 C/C++ 的协程所需要实现的两大功能: 协程调度 上下文切换 其中调度,其实在技术实现上与其他的线程、进程调度没有...
阅读 2776·2021-11-23 09:51
阅读 1598·2021-11-19 09:40
阅读 2865·2021-11-02 14:46
阅读 744·2021-11-02 14:45
阅读 2463·2021-09-23 11:57
阅读 2349·2021-09-23 11:22
阅读 1772·2019-08-29 16:29
阅读 632·2019-08-29 16:16