TQ2440开发板学习纪实(0.0)--- GNU ARM交叉汇编环境的搭建与测试

对ARM进行裸机开发时,汇编是必不可少的,因为C语言无法直接操作CPU的内置寄存器,也就无法完成很多硬件初始化的功能,如内存控制器的初始化。

市面上大多数讲解ARM汇编开发的书籍都把ADS作为开发环境,因此使用的汇编语言也就是ARMASM,开发平台也限制到了Windows。然而在嵌入式领域,Linux作为开发环境更加普及,那么如何在Linux下进行ARM的汇编程序设计呢?

其实,Linux平台上早就有了支持ARM的汇编器,那就是著名的binutils软件包,它包含了汇编、链接、二进制操作等所有的ARM汇编环境。

1 binutils简介

binutils是支持Unix类系统汇编与链接开源软件包,提供汇编、链接等二进制文件操作工具。其官方主页是: http://www.gnu.org/software/binutils。著名的GCC编译器本身并不提供汇编和链接功能,而是依赖于这个binutils才能完成高级语言的编译、汇编、链接过程。它提供的工具包括:
* GNU链接器ld
* GNU汇编器as
* objcopy 转换二进制格式
* ar 对象文件打包管理
* c++filt C++符号名字反mangling
* nm 打印二进制文件里的符号信息
* readelf 读取分析elf格式文件信息
* ranlib 产生archive包的索引
* size 列出二进制文件的大小
* objdump 打印二进制文件信息
* strip 去除二进制文件中的符号信息
* strings 打印二进制文件中的可打印符号
* dlltool 为构建和使用DLL创建文件
* gprof 显示配置信息
* addr2line 把内存地址对应到源文件中的行号(用于调试)
* nlmconv 转为对象代码为NLM
* gold 只用于ELF格式的新生代连接器(尚处于测试阶段)

对于ARM汇编程序开发,as,ld,objcopy是最基本的工具。

2 binutils安装

对于ARM汇编来说,最常见的就是开发平台是基于X86的LinuxPC机器,而运行平台则是基于ARM的嵌入式硬件平台。这就需要一个能生成ARM机器码的交叉汇编器和连接器。binutils自然能够满足这个小小的要求,只是需要进行编译安装就可以了。

2.1 准备binutils源码

去其官网下载最新的源码包,截至目前(2016年12月12日)最新的版本为2.27,下载得到的文件名为: binutils-2.27.tar.bz2。
解压

tar -jxvf binutils-2.27.tar.bz2

得到binutils-2.27文件夹。

不建议直接在binutils-2.27这个源文件目录下直接配置编译,以免造成混乱。为此,新建一个空目录build-binutils,用于编译。

2.2 配置,编译,安装

进入build-binutils目录,执行如下配置命令:

../binutils-2.27/configure --prefix=/home/smstong/ARM --target=arm-linux-gnueabihf

上面指定了binutils的安装目录与目标平台,运行完成会在当前目录下生成Makefile。执行make,make install完成安装

make
make install

与GCC不同,binutils的配置、编译、安装非常简单,很少会出错。安装完成后会产生如下目录结构:

ARM
├── arm-linux-gnueabihf
│   ├── bin
│   │   ├── ar
│   │   ├── as
│   │   ├── ld
│   │   ├── ld.bfd
│   │   ├── nm
│   │   ├── objcopy
│   │   ├── objdump
│   │   ├── ranlib
│   │   ├── readelf
│   │   └── strip
│   └── lib
│       └── ldscripts
│         
├── bin
│   ├── arm-linux-gnueabihf-addr2line
│   ├── arm-linux-gnueabihf-ar
│   ├── arm-linux-gnueabihf-as
│   ├── arm-linux-gnueabihf-c++filt
│   ├── arm-linux-gnueabihf-elfedit
│   ├── arm-linux-gnueabihf-gprof
│   ├── arm-linux-gnueabihf-ld
│   ├── arm-linux-gnueabihf-ld.bfd
│   ├── arm-linux-gnueabihf-nm
│   ├── arm-linux-gnueabihf-objcopy
│   ├── arm-linux-gnueabihf-objdump
│   ├── arm-linux-gnueabihf-ranlib
│   ├── arm-linux-gnueabihf-readelf
│   ├── arm-linux-gnueabihf-size
│   ├── arm-linux-gnueabihf-strings
│   └── arm-linux-gnueabihf-strip
└── share
    └── man
        └── man1

其中ARM/bin和ARM/arm-linux-gnueabihf/bin目录下存放的就是as,ld等二进制工具。实际上这两个目录里的文件是完全一样的,只是名字不同而已。

[smstong@centos192 ~]$ ls ARM/arm-linux-gnueabihf/bin -i
54202 ar  54228 ld      54209 nm       54201 objdump  54207 readelf
54218 as  54228 ld.bfd  54205 objcopy  54204 ranlib   54210 strip
[smstong@centos192 ~]$ ls ARM/bin/ -i
54206 arm-linux-gnueabihf-addr2line  54209 arm-linux-gnueabihf-nm
54202 arm-linux-gnueabihf-ar         54205 arm-linux-gnueabihf-objcopy
54218 arm-linux-gnueabihf-as         54201 arm-linux-gnueabihf-objdump
54211 arm-linux-gnueabihf-c++filt    54204 arm-linux-gnueabihf-ranlib
54208 arm-linux-gnueabihf-elfedit    54207 arm-linux-gnueabihf-readelf
54222 arm-linux-gnueabihf-gprof      54200 arm-linux-gnueabihf-size
54228 arm-linux-gnueabihf-ld         54203 arm-linux-gnueabihf-strings
54228 arm-linux-gnueabihf-ld.bfd     54210 arm-linux-gnueabihf-strip

可见,ARM/arm-linux-gnueabihf/bin/as 和 ARM/bin/arm-linux-gnueabihf-as共享同一个索引节点号,是彼此的硬链接。为了与开发主机上的本地binutils相区别,建议使用长文件名的那个硬链接。为此,把它们加入可执行路径:

export PATH=/home/smstong/ARM/bin:$PATH

这样我们就可以直接使用arm-linux-gnueabihf-as命令了。

3 目标机器是裸机下的汇编程序开发

下面直接给出一个具体的例子。

3.1 实验环境

本次实验的运行平台为TQ2440开发板。从Norflash启动硬件,并且Norflash中已经预装了u-boot,且u-boot支持通过tftp协议下载程序到指定内存地址执行。

开发主机安装运行了tftp服务器,文件根目录为/var/lib/tftpboot,编译生成的二进制文件led.bin被复制到此目录下。

这就是说,测试程序执行前,u-boot已经完成了硬件平台的基本初始化:SDRAM可用,堆栈环境已经设置,MMU未启用,所以虚拟地址和物理地址完全相同。

这样我们的测试程序就可以做的非常简单。

本次实验的结果是熄灭底板上的LED1。因为u-boot启动后,LED1被自动点亮,我们的测试程序用来熄灭它。

3.2 源码

源文件 led.s。
我们都知道,GNU的X86汇编指令与Intel文档提供的标准格式相差很大,而这种情况在ARM汇编中不再存在,
GNU ARM AS支持ARM公司提供的标准汇编指令格式,而且扩展了一些特有的伪指令。

.equ GPBCON, 0x56000010
.equ GPBDAT, 0x56000014
.equ GPBUP,  0x56000018
.equ UBOOT,  0x00000000

.section .text
    .global _start
_start:
    ldr r0, =GPBCON
    ldr r1, [r0]
    bic r1, r1, #0xC00
    orr r1, r1, #0x400
    str r1, [r0]

    ldr r0, =GPBUP
    ldr r1, [r0]
    bic r1, r1, #0x20
    str r1, [r0]

    ldr r0, =GPBDAT
    ldr r1, [r0]
    orr r1, r1, #0x20
    str r1, [r0]

    b UBOOT /* 跳转回Norflash,重新进入u-boot */

.end

链接脚文件 led.lds:

ENTRY(_start)

SECTIONS {
    . = 0x30000000;
    .text : {       /* text and : must be seperated by space */
        *(.text)
        *(.rodata)
    }
    .data ALIGN(4): {
        *(.data)
    }
    .bss ALIGN(4): {
        *(.bss)
    }
}

Makefile文件:

AS = arm-linux-gnueabihf-as
LD = arm-linux-gnueabihf-ld
OBJCPY = arm-linux-gnueabihf-objcopy

all: led.bin
    sudo cp led.bin /var/lib/tftpboot/

led.bin: led
    $(OBJCPY) -O binary $< $@

led: led.o
    $(LD)  --script=led.lds -o $@ $<

led.o: led.s
    $(AS) -o $@ $<

.PHONY: clean
clean:
    rm *.o led led.bin

3.3 编译链接说明

  • 链接时需要指定代码段的起始内存地址为0x30000000,以满足TQ2440开发板的内存布局,这通过链接脚本led.lds来完成。
  • 链接器生产的可执行程序led为elf格式,这是Linux操作系统下的标准可执行格式,需要操作系统提供的加载器才能加载执行,在开发板裸机上不能直接运行,这就需要通过objcopy工具把elf格式转化为单纯的二进制格式led.bin。

整个转换汇编链接过程:

led.s---(汇编器as)-->led.o---(连接器ld)-->led---(objcopy工具)--->led.bin

3.4 下载到开发板执行

#####    Boot for Nor Flash Main Menu   #####
#####     EmbedSky USB download mode     #####

[1] Download u-boot or other bootloader to Nand Flash
[2] Download Eboot (eboot.nb0) to Nand Flash
[3] Download Linux Kernel (zImage.bin) to Nand Flash
[4] Download WinCE NK.bin to Nand Flash
[5] Download CRAMFS image to Nand Flash
[6] Download YAFFS image (root.bin) to Nand Flash
[7] Download Program (uCOS-II or TQ2440_Test) to SDRAM and Run it
[8] Boot the system
[9] Format the Nand Flash
[0] Set the boot parameters
[a] Download User Program (eg: uCOS-II or TQ2440_Test)
[b] Download LOGO Picture (.bin) to Nand  Flash
[l] Set LCD Parameters
[n] Enter TFTP download mode menu
[o] Download u-boot to Nor Flash
[r] Reboot u-boot
[t] Test Linux Image (zImage)
[q] quit from menu
Enter your selection: n

#####    Boot for Nor Flash Main Menu   #####
#####     EmbedSky TFTP download mode     #####

[1] Download u-boot.bin to Nand Flash
[2] Download Eboot (eboot.nb0) to Nand Flash
[3] Download Linux Kernel (zImage.bin) to Nand Flash
[4] Download WinCE NK.bin to Nand Flash
[5] Set TFTP parameters(PC IP,TQ2440 IP,Mask IP...)
[6] Download YAFFS image (root.bin) to Nand Flash
[7] Download Program (uCOS-II or TQ2440_Test) to SDRAM and Run it
[8] Boot the system
[9] Format the Nand Flash
[0] Set the boot parameters
[a] Download User Program (eg: uCOS-II or TQ2440_Test)
[b] Download LOGO Picture (.bin) to Nand  Flash
[l] Set LCD Parameters
[o] Download u-boot to Nor Flash
[p] Test network (TQ2440 Ping PC's IP)
[r] Reboot u-boot
[t] Test Linux Image (zImage)
[q] Return main Menu
Enter your selection: 7
Enter downloads to SDRAM address:
0x30000000
Enter program name:
led.bin
tftp 0x30000000 led.bin
dm9000 i/o: 0x20000300, id: 0x90000a46
MAC: 0a:1b:2c:3d:4e:5f
TFTP from server 172.16.35.188; our IP address is 172.16.35.189
Filename 'led.bin'.
Load address: 0x30000000
Loading: #
done
Bytes transferred = 80 (50 hex)
## Starting application at 0x30000000 ...

可以看到开发板上的LED1灯已经被熄灭了。

4 目标机器是Linux系统下的汇编程序开发

汇编程序当然也适合在Linux系统下运行,而且有了OS的支持,汇编程序可以通过系统调用的方式非常爽的使用OS提供的API。下面给出一个hello world的例子。

4.1 源码

源码非常简单hello.s:

.section .data
msg:
    .asciz "hello,GNU ARM ASM\n"

.section .text
    .global _start
_start:
    mov r0,#1       /* file fd */
    ldr r1, =msg
    mov r2,#18
    swi #0x900004   /* sys_write(fd,msg,len) */

    mov r0,#0
    swi #0x900001   /* sys_exit(code) */

Makefile:

AS = arm-linux-gnueabihf-as
LD = arm-linux-gnueabihf-ld
all: hello
    sudo cp hello /var/lib/tftpboot/
hello: hello.o
    $(LD) -o $@ $<

hello.o: hello.s
    $(AS) -o $@ $<

.PHONY: clean
clean:
    rm *.o hello

4.2 编译链接说明

默认情况下链接器会把_start作为入口,代码段基地址默认为0x00010074。因为开发板运行的是Linux系统,所以会把这个虚拟地址转换为可用的物理地址。

通过swi软中断方式调用Linux内核API,很轻易就实现了打印字符串的功能。在OS下开发程序是多么幸福的事情!

4.3 系统调用还是标准C库?

汇编程序有两种使用系统API的方式,一是上面例子中的直接使用swi陷入内核;二是调用标准C库函数。个人建议还是第一种方法更好,因为第二种方法存在如下问题:

  • 开发环境下,目标机器的标准C库不一定存在,例如我们目前的环境,由于还没有编译安装标准C库;这样交叉链接无法完成链接任务。

也许有人说,使用标准C库会使得程序具有更强的可移植性。可是别忘了,我们开发的是汇编程序,汇编程序天生就不具有可移植性!!!

猜你喜欢

转载自blog.csdn.net/smstong/article/details/53583135