对编译连接过程的近距离分析

来源:互联网 发布:可信的玉石品牌 知乎 编辑:程序博客网 时间:2024/05/23 13:47

在我们学习一门编译型语言的时间,几乎每本书都会讲到源文件的编译连接过程,但又几乎每本书都没有讲明白这个过程,或者让读者很费解。“源文件先编译成目标文件,目标文件再连接成可执行文件。”已经成了一个”标准”的说法了。但也不免让人有怀疑,是不是这么一句简单的话就可以概括这个过程?还有什么“黑科技”在里面吗?本文就近距离地就这个过程进行分析,以ST Assembler Linker编写汇编语言为例来讲解这个过程。
如果想要自己做实验的话,要一些准备条件:
1. 电脑上装有STVD(你也可以直接安装ST Assembler Linker,不过STVD比较直接方便一些)。
2. 懂得基本的STM8汇编指令,由于代码的目的是做实验,并不需要编写出可以运行在STM8单片机上的代码,所以并不需要很懂STM8单片机。
3. 懂得ST Assembler Linker“汇编器连接器”的基本使用,和源代码常用的伪指令。

如果你只是想看一遍过程的话,就不用花太多心思了,直接往下看吧。
首先我们编写两个简单的asm源文件(请注意分号;后代表注释):

test.asm

stm8/;表明指令使用的是STM8的指令集。  segment byte at 8080-FFFF 'rom';定义rom的地址范围  ;以下的代码是放在8080-FFFF范围的地址上的。  ;注意这里代码只是演示功能,并不是为了实现什么功能,不用花心思去理解。.func.w;定义一个全局标号(标号前加点代表全局),相当于一个函数名。  ld a,#$ff;函数指令,把立即数FF加载到a寄存器中。  ret;函数返回  end ;源文件结束

test2.asm

stm8/  segment  'rom';使用test.asm中定义的rom  extern func.w ;声明func.w的一个外部标号  call func ;调用test.asm中func函数  end

建一个文件夹把这两个文件放进去。
这里写图片描述
我们再来看一下这两源文件是怎么汇编(跟“编译”的过程类似,也是生成obj文件)成obj,再进行连接的。看一下STVD的asm目录,我们可以看到这些exe文件:
这里写图片描述
这里我们用到的是:
1. asm.exe 用于把asm文件汇编成obj文件。
2. lyn.exe 用于把不同的obj文件连接起来,生成cod文件。
3. obsend.exe 用于使用cod文件来生成可烧写到STM8单片机里面的.s19或hex格式的可执行文件。

我们再来看一下这个过程。
在test.asm和test2.asm所在目录下创建一个.bat文件如下(注意::后面跟的是注释)。

run.bat

cd %~dp0  ::切换当前目录到bat文件所在的目录mkdir obj ::在当前目录下创建obj文件夹mkdir cod ::在当前目录下创建cod文件夹mkdir s19 ::在当前目录下创建s19文件夹asm -obj=./obj/test.obj test.asm ::汇编test.asm成test.obj文件asm -obj=./obj/test2.obj test2.asm ::汇编test2.asm成test2.obj文件lyn ./obj/test+./obj/test2,./cod/test.cod ::连接test.obj和test2.obj成cod文件obsend ./cod/test.cod,f,./s19/test.s19,s ::使用cod文件生成.s19格式的文件(可以用于单片机程序烧写)。

这里写图片描述
点击运行run.bat可以得到:
这里写图片描述
obj,cod,s19文件分别分在obj,cod,s19文件夹中。
我们先来看obj文件:
这里写图片描述
打开里面的test.obj得到:

633a 5c75 7365 7273 5c63 686f 6b65 5c646573 6b74 6f70 5c61 736d 5c74 6573 742e6173 6d00 7801 0001 0175 0020 0280 800000ff ff00 0072 6f6d 0003 0000 0081 00000002 0005 0000 7600 0000 0070 6675 6e630011 8080 0000 0100 0300 633a 5c75 73657273 5c63 686f 6b65 5c64 6573 6b74 6f705c61 736d 5c74 6573 742e 6173 6d00 770200fe 7b63 3a5c 7573 6572 735c 6368 6f6b655c 6465 736b 746f 705c 6173 6d5c 74657374 2e61 736d 0077 0300 7704 0012 a6ff7705 0011 8177 0600 fa

那么问题来了,简单几行代码的test.asm文件,为什么产生了这么大的test.obj文件呢?原来obj文件里面除了包含源文件里代码汇编成的机器码外,还包含了很多其它的信息,比如说使用到的外部变量,还有源文件中的代码行号等,也可能包含一些能被连接器所识别的指令。
再看test2.obj:

633a 5c75 7365 7273 5c63 686f 6b65 5c646573 6b74 6f70 5c61 736d 5c74 6573 74322e61 736d 0078 0100 0101 7500 3400 00000000 ffff ffff 726f 6d00 0300 0000 5a000000 0300 0500 0076 0000 0000 7f66 756e632e 7700 7702 0077 0300 fe7b 633a 5c757365 7273 5c63 686f 6b65 5c64 6573 6b746f70 5c61 736d 5c74 6573 7432 2e61 736d0077 0400 7705 00ff 000e 00f0 0001 00040071 0001 0000 0000 0013 cd00 0077 0600fa

也是差不多一个样子。

下面我们看一下连接产生的cod文件:
这里写图片描述
图中除了cod文件,还有grp,map,sym文件,后面这用于记录obj中使用的符号(其实也是从asm源文件中来的)和内存的映射关系。主要用于另一个工具abslist.exe,本文没有用到它就不深究它了。就看cod文件吧:

test.cod

8080 0000 0300 0000 a6ff 8183 8000 00030000 00cd 8080 0000 0000 0000 0000 

cod文件是不是比obj文件小了很多呢?因为这里面除了机器码外,已经很少包含其它信息了,主要是连接之后,已经不用再包含跟其它文件之间的关系信息了。
这里我们罗列一下test.asm和test2.asm里所用到的指令对应的机器码:

           ;test.asmld a,#$ff  ;对应的机器码为(十六进制):A6FFret        ;对应的机器码为(十六进制):81           ;test2.asmcall func  ;对应的机器码为(十六进制):CD8080;注意其它test.asm和test2.asm中没有列到的指令都是伪指令,不产生实际的机器码。

以上的机器码都可以在cod文件里找得到。
我们最后再来看一下s19文件。
这里写图片描述

test.s19

S00600004844521BS1068080A6FF81D3S1068083CD808029S9030000FC

这个文件就更加直接了。这时我先介绍一下该s19格式文件的组成:
1. 首行S00600004844521B和末行S9030000FC是固定不变的。不会随着源代码的改变而改变。
2. S1068080A6FF81D3:
- 以S1开头代表使用的地址是16位的,如果以S2开头,则代表地址是大于16位的。
- 06代表接下来还有6个字节。
- 8080代表接下来的代码在从8080地址开始。
- A6FF是机器码,对应汇编指令:ld a,#$ff
- 81是机器码,对应汇编指令:ret
- D3是本行信息的校验和(checksum)
3. S1068083CD808029:
- 以S1开头代表使用的地址是16位的,如果以S2开头,则代表地址是大于16位的。
- 06代表接下来还有6个字节。
- 8083代表接下来的代码从8083地址开始(因为前面两条指令占去了3个字节)。
- CD8080是机器码,对应的汇编指令:call func
- 29是本行信息的校验和(checksum)

所以这个文件包含了更加简单直接的信息,其本上就是机器码了,可以直接烧写到单片机了(但还不可以真正运行,这个是单片机的范畴了,跟本文关系不太,主要是因为没有把程序入口配置到复位中断向量里面,单片机复位后并不会运行我们的代码,这个需要额外的代码配置,由于本文只是讲汇编(编译)连接的过程,所以为求相对简单尽量用最少代码来讲。)
好的,本文就到这里。

0 0
原创粉丝点击