汇编语言学习(六)exe的制作
我们现在开始编写程序并生成exe。
它的过程主要分为两步:
1.编写汇编源程序。
2.对源程序进行编译连接。
连接生成的exe为windows系统下的可执行文件,可执行文件包含两部分内容:程序(汇编指令翻译过来的机器码)和数据(源程序中定义的数据),相关的描述信息(占用内存等)。
3.执行可执行文件中的程序。
先进行相关初始化,比如调整CS:IP指针的位置,让CPU开始执行。
源程序
程序示例:
1 | assume cs:codesg |
1.伪指令
指令分为汇编指令和伪指令,伪指令由编译器执行,汇编指令翻译成机器码由CPU执行。
segment和ends是一对成对的伪指令,功能是定义一个段。segment表示段开始,ends表示段结束。
而定义的方式也很形象,段名 + segment,如代码中所示,第二行即为开始。
一个有意义的汇编程序至少要有一个段,用来存放代码。
最后一行的end和ends是不同的,end代表整个汇编程序的结束。
assume代表某一个段寄存器和程序中某一个用segment ends定义的段相关联。
而CS后面也就是指定了CPU的指向的段地址,也就是直接去执行我们的汇编程序。
源程序中的程序
源程序中,最终由计算机执行,处理的指令或数据,称为程序。程序经过编译,连接后转为机器码,存储在可执行文件中。
下面详解编写的过程
先定义一个段abc
1 | abc segment |
在段中写入汇编指令
1 | abc segment |
然后指出程序在何处结束
1 | abc segment |
abc既然被当作代码段来使用,应该将abc与CS寄存器联系起来
1 | assume CS:abc |
大功告成!
程序加载和程序返回
一个程序P2在可执行文件中,则必须有一个正在运行的程序P1,将P2从可执行文件中加载如入内存后,将CPU的控制权交给P2,P2才得以运行。P2开始运行后,P1暂停运行。
P2运行完毕后,应该将CPU的控制权交还给P1,然后P1继续运行,此交还的过程就称为程序返回。
要我们给它加代码才能执行程序返回,代码如下。
1 | mov ax,4c00h |
目前记住这个指令即可,还不必去探究它的执行。
目前我们讨论了三个结束相关的词:段结束,程序结束,程序返回。
语法错误和逻辑错误
编译时被编译器发现的位语法错误,运行时发生的错误位逻辑错误。
下面开始实操!
编辑源程序
打开编译器
挂载之后打开,打开edit编辑器
把我们的程序输进去。然后在上面的选项中保存为1.asm文件,然后在file选项中点击退出即可。
编译
运行masm编译器,
输入刚才的文件名1.asm
后面的obj为编译输出的目标,剩下的nul.lst等都是中间产物,不用管,一路回车即可。
连接
将obj文件连接为exe文件。
运行link程序
输入我们需要连接的obj名为1,然后一路回车即可,完成。 中间提示的.lib文件为程序中调用的库文件,我们并没有调用,直接回车即可。
连接的作用:
当源程序很大时,分成多个源程序来编译,生成的目标文件连接在一起,成为可执行文件。
程序中调用了某个库文件中的子程序,要将这个库文件和该程序生成的目标文件连接到一起,生成可执行文件。
简化编译和连接:
windows的DOS命令中,有一个程序叫command也就是CMD,就是DOS的shell
在command中输入可执行程序时,command首先找到可执行文件,然后将这个可执行文件中的程序载入内存,设置CS:IP指向程序的入口,然后command暂停,CPU运行程序。程序运行结束后,返回到command中,command继续运行。
现在谁是P2,谁是P1已经显而易见了吧。
跟踪和调试
用我们之前讲过的debug程序
我们看到DS指向075A而CS直接指向076A,也就是直接从076A开始执行,这是怎么回事?
也就是我们找到起始地址SA之后,还要设置一段PSP也就是程序段前缀,256个字节。加上256字节后,剧段地址和偏移地址的组合,可以有很多种表示,我们采用这样的计算:
1 | sa x 16 + 0 --> sa x 16 + 0 + 256 == sa x 16 + 16 x 16 == (sa + 16) x 16 |
也就是把段地址左移一位。这样就解释了CS指向的地址。
用进制数学的角度来看,就是说256字节用16进制为100h,所以应该是sa:00 + 100h也就相当于在段地址上加10,因为段地址 +10相当于物理地址+100。当然这里是16进制,所以物理地址+100就相当于 + 256。
我们用u来查看所有汇编指令。
注意,当加载到int 21即将执行的时候,我们知道这是程序要返回退出了,我们按p退出。从debug返回到command。