关于后端:手撕汇编

5次阅读

共计 5496 个字符,预计需要花费 14 分钟才能阅读完成。

汇编系列文章曾经更新了三篇,每一篇都是笔者用心总结,心愿对你有帮忙

手把手教你汇编 Debug

爱了爱了,这篇寄存器讲的有点意思

之前的文章咱们次要聊了一些根本的汇编指令,并且通过一个名为 Debug 的调试软件,让咱们看到了内存中是如何存储指令和数据的,在学习了这些之后,咱们就能够理解汇编程序了。

程序的执行过程

首先通过一个示意图给大家介绍一下程序的执行过程,咱们以 C 语言一个简略的 hello.c 程序为例。

这就是一个残缺的 hello world 程序执行过程,会波及几个外围组件:预处理器、编译器、汇编器、连接器,上面咱们一一击破。

  • 预处理阶段(Preprocessing phase),预处理器会依据开始的 # 字符,批改源 C 程序。#include <stdio.h> 命令就会通知预处理器去读零碎头文件 stdio.h 中的内容,并把它插入到程序作为文本。
  • 而后是 编译阶段(Compilation phase),编译器会把文本文件 hello.i 翻译成文本hello.s,它包含一段汇编语言程序(assembly-language program)。

汇编语言是十分有用的,因为它可能针对不同高级语言来提供本人的一套规范输入语言。

  • 编译实现之后是 汇编阶段(Assembly phase),这一步汇编器会把 hello.s 翻译成机器指令,把这些指令打包成可重定位的二进制程序(relocatable object program) 放在 hello.c 文件中。
  • 最初一个是 链接阶段(Linking phase),这个阶段就是用链接器把翻译过后的程序合并在一起,生成在操作系统上间接运行的可执行文件的过程。

所以,一般来说,可执行文件包含两个方面

  1. 程序和数据,这些是形成可执行文件的根本信息。
  2. 相干的形容信息,比方空间多大,程序有多大等,这些是形成可执行文件的必要因素。

意识汇编程序

同样的,先上一则汇编代码,而后上面再缓缓概述。

assume cs:code
code segment
        mov ax,1234H
        add ax,ax
        mov bx,1111H
        add bx,bx
code ends
end

这段汇编代码有几个中央你可能不太理解,不过 mov、add 指令你应该晓得是什么意思(如果你看完笔者之前文章并进行了认真钻研的话)。

形成汇编程序的指令分为两种:一种是 汇编指令 ,一种是 伪指令 ,汇编指令就是咱们下面提到的 mov、add 指令,这些指令有理论的意义,比方 mov 就是挪动寄存器或者数据,add 就是对寄存器或者数据进行加法操作。而且 mov 和 add 这类汇编指令在内存中有对应的机器码存在,最终会有 CPU 执行。而伪指令没有理论的意义,它们指令简略的定义一个程序段,这些伪指令会由 编译器 来间接解释,它们在内存中没有对应的机器码,所以不会由 CPU 来执行。

下面提到的伪指令有三种,即

code segment
    ......
code ends

segment 和 ends 是一组成对呈现的指令,而且这一对指令必须成对呈现,缺了谁都不行。这一对指令定义了一个段,segment 标识着段的开始,ends 标识着段的完结。code 示意段的名称,段名称能够随便替换。

汇编程序由多个段组成(至多蕴含一个段),这些段被用来寄存代码、数据或者当做栈空间来应用。下面例子代码中的段由代码组成,所以叫代码段。

除了段之外,汇编程序还须要有 assume,这同样是一条伪指令,它的意思是假如,它假如某一段寄存器和某个段相关联,通过 assume 来阐明这种关联关系。assume 不必深刻了解,咱们只有晓得编程时将特定用处的段和相干寄存器关联起来即可。

end 是一段汇编程序完结的标记,它也是一条伪指令,编译器在编译汇编程序的过程中,遇到 end 就会进行编译,所以,如果咱们汇编程序写完了,就须要在程序的开端加上 end,示意程序的完结。

在汇编程序中,除了汇编指令和伪指令,还有一种 标号,比方下面代码中的 code,标号位于 segment 的后面,作为段的名称,这个段的名称最终将被编译、连贯解决为一个段的段地址。

再次揭示下,留神这里不要搞混了 end 和 ends,ends 是和 segment 一起应用的示意汇编段,而 end 是汇编完结的标识。

所以总结下,用汇编语言编写的源程序,包含伪指令和汇编指令,伪指令是由编译器来执行,汇编指令能够翻译成机器代码并最终由 CPU 执行。

当前,咱们能够将源程序文件中的内容称为源程序,将源程序中最终由计算机执行、解决的指令或数据,称为程序。程序最先以汇编指令的模式存在于原程序中,而后通过编译、连贯后转变为机器码,存储在可执行文件中,如下图所示

所以,总结一点来说,编写一个汇编程序次要分为上面这几步

  • 首先定义一个段,比方 code、abc 等
  • 在段中写入汇编指令
  • 指出程序在何时处完结
  • 标号要和寄存器关联起来。
  • 程序返回(前面要说)

程序返回

一个残缺的程序是要有返回条件的,程序只有在执行完相干代码后,执行返回条件,让出 CPU 执行权,操作系统才会调配工夫片给其余程序,程序不能始终霸占着 CPU 不放,这是一种资源的节约,而且始终占用着 CPU,也会导致程序解体。

汇编语言中,实现程序返回的指令只有两行

mov ax,4c00H
int 21H

解释下这两句指令的意思:

mov ax,4c00H 就是把 4c00 挪动到 ax,中,INT 21H 是调用零碎中断指令,这两句代码起作用的就是 AH = 4CH,意思就是调用 INT 21H 的 4CH 号中断,该中断就是平安退出程序。

到目前为止,咱们曾经理解到了几种和完结的相干内容,比方段完结,汇编程序完结、还有咱们刚刚说的程序返回,下表列出了这三个指令的区别。

程序谬误

一般来说,汇编语言的程序谬误分为两种:即 语法错误 逻辑谬误

语法错误很简略,说白了就是你汇编语言指令写错了,这个程序编译期间就可能发现。

逻辑谬误是在运行时产生的,个别不容易被发现,排查起来比拟艰难,比方上面这段代码不写程序返回就是属于逻辑谬误。

assume cs:code
code segment
        mov ax,1234H
        add ax,ax
        mov bx,1111H
        add bx,bx
code ends
end

为什么?因为你这段代码没有加程序返回逻辑。相似的这种逻辑谬误还有很多,这些谬误须要在具体的场景中能力发现。

编写汇编

上面咱们开始用编辑器来编写汇编源程序,只有将汇编存储为文本文件,再通过编译器编辑,CPU 运行即可。

咱们能够应用多种文本格式来编写汇编程序,比方咱们能够应用最简略的文本文件来编写(基于 win7 操作系统环境)

assume cs:codeseg
codeseg segment
        mov ax,0123H
        mov bx,0456H
        add ax,bx
        add ax,ax
codeseg ends
end

编写实现后,存储为 .asm 后缀文件,这是一种汇编格局。

编译

一个残缺的汇编程序执行流程分为编写、编译、链接和运行,所以接下来咱们须要对编写实现的汇编程序进行编译。在编译之前咱们须要找到一个相应的编译器,这里咱们采纳的是 masm 5.0 汇编编译器,执行程序是 masm.exe

(为了避免大家再从网站上乱找资源,我下载下来放在了网盘中,大家在程序员 cxuan 后盾回复 masm 即可支付)

说到应用 masm 5.0 的这个过程我踩了很多坑,这里给大家提醒下,及时闭坑!!!

  • masm 5.0 是稳固版本,网络上流传的 6.x 不晓得怎么样,我是没运行胜利。
  • masm 5.0 要在 win7 环境下运行,我应用 win11 测试,程序不兼容,不晓得其余版本如何。win7 版本能够失常运行。

装置实现后,咱们关上 cmd,进入下载并解压好的 masm 5.0 文件夹下。

而后间接键入 masm

运行 masm 后,首先会显示一些版本信息,而后输出须要被编译的原程序文件名称,这里须要留神一下,[.ASM]提醒咱们,默认的文件扩展名是 asm,比方咱们要编译的源程序文件名是 test.asm,这里间接输出 asm 即可。如果源程序文件不是以 .asm 为后缀,须要输出它的全名,也就是 test.txt。

这里咱们输出的是 test,因为咱们编写的文件是 .asm 后缀。

输出源程序文件名后,按 enter 键,程序会提醒咱们输出要编译出的指标文件名称,指标文件名称是咱们对源程序进行编译后的最终后果。Object filename 的后缀名是 .obj,因为 .asm 文件会主动编译为 .obj 文件,所以咱们不用再指定文件名,间接按 enter 键,会间接生成 .obj 文件。

确定了指标文件名称后,会呈现 Source listing,这是提醒咱们要输出列表文件的名称,这个文件是编译器将源程序编译为指标文件的过程中产生的两头后果,能够让编译器不生成这个文件,间接键入 enter 即可。如果编译器要生成这个文件,它的后缀名是 .lst

而后持续提醒出 Cross-reference,这是提醒咱们要输出穿插援用文件名称,这个文件和 Source listing 一样,是编译器产生的两头后果,能够不让编译器生成这个文件,咱们间接按 enter 即可。如果编译器要生成这个文件,它的后缀名是 .crf

最初编译器会进行一个后果输入,这个输入后果会显示正告谬误和必须要改过的谬误,能够从上图中看进去,咱们程序没有正告和编译谬误。

在输出源程序文件名的时候要指出所在门路,如果遇到 unable to open input file 这个问题,最好把汇编程序间接放在 C 盘,我放在桌面上,也就是 C:\Users\Administrator\Desktop 下,也会呈现此谬误。

连贯

在对源程序编译后失去指标文件后,咱们须要对指标文件进行连贯,从而失去可执行文件。上一步咱们失去了 .obj 文件,当初咱们须要将 .obj 文件连贯成为 .exe 也就是可执行文件。

为了实现咱们的需要,咱们须要借助微软的 Overlay Linker 3.60 连接器,文件名为 link.exe,这个应用程序不必再次下载(在我公众号回复拿到的软件会包含编译器和连接器,解压后,它们都会在 masm 文件夹下)。

当初咱们进入 DOS,cd 到 masm 文件中,键入 link

运行 link 后,会呈现一些版本信息,而后提醒须要被连贯的指标文件名称,这里仍须要留神,默认文件是 .obj 结尾,所以如果你须要连贯的文件是 obj 文件,就不必输出后缀名,如果不是 obj 文件,则须要输出全名。

咱们刚刚编译了一个 test.obj 文件,所以咱们间接对这个 obj 文件进行连贯。

输出要连贯的文件名(这里仍须要输出 obj 所在的门路),按 enter。

输出 enter 后,会持续来一个三连提醒。

第一个提醒表明程序持续提醒咱们输出要生成可执行文件的名称,可执行文件是咱们对一个程序进行连贯要失去的最终后果,默认的 .exe 文件是 TEST.EXE,所以咱们不再须要指定文件名。这里也能够指定生成可执行文件所在的目录,咱们也不须要,持续向下走。

第二个提醒是连贯程序提醒输出映像文件的名称,这个文件是连贯程序将指标文件连贯为可执行文件过程中的两头后果,也能够让连贯程序不生成这个文件,持续向下走。

第三个提醒是连贯程序提醒输出库文件的名称,库文件蕴含了一些能够调用的子程序,如果程序调用了库中的子程序,就须要指定,否则不须要。

最初会呈现一个 waring: no stack segment,我曾始终认为呈现这个提醒就不会再生成最终执行文件,然而当我仔细检查之后我才发现这只是一个 waning,最终的执行文件在 masm 文件夹下,我截个图给你看。

这个提醒只是通知咱们 没有栈段,咱们能够齐全疏忽这个提醒,当然如果你的程序有问题,是无奈生成连贯之后的文件的。

连贯这个过程很有用,归纳来说,次要有三个作用

  • 当源程序很大时,能够将它分为多个源程序文件来进行编译,每个独自编译之后的指标文件,能够再通过连贯将它们连贯到一起生成可执行文件。
  • 程序中调用了某个库文件中的子程序,须要将这个库文件和指标文件连贯到一起生成一个可执行文件。
  • 在编译过后生成的机器码文件,其中有些内容还不能间接执行,连贯程序须要将这些内容转换为可执行信息,才可能把编译过后的机器码文件,连贯成为可执行文件。

执行应用程序

当初我左手一个 asm 文件,右手一个 obj 文件,嘴里叼着一个 exe 文件,所以我就是嘴遁王者。废了半天劲,终于将 asm 搞成 exe 文件了,累屁了,不过先别急着劳动,还差最初一步,执行它!

于是咱们执行以下 TEST.EXE 文件

我有点蒙,这怎么啥都没有啊,输入后果呢?。。。。。。

细想了一下,哦,咱们没有用任何库来向控制台输入信息,咱们只是做了一些数据和寄存器的挪动、相加操作。

当然咱们能够向控制台输入信息,不过这个咱们前面在演示。

简略聊聊程序的装载过程

咱们大家晓得,一个程序如果要执行,就须要装载进入内存,而后 CPU 从内存中取指执行命令。

那么,当咱们应用 DOS 的时候,谁负责将可执行程序装载进入内存的呢?

在 DOS 中,有一个叫做命令解释器 command.com 这个玩意儿,它也是 DOS 零碎的 shell。

DOS 启动后,会先进行初始化,而后运行 command.com,command.com 运行后,执行完其余相干工作后,会在屏幕上显示提示符,期待用户输出。

如果用户输出要执行的命令,比方 cd,taskkill 等,这些命令由 command 执行,执行实现后再次期待用户输出。

如果用户输出要执行的程序,command 会通过文件名找到可执行文件,而后将它载入内存,设置 CS:IP 执行入口,而后 command 暂停运行,CPU 执行程序,程序执行实现后,返回 command,command 再次期待用户输出。

所以,一个残缺的汇编程序的执行过程如下。

如果这篇文章写的不错并且对你有一些帮忙,那我就求个赞呀!

正文完
 0