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-objcopyall: test.binsudo 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, #0ldr r2, .L2ldr r3, .L2ldr r3, [r3]bic r3, r3, #3072str r3, [r2]ldr r2, .L2ldr r3, .L2ldr r3, [r3]orr r3, r3, #1024str r3, [r2]ldr r2, .L2+4ldr r3, .L2+4ldr r3, [r3]bic r3, r3, #32str r3, [r2]ldr r2, .L2+8ldr r3, .L2+8ldr r3, [r3]orr r3, r3, #32str r3, [r2]nopsub sp, fp, #0@ sp neededldr fp, [sp], #4bx  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 #0x900004mov pc,lrexit:swi #0x900001mov 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-objcopyall: testsudo 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.1)--- GNU Freestanding(Naked)C ARM交叉开发环境创建与测试相关推荐

  1. TQ2440开发板学习纪实(1)---最简单的独立运行汇编程序

    0 如何做到简单 TQ2440开发板,采用的CPU是三星S3C2440,核心板附加了2MB运行ROM和64MB运行RAM,并且搭配了64MB的Nand Flash.而S3C2440直接支持从NandF ...

  2. TQ2440开发板学习纪实(5)--- 设置UART串口,输出Hello World!

    0 串口,UART,RS232,RS485傻傻分不清 0.1 串行通信与并行通信 串口名字表示采用的通信方式为串行而不是并行.那么串行与并行的区别是啥呢?很简单,串行就是同一时刻只能传输一个bit,而 ...

  3. 黑金AX301开发板学习(1)——流水灯实验及黑金AX301开发板资料

    第一次尝试使用AX301开发板进行学习,本篇文章主要通过一个流水灯的小实验聊一下AX301这块开发板的使用. 一.黑金AX301是一款基础的学生实验板,用来学习FPGA是一个不错的选择.此款开发板是A ...

  4. 正点原子stm32f407开发板pcb图_#试用名单公布#正点原子ARM Linux开发板I.MX6ULL

    活动报名链接: http://www.cirmall.com/bbs/thread-161572-1-1.html 恭喜以下五位获得试用资格,管理员会将确认邮件发送至各位获奖者邮箱,请在3个工作日内回 ...

  5. TQ2440开发板学习纪实(9)--- 利用Undefined异常模拟BLX指令

    在博文 <紧急求助!ARM-GCC对于函数指针调用的编译有错误?>中,我提到了GCC在编译函数指针调用的时候,会生成绝对地址跳转指令BLX.而S3C2440A这款CPU不支持BLX指令,从 ...

  6. 海思SD3403开发板学习(一)

    海思SD3403开发板学习系列:一 简介 文章目录 海思SD3403开发板学习系列:一 简介 一.3403开发板 二.性能介绍 1.简介 2.主要特点 3.性能参数 一.3403开发板 SD3043标 ...

  7. 海思SD3403开发板学习(三)

    海思SD3403开发板学习系列:三 CANN和MindStudio安装 文章目录 海思SD3403开发板学习系列:三 CANN和MindStudio安装 前言 一.CANN安装 1.安装依赖项 2.C ...

  8. 【STM32 .Net MF开发板学习-11】步进电机控制(非PWM模式)

    选用的步进电机的型号为28BYJ-48(或MP28GA,5V,转速比1/64),驱动电路选用 uln2003芯片的驱动板,其控制时序图如下: 四相八拍:A->AB->B ->BC - ...

  9. 【STM32 .Net MF开发板学习-02】GPIO测试

    前段时间我借用市面上现成的Corex-M3开发板,打造了最低价的.Net Micro Framework开发板(参见<免费发放firmwave,打造史上最低价.Net MF开发板>),在此 ...

最新文章

  1. vue实现移动端圆形旋钮插件
  2. 常用Linux基础命令
  3. 勘误《iOS网络高级编程:iPhone和iPad的企业应用开发》
  4. 图解Linux的Socket
  5. 全球及中国新式茶饮行业销售价值与经营布局渠道研究报告2022版
  6. 大数据场景中语言虚拟机的应用和挑战
  7. flutter offset_牛笔!自己用Flutter撸一个天气APP
  8. matlab 正交特征向量,怎么对一个矩阵进行对称正交化?? matlab
  9. 给Nodejs回调加超时限制
  10. 史上最全的phpstorm常用配置
  11. Java画韦恩图_R绘制韦恩图 | Venn图
  12. 更改Android 默认键盘映射值
  13. 计算机硕士工资一览表
  14. AD元器件英中名称对照【Ctrl + F 快速查找元器件英文名称】
  15. 配置web.xml文件时报The word ‘***‘ is not correctly spelled的解决方案
  16. 计算机网络授课办法设计,计算机网络基础教学过程如何优化设计
  17. 解决phpmyadmin 访问卡慢等问题
  18. selenium框架爬取p2p问题平台信息,需加载点击页面的。
  19. 苹果iPhone手机内文件目录大全
  20. Lazada代运营分享—Lazada新手运营快速提升流量交易额的三大核心技巧

热门文章

  1. To_10_r_100_8_1---判断出三盏灯分别是由哪个开关控制的
  2. java毕业设计电影院购票系统Mybatis+系统+数据库+调试部署
  3. 我是一个转行的程序员(一) | 青鸟之忆
  4. 设置(setting)和其它设置
  5. gamma分布_如何通俗的理解伽马(gamma)函数
  6. vscode如何创建一个go项目_编辑器 VS Code 如何快速查看 Go 接口?
  7. SAP中客户寄售发退货(移动类型631/632)对PIR产生影响的现实案例分析测试
  8. 音视频开发成长之路—进阶之路3个重要知识点丨WebRTC丨FFmpeg丨SRS流媒体服务器丨C++音视频丨嵌入式音视频
  9. 实变函数自制笔记8:初识勒贝格积分
  10. java 泰勒级数_java编程用泰勒级数计算arcsin