文章目录
编译程序步骤
- 使用arm-poky-linux-gnueabi-gcc交叉编译器,将.c .s文件变为.o
- 将所有的.o文件链接为elf格式的可执行文件
- 将elf文件转为bin文件
- 将elf文件转为汇编,反汇编
链接:
链接就是将所有.o文件链接在一起,并且链接到指定的地方。
1.将.c/ .s文件变为.o
这段代码是罪魁祸首,先拉出来,里面包含了三个错误,所以导致后面汇编的交叉编译出错
.global _start @全局标号
_start:
/* 使能所有外设时钟 */
ldr r0, = 0x020c4068 @CCGR0
ldr r1, = 0xffffffff @要向CCGR0写入的数据
str r1, [r0] @将0xffffffff写入到CCGR0
ldr r0, = 0x020c406c @CCGR1
str r1, [r0] @将0xffffffff写入到CCGR1
ldr r0, = 0x020c4070 @CCGR2
str r1, [r0] @将0xffffffff写入到CCGR2
ldr r0, = 0x020c4074 @CCGR3
str r1, [r0] @将0xffffffff写入到CCGR3
ldr r0, = 0x020c4078 @CCGR4
str r1, [r0] @将0xffffffff写入到CCGR4
ldr r0, = 0x020c407c @CCGR5
str r1, [r0] @将0xffffffff写入到CCGR5
ldr r0, = 0x020c4080 @CCGR6
str r1, [r0] @将0xffffffff写入到CCGR6
/* IO复用 配置GPIO1_IO03 PIN的复用为GPIO,也就是设置寄存器
* IOMUXC_SW_MUX_CTL_PAD_GPIO03 = 5
* IOMUXC_SW_MUX_CTL_PAD_GPIO03 地址为0x20e022ch
*/
ldr r0, = 0x20e022ch @IOMUXC_SW_MUX_CTL_PAD_GPIO03
ldr r1, = 0x00000101 @要向IOMUXC_SW_MUX_CTL_PAD_GPIO03写入的数据
str r1, [r0] @将0x00000101 = 5写入到IOMUXC_SW_MUX_CTL_PAD_GPIO03
/* 配置GPIO01_IO3的电气属性 也就是寄存器:
* IOMUXC_SW_PAD_CTL_PAD_GPIO03
* IOMUXC_SW_MUX_CTL_PAD_GPIO03 寄存器地址为0x20e05fch
*
* bit0: 0 低速率
* bit5:3 110 R0/6驱动能力
* bit7:6 10 100MHz速度
* bit11: 0 关闭开路输出
* bit12: 1 使能pull/kepper
* bit15:14: 00 默认100K下拉
* bit16: 0 关闭hys
*/
ldr r0, = 0x20e05fch
ldr r1, = 0x0x10b0
str r1, [r0] @将0x0x10b0写入到IOMUXC_SW_PAD_CTL_PAD_GPIO03
/* 设置GPIO
* 设置GPIO1_GDIR寄存器,设置GPIO1_GPIO03为输出
* GPIO1_GDIR寄存器地址为209c004,设置GPIO1_GDIR寄存器bit3为1
* 也就是设置GPIO1_IO03为输出
*/
ldr r0, = 0x209c004
ldr r1, = 0x8
str r1, [r0]
/* 设置GPIO1_IO03 为1
* GPIO1_DR寄存器地址为0x0209c000
*/
ldr r0, = 0x0209c00
ldr r1, 1
str r1, [r0]
loop: @b loop 在73 74行死循环
b loop
交叉编译器使用
. /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa9hf-neon-poky-linux-gnueabi
我用到的交叉编译器为arm-poky-linux-gnueabi-gcc,每次使用前设置环境变量,然后检查版本
arm-poky-linux-gnueabi-gcc -v
正常情况下会打印出gcc 的版本信息:
gcc version 5.3.0 (GCC)
下面就可以对目标文件进行交叉编译
arm-poky-linux-gnueabi-gcc -g -c GPIOs.s -o GPIOs.o
上述命令就是将 GPIOs.s编译为 GPIOs.o,其中 ““-g”选项是产生调试信息 GDB能够使用这些调试信息进行代码调试。“ “-c”选项 是编译源文件,但是不链接。“ “-o”选项是指定编译产生的文件名字,这里我们指定 GPIOs.s编译完成以后的文件名字为 GPIOs.o。执行上述命令以后就会编译生成一个 GPIOs.o文件
然后,就,报错了…
现在可以开始愉快(秃头)的改错误环节了。
Error: bad arguments to instruction
先来看第一个错误,Error: bad arguments to instruction – `str r1, [r0]’
错误指令,先来检查一下书写是不是规范了吧。
定位到错误的位置
淦!“,” “,” 中英文逗号用错了修改后,错误少了一大批。
Error: garbage following instruction
接下来的错误Error: garbage following instruction – `ldr r0,=0x20e022ch’
网上百度的结果都说是编译器版本问题,我这个给arm的交叉编译,版本肯定没问题,也么的换
还是先定位到错误位置
这里是地址错误,对比前面正确的地址,还有比对芯片参考手册,最后发现问题是自己的地址写错了,是个非常非常低级的错误,弱智的操作。
如图所示的最后一位h,代表的是16进制格式,同时地址是4个字节32位组成,所以,地址的正确写法应该是
0x020e022c
而不是下面这个本就是错误的存在,在程序中,字节不对,同时也不应该有最后的十六进制标识符
0x20e022ch
全部按照正确的十六进制写法还有4字节规范后,错误解决。
Error: internal_relocation (type: OFFSET_IMM) not fixed up
再次编译后,还有一个错误提示
Error: internal_relocation (type: OFFSET_IMM) not fixed up
定位到错误位置
显而易见,语法错误
全部修改完毕,保存,重新编译,没有错误,耶!
2.将所有的.o文件链接为elf格式的可执行文件
本实验链接的时候要指定链接起始地址。链接起始地址就是代码运行的起始地址。
在STM32中,Keil软件,一般都帮我们设置好了链接起始地址,从图中可以看到,起始地址指向了flash的首位。
对于i.mx6来说,链接起始地址应该指向RAM地址。RAM分为内部RAM和外部RAM,也就是DDR。
6ull裸机代码的链接起始地址为0x87800000。要使用DDR,那么必须要初始化DDR,对于imx来说bin文件不能直接运行(bin文件不能直接烧写到SD卡、EMMC、NAND等外置存储中,然后从这些外置存储中启动运行。而不是bin文件不能直接运行,使用JLINK将bin文件直接下载到内部RAM中还是可以运行的。),需要添加一个头部,这个头部信息包含了DDR的初始化参数,imx系列SOC内部boot rom会从SD卡、EMMC等外置存储中读取头部信息,然后初始化DDR,并且将bin文件拷贝到指定的地方。
Bin的运行地址一定要和链接起始地址一致。位置无关代码除外。
选择
0X87800000
这个地址是因为后面要讲的 Uboot其链接地址就是 0X87800000,这样我们统一使用 0X87800000这个链接地址,不容易记混。
确定了链接地址以后就可以使用 arm-linux-gnueabihf-ld来将前面编译出来的 led.o文件链接到 0X87800000这个地址,使用如下命令:
arm-poky-linux-gnueabi-ld -Ttext 0X87800000 GPIOs.o -o GPIOs.elf
上述命令中 -Ttext就是指定链接地址,“-o”选项指定链接生成的 elf文件名,这里我们命名为 GPIOs.elf。上述命令执行完以后就会在工程目录下多一个 GPIOs.elf文件,如图
led.elf文件也不是我们最终烧写到 SD卡中的可执行文件,我们要烧写的 .bin文件,因此还
需要将 led.elf文件转换为 .bin文件,这里我们就需要用到
arm-poky-linux-gnueabi-objcopy
这个工具了。
3.将elf文件转为bin文件
arm-poky-linux-gnueabi-objcopyy更像一个格式转换工具,我们需要用它将 GPIOs.elf文件转换为
GPIOs.bin文件,命令如下:
arm-poky-linux-gnueabi-objcopy -O binary -S -g GPIOs.elf GPIOs.bin
上述命令中,“ ,“-O”(大写)选项指定以什么格式输出,后面的 binary”表示以二进制格式输出选项“ “-S”(大写)表示不要复制源文件中的重定位信息和符号信息 ,“-g”表示不复制源文件中的调试信息。上述命令执行完成以后,工程目录如图
至此我们终于等到了想要的东西 GPIOs.bin文件。
4.将elf文件转为汇编,反汇编
大多数情况下我们都是用 C语言写试验例程的,有时候需要查看其汇编代码来调试代码,因此就需要进行反汇编,一般可以将 elf文件反汇编,比如如下命令:
arm-poky-linux-gnueabi-objdump -D GPIOs.elf > GPIOs.dis
上述代码中的“ “-D”选项表示反汇编所有的段,反汇编完成以后就会在当前目录下出现一个名为 GPIOs.dis文件,如图
打开GPIOs.dis文件,是汇编代码
可以看出 GPIOs.dis里面是汇编代码,而且还可以看到内存分配情况。在0X87800000处就是全局标号 _start,也就是程序开始的地方。通过 GPIOs.dis这个反汇编文件可以明显的看出到我们的代码已经链接到了以 0X87800000为起始地址的区域。
总结
总结一下我们为了编译 ARM开发板上运行的 led.o这个文件使用了如下命令:
arm-poky-linux-gnueabi-gcc -g -c GPIOs.s -o GPIOs.o
arm-poky-linux-gnueabi-ld -Ttext 0X87800000 GPIOs.o -o GPIOs.elf
arm-poky-linux-gnueabi-objcopy -O binary -S -g GPIOs.elf GPIOs.bin
arm-poky-linux-gnueabi-objdump -D GPIOs.elf > GPIOs.dis
如果我们修改了 GPIOs.s文件,那么就需要在重复一次上面的这些命令,太麻烦了,这个时候我们就可以使用 Makefile文件了。