新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > S3C2440開發(fā)工具realview MDK4.22之庫的使用

S3C2440開發(fā)工具realview MDK4.22之庫的使用

作者: 時間:2016-11-19 來源:網(wǎng)絡(luò) 收藏
一。與c庫會強(qiáng)制鏈接

如果你寫了一個c程序,必然會和c庫鏈接,盡管你沒有直接使用c庫函數(shù)。這是因?yàn)榫幾g器為了改進(jìn)程序,可能隱含的產(chǎn)生了對c庫函數(shù)調(diào)用。

本文引用地址:http://2s4d.com/article/201611/318141.htm

即便你的程序沒有main()函數(shù),也只是說c庫沒變初始化而已,一些c庫函數(shù)仍然可以使用并且編譯器可以隱含地調(diào)用這些函數(shù)。

二。ARM的c的運(yùn)行時庫

c標(biāo)準(zhǔn)庫由以下組成:

ISO99標(biāo)準(zhǔn)庫定義的所有函數(shù)。

依賴于目標(biāo)的函數(shù),用來在semihosted環(huán)境中執(zhí)行c庫函數(shù)。你可以在你的應(yīng)用程序中重定義這些函數(shù)。

被編譯器隱含調(diào)用的函數(shù)。

由ARM擴(kuò)展的,但是不是由ISO C定義,且包含在這個庫里的。

c微型庫由以下組成(可以代替c標(biāo)準(zhǔn)庫,它是非常適合只有小容量內(nèi)存的深度嵌入式應(yīng)用):

為了達(dá)到最小的代碼體積,已經(jīng)高度優(yōu)化的函數(shù)。

不服從ISO C規(guī)范的函數(shù)。

不服從1985 IEEE 754 標(biāo)準(zhǔn)

三。c庫的特性

c庫用標(biāo)準(zhǔn)的ARM semihosted 環(huán)境提供一些功能,不如說輸入、輸出。該環(huán)境由ARM RVI調(diào)試單元和Real-Time Simulator Model/(RTSM)所支持的。

你可以重新定義任何與設(shè)備相關(guān)的c庫函數(shù),寫在自己的應(yīng)用程序中即可。這允許你裁剪c庫以應(yīng)用在自己的執(zhí)行環(huán)境中。

也可以裁剪與設(shè)備無關(guān)的函數(shù),以適應(yīng)自己的特殊應(yīng)用要求。例如malloc簇,ctype簇,所有的locale-specific函數(shù)。

許多c庫函數(shù)與其他函數(shù)是獨(dú)立的并且是目標(biāo)無關(guān)的,你可以簡單的從庫里利用這些函數(shù)。

c庫函數(shù)負(fù)責(zé)以下事情:

1.創(chuàng)建一個c環(huán)境的執(zhí)行環(huán)境(創(chuàng)建棧,如果需要創(chuàng)建堆,初始化程序里用到的庫的部分)

2.開始執(zhí)行main()

3.支持ISO C定義的函數(shù)

4.捕獲允許錯誤、信號,根據(jù)情況終止程序或者退出程序

四。ARM C庫的堆使用需求

任何暗含的使用堆或者顯示調(diào)用使用堆都需要事先準(zhǔn)備好堆。

在c標(biāo)準(zhǔn)庫里,暗含的堆使用發(fā)生在:

1.調(diào)用庫函數(shù)fopen()并且一個I/O操作第一次使用這個fopen()所產(chǎn)生的stream

2.傳遞命令行參數(shù)給main()函數(shù)

分配80字節(jié)堆作為FILE結(jié)構(gòu)體的存儲空間。當(dāng)?shù)谝淮蜪/O操作發(fā)生,且直到這個操作產(chǎn)生,一個附加的512字節(jié)堆空間分配給與該操作相關(guān)的buffer區(qū)??梢岳胹etvbuf()重新

定義堆大小。

當(dāng)fclose()調(diào)用的時候,80字節(jié)未被釋放,保存在一個自由表中以備再次使用。512字節(jié)在fclose()時候被釋放了。

聲明帶參數(shù)的main()需要256字節(jié)的堆空間。這個內(nèi)存一直未釋放,因?yàn)橐趍ain()期間有效。在microlib里,不允許聲明帶參的main(),因此這個用法僅使用于標(biāo)準(zhǔn)庫。在

標(biāo)準(zhǔn)庫的環(huán)境里,如果存在堆,它就可以生效。

五。c庫基于不同的build options會選擇不同的目標(biāo)集

比如說,目標(biāo)架構(gòu),指令集(ARM,Thumb,Thumb-2);字節(jié)須(大小端);浮點(diǎn)支持(softVFP,VFP);位置無關(guān)

六。Thumb C 庫

當(dāng)連接器探測到以下事情發(fā)生的時候,會自動連接Thumb C庫:

1.Thumb or Thumb-2 or --thumb選項(xiàng) or #pragma thumb

2.在ARMv4T下,使用--apcs / interwork

3.ARMv6-M Cortex-M1/MO

4.ARMv7-M Cortex-M3

七。ARM C庫和多線程

當(dāng)你使用RTOS,ARM C庫支持多線程

八。__user_libspace

__user_libspace為C庫保持了靜態(tài)數(shù)據(jù)。這是一個96字節(jié),0初始化的數(shù)據(jù)塊,該塊由C庫創(chuàng)建。在C庫初始化期間可以用來當(dāng)做臨時棧。

默認(rèn)的ARM C庫用__user_libspace區(qū)域保持以下內(nèi)容:

1.errno,由可以設(shè)置errno的函數(shù)使用。默認(rèn),__rt_errno_addr()返回指向errno的指針。

2.浮點(diǎn)狀態(tài)字,用于軟件浮點(diǎn)。在硬件浮點(diǎn)里,不使用。默認(rèn),__re_fp_status_addr()返回指向FP狀態(tài)字的指針。

3.一個指向堆基址的指針,也就是__Heap_Descriptor,被所有malloc-相關(guān)的函數(shù)使用。

4.當(dāng)前的locale設(shè)置,被像setlocale()函數(shù)使用,同時也被所有依賴于它們的其它函數(shù)使用。

九。在一個程序中使用庫

可以用以下方法在應(yīng)用程序使用C庫:

1.建立一個semihosting應(yīng)用,它可以在一個semihosted環(huán)境中調(diào)試,比如說利用RV。

2.建立一個非host的應(yīng)用,它可以嵌入到ROM中。

3.建立一個不包含main()的應(yīng)用,且不初始化庫。該應(yīng)用具有非常有限的庫功能,除非重定義一些函數(shù)。

十。在一個semihosting環(huán)境利用C庫

如果你開發(fā)一個應(yīng)用,為了調(diào)試,運(yùn)行在semihosted環(huán)境,你必須有一個執(zhí)行環(huán)境,可以支持ARM 或者 Thumb semihosting,且有足夠的存儲空間。

1.用標(biāo)準(zhǔn)的半主機(jī)功能,默認(rèn)情況下,在RVI中會提供。

2.為半主機(jī)調(diào)用執(zhí)行自己的處理過程。

如果使用默認(rèn)的C庫的半主機(jī)功能,不必重寫任何函數(shù)和頭文件。ARM調(diào)試代理支持半主機(jī),但是C庫所假定的內(nèi)存格局需要裁減以匹配正在調(diào)試的硬件。

十一。使用$sub$$使用半主機(jī)和非半主機(jī)I/O功能

例如,fputc()的實(shí)現(xiàn)是直接往UART寫,還有1個fputc()的實(shí)現(xiàn)是半主機(jī)的??梢蕴峁┻@兩個版本,具體要看傳遞到函數(shù)的FILE指針的性質(zhì)。

int $Super$fputc(int c, FILE *fp);int $Sub$fputc(int c, FILE *fp){if (fp == (FILE *)MAGIC_NUM) // where MAGIC_NUM is a special value that{                            // is different to all normal FILE * pointer// values.write_to_UART(c);return c;}else{return $Super$fputc(c, fp);}}
可以看到根據(jù)FILE指針判斷是哪種類型,$$super$代表正常的,$$sub$代表特殊的,一般和自己的目標(biāo)硬件相關(guān)。

十二。以非半主機(jī)模式使用庫

一些C庫函數(shù)使用半主機(jī)。如果不想使用的話,下列方式之一可以實(shí)現(xiàn):

1.移除所有的半主機(jī)函數(shù)調(diào)用

2.重定義低層函數(shù),比如fputc()。不必重定義所有的半主機(jī)函數(shù)。只是重定義在程序里需要使用到的函數(shù)。

有些函數(shù)雖然不直接與目標(biāo)依賴,但是它依賴于某些底層函數(shù),而底層函數(shù)依賴于目標(biāo)。例如,printf(),你必須重定義fputc(),如果不用得話,那就不必重定義了。

3.以自己特別的方式實(shí)現(xiàn)所有半主機(jī)調(diào)用的一種方式。一個作為這種處理的例子是,攔截調(diào)用,將它們重指向自己的非主機(jī)實(shí)現(xiàn),也就是,具體目標(biāo)的函數(shù)。

IMPORT __use_no_semihosting from ASM

#pragma import(__use_no_semihosting) from C

十三。直接的半主機(jī)依賴


十四。間接的半主機(jī)依賴


十五。建立一個不使用C庫的應(yīng)用程序

如果程序中不包含main(),那么庫不會被初始化,且如下功能不可用:

1.帶有_sys_前綴的底層stdio函數(shù)

2.信號處理函數(shù)

3.其它函數(shù),如atexit()

有些函數(shù)即便庫未初始化也能使用,一些其它不可用的函數(shù)也能被使用,除非它們所依賴的庫函數(shù)被重定義。

十六。裸機(jī)代碼

如果你寫一個程序,里面沒有使用庫,且沒有任何環(huán)境初始化,你必須:

1.如果用到heap的話,重定義__rt_raise()

2.不定義main()

3.寫一個匯編語言代碼建立c語言運(yùn)行需要的環(huán)境,必須要跳轉(zhuǎn)到你c函數(shù)的入口

4.提供自己的RW/ZI初始化代碼

5.確保你的匯編代碼在你的重啟處理段

6.用--fpu=none

當(dāng)滿足這些要求,連接器用合適的C庫變體尋找任何需要編譯的函數(shù),它們是隱形調(diào)用的。

盡管沒有main(),__user_lbspace()被自動創(chuàng)建,占用96字節(jié)的ZI段。

十七。定制C庫啟動代碼且訪問C庫函數(shù)

應(yīng)用程序里不需要C庫,如果你程序里有main(),連接器會自動包含初始化代碼??赡艽嬖谝恍┣闆r,這是不必的。例如,一個運(yùn)行RTOS系統(tǒng)可能通過RTOS啟動代碼

有它自己的執(zhí)行環(huán)境配置。

你可以創(chuàng)建一個應(yīng)用包含自己的啟動代碼且仍然可以使用庫函數(shù)的大部分功能。你可以做下列之一的選擇:

1.避免使用需要初始化的函數(shù)

2.提供初始化和底層函數(shù)函數(shù)實(shí)現(xiàn)

十八。利用C庫使用底層函數(shù)

如果你的應(yīng)用中沒有main(),而使用庫的話,必須重定義一些在庫里的函數(shù)

注意:如果用heap的話,__rt_raise()是必須的。

十九。利用庫中的高層函數(shù)

如果低層的函數(shù)被重定義了,那么高層I/O函數(shù)能被使用。高層函數(shù)是像fprintf() printf() scanf() puts()等,低層函數(shù)是那些如fputc() fgetc() 以及__backspace()函數(shù)。

大多數(shù)格式化輸出函數(shù)也需要調(diào)用setlocale()。

二十。利用庫中的malloc()函數(shù)

如果在裸機(jī)C代碼中需要堆得支持,那么__init_alloc()必須要首先調(diào)用支持堆的邊界初始化,__rt_heap_extend()必須要提供,盡管返回失敗。如果沒有__rt_heap_extend()函數(shù),特定的庫功能會被包含進(jìn)去,這會導(dǎo)致裸機(jī)C代碼出現(xiàn)問題。

二十一。執(zhí)行環(huán)境的初始化和程序的執(zhí)行

一個程序的入口是位于C庫里的__main,它會做如下事情:

1.拷貝非根區(qū)代碼從它們的加載地址到運(yùn)行地址。如果任何數(shù)據(jù)段被壓縮了得話,它們也會被解壓縮。

2.用0初始化ZI區(qū)。

3.跳轉(zhuǎn)到__rt_entry。

如果你不想C庫做這些事情,你可以定義自己的__main,__main里可以跳轉(zhuǎn)到__rt_entry。例如:

IMPORT __rt_entryEXPORT __mainENTRY__mainB  __rt_entryEND

庫函數(shù)__rt_entry像下面這樣運(yùn)行程序:

1.建立堆和棧通過以下方式之一:調(diào)用__user_setup_srackheap(),調(diào)用__rt_stackheap_int()或加載scatter文件里的絕對地址。

2.調(diào)用__rt_lib_init()初始化引用的庫函數(shù),初始化locale,如果必要的話,為main()建立argc和argv。

3.調(diào)用main(),用戶級的根函數(shù)。

4.從main()函數(shù)起,你的程序可能調(diào)用庫函數(shù)。

5.利用main()返回的數(shù)值作為參數(shù)調(diào)用exit()函數(shù)。

二十二。在main()里調(diào)用庫函數(shù)

.main()是用戶級的根函數(shù)。它需要已經(jīng)初始化了的執(zhí)行環(huán)境和可以使用的輸入輸出函數(shù)。在main()里程序可能執(zhí)行下列的動作之一,下列動作調(diào)用了用戶定制的C庫函數(shù):

1.擴(kuò)展堆,棧。

2.調(diào)用庫函數(shù)。該庫函數(shù)需要調(diào)用用戶定義的函數(shù)。例如__rt_fp_status_addr() 或者clock()。

3.調(diào)用使用locale或CTYPE的庫函數(shù)。

4.執(zhí)行需要浮點(diǎn)單元或者浮點(diǎn)庫支持的浮點(diǎn)運(yùn)算。

5.通過低層函數(shù)的輸入輸出函數(shù),如putc()或間接通過高層函數(shù),如fprintf()等。

6.發(fā)起一個錯誤或其他信號,例如ferror。

二十三。修改用于錯誤信號,錯誤處理和程序退出的C庫修改

所有由C庫發(fā)起的陷阱或錯誤信號都是通過__raise()函數(shù)??梢灾囟x這個函數(shù)或者它所使用到的低層函數(shù)。

二十四。避免使用堆和使用堆的庫函數(shù)

IMPORT __use_no_heap from assembly language#pragma import(__use_no_heap) from C.

二十五。在裸機(jī)C代碼里使用heap

1.調(diào)用__init_alloc(base, top)

2.定義函數(shù)unsigned _rt_heap_extend(unsigned size, void block)處理擴(kuò)展堆得需要。

二十六。棧的初始化和堆界限

可以指定棧指針,指定哪一塊區(qū)域?yàn)槎?,用以下任何一種方式:

1.定義__initial_sp,如果需要堆, 定義__heap_base and __heap_limit

2.用scatter文件,下列方式之一

2.1定義ARM_LIB_STACK and ARM_LIB_STACK區(qū)

2.2不用堆,只需ARM_LIB_STACK

2.3定義一個ARM_LIB_STACKHEAP,此時堆棧是一體的,相向生長。

微庫僅支持上述2種方式。

3.實(shí)現(xiàn)__user_setup_stackheap()建立棧指針和返回初始堆區(qū)域。

4.用遺留的__user_initial_stackheap()也可以實(shí)現(xiàn)

初始化堆指針必須指向的時8字節(jié)倍數(shù)對齊的區(qū)域。

默認(rèn)情況下,潛在的堆棧沖突會被自動探測到且請求的堆分配失敗。如果不希望自動的沖突檢測,可以通過用#pragma import __use_two_region_memory預(yù)留一小段空間。

5.作為遺留的原因,也可以使用__rt_stackheap_init()和__rt_heap_extend()。

二十七。C庫中對低層函數(shù)的依賴

表中顯示了高層函數(shù)對低層函數(shù)的依賴,如果定義了自己的低層函數(shù)版本,可以直接使用高層函數(shù)庫中的版本。

fgetc()用__FILE,但是fputc()用__FILE和ferror()。

必須提供__stdin和__stdout的定義,如果使用和它們相關(guān)的高層函數(shù)。雖然重定義了其它函數(shù),如fgetc()和fputc(),它們沒有引用任何在__stdin __stdout里的數(shù)據(jù)。

Table key:

  1. __FILE, the file structure.

  2. __stdin, the standard input object of type__FILE.

  3. __stdout, the standard output object of type__FILE.

  4. fputc(), outputs a character to a file.

  5. ferror(), returns the error status accumulated during file I/O.

  6. fgetc(), gets a character from a file.

  7. fgetwc()

  8. fputwc()

  9. __backspace(), moves the file pointer to the previous character.

  10. __backspacewc().

如果選擇重定義fgetc() fputc() __backspace() 要認(rèn)識到fopen和相關(guān)的函數(shù)使用ARM默認(rèn)的FILE結(jié)構(gòu)。如果重定義了__FILE,那么也必須重定義fopen()和相關(guān)的函數(shù)。

C庫輸出函數(shù)族僅依賴fputc() ferror();C庫輸入函數(shù)族僅依賴于fgetc() __FILE __backspace()。



二十八。重定義低層函數(shù),以便直接使用高層函數(shù)庫函數(shù)

如果你重定義了__FILE fputc() ferror() __stdout可以使用所有的printf()函數(shù)族。

Example 9. Retargeting printf()#include struct __FILE{int handle;/* Whatever you require here. If the only file you are using is *//* standard output using printf() for debugging, no file handling *//* is required. */};/* FILE is typedef’d in stdio.h. */FILE __stdout;int fputc(int ch, FILE *f) {/* Your implementation of fputc(). */return ch;}int ferror(FILE *f){/* Your implementation of ferror(). */return 0;}void test(void){printf("Hello worldn");}


二十九。

__backspace()被scanf()函數(shù)族使用。

__backspace()必須僅在從流里讀取一個字符后調(diào)用。



評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉