汇编(六)栈段、第一个汇编程序
3.10 栈段
- 我们可以根据需要,将一组内存单元定义为一个段
- 我们可以将长度为n(n<=64k)的一组地址连续。起始地址为16的倍数的内存单元,当做栈来使用,从而定义了一个栈段
- 将内存当做栈栈,仅仅是我们再编程时的一种安排。CPU并不会由于这种安排,就在执行push、pop等栈操作指令时就自动的将我们定义的栈段当做栈空间来访问
-
如果我们将10000~1FFFF这段空间当做栈段。初始状态是空的,此时ss=1000,sp=?
- 栈最底部的内存单元为1000:FFFE
- 任意时刻,ss:sp执行栈顶,当栈中只有一个元素的时候,ss=1000, sp=FFFEH
- 栈为空的,就相当于与栈中唯一的元素出栈, 出栈后,sp=sp+2
FFFE + 2 = 10000 因为进位存不下的问题,又用到了前面相加的知识,所以当这段栈为空的时候:SS=1000H,SP=0
-
一个栈段的最大内容为多少?
- 栈顶的变化范围是0~FFFF, 为64k,从栈空的时候sp=0, 一直压栈,直到栈满时, sp=0, 如果再次压栈, 栈顶将环绕,覆盖原来栈中的内容
- 栈的存在主要是来临时存放东西,当一个函数被调用时还会返回,保存函数的返回地址,防止数据丢失,不管任何函数调用都是一样, 就递归来说是最典型的例子
-
我们可以将一段内存定义为一个段,用一个段地址指示daunt, 用偏移地址访问段内单元, 这里完全是我们自己安排的:
-
用一个段存放数据,将它定义为”数据段“
- 对于数据段,将它的段地址存放在ds中, 用mov、add、sub等访问内存但由于的指令时,CPU就将我们定义的数据段中的内容当做数据段来访问
-
用一个段存放代码,将它定义为”代码段“
- 对于代码段,将他的段地址存放在cs中,将段中第一条指令的偏移放在ip中, 这样CPU就将执行我们定义的代码段
-
用一个段当做栈, 将它定义为”栈段“
- 对于栈段, 将它的段地址存放在ss中, 将栈顶单元的偏移地址放在sp中, 这样CPU在需要进行 栈操作的时候, 比如执行push, pop指令等, 就 将我们定义的 栈当做栈空间来使用
-
一段内存,可以时代码 的存储空间, 又是数据的存储空间,还可以是栈空间, 也可以什么都不是
关键在于CS、IP、SS、SP、DS的指向
mov ax, 1000 mov ds, ax mov bx, 2000 mov ss, bx mov sp, 10 push [0] push [2] push [4] push [6] push [8] push [a] push [c] push [e]
mov ax, 2000 mov ds, ax mov ax, 1000 mov ss, ax mov sp, 10 pop [e] pop [c] pop [a] pop [8] pop [6] pop [4] pop [2] pop [0]
4.1 一个源程序从写入到执行的过程
-
一个汇编语言程序从写入到最终执行简要过程:
-
编写
- sublimtext、nodepad++、ultraEdit,用汇编语言写汇编源程序
-
编译和链接
- 使用汇编语言程序(MASM.EXE)对源程序文件进行编译,产生目标文件(.obj)
- 再用链接程序(LINK.EXE)对目标文件进行链接, 生成可在操作系统中直接运行的可执行文件
-
可执行文件
- 程序(从源程序中编译指令翻译过来的机器码)
- 数据 (源程序中定义的数据)
- 相关 描述的信息(比如:程序大小、占用内存空间)
-
执行
- 操作系统依照可执行卫建中的信息, 将执行文件中的机器码和数据加载到内存, 并进行相关的初始化, (比如:设置CS:IP)指向第一条执行的命令, 然后由CPU执行程序
-
4.2 源程序
assume cs:codesg # 假设代码段名称为codesg codesg segment # 段的名字为codesg start: mov ax, 0123 mvo bx, 0456 add ax, bx add ax, ax mov ax, 4c00 init 21h codesg ends end
-
sement和ends是成对出现的:
- 功能:定义以一个段, segment说明一个段的开始, ends表示结束
- 语法:段的名字 segment, 段的名字 ends
- 一个段必须有一个名称表示
- 一个汇编程序由多个段组成, 这些段被用来存放代码、数据、或栈空间使用
- 一个有意义的汇编程序至少要有一个段、用来存放代码
-
end:
- 是一个汇编程序的结束符, 编译器在编译汇编程序过程中, 如果遇到了end,就表示结束对源程序的编译
- 如果程序写完了。要在结尾处加上end, 否则编译器无法知道程序在何处结束
- end是结束, ends是段的结束
-
assume:
- 含义为“假设” 编译器会将codesg处理为一个地址,默认为cs代码段地址
- 它假设某一段寄存器和程序中的 某一个segment 。。。ends 定义的段相关联
- 通过assume说明这种关联,在需要的情况下,编译程序可以将段寄存器和某一个具体的段相联系
-
汇编源程序:
- 伪指令(编译器处理)(上面的几个都属于伪指令)
- 汇编指令(编译为机器码)
- 源程序最终由计算机执行, 处理的指令和数据
-
标号:
- 一个标号指代了一个地址
- codesg:放在segment的前面,作为一个段的名称,这个名称最终被编译、链接程序 处理为一个段的地址
-
程序的返回:
-
mov ax, 4c00 int 21 # int:中断标志 第21号中断
-
- 一个程序结束后,将 CPU的控制权交还给使他得以运行的程序(执行它的程序,如:dos窗口), 我们成这个过程为:**程序返回** - **应该在程序的末尾添加返回的程序段**: - - 这两条指令所实现的功能就是程序返回
4.3 编辑源程序
4.4 编译和 链接
-
编译和链接的作用是什么?
-
链接的作用有以下几个
- 当源程序很大时。可以将它分为多个源程序文件编译,每个源程序编译成为目标文件后,在用链接程序将他们链接到一起, 生成一个可执行文件
- 程序中调用了某些库文件中的子程序,需要将这个库文件和改程序生成的目标文件链接到一起, 生成一个可执行文件
- 一个源程序编译后,得到了有机器码的目标文件, 目标文件中的有些内容还不能直接用来生成可执行文件, 链接程序将此内容出来了为最终的可执行信息, 所以,在只有一个源程序文件, 而又不需要调用 某些库中子程序的情况下, 也必须用链接程序对目标文件处理, 生成可执行文件
-
4.5 可执行文件中的程序装入内存并运行的原理
-
操作系统的外壳:
- 操作系统是由于多个功能模块组成的庞大、复杂的软件系统, 任何通用的操作系统,都要提供一个称为shell(外壳)的程序,用户使用这个程序来操作计算机系统工作
-
为了观察程序的运行过程,我们可以使用dug
- debug将程序加载到内存, 设置cs:ip的指向, 三debug不会放弃对CPU的控制, 这样就可以使用debug的相关命令来单步执行程序, 查看每条命令执行的结果
4.6 程序执行过程的跟踪
-
用debug打开程序跟踪
- 可以看到debug将程序从可执行卫建加载入内存后, cx中存放的是程序的长度, 2.exe 程序的机器码共有15个字节
- 现在程序从2.exe中装入内存, 接下来我们查看一样他的内容,查看哪里的内容?
- 程序被装入内存的什么地方
- 我们如何得知?
-
在dos系统中 .exe文件中的程序加载过程如下:
-
总结
- 程序加载后,ds中存放着程序所在内存区的段地址, 这个内存区的偏移地址为0, 则程序所在的内存区地址为 ds:0
- 这个内存区的前256个字节中存放的是psp, dos用来和程序进行通信
- 所以256字节处往后的空间存放的是程序
- 所以我们从ds中可以得到psp的段地址 sa, psp的偏移地址为0,则物理地址为 sa*16+0
- 因为psp占256字节, 所以程序的物理地址是:sa16+0+256=sa16+1616=(sa+16)16+0
- 可用段地址和偏移地址表示为:SA+10:0
- 用T命令单步执行程序中的每一条指令, 并观察每条指令的执行结果
- 到了int21 我们要用p命令执行
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java并发系列(4)java关键字-synchronized
本站小福利 点我获取阿里云优惠券 原文作者:github:CL0610/Java-concurrency免责声明: 1.本文所转载文章均来自公开网络。2.如果出处标注有误或侵犯到原著作者权益,请联系删除。3.转载文章请注明原文链接和作者,否则产生的任何版权纠纷均与本站无关。 1. synchronized简介 在学习知识前,我们先来看一个现象: public class SynchronizedDemo implements Runnable { private static int count = 0; public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(new SynchronizedDemo()); thread.start(); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.print...
- 下一篇
spring-boot-plusV1.2.3发布,CentOS快速安装环境/构建/部署/启动项目
spring-boot-plusV1.2.3发布,CentOS快速安装环境/构建/部署/启动项目 [V1.2.3-RELEASE] 2019.09.09 :computer: spring-boot-plusV1.2.3发布,CentOS快速安装环境/构建/部署/启动项目 ⭐️ New Features 项目运行环境安装脚本 CentOS快速构建/部署/启动项目脚本 ️ Optimization 优化 maven-assembly-plugin 项目打包插件 Added/Modified Add install-jdk.sh yum安装jdk8脚本 Add install-git.sh yum安装git脚本 Add install-maven.sh yum安装maven脚本 Add install-redis.sh yum安装redis脚本 Add install-mysql.sh yum安装mysql脚本 Add install-all.sh 安装所有环境脚本 Add download-install-all.sh 下载并安装所有环境脚本 Add deploy.sh 下载项目/构建/...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- Hadoop3单机部署,实现最简伪集群
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Redis,开启缓存,提高访问速度