i.MX6 裸机 汇编 | Linux环境交叉编译器编译驱动程序 (出现错误和解决)

编译程序步骤

  1. 使用arm-poky-linux-gnueabi-gcc交叉编译器,将.c .s文件变为.o
  2. 将所有的.o文件链接为elf格式的可执行文件
  3. 将elf文件转为bin文件
  4. 将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文件了。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值