IT技術互動交流平臺

GNUFreestanding(Naked)CARM交叉開發環境建與測試

來源:IT165收集  發布日期:2016-12-14 20:33:08

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
';
    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完整開發環境。

Tag標簽: 環境  
  • 專題推薦

About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地圖 - 網友投稿 - 聯系方式
本站內容來自于互聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
香港最快开奖现场直播结果