用汇编来河北2012定额解释汇编“计算机是怎么工作的”

计算机是如何工作的?
计算机是如何工作的呢?
代码编写及反汇编
main.s执行过程分析
1. 计算机是如何工作的呢?
从宏观上来讲,计算机是给人提供服务的,提供方便的。人类想要告诉计算机一些东西,通过什么途径呢?输入设备(如鼠标,键盘,等等);计算机想要告诉人类一些东西也会通过输出设备(如屏幕,打印机,等等);计算机在读入用户的数据后,会对数据进行计算加工,这时会用到运算器;而运算的结果可能不会立刻输出给用户,而是暂存起来,这时计算机会用到存储器;而不论是运算的过程还是存储的过程,逻辑流程的安排,步骤序列则需要控制器。人类与计算机的交互,就是通过这五大部件的通力合作展开的,或者说,计算机的工作就是通过这五大部件实现的。
现代计算机基本上都是遵照冯·诺依曼体系结构进行设计、生产建造的。而该体系结构的核心概念就是存储程序计算机,即存储在计算机中的程序的执行,是通过一条条指令的执行而实现的(三阶段:取指令、指令译码、执行指令)。这是我们从微观的角度来看待计算机是如何工作的。
2.实验过程&
下面实验我们通过反汇编程序代码,来观察从程序代码到汇编代码的变化,去观察一条条的指令是如何执行的。
代码编写及反汇编
在terminal中输入”cd
Code”进入Code目录,用“ls”命令查看该目录中的文件,输入“vim
main.c”,用vim文本编辑器创建一个main.c文件。
在vim编辑器中输入图中代码,用”:wq”进行保存代码。
用”gcc -S -o main.s main.c
-m32”将main.c反汇编到main.s,其中”-m32”表示使用32位机模式。
用vim打开main.s,发现文件结构如图所示。
将上图中的以”.”开头的冗余代码(说明代码)删除得到如图干净的,AT&T格式的汇编代码。
main.s执行过程分析
标号开始(17)[注:括号内的数字为vim中汇编代码所在行号]
第一句(18):pushl %ebp
pushl做的工作是:先在栈内增长4字节(32位机),即esp偏移4个字节。然后再把ebp的内容存到栈顶。[注:虽是增长,但实际却是从高地址到低地址的过程,即栈底所在为高地址,栈顶从高地址向低地址增长]
该语句可以用以下两条语句表示:
subl $4, %esp
movl %ebp, (%esp)
效果如下图:
第二句(19):movl %esp, %ebp
使得ebp指向esp指向的位置,效果如下图:
第三句(20):subl $4, %esp
栈顶向下(低地址)增长,或者说偏移4个字节,效果如下图:
第四句(21):movl $9, (%esp)
将一个立即数9存入栈顶,效果如下图:
第五句(22):call f
调用f(),call的工作是将当前“call
f”的下一条指令的eip存入栈中(根据代码显示应为23行的”addl $6,
%eax”地址作为eip),并将标号f所在的地址视作当前指令的地址,该指令等同于以下指令:
pushl %eip
movl $0x****, %eip
可进一步扩展为:
subl $4, %esp
movl %eip, (%esp)
movl $0x****, %eip
效果如下图:
标号f开始(8)
第一句(9):pushl %ebp
同上,栈顶增长4字节,然后再把ebp的内容存到栈顶,效果如下图:
[注:ebp(1)中的“1”非汇编代码中的行号,而是栈的相对标号]
第二句(10):movl %esp, %ebp
使得ebp指向esp指向的位置,效果如下图
第三句(11):subl $4, %esp
栈顶向下(低地址)增长,或者说偏移4个字节,效果如下图:
第四句(12):movl 8(%ebp),
8(%ebp)是变址寻址方式,表示ebp+8地址的内容;而这条指令的含义就是将(ebp+8)中的内容赋值给eax寄存器。(ebp+8)所指向的内容就是栈标号2所代表的9;指令既是将9赋值给eax寄存器。此时eax值为9。[注:画的图仅为内存中栈的变化,而寄存器的变化则需要脑补]
第五句(13):movl %eax, (%esp)
将寄存器eax中的内容放到栈顶,其效果如下图:
第六句(14):call g
调用g(),call的工作是将当前“call
g”的下一条指令的eip存入栈中(根据代码显示应为15行的”leave”地址作为eip),并将标号g所在的地址视作下一条指令的地址,该指令等同于以下指令:
pushl %eip
movl 0x****, %eip
可进一步扩展为:
subl $4, %esp
movl %eip, (%esp)
movl 0x****, %eip
效果如下图:
标号g开始(1)
第一句(2):pushl %ebp
同上,栈顶增长4字节,然后再把ebp的内容存到栈顶,效果如下图:
第二句(3):movl %esp, %ebp
使得ebp指向esp指向的位置,效果如下图:
第三句(4):movl 8(%ebp), %eax
同上,8(%ebp)是变址寻址方式,表示ebp+8地址的内容;而这条指令的含义就是将(ebp+8)中的内容赋值给eax寄存器;(ebp+8)所指向的内容就是栈标号5所代表的9。指令即是将9赋值给eax寄存器。此时eax值为9。
第四句(5):addl $8, %eax
将一个立即数与eax寄存器中的数相加,最后结果存回eax寄存器中,此时eax寄存器值为17(9+8)。
第五句(6):popl %ebp
该条指令的作用是,将栈顶的内容放到ebp中去,然后栈顶指针减1(实际地址增加4个字节);
该指令相当于:
movl (%esp), (%ebp)
addl $4, %esp
此时汇编指令已经回到了上层函数,效果如下图:
第六句(7):ret
该语句相当于popl %eip
是将栈顶的元素置到eip寄存器中,此时计算机的当前指令为第15行指令,真正意义上的回到了上层函数,并把控制权交还给上层函数,效果如下图:
标号f(8)
第七句(15):leave
该指令相当于:
movl %ebp, %esp
popl %ebp
用该函数的栈基址指针直接覆盖栈顶指针,使得尽管函数中还有其他的“东西”在栈中,也可以直接跳过,退出函数。
效果如下图:
第八句(16):ret
该语句相当于popl %eip
是将栈顶的元素置到eip寄存器中,此时计算机的当前指令为第23行指令,真正意义上的回到了上层函数,并把控制权交还给上层函数,效果如下图:
标号main(17)
第六句(23):addl $6, %eax
将一个立即数与eax寄存器中的数相加,最后结果存回eax寄存器中,此时eax寄存器值为23(17+6)。
第七句(24):leave
该指令相当于:
movl %ebp, %esp
popl %ebp
用该函数的栈基址指针直接覆盖栈顶指针,使得尽管函数中还有其他的“东西”在栈中,也可以直接跳过,退出函数。
效果如下图:
第八句(25):ret
该语句相当于popl %eip
是将栈顶的元素置到eip寄存器中,退出了main函数。
至此,程序运行完毕。
读大学每个人都会有一个专业,而读计算机科学与技术的人需要用4年去认识、了解一台计算机,我们需要了解她的方方面面,需要了解她到底是如何工作的。
从宏观上讲,计算机的功能部件包括输入、输出、运算、存储、控制,而计算机的工作也是依托于它们的相互协作。这就是冯·诺依曼体系结构,而通过冯·诺依曼体系结构又将我们带到了“计算机是如何工作的”的微观世界;从微观上讲,存储程序的概念告诉我们存储在计算机中的程序的执行,是通过不断地重复“取指令、指令译码、执行指令”这三个过程实现的。
而一条条堆栈操作指令的确定,是通过ebp+esp的组合找到的;同样地,寄存器ecs+eip的组合也是确定当前代码段中当前指令的重要一环,当然了,它们都是寄存器,不在内存中。
这篇文章仅仅是对实验的流程进行解释说明,只是着重展示了计算机是如何工作的这一个话题,并不会面面具到地介绍冯·诺依曼体系结构、汇编语言语法、vim的使用等等。当然,仅仅聚焦在一点上会显得不足,若文章中有错误,欢迎大家指正。
陈金雷 + 原创作品转载请注明出处 +
《Linux内核分析》MOOC课程http://mooc./course/USTC-
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。通过分析汇编代码理解计算机是如何工作的 - 简书
通过分析汇编代码理解计算机是如何工作的
网易云课堂《》作业实验目的:通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的实验过程:登陆实验楼虚拟机/courses/195准备main.c源码
打开终端shell,输入以下命令:gcc –S –o main.s main.c -m32生成汇编代码main.s
main.s.part1
main.s.part2
main.s.part3
精简后的汇编代码
实验分析:先看C语言代码,该程序由3个函数组成,分别是main()、f()、g(),实现了2+7+5的简单数学计算。程序首先从main()开始,将数字2传入f(),f()又将main()传入的数字2原封不动的传给g(),g()实现了将传入2加5的操作,结果7返回给f(),f()获得7后直接返回给main(),mian()将获得的7加7后返回最终结果14.理论上编译后的汇编语言也应该实现上述功能,即计算2+5+7值。下面我们来分析精简后的汇编代码:左边代码行标表示eip,红框表示当前运行的代码,eax初始化为未知变量,右边蓝色格子表示内存段,为简化描述在32位环境下1个内存单元格表示4byte,ebp和esp初始值均为0程序从main开始
将ebp压栈,相当于执行以下两条语句:subl $4, %esp //esp减4byte,向下移动1格,此时esp指向1movl %ebp, (%esp) //将ebp的值存入esp指向的存储单元内
将esp的值赋值给ebp,即ebp也指向1
将esp的值减4,即esp向下移动1格,此时esp指向2
将数字2存入esp指向的内存块
调用f,相当于执行以下两条语句:pushl %eipmove f, %eip
call f执行完后的状态,此时程序完成由main跳转到f
该条指令将ebp-8的位置,即标号为2的内存单元所存放的数字2赋值给eax,此时eax等于2
将eax的值加5后再存入eax,此时eax等于7
出栈操作,相当于以下两条语句:movl (%esp), %ebpaddl $4, %esp
ret相当于执行以下语句:popl %eip
leave相当于执行以下两条语句:movl %ebp, %esppopl %ebpg没有leave指令语句的原因是它没有再调用其他函数
将数字7累加到eax,此时eax等于14
至此整个程序完成了所有操作,结果存放在eax,即14为最终计算结果,与C语言代码一致。实验总结:计算机有一套规则来控制程序运行,在C语言和汇编语言中,CPU从程序main函数所在的内存地址开始做取指操作,根据当前状态和输入计算出新值,并且进入一个新的状态,同时改变寄存器所存储的数值。参考:PS:由于超过了提交作业时间,本篇作业未参与评分互评作业:(该篇作业未完成,提交了无效链接)(QZONE)作业13(未提供博客链接,仅给出精简后的汇编代码)(onedrive)作业21(未提供博客链接,仅给出C源码和精简后的汇编代码)(给个微博链接是要互粉吗?)(QZONE)(QZONE需要密码无法查看)
aapu原创作品转载请注明出处《Linux内核分析》MOOC课计算机系统(6)
学过计算机组成原理的同学可能对计算机的工作过程有一定的了解了。计算机的工作过程,笼统地讲就是程序的执行过程。现在,我们通过反汇编一个简单的C程序,逐步分析汇编代码,来了解程序是怎样工作的。
准备一个C程序
int g( int x )
return x + 7;
int f( int x )
return g(x);
int main(void)
return f(11) + 1;
使用如下命令得到汇编代码
gcc -S -o main.S main.c -m32
命令中的参数意义:
-S :表示生成汇编代码
-m32 :表示生成32位格式
生成的汇编代码如下
为了简洁,删掉了其中的一些辅助信息
分析汇编代码
大家知道,程序是从main处开始执行的,我们就先来看看main函数的汇编码。
首先看这两行
pushl %ebp
%esp, %ebp
可以发现,三段代码开头都是这两句。将栈基指针压栈,再将栈基指针指向栈顶,这么做的用意是保存当前基址,并开始一个新的栈,每个函数就有独立的栈空间。当函数返回时,能恢复到之前的栈空间。
再看下面两行
%11, (%esp)
这两行的作用是将参数压栈。由于栈是由高地址向低地址扩充,所以入栈是栈顶指针减4(32位指令格式)。在函数 f 中也看到了类似的语句(以下语句中为什么参数是8(%esp)会在后面说明)
8(%ebp), %eax
%eax, (%esp)
由此我们知道在调用函数前需要把参数逐个压栈。这里函数只有一个参数,而如果是两个或更多参数,入栈顺序是怎样的呢?经查资料并验证,参数入栈顺序是从右到左依次入栈的。
接下来调用 call 命令,跳转到函数 f 。我们知道 call 命令等同以下语句
将下一条指令入栈后,再将寄存器 eip 的值赋为目标函数 f 的第一条指令地址。这么做是为了当被调用的函数执行结束后,需要返回当前函数继续执行,所以必须要保存下一条指令,否则程序就无法继续执行了。
按着执行顺序,我们来看函数 f ,如前所说,前两条是保存main函数的栈基指针,后面三句是因为要调用函数 g ,先将参数入栈。
现在来回答前面的问题,为什么参数传递是
mov 8(%ebp), %eax
根据刚刚分析的过程,在main函数中参数入栈后进行了下面的操作:
- 调用 f 函数的指令 call f,使 call f 下一条指令的地址被压栈了,占用栈空间4个字节
- 进入 f 函数后,将main函数的栈基地址入栈了,这也占用了4个字节
因此, 8(%ebp) 即是前一个函数的第一个参数。
函数 g 过程与 f 类似,不再赘述,现在来看看函数退出过程是怎样的。
我们看到,函数 main 和函数 f 退出时都使用了语句
leave指令相当于如下指令:
%ebp, %esp
第一句是将 esp 赋值为 ebp,其实就是释放当前函数所使用的栈空间。
第二句是将栈顶指针赋值给ebp,并出栈,注意,此时的栈顶值实际上是前一个函数的栈基地址,所以这一句的作用就是把ebp恢复到调用函数(前一个函数)的栈基地址。
ret 作用是继续执行原来的程序,相当于
细心的你可能已经发现,为什么函数 g 没有leave呢?这是因为函数 g 内部没有使用变量,没有调用函数,栈空间是空的,编译器优化了指令。
最后,通过这个实验,总结一下程序运行的过程:
调用其它函数时,将指令指针入栈保存,以便函数执行结束能返回来继续下一条指令的执行;
被调用函数执行时,要将当前栈基地址压栈,以便调用结束后能恢复到调用函数栈空间;
函数参数入栈,参数入栈顺序是从右到左进栈;
函数退出时,将 esp 赋值为 ebp,释放当前函数所使用的栈空间;
然后将栈顶元素出栈保存到 ebp,把ebp恢复到调用函数(前一个函数)的栈基地址;
eip退回到上一个函数即将要执行的那条语句的地址上。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1569次
排名:千里之外实验目的:
通过反汇编一个简单的C程序,分析汇编代码理解计算机是如何工作的
实验过程:
通过vi程序进行编程:
int&g(int&x)
&&return&x&+&3;
int&f(int&x)
&&return&g(x);
int&main(void)
&&return&f(8)&+&1;
保存为main.c文件
使用反汇编命令
gcc -S -o main.s main,c -m32
-S : 编译为汇编语言程序
-o : 指定目标程序名称
-m32 : 指定编译为32位环境的汇编代码
得到结果:
删除符号项:
首先main函数开始执行,将基址寄存器的值进栈,用于系统控制main函数返回。
movl&&&&%esp,&%ebp &
此行代码将esp寄存器的内容赋值给ebp,即ebp和esp都指向地址1的位置
subl&&&&$4,&%esp &
将esp的指针指向下面一个位置
movl&&&&$8,&(%esp) &
将立即数8存放在当前esp所指位置
call&&&&f &
call&&&&g &
最后返回main函数,结果:
实验总结:
学到一些基本的汇编语言,知道了汇编语言是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。普遍地说,特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植
eax默认作为函数返回值保存寄存器
ebp为栈低指针寄存器
esp为栈顶指针寄存器(运行中的每个程序都有一段堆栈空间,用来存放程序运行时数据)
eip为当前指令位置寄存器
push为压栈指令
pop为出栈指令
add为加法指令
阅读(...) 评论()}

我要回帖

更多关于 湖南06定额解释汇编 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信