S3C2440開發工具realview MDK4.22之庫的使用
如果你寫了一個c程序,必然會和c庫鏈接,盡管你沒有直接使用c庫函數。這是因為編譯器為了改進程序,可能隱含的產生了對c庫函數調用。
本文引用地址:http://www.104case.com/article/201611/318141.htm即便你的程序沒有main()函數,也只是說c庫沒變初始化而已,一些c庫函數仍然可以使用并且編譯器可以隱含地調用這些函數。
二。ARM的c的運行時庫
c標準庫由以下組成:
ISO99標準庫定義的所有函數。
依賴于目標的函數,用來在semihosted環境中執行c庫函數。你可以在你的應用程序中重定義這些函數。
被編譯器隱含調用的函數。
由ARM擴展的,但是不是由ISO C定義,且包含在這個庫里的。
c微型庫由以下組成(可以代替c標準庫,它是非常適合只有小容量內存的深度嵌入式應用):
為了達到最小的代碼體積,已經高度優化的函數。
不服從ISO C規范的函數。
不服從1985 IEEE 754 標準
三。c庫的特性
c庫用標準的ARM semihosted 環境提供一些功能,不如說輸入、輸出。該環境由ARM RVI調試單元和Real-Time Simulator Model/(RTSM)所支持的。
你可以重新定義任何與設備相關的c庫函數,寫在自己的應用程序中即可。這允許你裁剪c庫以應用在自己的執行環境中。
也可以裁剪與設備無關的函數,以適應自己的特殊應用要求。例如malloc簇,ctype簇,所有的locale-specific函數。
許多c庫函數與其他函數是獨立的并且是目標無關的,你可以簡單的從庫里利用這些函數。
c庫函數負責以下事情:
1.創建一個c環境的執行環境(創建棧,如果需要創建堆,初始化程序里用到的庫的部分)
2.開始執行main()
3.支持ISO C定義的函數
4.捕獲允許錯誤、信號,根據情況終止程序或者退出程序
四。ARM C庫的堆使用需求
任何暗含的使用堆或者顯示調用使用堆都需要事先準備好堆。
在c標準庫里,暗含的堆使用發生在:
1.調用庫函數fopen()并且一個I/O操作第一次使用這個fopen()所產生的stream
2.傳遞命令行參數給main()函數
分配80字節堆作為FILE結構體的存儲空間。當第一次I/O操作發生,且直到這個操作產生,一個附加的512字節堆空間分配給與該操作相關的buffer區??梢岳胹etvbuf()重新
定義堆大小。
當fclose()調用的時候,80字節未被釋放,保存在一個自由表中以備再次使用。512字節在fclose()時候被釋放了。
聲明帶參數的main()需要256字節的堆空間。這個內存一直未釋放,因為要在main()期間有效。在microlib里,不允許聲明帶參的main(),因此這個用法僅使用于標準庫。在
標準庫的環境里,如果存在堆,它就可以生效。
五。c庫基于不同的build options會選擇不同的目標集
比如說,目標架構,指令集(ARM,Thumb,Thumb-2);字節須(大小端);浮點支持(softVFP,VFP);位置無關
六。Thumb C 庫
當連接器探測到以下事情發生的時候,會自動連接Thumb C庫:
1.Thumb or Thumb-2 or --thumb選項 or #pragma thumb
2.在ARMv4T下,使用--apcs / interwork
3.ARMv6-M Cortex-M1/MO
4.ARMv7-M Cortex-M3
七。ARM C庫和多線程
當你使用RTOS,ARM C庫支持多線程
八。__user_libspace
__user_libspace為C庫保持了靜態數據。這是一個96字節,0初始化的數據塊,該塊由C庫創建。在C庫初始化期間可以用來當做臨時棧。
默認的ARM C庫用__user_libspace區域保持以下內容:
1.errno,由可以設置errno的函數使用。默認,__rt_errno_addr()返回指向errno的指針。
2.浮點狀態字,用于軟件浮點。在硬件浮點里,不使用。默認,__re_fp_status_addr()返回指向FP狀態字的指針。
3.一個指向堆基址的指針,也就是__Heap_Descriptor,被所有malloc-相關的函數使用。
4.當前的locale設置,被像setlocale()函數使用,同時也被所有依賴于它們的其它函數使用。
九。在一個程序中使用庫
可以用以下方法在應用程序使用C庫:
1.建立一個semihosting應用,它可以在一個semihosted環境中調試,比如說利用RV。
2.建立一個非host的應用,它可以嵌入到ROM中。
3.建立一個不包含main()的應用,且不初始化庫。該應用具有非常有限的庫功能,除非重定義一些函數。
十。在一個semihosting環境利用C庫
如果你開發一個應用,為了調試,運行在semihosted環境,你必須有一個執行環境,可以支持ARM 或者 Thumb semihosting,且有足夠的存儲空間。
1.用標準的半主機功能,默認情況下,在RVI中會提供。
2.為半主機調用執行自己的處理過程。
如果使用默認的C庫的半主機功能,不必重寫任何函數和頭文件。ARM調試代理支持半主機,但是C庫所假定的內存格局需要裁減以匹配正在調試的硬件。
十一。使用$sub$$使用半主機和非半主機I/O功能
例如,fputc()的實現是直接往UART寫,還有1個fputc()的實現是半主機的。可以提供這兩個版本,具體要看傳遞到函數的FILE指針的性質。
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);}}可以看到根據FILE指針判斷是哪種類型,$$super$代表正常的,$$sub$代表特殊的,一般和自己的目標硬件相關。
十二。以非半主機模式使用庫
一些C庫函數使用半主機。如果不想使用的話,下列方式之一可以實現:
1.移除所有的半主機函數調用
2.重定義低層函數,比如fputc()。不必重定義所有的半主機函數。只是重定義在程序里需要使用到的函數。
有些函數雖然不直接與目標依賴,但是它依賴于某些底層函數,而底層函數依賴于目標。例如,printf(),你必須重定義fputc(),如果不用得話,那就不必重定義了。
3.以自己特別的方式實現所有半主機調用的一種方式。一個作為這種處理的例子是,攔截調用,將它們重指向自己的非主機實現,也就是,具體目標的函數。
IMPORT __use_no_semihosting from ASM
#pragma import(__use_no_semihosting) from C
十三。直接的半主機依賴
十四。間接的半主機依賴
十五。建立一個不使用C庫的應用程序
如果程序中不包含main(),那么庫不會被初始化,且如下功能不可用:
1.帶有_sys_前綴的底層stdio函數
2.信號處理函數3.其它函數,如atexit()
有些函數即便庫未初始化也能使用,一些其它不可用的函數也能被使用,除非它們所依賴的庫函數被重定義。
十六。裸機代碼
如果你寫一個程序,里面沒有使用庫,且沒有任何環境初始化,你必須:
1.如果用到heap的話,重定義__rt_raise()
2.不定義main()
3.寫一個匯編語言代碼建立c語言運行需要的環境,必須要跳轉到你c函數的入口
4.提供自己的RW/ZI初始化代碼
5.確保你的匯編代碼在你的重啟處理段
6.用--fpu=none
當滿足這些要求,連接器用合適的C庫變體尋找任何需要編譯的函數,它們是隱形調用的。
盡管沒有main(),__user_lbspace()被自動創建,占用96字節的ZI段。
十七。定制C庫啟動代碼且訪問C庫函數
應用程序里不需要C庫,如果你程序里有main(),連接器會自動包含初始化代碼??赡艽嬖谝恍┣闆r,這是不必的。例如,一個運行RTOS系統可能通過RTOS啟動代碼
有它自己的執行環境配置。
你可以創建一個應用包含自己的啟動代碼且仍然可以使用庫函數的大部分功能。你可以做下列之一的選擇:
1.避免使用需要初始化的函數
2.提供初始化和底層函數函數實現
十八。利用C庫使用底層函數
如果你的應用中沒有main(),而使用庫的話,必須重定義一些在庫里的函數
注意:如果用heap的話,__rt_raise()是必須的。
十九。利用庫中的高層函數
如果低層的函數被重定義了,那么高層I/O函數能被使用。高層函數是像fprintf() printf() scanf() puts()等,低層函數是那些如fputc() fgetc() 以及__backspace()函數。
大多數格式化輸出函數也需要調用setlocale()。
二十。利用庫中的malloc()函數
如果在裸機C代碼中需要堆得支持,那么__init_alloc()必須要首先調用支持堆的邊界初始化,__rt_heap_extend()必須要提供,盡管返回失敗。如果沒有__rt_heap_extend()函數,特定的庫功能會被包含進去,這會導致裸機C代碼出現問題。
二十一。執行環境的初始化和程序的執行
一個程序的入口是位于C庫里的__main,它會做如下事情:
1.拷貝非根區代碼從它們的加載地址到運行地址。如果任何數據段被壓縮了得話,它們也會被解壓縮。
2.用0初始化ZI區。
3.跳轉到__rt_entry。
如果你不想C庫做這些事情,你可以定義自己的__main,__main里可以跳轉到__rt_entry。例如:
IMPORT __rt_entryEXPORT __mainENTRY__mainB __rt_entryEND庫函數__rt_entry像下面這樣運行程序:
1.建立堆和棧通過以下方式之一:調用__user_setup_srackheap(),調用__rt_stackheap_int()或加載scatter文件里的絕對地址。
2.調用__rt_lib_init()初始化引用的庫函數,初始化locale,如果必要的話,為main()建立argc和argv。
3.調用main(),用戶級的根函數。
4.從main()函數起,你的程序可能調用庫函數。
5.利用main()返回的數值作為參數調用exit()函數。
二十二。在main()里調用庫函數
.main()是用戶級的根函數。它需要已經初始化了的執行環境和可以使用的輸入輸出函數。在main()里程序可能執行下列的動作之一,下列動作調用了用戶定制的C庫函數:
1.擴展堆,棧。
2.調用庫函數。該庫函數需要調用用戶定義的函數。例如__rt_fp_status_addr() 或者clock()。
3.調用使用locale或CTYPE的庫函數。
4.執行需要浮點單元或者浮點庫支持的浮點運算。
5.通過低層函數的輸入輸出函數,如putc()或間接通過高層函數,如fprintf()等。
6.發起一個錯誤或其他信號,例如ferror。
二十三。修改用于錯誤信號,錯誤處理和程序退出的C庫修改
所有由C庫發起的陷阱或錯誤信號都是通過__raise()函數??梢灾囟x這個函數或者它所使用到的低層函數。
二十四。避免使用堆和使用堆的庫函數
IMPORT __use_no_heap from assembly language#pragma import(__use_no_heap) from C.
二十五。在裸機C代碼里使用heap
1.調用__init_alloc(base, top)
2.定義函數unsigned _rt_heap_extend(unsigned size, void block)處理擴展堆得需要。
二十六。棧的初始化和堆界限
可以指定棧指針,指定哪一塊區域為堆,用以下任何一種方式:
1.定義__initial_sp,如果需要堆, 定義__heap_base and __heap_limit
2.用scatter文件,下列方式之一
2.1定義ARM_LIB_STACK and ARM_LIB_STACK區
2.2不用堆,只需ARM_LIB_STACK
2.3定義一個ARM_LIB_STACKHEAP,此時堆棧是一體的,相向生長。
微庫僅支持上述2種方式。
3.實現__user_setup_stackheap()建立棧指針和返回初始堆區域。
4.用遺留的__user_initial_stackheap()也可以實現
初始化堆指針必須指向的時8字節倍數對齊的區域。
默認情況下,潛在的堆棧沖突會被自動探測到且請求的堆分配失敗。如果不希望自動的沖突檢測,可以通過用#pragma import __use_two_region_memory預留一小段空間。
5.作為遺留的原因,也可以使用__rt_stackheap_init()和__rt_heap_extend()。
二十七。C庫中對低層函數的依賴
表中顯示了高層函數對低層函數的依賴,如果定義了自己的低層函數版本,可以直接使用高層函數庫中的版本。
fgetc()用__FILE,但是fputc()用__FILE和ferror()。
必須提供__stdin和__stdout的定義,如果使用和它們相關的高層函數。雖然重定義了其它函數,如fgetc()和fputc(),它們沒有引用任何在__stdin __stdout里的數據。
Table key:
__FILE, the file structure.
__stdin, the standard input object of type__FILE.
__stdout, the standard output object of type__FILE.
fputc(), outputs a character to a file.
ferror(), returns the error status accumulated during file I/O.
fgetc(), gets a character from a file.
fgetwc()
fputwc()
__backspace(), moves the file pointer to the previous character.
__backspacewc().
C庫輸出函數族僅依賴fputc() ferror();C庫輸入函數族僅依賴于fgetc() __FILE __backspace()。
二十八。重定義低層函數,以便直接使用高層函數庫函數
如果你重定義了__FILE fputc() ferror() __stdout可以使用所有的printf()函數族。
Example 9. Retargeting printf()#includestruct __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()函數族使用。
__backspace()必須僅在從流里讀取一個字符后調用。
評論