我们不如从每个程序员都曾写过的“hello world”切入,来尝试编写能在虚拟环境中运行的第一段代码,看看操作系统的开发过程究竟是怎样的。
首先请打开文本编辑器,输入以下内容。
代码 2-1
程序简单到无须介绍,这里只需要说明一点,物理地址0x50000020代表的是s3c2410的串口FIFO寄存器地址,简单地说,就是写向该地址的数据都将会通过串口发送给另一端。这段程序只是串行的将字符串“helloworld”依次送给串口FIFO寄存器。我们将以上内容保存成文件,命名为“helloworld.c”。
接下来让我们尝试编译这段程序,在终端里运行如下命令。
命令2-1
编译过程中,我们使用了一个优化参数-O,这样最终生成的代码将由编译器视情况优化,而数字2则代表了一个优化级别,数字越高,优化程度越深,但同时带来的不确定性也越多。
目前我们只能将这段代码首先编译生成目标文件,而不能直接生成可执行文件,原因很简单,因为我们的程序当中没有main函数。也就是说,编译器找不到程序的运行入口,在链接过程中便会报错。那么是不是说凡是C代码,想要生成可执行程序并且成功运行,就必须要有main函数呢?
不是。这个答案和我们理解的传统C语言程序有些出入,初学者也许不能接受。但事实是main函数和普通的函数并没有任何差别,它只不过是由标准所规定的程序入口而已。我们完全可以使用一些手段迫使程序从我们指定的任意函数处开始运行,这一方法可以通过添加适当的编译参数或使用特定的链接脚本来实现。这些内容稍后我们就会讨论。
在命令2-1运行之后,一个名为“helloworld.o”的文件便生成了。接下来需要运行链接工具,用该目标文件来生成一个ELF格式的可执行程序。
命令2-2
链接器将目标文件“helloworld.o”链接生成可执行程序helloworld。参数-e的作用是指定程序的运行入口。也就是说,可执行程序将会从代码2-1中的helloworld函数开始执行,取代了默认使用的main函数。这样我们就实现了从自定义的函数入口运行程序。参数-Ttext的作用是指定该程序的运行基地址,此处的值是内存零地址。也就是说,链接工具将目标文件的hellworld函数链接到内存0x0的位置上,并且从此处执行。
那为什么是内存零地址呢?这是因为在ARM体系结构中,程序必须从内存零地址处开始运行。将目标文件链接到内存0x0处,我们的helloworld理论上就具备了运行条件。
此时,helloworld程序虽然已经生成,但它仍然不能拿来直接加载到硬件环境里去运行,我们还必须使用如下命令生成一个只包含程序机器码的二进制文件。
命令2-3
这里的arm-elf-objcopy命令将ELF格式文件中的二进制机器码抽离出来,生成“helloworld.bin”文件,该文件除了运行时所必要的数据和代码之外,不含有任何其他信息,是纯粹的可执行机器码镜像。而我们原来所理解的可执行文件格式,除了实际代码之外,还会包含ELF格式文件头、段头或节头等附加信息。这样程序在运行时,就要首先解析这些附加信息,根据ELF格式信息构造出程序运行时所需要的环境,然后才能运行。
接下来我们需要根据程序的编译情况来编写一个SkyEye的配置文件,其内容如下。
在该配置文件中,我们将“helloworld.bin”文件加载到硬件平台0x0地址处,与之前编译时程序的链接环境一致。同时,还要保证0x50000020处的地址是作为外设寄存器被映射出来的。
将上述内容保存成文件,名为“skyeye.conf”,与“helloworld.bin”文件放置在同一个目录下。在终端运行skyeye命令,就会看到如下结果。
可以看出,helloworld字符串从SkyEye虚拟机中打印了出来。我们的第一段ARM程序正在运行中!
在上述程序的执行过程中,有一点可能会令读者感到费解。我们的程序明明是将字符串helloworld发送给了串口,但为什么最终会在SkyEye的页面中出现?
通常在嵌入式产品的开发过程中,串口多是作为设备终端,用于进行产品的调试和操作的。于是SkyEye默认地将串口映射到命令行中,以方便虚拟硬件的模拟。当然这也仅仅是虚拟环境所提供的一种方便而已,在实际的串口设备通信中,由于会涉及到传输速率、数据格式等诸多问题,这段程序能够成功运行的可能性并不大。
简要总结一下上面这段程序:我们首先使用编译器将一段代码编译成目标文件,同时又使用链接器将目标文件链接成了可执行程序,并在链接的过程中做了点小动作,使得程序没有使用main函数也可以成功编译,又将程序的入口设置在了内存0x0处。最后,我们又使用了目标文件复制工具将ELF格式的可执行程序转换成只包含代码和数据的原始格式,并通过SkyEye成功运行。
在整个过程中,我们涉及了一些编译工具的基本用法。下面我们就来了解一下链接工具的一种高级用法,那就是通过链接脚本来链接目标文件。