历史上的今天
返回首页

历史上的今天

今天是:2024年09月15日(星期日)

2021年09月15日 | GNU Freestanding(Naked)C ARM交叉开发环境创建与测试

2021-09-15 来源:eefocus

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的核心部件。


然后,进入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

1

上述命令会生成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 Cn";

    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完整开发环境。

推荐阅读

史海拾趣

潮州三环(Three-circle)公司的发展小趣事

潮州三环(集团)股份有限公司,最初成立于1970年,主要从事陶瓷基体及固定电阻器的制造和销售。然而,随着电子行业的快速发展,公司意识到单一产品已无法满足市场需求。因此,在1996年,三环集团开始投资生产片式电阻用的氧化铝陶瓷基片,这一决策标志着公司正式进入片式化元件制造领域。通过引进国外先进设备和技术,三环集团成功实现了电阻及瓷体的自动化生产,为公司后续的发展奠定了坚实基础。

Electro Technik Industries公司的发展小趣事

为了扩大市场份额,ETI开始积极寻求国际合作。公司先后与多家国际知名企业建立了战略合作关系,共同研发新产品、开拓市场。同时,ETI还积极参加国际电子展览会,展示公司的技术实力和产品优势,吸引了众多客户的关注。通过不断努力,ETI的产品逐渐打入国际市场,成为了一家具有全球影响力的电子企业。

Hengstler GmbH公司的发展小趣事

随着电子技术的快速发展,ETI意识到只有不断创新才能在竞争中立于不败之地。公司加大了对研发的投入,积极引进国内外先进技术,并鼓励员工提出创新性的想法。在一次偶然的实验中,ETI的研发团队成功研发出了一种具有高效能、低功耗特点的电子元器件,这一突破性的技术成果迅速获得了市场的认可,为ETI带来了可观的收益。

Gespac Inc公司的发展小趣事

为了扩大市场份额,ETI开始积极寻求国际合作。公司先后与多家国际知名企业建立了战略合作关系,共同研发新产品、开拓市场。同时,ETI还积极参加国际电子展览会,展示公司的技术实力和产品优势,吸引了众多客户的关注。通过不断努力,ETI的产品逐渐打入国际市场,成为了一家具有全球影响力的电子企业。

DEMMELPRODUCTS公司的发展小趣事

在追求技术创新和市场拓展的同时,DEMMELPRODUCTS公司始终将品质管理放在首位。公司建立了严格的质量管理体系,从原材料采购到产品生产的每一个环节都进行严格的质量控制。此外,公司还积极引入先进的生产设备和技术,不断提高生产效率和产品质量。通过持续改进和优化,DEMMELPRODUCTS公司的产品品质得到了客户和行业的广泛认可。

CHIMEI(奇美)公司的发展小趣事

随着电子行业的快速发展,市场竞争也日益激烈。奇美公司深知创新是企业发展的核心动力,因此不断加大研发投入,推动技术创新和产品升级。公司研发团队积极探索新技术、新工艺和新材料的应用,推出了一系列具有创新性和竞争力的产品。这些产品不仅满足了客户的需求,还为公司开拓了新的市场领域。

问答坊 | AI 解惑

433M无线导游应用评估方案

无论在人山人海的热门旅游景点、展览中心,拥挤的集市上,声音吵杂的工厂,还是在环境安静的博物馆、公司单位、会场、教室, 无线龙无线导游系统都可以让您清晰的听到导游、演说者的一字一句,您不用担心因为注意力分散而掉队,或听不清领导的指示 ...…

查看全部问答>

请教ATmega16使用EEPROM存储数据时遇到的现象???

以下是我做实验的一段小程序: #include #include void port_init(void) { PORTA = 0xFF; DDRA  = 0xFF; PORTB = 0x00; DDRB  = 0x00; PORTC = 0x00; //m103 output only DDRC  = 0x00; PORTD = ...…

查看全部问答>

nand flash驱动的问题

大家好,请问:smflash_lib.lib这个库是在哪里生成的呀?我在BSP没有发现在生成这个库的地方!谢谢 了!…

查看全部问答>

对IDE和SATA的I/O端口比较了解的高手看看

  最近要把原来写过的IDE接口的硬盘读写程序改成SATA的,用到1F0~1F7和3F6~3F7这些I/O映射地址对硬盘控制芯片的寄存器进行寻址,现在我需要知道用SATA接口时,这些I/O地址应该是什么,只要知道对SATA操作的I/O端口地址就能解决所有问题了,请 ...…

查看全部问答>

求教

本帖最后由 paulhyde 于 2014-9-15 09:43 编辑 请问:单电源和双电源运放之接有什么区别,在运用上应当注意什么?  …

查看全部问答>

MEGA8熔丝位

MEGA8用STK500下载程序时, STUDIO里 选择FUSE  里选择内部1M晶振 然后点入别的界面  在点FUSE 他有改成默认的外部的了 以前用MEGA16的时候,不会出现这种情况的   不知道为什么…

查看全部问答>

请教个问题:关于CC2530_2591模块

原理图中CC2530很多引脚都是连接到插座上,想请教下这些插座的作用以及插座上几个焊盘接gnd或者VCC的原因。另外一个问题就是可不可以改变插座引脚的位置以更方便自己PCB布局。我画的是2层板布局。…

查看全部问答>

JLINK仿真器

使用JLINK仿真器在STM32调试的时候,要添加相关的器件中,下拉菜单中还有好多其他的芯片,我在想不同的芯片就有不同的外设,那JLINK都能仿真吗…

查看全部问答>

怎么在basic rf中使用外部中断呢?

大家好,请教大家一个问题,怎么在basic rf中使用外部中断呢?因为我发现basic rf有时会关闭全局中断。谢谢!…

查看全部问答>

关于航模飞行控制

本人新手,想做直升机航模的自行控制测量,求大神指点各模块采用哪些元器件比较好…

查看全部问答>