TQ2440开发板学习纪实(0.1)--- GNU Freestanding(Naked)C ARM交叉开发环境创建与测试
2016-12-13 14:41
405 查看
0 由来
在我的博文 GNU ARM交叉汇编环境的搭建与测试中,详细讲解了GNU ARM汇编环境的创建与使用方法。实际开发中,直接使用汇编语言写的代码往往很少,尽在系统启动和性能要求极其苛刻的时候才会用到汇编代码。在可读性、可移植性、逻辑表达能力方面,C语言的表现要比汇编强太多,正是C语言的这种优势造就了Unix世界,造就了Linux在多种平台上顺利编译运行的活泼场面。所以,在嵌入式开发领域,C语言是主力语言。在使用开发ARM上运行的程序之前,必须构建一个好用的C交叉编译环境。在博文Freestanding C与交叉编译器的生成原理分析中,阐述了Freestanding C的概念和交叉编译器构建的原理。构建一个完整的Hosted C交叉编译器是一个相当复杂的过程,尤其是对于GCC来说,这个过程更是充满艰难险阻。为了避免初学者受挫,我们从简单开始,先构建一个Freestanding 的C交叉编译器,然后写一个具体的C项目来测试。
1 Freestanding C的构建
GCC项目主要有两大功能,一是提供C,C++,Fortran等多种语言的前端(front end)编译器,也就是负责把高级语言代码翻译成汇编代码;二是作为整个开发环境的总入口,负责调用其他汇编、链接工具,来控制整个编译–>汇编–>链接过程。可见GCC本身并不能独立工作,必须依赖于外部提供的汇编、链接等工具,而提供这些外部工具的最著名软件就是binutils。虽说理论上gcc和binutils的安装没有先后的必要性,但实际上gcc编译的过程中,需要运行binutils提供的工具来进行测试,并根据测试结果来动态控制自身源码编译。故binutils必须先安装,之后才能编译安装gcc。
1.1 使用binutils构建交叉汇编环境
binutils的编译安装详见 GNU ARM交叉汇编环境的搭建与测试,本文不再重复表述。为便于参考,只给出binutils的配置命令:../binutils-2.27/configure --prefix=/home/smstong/ARM --target=arm-linux-gnueabihf
后面配置GCC时,需要提供与之完全一致的配置参数才行。
1.2 使用GCC构建Freestanding C交叉编译环境
1.2.1官网下载GCC最新源码包
GCC的官网主页是http://www.gnu.org/software/gcc,这是GCC的大本营,也是整个GNU的核心部件。截至今天(2016年12月13日)GCC的官方最新版本为gcc-6.2.0,下载的软件包名为gcc-6.2.0.tar.bz2。解压后得到文件夹gcc-6.2.0。
然后,进入gcc-6.2.0文件夹,执行./contrib/download_prerequisites脚本,这个脚本会自动下载编译GCC必须的库isl,mpc,gmp,mpfr等。不知道为啥GCC供下载的源码包里不直接附带这几个软件包,还非得让用户重新下载它。
其他的常规编译环境:本地GCC,GNU make,perl,awk,bash等等,就不在这里啰嗦了,一般的用于开发的Linux主机上都已经安装好了这些基本的开发环境。
1.2.2 配置安装
GCC项目也是使用GNU autotools 管理编译过程的,所以生成它第一步必须是执行configure命令。与binutils一样,gcc也建议把构建目录和源码目录分离,所以新建一个目录名为 build-gcc,然后进入这个目录进行整个构建过程。mkdir build-gcc cd build-gcc ../gcc-6.2.0/configure --prefix=/home/smstong/ARM # 要与binutils配置时相同 --target=arm-linux-gnueabihf # 要与binutils配置时相同 --enable-languages=c # 只生成C编译器 --without-headers # 不使用头文件 --disable-multilib # 不生成多个库版本 make all-gcc # 注意此处的目标是all-gcc,也就是freestanding C make install-gcc # 相应的安装的也只是GCC
安装完成以后,会发现新生成的交叉编译器 /home/smstong/ARM/bin/arm-linux-gnueabihf-gcc,同时还有一个硬链接在/home/smstong/ARM/arm-linux-gnueabihf/bin/gcc。执行如下命令测试:
[smstong@centos192 bin]$ ./arm-linux-gnueabihf-gcc -v 使用内建 specs。 COLLECT_GCC=./arm-linux-gnueabihf-gcc COLLECT_LTO_WRAPPER=/home/smstong/ARM/libexec/gcc/arm-linux-gnueabihf/6.2.0/lto-wrapper 目标:arm-linux-gnueabihf 配置为:../gcc-6.2.0/configure --prefix=/home/smstong/ARM/ --target=arm-linux-gnueabihf --enable-languages=c --without-headers --disable-multilib 线程模型:posix gcc 版本 6.2.0 (GCC)
2 测试环境
目标机器环境:(1)硬件平台TQ2440开发板,Soc CPU为三星2440, ARM920T核心。
(2)Norflash装有u-boot,可以通过tfgtp下载程序到指定物理内存地址并执行
(3)Nandflash装有Linux2.6系统,带有tftp客户端工具。
开发主机:
(1)Centos 7 PC机器
(2)装有tftp server,服务目录为/var/www/tftpboot/。
3 裸机环境下C程序测试实例
2.1 项目源码
源码文件结构:. ├── Makefile ├── test.c ├── test.lds └── test.s
test.c
#define rGPBCON (*(volatile unsigned*)0x56000010) #define rGPBDAT (*(volatile unsigned*)0x56000014) #define rGPBUP (*(volatile unsigned*)0x56000018) void init() { /* 初始化led1 */ rGPBCON &= ~(3<<10); rGPBCON |= (1<<10); rGPBUP &= ~(1<<5); /* 熄灭led1 */ rGPBDAT |= (1<<5); return; }
test.lds
ENTRY(init) SECTIONS { . = 0x30000000; .text : { *(.text) *(.rodata) } .data ALIGN(4): { *(.data) } .bss ALIGN(4): { *(.bss) } }
Makefile
CC = arm-linux-gnueabihf-gcc LD = arm-linux-gnueabihf-ld OBJCPY = arm-linux-gnueabihf-objcopy all: test.bin sudo cp test.bin /var/lib/tftpboot/ test.bin: test $(OBJCPY) -O binary $< $@ test: test.o $(LD) --script=test.lds -o $@ $< test.o: test.c $(CC) -c $< .PHONY: clean clean: rm -rf *.o test test.bin
2.2 编译链接说明
交叉连接器默认的入口点名称为_start,默认的代码段基地址为0x00001074,生成的可执行文件格式为elf。而我们要想让程序在裸机上运行,需要代码段基地址为0x30000000,文件格式为纯二进制镜像。这都可以通过链接脚本轻松完成。另外我们还手动指定了程序入口点为init函数。通过Norflash里的u-boot把生成的test.bin加载到物理内存0x30000000处并执行,会发现LED1灯被熄灭。而且执行完成后自动返回到了u-boot中。因为init()函数的最后是return语句。
2.3 看看编译器生成的汇编代码
使用gcc test.c -c 时,gcc会把中间产生的汇编代码文件隐藏,为了看到这个中间文件,需要通过-S选项调用gcc来生成汇编代码文件。arm-linux-gnueabihf-gcc -S test.c
上述命令会生成test.s文件如下:
.eabi_attribute 18, 4 .file "test.c" .text .align 2 .global init .syntax unified .arm .fpu softvfp .type init, %function init: @ args = 0, pretend = 0, frame = 0 @ frame_needed = 1, uses_anonymous_args = 0 @ link register save eliminated. str fp, [sp, #-4]! add fp, sp, #0 ldr r2, .L2 ldr r3, .L2 ldr r3, [r3] bic r3, r3, #3072 str r3, [r2] ldr r2, .L2 ldr r3, .L2 ldr r3, [r3] orr r3, r3, #1024 str r3, [r2] ldr r2, .L2+4 ldr r3, .L2+4 ldr r3, [r3] bic r3, r3, #32 str r3, [r2] ldr r2, .L2+8 ldr r3, .L2+8 ldr r3, [r3] orr r3, r3, #32 str r3, [r2] nop sub sp, fp, #0 @ sp needed ldr fp, [sp], #4 bx lr .L3: .align 2 .L2: .word 1442840592 .word 1442840600 .word 1442840596 .size init, .-init .ident "GCC: (GNU) 6.2.0" .section .note.GNU-stack,"",%progbits
通过gcc生成的汇编代码,我们也可以学习GNU ARM汇编的基本语法。
4 Linux环境下Freestanding C程序测试实例
由于是Freestanding C环境,所以即使在Linux系统下,仍然没有可用的标准C库。而C语言又不能直接执行软中断指令调用Linux的系统调用,这就导致操作系统提供的API完全不可用!(汇编语言反而可以直接通过swi指令来调用系统API)可见在操作系统下,如果没有C库,C语言根本无法对硬件进行操作,也就不可能操控开发板上的LED灯,甚至也不能打印简单的hello world,这是何等的悲哀!为了便于测试,我们不得不借助汇编的帮助,采用C语言和汇编语言混合编程的方式。其中汇编语言提供一个打印字符串的函数和一个退出进程的函数,C语言调用之。
其实这就相当于自己用汇编语言实现了一个超级简化的POSIX系统调用C库。
C语言和汇编进行彼此调用,就必须要遵守相应的函数调用规范,及APCS(ARM Process Call Standard),请大家自行学习之。
4.1 项目源码
项目文件结构图:. ├── api.h # api 头文件说明 ├── api.s # api 实现 ├── Makefile ├── test.c └── test.lds # 链接脚本,指示程序入口
文件 api.h
void print(int fd, char* msg, int len); int exit(int code);
文件api.s
/*
void print(int fd, char* msg, int len); int exit(int code);
*/
.text
.global print
.global exit
print:
swi #0x900004
mov pc,lr
exit:
swi #0x900001
mov pc,lr
~
文件test.c
#include "api.h" void test() { char* msg = "hello, freestanding C\n"; int i; for (i = 0; i < 10; i++) { print(1, msg, 22); } exit(0); }
文件test.lds
ENTRY(test)
文件Makefile
CC = arm-linux-gnueabihf-gcc AS = arm-linux-gnueabihf-as LD = arm-linux-gnueabihf-ld OBJCPY = arm-linux-gnueabihf-objcopy all: test sudo cp test /var/lib/tftpboot/ test: test.o api.o $(LD) --script=test.lds -o $@ $^ test.o: test.c api.h $(CC) -c $< api.o: api.s $(AS) -o $@ $< .PHONY: clean clean: rm -rf *.o test
4.2 编译链接说明
交叉链接器默认生成elf格式文件,可以直接被Linux加载执行。应为是Freestanding C,需要在链接脚本中指定程序入口点。程序执行结果:
[root@EmbedSky /]# tftp -g -r test 172.16.35.188 [root@EmbedSky /]# ./test hello, freestanding C hello, freestanding C hello, freestanding C hello, freestanding C hello, freestanding C hello, freestanding C hello, freestanding C hello, freestanding C hello, freestanding C hello, freestanding C
5 对Freestanding C的思考
在裸机下,Freetanding C尚可以通过指针的方式直接操控部分硬件资源;在OS下,所有硬件资源受到操作系统的保护(通过MMU),Freestanding C根本无法独立操控任何硬件。所以在实际的开发中:
如果是裸机项目,C库本来就不可用,Freestanding C是唯一可选C环境,而且能够完美完成任务;
如果是基于OS的项目,那么Freestanding C能力不足,必须要有完整的Hosted C环境才能胜任(当然,也可以自己用汇编写一个小型C库,但是有现成的GLIBC,为啥要重复造轮子呢?)。
6 小结
到目前为止,博文 GNU ARM交叉汇编环境的搭建与测试完成了ARM汇编环境的搭建,本文完成了Freestanding C 编译环境的搭建,并给出了详细的步骤和应用实例。下一步,就是在这两个环境下多多练习,等熟练了,再开始搭建最终的Hosted C完整开发环境。
相关文章推荐
- TQ2440开发板学习纪实(0.0)--- GNU ARM交叉汇编环境的搭建与测试
- TQ2440开发板学习纪实(7)--- 基于中断的UART串口接收
- TQ2440开发板学习纪实(1)---最简单的独立运行汇编程序
- TQ2440开发板学习纪实(6)--- 启用IRQ中断,告别低效的轮询!
- 【嵌入式Linux学习七步曲之第二篇 ARM+Linux开发环境】详解为什么开发板的TFTP、NFS等网络功能神奇消失
- TQ2440开发板学习纪实(9)--- 利用Undefined异常模拟BLX指令
- TQ2440开发板学习纪实(10)--- 实现多任务处理,最简单OS模型
- TQ2440开发板交叉编译工具安装之后的环境变量设置
- TQ2440 学习笔记—— 1、Windows平台下开发工具安装与环境建立
- TQ2440开发板学习纪实(8)--- 从NAND Flash读取数据,把代码搬运到SDRAM运行
- TQ2440开发板学习纪实(2)--- 设置堆栈并调用C函数
- 第二课 创建嵌入式Linux开发环境之交叉编译环境
- 一步一步建立linux交叉编译开发环境(学习笔记)
- Android开发学习笔记(三)——基于Eclipse开发环境的搭建及HelloAndroid示例程序的创建
- Spring学习笔记(一)环境搭建和测试开发环境
- arm-linux-gcc安装 和 环境变量设置 分类: 嵌入式开发学习 2011-04-13 11:34 10197人阅读 评论(1) 收藏
- 搭建arm-linux-gcc交叉编译工具链环境(Android原生(JNI)开发环境搭建)
- Asp.net MVC 3实例学习之ExtShop(一)――――创建应用并设置开发环境
- 【嵌入式Linux学习七步曲之第二篇 交叉开发环境】FC6 上架设TFTP服务器,Transfer timed out
- 用C#控制TQ2440开发板上的LED小灯(C# GPIO学习笔记)