当前位置: 首页 > news >正文

【汇编语言】包含多个段的程序(一)—— 在代码段中使用数据和栈

在这里插入图片描述

文章目录

  • 前言
  • 1. 引言
  • 2. 在代码段中使用数据
    • 2.1 问题引入
    • 2.2 代码实现
    • 2.3 代码分析
      • 2.3.1 提出问题
      • 2.3.2 编译、链接、加载、查看程序
      • 2.3.3 查看内存内容
      • 2.3.4 正确的执行程序
    • 2.4 面临的问题
    • 2.5 改进代码
      • 2.5.1 程序的执行过程
      • 2.5.2 CPU如何知道第一条执行什么指令?
    • 2.6 程序的框架
  • 3. 在代码段中使用栈
    • 3.1 问题引入
    • 3.2 代码实现
    • 3.3 重要说明
  • 结语

前言

📌

汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。

本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。

1. 引言

前面的程序中,只有一个代码段。现在有一个问题是,如果程序需要用其他空间来存放数据,使用哪里呢?前面的内容中,我们讲到要使用一段安全的空间。可哪里安全呢?

我们说 0:200~0:2FF是相对安全的,可这段空间的容量只有256个字节,如果我们需要的空间超过 256个字节该怎么办呢?

操作系统的环境中,合法地通过操作系统取得的空间都是安全的,因为操作系统不会让一个程序所用的空间和其他程序以及系统自己的空间相冲突。在操作系统允许的情况下,程序可以取得任意容量的空间。

程序取得所需空间的方法有两种:

  • 一是在加载程序的时候为程序分配

  • 二就是程序在执行的过程中向系统申请。

在我们的内容中,不讨论第二种方法。

加载程序的时候为程序分配空间,我们在前面已经有所体验,比如我们的程序在加载的时候,取得了代码段中的代码的存储空间。

我们若要一个程序在被加载的时候取得所需的空间,则必须要在源程序中做出说明。我们通过在源程序中定义段来进行内存空间的获取

上面是从内存空间获取的角度上,谈定义段的问题。我们再从程序规划的角度来谈一下定义段的问题。大多数有用的程序,都要处理数据,使用栈空间,当然也都必须有指令,为了程序设计上的清晰和方便,我们一般也都定义不同的段来存放它们。

对于使用多个段的问题,我们先简单说到这里,后面我们将以这样的顺序来深入地讨论多个段的问题:

(1)在一个段中存放数据、代码、栈,我们先来体会一下不使用多个段时的情况。

(2)将数据、代码、栈放入不同的段中。

本文先来看第一点。

2. 在代码段中使用数据

2.1 问题引入

考虑这样一个问题,编程计算以下8个数据的和,结果存在ax寄存器中:

0123h、0456h、0789h、0abch、0defh、0fedh、0cbah、0987h

在前面的文章中,我们都是累加某些内存单元中的数据,并不关心数据本身。可现在要累加的就是已经给定了数值的数据。我们可以将它们一个一个地加到ax寄存器中,但是,我们希望可以用循环的方法来进行累加,所以在累加前,要将这些数据存储在一组地址连续的内存单元中。

如何将这些数据存储在一组地址连续的内存单元中呢?我们可以用指令一个一个地将它们送入地址连续的内存单元中,可是这样又有一个问题,到哪里去找这段内存空间呢?

从规范的角度来讲,我们是不能自己随便决定哪段空间可以使用的,应该让系统来为我们分配。我们可以在程序中,定义我们希望处理的数据,这些数据就会被编译、连接程序作为程序的一部分写到可执行文件中。

当可执行文件中的程序被加载入内存时,这些数据也同时被加载入内存中。与此同时,我们要处理的数据也就自然而然地获得了存储空间。

2.2 代码实现

具体的做法看下面的程序。

assume cs:codecode segmentdw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987hmov bx,0mov ax,0mov cx,8
s:	add ax,cs:[bx]add bx,2loop smov ax,4c00hint 2lhcode endsend

2.3 代码分析

2.3.1 提出问题

解释一下,程序第一行中的“dw”的含义是定义字型数据。dw 即“define word”。在这里,使用 dw 定义了8个字型数据(数据之间以逗号分隔),它们所占的内存空间的大小为16个字节。

(1)程序中的指令就要对这8个数据进行累加,可这8个数据在哪里呢?

由于它们在代码段中,程序在运行的时候CS中存放代码段的段地址,所以可以从CS中得到它们的段地址

(2)它们的偏移地址是多少呢?

因为用dw定义的数据处于代码段的最开始,所以偏移地址为0,这8个数据就在代码段的偏移0、2、4、6、8、A、C、E处。程序运行时,它们的地址就是CS:0、CS:2、CS:4、CS:6、CS:8、CS:A、CS:C、CS:E。

(3)

程序中,用bx存放加2递增的偏移地址,用循环来进行累加。在循环开始前,设置(bx)=0,cs:bx 指向第一个数据所在的字单元。每次循环中(bx)=(bx)+2,cs:bx 指向下一个数据所在的字单元。

2.3.2 编译、链接、加载、查看程序

将上面的程序编译、连接为可执行文件p61.exe,先不要运行,用Debug 加载查看下,情况如下图所示。

在这里插入图片描述

上图中,通过“DS=0B2D”,可知道程序从0B3D:0000开始存放。用u命令从0B3D:0000查看程序,却看到了一些让人读不懂的指令。

但是,为什么没有看到程序中的指令呢?

实际上用u命令从0B3D:0000查看到的也是程序中的内容,只不过不是源程序中的汇编指令所对应的机器码,而是源程序中,在汇编指令前面,用dw定义的数据。实际上,在我们的程序中,有一个代码段,在代码段中,前面的16个字节是用“dw”定义的数据,从第16个字节开始才是汇编指令所对应的机器码。

2.3.3 查看内存内容

可以用d命令更清楚地查看一下程序中前16个字节的内容,如下图所示。

在这里插入图片描述

2.3.4 正确的执行程序

可以从0B3D:0010查看程序中要执行的机器指令,如下图所示。

在这里插入图片描述

从上面两个图中,我们可以看到程序加载到内存中后,所占内存空间的前16个单元存放的是源程序中用“dw”定义的数据,后面的单元存放源程序中汇编指令所对应的机器指令

怎样执行程序中的指令呢?

用Debug加载后,可以将IP设置为10h,从而使CS:IP指向程序中的第一条指令。然后再用t命令、p命令,或者是g命令执行。

2.4 面临的问题

可是这样一来,我们就必须用Debug来执行程序。之前的程序编译、连接成可执行文件后,在系统中直接运行可能会出现问题,因为程序的入口处不是我们所希望执行的指令

2.5 改进代码

如何让这个程序在编译、连接后可以在系统中直接运行呢?

我们可以在源程序中指明程序的入口所在,具体做法如下。

assume cs:codecode segmentdw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987hstart:	mov bx,0mov ax,0mov cx,8S:	add ax,cs:[bx]add bx,2loop smov ax,4c00hint 21h
code endsend start

2.5.1 程序的执行过程

注意在上面的程序中加入的新内容,在程序的第一条指令的前面加上了一个标号start,而这个标号在伪指令end的后面出现。这里,我们要再次探讨end的作用。end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。在上面的程序中我们用end指令指明了程序的入口在标号 start 处,也就是说,“mov bx,0”是程序的第一条指令。

根据前面的内容,我们已经知道在单任务系统中,可执行文件中的程序执行过程如下。

(1)由其他的程序(Debug、command或其他程序)将可执行文件中的程序加载入
内存。

(2)设置CS:IP指向程序的第一条要执行的指令(即程序的入口),从而使程序得以运行。

(3)程序运行结束后,返回到加载者。

2.5.2 CPU如何知道第一条执行什么指令?

现在的问题是,根据什么设置CPU的CS:IP指向程序的第一条要执行的指令?也就是说,如何知道哪一条指令是程序的第一条要执行的指令?

这一点,是由可执行文件中的描述信息指明的。我们知道可执行文件由描述信息和程序组成程序来自于源程序中的汇编指令和定义的数据描述信息则主要是编译、连接程序对源程序中相关伪指令进行处理所得到的信息

我们在刚才的程序中,用伪指令end描述了程序的结束和程序的入口。在编译、连接后,由“end start”指明的程序入口,被转化为一个入口地址,存储在可执行文件的描述信息中。

在刚才程序生成的可执行文件中,这个入口地址的偏移地址部分为:10H。当程序被加载入内存之后,加载者从程序的可执行文件的描述信息中读到程序的入口地址,设置CS:IP。这样CPU就从我们希望的地址处开始执行。

归根结底,我们若要CPU从何处开始执行程序,只要在源程序中用“end 标号”指明就可以了。

2.6 程序的框架

有了这种方法,就可以这样来安排程序的框架:

assume cs:codecode segment;;数据;;
start:;;代码;;
code endsend start

3. 在代码段中使用栈

3.1 问题引入

完成下面的程序,利用,将程序中定义的数据逆序存放。

assume cs:codesgcodesq segmentdw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h?
codesg endsend

程序的思路大致如下。

程序运行时,定义的数据存放在cs:0~cs:F单元中,共8个字单元依次将这8个字单元中的数据入栈,然后再依次出栈到这8个字单元中,从而实现数据的逆序存放。

问题是,我们首先要有一段可当作栈的内存空间。

如前所述,这段空间应该由系统来分配。可以在程序中通过定义数据来取得一段空间,然后将这段空间当作空间来用

3.2 代码实现

程序实现如下。

assume cs:codesg
codesg segmentdw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987hdw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;用dw定义16个字型数据,在程序加载后,将取得16个字的内存空间,存放这16个数据。在后面的程序中将这段空间当作栈来使用。
start:	mov ax,csmov ss,axmov sp,30h		;将设置栈顶ss:sp指向cs:30mov bx,0mov cx,8s:	push cs:[bx]add bx,2loop s			;以上将代码段0~15单元中的8个字型数据依次入栈mov bx,0mov cx,8s0: pop cs:[bx]add bx,2loop s0			;以上依次出栈8个字型数据到代码段0~15单元中mov ax,4c00hint 21h
codesg endsend start				;指明程序的入口在 start 处

3.3 重要说明

(1)

注意上面程序中的指令:

mov ax,cs
mov ss,aX
mov sp,30h

我们要将cs:10~cs:2F的内存空间当作栈来用,初始状态下栈为空,所以ss:sp要指向栈底,则设置 ss:sp指向 cs:30。如果对这点还有疑惑,建议回头认真复习一下之前栈的相关内容。

(2)

在代码段中定义了16个字型数据,它们的数值都是0。这16个字型数据的值是多少,对程序来说没有意义。

我们用dw定义16个数据,即在程序中写入了16个字型数据,而程序在加载后,将用32个字节的内存空间来存放它们。这段内存空间是我们所需要的,程序将它用作栈空间。可见,我们定义这些数据的最终目的是,通过它们取得一定容量的内存空间。

所以我们在描述dw的作用时,可以说用它定义数据,也可以说用它开辟内存空间。比如对于:

dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

可以说,定义了8个字型数据,也可以说,开辟了8个字的内存空间,这段空间中每个字单元中的数据依次是:0123h、0456h、0789h、0abch、0deth、0fedh、0cbah、0987h。因为它们最终的效果是一样的。

结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。

也可以点点关注,避免以后找不到我哦!

Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

在这里插入图片描述


http://www.mrgr.cn/news/75437.html

相关文章:

  • Vue3 笔记 (万字速通)
  • Docker 基础命令介绍和常见报错解决
  • MYSQL 库,表 基本操作
  • PostgreSQL 开启密码验证插件
  • 基于Spring Boot与Redis的令牌主动失效机制实现
  • 安装仓库,ssh连接与nfs共享文件
  • 无需魔法的AI绘图神器,不能错过的宝藏软件!
  • Android 无签名系统 debug 版本APK push到设备引起的开机异常问题分析(zygote进程)
  • 双十二有什么好物推荐?盘点2024年双十二最值得入手的好物!
  • P3372 【模板】线段树 1
  • 大模型重塑软件研发,从辅助编程到多 Agent 协同还有多远?
  • WSADATA 关键字详细介绍
  • 用EXCEL一列数据拼接SQL的where条件in语句
  • 使用Python实现智能食品储存管理的深度学习模型
  • 快速上手 Hugging Face Transformers:完整模型微调训练步骤全攻略
  • 历久弥新的c-Met:靶向疗法研究进展
  • 【route】route add命令详解
  • 去中心化应用(DApps)在Web3生态中的发展趋势
  • 大模型时代,呼叫中心的呼入机器人系统如何建设?
  • 【Visual Studio】使用VS调试(Debug)
  • APEX高性能减速机MG/MGH系列 高负载应用下的精准动力传输
  • 2024年11月14日
  • 如何有效的解决LabVIEW项目中的问题?
  • win11修改鼠标右键界面
  • 计算机组成原理之总线和输入/输出系统
  • 【Kafka】集成案例:与Spark大数据组件的协同应用