2024年12月10日 04:34

公告:

欢迎来到havte!站点已升级完成,域名已迁移至dev.havte.com,大家收藏访问dev.havte.com站点。


linux ARM交叉编译器编译原理

作者 admin, 2021年11月30日 15:30

« 上一篇 - 下一篇 »

admin

2021年11月30日 15:30 Last Edit: 2021年11月30日 15:37 by admin
arm-linux-gnueabihf-gcc 编译文件
我们是要编译出在 ARM 开发板上运行的可执行文件,所以要使用交叉编译器 arm-linux-gnueabihf-gcc 来编译。

如果使用一个 led.s 源文件,先将 led.s 编译为对应的.o 文件,在终端中输入如下命令:

程序代码 [选择]
arm-linux-gnueabihf-gcc -g -c led.s -o led.o
上述命令就是将 led.s 编译为 led.o,其中"-g"选项是产生调试信息,GDB 能够使用这些
调试信息进行代码调试。"-c"选项是编译源文件,但是不链接。"-o"选项是指定编译产生的文
件名字,这里我们指定 led.s 编译完成以后的文件名字为 led.o。执行上述命令以后就会编译生
成一个 led.o 文件

arm-linux-gnueabihf-ld 链接文件
arm-linux-gnueabihf-ld 用来将众多的.o 文件链接到一个指定的链接位置

程序代码 [选择]
arm-linux-gnueabihf-ld -Ttext 0X87800000 led.o -o led.elf
上述命令中-Ttext 就是指定链接地址,"-o"选项指定链接生成的 elf 文件名,这里我们命名
为 led.elf。上述命令执行完以后就会在工程目录下多一个 led.elf 文件

那么为什么链接到0X87800000 这个地址呢?

以STM32为例:

IROM1 我们都知道是设置 STM32 芯片的 ROM 起始地址和大小的,右边的 IRAM1 是设置 STM32 芯片的 RAM 起始地址和大小的。其中 0X08000000 就是 STM32 内部 ROM 的起始地址,编译出来的指令肯定是要从 0X08000000 这个地址开始存放的。对于STM32 来说 0X08000000 就是它的链接地址 .o 文件就是这个链接地址开始依次存放,最终生成一个可以下载的 hex 或者 bin 文件,我们可以打开.map 文件查看一下这些文件的链接地址。

STM32 的各个.o 文件所处的位置,起始位置是 0X08000000。由此可以得知,我们用 MDK 开发 STM32 的时候也是有链接的,只是这些工作 MDK 都帮我们全部做好了,我们不用关心而已。但是我们在 Linux 下用交叉编译器开发 ARM 的是时候就需要自己处理这些了。

因此我们现在需要做的就是确定一下本试验最终的可执行文件其运行起始地址,也就是链接地址。这里我们要区分"存储地址"和"运行地址"这两个概念,

"存储地址"就是可执行文件存储在哪里,可执行文件的存储地址可以随意选择。

"运行地址"就是代码运行的时候所处的地址,这个我们在链接的时候就已经确定好了,代码要运行,那就必须处于运行地址处,否则代码肯定运行出错。

比如 I.MX6U 支持 SD 卡、EMMC、NAND 启动,因此代码可以存储到 SD 卡、EMMC 或者 NAND 中,但是要运行的话就必须将代码从 SD 卡、EMMC 或者NAND 中拷贝到其运行地址(链接地址)处,"存储地址"和"运行地址"可以一样,比如STM32 的存储起始地址和运行起始地址都是 0X08000000。

本教程所有的裸机例程都是烧写到 SD 卡中,上电以后 I.MX6U 的内部 boot rom 程序会将
可执行文件拷贝到链接地址处,这个链接地址可以在 I.MX6U 的内部 128KB RAM 中(0X900000~0X91FFFF),也可以在外部的 DDR 中。本教程所有裸机例程的链接地址都在 DDR
中,链接起始地址为 0X87800000。之所以选择 0X87800000 这个地址是因为后面要讲的 Uboot 其链接地址就是 0X87800000,这样我们统一使用 0X87800000 这个链接地址,不容易记混。

链接生成的led.elf 文件也不是我们最终烧写到 SD 卡中的可执行文件,我们要烧写的.bin 文件,因此还需要将 led.elf 文件转换为.bin 文件,这里我们就需要用到 arm-linux-gnueabihf-objcopy 这个工具了。

arm-linux-gnueabihf-objcopy 格式转换
arm-linux-gnueabihf-objcopy 更像一个格式转换工具,我们需要用它将 led.elf 文件转换为led.bin 文件,命令如下:
程序代码 [选择]
arm-linux-gnueabihf-objcopy -O binary -S -g led.elf led.bin上述命令中,"-O"选项指定以什么格式输出,后面的"binary"表示以二进制格式输出,选项"-S"表示不要复制源文件中的重定位信息和符号信息,"-g"表示不复制源文件中的调试信息。

至此我们终于等到了想要的东西--led.bin 文件。

arm-linux-gnueabihf-objdump 反汇编
大多数情况下我们都是用 C 语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,一般可以将 elf 文件反汇编,比如如下命令:

程序代码 [选择]
arm-linux-gnueabihf-objdump -D led.elf > led.dis上述代码中的"-D"选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 led.dis 文件

led.dis 里面是汇编代码,而且还可以看到内存分配情况。在0X87800000 处就是全局标号_start,也就是程序开始的地方。通过 led.dis 这个反汇编文件可以明显的看出到我们的代码已经链接到了以 0X87800000 为起始地址的区域

代码烧写
肯定有人会认为直接复制led.bin 到 SD 卡中不就行了,错!编译出来的可执行文件是怎么存放到 SD 中的,存放的位置是什么?这个 NXP 是有详细规定的!我们必须按照 NXP 的规定来将代码烧写到 SD 卡中,否则代码是绝对运行不起来的。

对于IMX6U来说,我们需要使用 imxdownload软件来烧写到SD卡

imxdownload 会在 led.bin前面添加一些头信息,重新生成一个叫做 load.imx 的文件,最终实际烧写的是 laod.imx。

具体详情可以参考 I.MX6U驱动开发/

使用 imxdownload 向 SD 卡烧写 led.bin 文件,命令格式如下:

程序代码 [选择]
./imxdownload <.bin file> <SD Card>
其中.bin 就是要烧写的.bin 文件,SD Card 就是你要烧写的 SD 卡,比如使用如下
命令烧写 led.bin 到/dev/sdd 中:( /dev/sdd为你的SD卡)

程序代码 [选择]
./imxdownload led.bin /dev/sdd