对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 ASMn"
.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库会使得程序具有更强的可移植性。可是别忘了,我们开发的是汇编程序,汇编程序天生就不具有可移植性!!!
上一篇:GNU Freestanding(Naked)C ARM交叉开发环境创建与测试
下一篇:TQ2440开发板学习纪实(9)--- 利用Undefined异常模拟BLX指令
推荐阅读最新更新时间:2024-11-01 13:25
设计资源 培训 开发板 精华推荐
- LT1021CCN8-10 电压基准作为超线性铂金温度传感器的典型应用
- labview汽车仪表程序
- AKD4586,带 DIR 的 AK4586 多通道音频编解码器评估板
- 使用 Semtech 的 SC2612A 的参考设计
- LD29150DT18R 1.5A超低压降稳压器典型应用电路
- EVAL-ADF4351EB1Z,ADF4351Fractional-N PLL 频率合成器评估板
- ADP7156ARDZ-3.0-R7 3V 输出电压、1.2A、超低噪声、高 PSRR、RF 线性稳压器的典型应用
- 使用 Analog Devices 的 LT3470HTS8 的参考设计
- NCV7101方波振荡器典型应用电路
- 无电路钥匙扣