ARM的位置無(wú)關(guān)程序設計在Bootloader中的應用
引言
本文引用地址:http://dyxdggzs.com/article/82416.htm基于位置無(wú)關(guān)代碼PIC(PositionIndependent Code)的程序設計在嵌入式應用系統開(kāi)發(fā)中具有重要的作用,尤其在裸機狀態(tài)下開(kāi)發(fā)Bootloader程序及進(jìn)行內核初始化設計;利用位置無(wú)關(guān)的程序設計方法還可以在具體應用中用于構建高效率動(dòng)態(tài)鏈接庫,因而深入理解和熟練掌握位置無(wú)關(guān)的程序設計方法,有助于開(kāi)發(fā)人員設計出結構簡(jiǎn)單、清晰的應用程序。本文首先介紹位置無(wú)關(guān)代碼的基本概念和實(shí)現原理,然后闡述基于ARM匯編位置無(wú)關(guān)的程序設計方法和實(shí)現過(guò)程,最后以Bootloader程序設計為例,介紹了位置無(wú)關(guān)程序設計在Bootloader程序設計中的作用。
1 位置無(wú)關(guān)代碼及程序設計方法
1.1 基本概念與實(shí)現原理
應用程序必須經(jīng)過(guò)編譯、匯編和鏈接后才變成可執行文件,在鏈接時(shí),要對所有目標文件進(jìn)行重定位(relocation),建立符號引用規則,同時(shí)為變量、函數等分配運行地址。當程序執行時(shí),系統必須把代碼加載到鏈接時(shí)所指定的地址空間,以保證程序在執行過(guò)程中對變量、函數等符號的正確引用,使程序正常運行。在具有操作系統的系統中,重定位過(guò)程由操作系統自動(dòng)完成。
在設計Bootloader程序時(shí),必須在裸機環(huán)境中進(jìn)行,這時(shí)Bootloader映像文件的運行地址必須由程序員設定。通常情況下,將Bootloader程序下載到ROM的0x0地址進(jìn)行啟動(dòng),而在大多數應用系統中,為了快速啟動(dòng),首先將Bootloader程序拷貝到SDRAM中再運行。一般情況下,這兩者的地址并不相同,程序在SDRAM中的地址重定位過(guò)程必須由程序員完成。實(shí)際上,由于Bootloader是系統上電后要執行的第一段程序,Bootloader程序的拷貝和在這之前的所有工作都必須由其自身來(lái)完成,而這些指令都是在ROM中執行的。也就是說(shuō),這些代碼即使不在鏈接時(shí)所指定的運行時(shí)地址空間,也可以正確執行。這就是位置無(wú)關(guān)代碼,它是一段加載到任意地址空間都能正常執行的特殊代碼。
位置無(wú)關(guān)代碼常用于以下場(chǎng)合:
程序在運行期間動(dòng)態(tài)加載到內存;
程序在不同場(chǎng)合與不同程序組合后加載到內存(如共享的動(dòng)態(tài)鏈接庫);
在運行期間不同地址相互之間的映射(如Bootloader程序)。
雖然在用GCC編譯時(shí),使用-fPIC選項可為C語(yǔ)言產(chǎn)生位置無(wú)關(guān)代碼,但這并不能修正程序設計中固有的位置相關(guān)性缺陷。特別是匯編語(yǔ)言代碼,必須由程序員遵循一定的程序設計準則,才能保證程序的位置無(wú)關(guān)性。
1.2 ARM處理器的位置無(wú)關(guān)程序設計要點(diǎn)
ARM程序的位置無(wú)關(guān)可執行文件PIE(PositionIndependent Executable)包括位置無(wú)關(guān)代碼PIC和位置無(wú)關(guān)數據PID(PositionIndependent Data)兩部分。
PID主要針對可讀寫(xiě)數據段(.data段),其中保存已賦初值的全局變量。為實(shí)現其位置無(wú)關(guān)性,通常使用寄存器R9作為靜態(tài)基址寄存器,使其指向該可讀寫(xiě)段的首地址,并使用相對于基址寄存器的偏移量來(lái)對該段的變量進(jìn)行尋址。這種方法常用于為可重入程序的多個(gè)實(shí)例產(chǎn)生多個(gè)獨立的數據段。在程序設計中,一般不必考慮可讀寫(xiě)段的位置無(wú)關(guān)性,這主要是因為可讀寫(xiě)數據主要分配在SDRAM中。
PIC包括程序中的代碼和只讀數據(.text段),為保證程序能在ROM和SDRAM空間都能正確運行(如裸機狀態(tài)下的Bootloader程序),必須采用位置無(wú)關(guān)代碼程序設計。下面重點(diǎn)介紹PIC的程序設計要點(diǎn)。
PIC遵循只讀段位置無(wú)關(guān)ROPI(ReadOnly Position Independence)的ATPCS(ARMThumb Procedure Call Standard)的程序設計規范:
(1) 程序設計規范1
引用同一ROPI段或相對位置固定的另一ROPI段中的符號時(shí),必須是基于PC的符號引用,即使用相對于當前PC的偏移量來(lái)實(shí)現跳轉或進(jìn)行常量訪(fǎng)問(wèn)。
?、?位置無(wú)關(guān)的程序跳轉。在A(yíng)RM匯編程序中,使用相對跳轉指令B/BL實(shí)現程序跳轉。指令中所跳轉的目標地址用基于當前PC的偏移量來(lái)表示,與鏈接時(shí)分配給地址標號的絕對地址值無(wú)關(guān),因而代碼可以在任何位置進(jìn)行跳轉,實(shí)現位置無(wú)關(guān)性。
另外,還可使用ADR或ADRL偽指令將地址標號值讀取到PC中實(shí)現程序跳轉。這是因為ADR或ADRL等偽指令會(huì )被編譯器替換為對基于PC的地址值進(jìn)行操作,但這種方式所能讀取的地址范圍較小,并且會(huì )因地址值是否為字對齊而異?! 〉贏(yíng)RM程序中,使用LDR等指令直接將地址標號值讀取到PC中實(shí)現程序跳轉不是位置無(wú)關(guān)的。例如:
LDRPC, =main
上面的LDR匯編偽指令編譯后的結果為:
LDRPC, [PC, OFFSET_TO_LPOOL]
LPOOLDCD main
可見(jiàn),雖然LDR是把基于PC的一個(gè)存儲單元LPOOL的內容加載到PC中,但該存儲單元中保存的卻是鏈接時(shí)所決定的main函數入口的絕對地址,所以main函數實(shí)際所在的段不是位置無(wú)關(guān)。
?、?位置無(wú)關(guān)的常量訪(fǎng)問(wèn)。在應用程序中,經(jīng)常要讀寫(xiě)相關(guān)寄存器以完成必要的硬件初始化。為增強程序的可讀性,利用EQU偽指令對一些常量進(jìn)行賦值,但在訪(fǎng)問(wèn)過(guò)程中,必須實(shí)現位置無(wú)關(guān)性。下面以PXA270的GPIO初始化介紹位置無(wú)關(guān)的常量訪(fǎng)問(wèn)方法。
可見(jiàn),LDR偽指令實(shí)際上使用基于PC的偏移量來(lái)對符號常量GPIO_BASE和init_GPDR0進(jìn)行引用,因而是位置無(wú)關(guān)的。由此可以得出如下結論:使用LDR偽指令將一個(gè)常量讀取到非PC的其他通用寄存器中可實(shí)現位置無(wú)關(guān)的常量訪(fǎng)問(wèn);但將一個(gè)地址值讀取到PC中進(jìn)行程序跳轉時(shí),跳轉目標則是位置相關(guān)的。
?。?) 程序設計規范2
其他被ROPI段中的代碼引用的必須是絕對地址,或者是基于可讀寫(xiě)位置無(wú)關(guān)(RWPI)段的靜態(tài)基址寄存器的可寫(xiě)數據。
使用絕對地址只能引用被重定位到特定位置的代碼段中的符號,通過(guò)在位置無(wú)關(guān)代碼中引入絕對地址,可以讓程序跳轉到指定位置。例如,假設Bootloader的階段1將其自身代碼拷貝到鏈接時(shí)所指定的SDRAM地址空間后,當要跳轉到階段2的C程序入口時(shí),可以使用指令“LDRPC, =main”跳轉到程序在SDRAM中的main函數入口地址開(kāi)始執行。這是因為程序在編譯鏈接時(shí)給main函數分派絕對地址,系統通過(guò)將main函數的絕對地址直接賦給PC實(shí)現程序跳轉。如果使用相對跳轉指令“Bmain”,那么只會(huì )跳轉到啟動(dòng)ROM內部的main函數入口。
2 位置無(wú)關(guān)代碼在Bootloader設計中的應用
在使用GNU工具開(kāi)發(fā)Bootloader時(shí),程序在鏈接時(shí)會(huì )通過(guò)一個(gè)鏈接腳本(linker script)來(lái)設定映像文件的內存映射。一個(gè)簡(jiǎn)單的鏈接腳本結構如下:
這里不再介紹鏈接腳本的語(yǔ)法。需要指出的是,鏈接腳本中所描述的輸出段地址為虛擬地址VMA(Virtual Memory Address)。這里的“虛擬地址”僅指映像文件執行時(shí),各輸出段所重定位到相應的存儲地址空間,與內存管理無(wú)關(guān)。因此,上面的鏈接腳本實(shí)際上指定了Bootloader映像在執行時(shí),將被重定位到BOOTADDR開(kāi)始的存儲地址空間,以保證在相關(guān)位置對符號進(jìn)行正確引用,使程序正常運行。
ARM處理器復位后總是從0x0地址取第1條指令,因此只需把BOOTADDR設置為0,再把編譯后生成的可執行二進(jìn)制文件下載到ROM的0x0地址開(kāi)始的存儲空間,程序便可正常引導;但是,一旦在鏈接時(shí)指定映像文件從0x0地址開(kāi)始,那么Bootloader就只能在0x0地址開(kāi)始的ROM空間內運行,而無(wú)法拷貝到SDRAM空間運行實(shí)現快速引導。當然,對PXA270等具有MMU功能的微處理器來(lái)說(shuō),雖然可以先將Bootloader映像整個(gè)拷貝到SDRAM中,再使用MMU功能將SDRAM空間映射到0x0地址,進(jìn)而繼續在SDRAM中運行;但這樣一方面會(huì )使得Bootloader的設計與實(shí)現復雜化,另一方面在一些必須屏蔽MMU功能的應用中(例如引導armlinux系統),無(wú)法使用MMU進(jìn)行地址重映射。
利用ARM的基于位置無(wú)關(guān)的程序設計可以解決上述問(wèn)題。只需在程序鏈接時(shí),將BOOTADDR設置為SDRAM空間的地址(一般情況下利用SDRAM中最高的1 MB存儲空間作為起始地址),這樣ARM處理器上電復位后Bootloader仍然可以從地址0開(kāi)始執行,并將自身拷貝到指定的__boot_start起始的SDRAM中運行。實(shí)現上述功能的鏈接腳本所對應的啟動(dòng)代碼架構如下:
程序入口為_(kāi)start,即復位異常,所有其他異常向量都使用相對跳轉指令B來(lái)實(shí)現,以保證位置無(wú)關(guān)特性。在完成基本的硬件初始化后,利用鏈接腳本傳遞過(guò)來(lái)__boot_start和__boot_end的參數,將Bootloader映像整個(gè)拷貝到指定的SDRAM空間,并清零.bss段,初始化堆棧后,程序將main函數入口的絕對地址賦給PC,進(jìn)而跳轉到SDRAM中繼續運行。程序在跳轉到main函數之前,所有的代碼都在ROM中運行,因而必須要保證代碼的位置無(wú)關(guān)性,所以在調用初始化GPIO、存儲系統和堆棧等子程序時(shí),都使用相對跳轉指令來(lái)完成。
使用位置無(wú)關(guān)設計Bootloader程序有如下優(yōu)點(diǎn):
?、?簡(jiǎn)化設計,方便實(shí)現系統的快速引導。位置無(wú)關(guān)代碼可以避免在引導時(shí)進(jìn)行地址映射,并方便地跳轉到SDRAM中實(shí)現快速引導。
?、?實(shí)現復位處理智能化。由于位置無(wú)關(guān)代碼可以被加載到任意地址空間運行,因此其運行時(shí)的當前地址與鏈接時(shí)所指派的地址并不一定相同。利用這一特性,可以在復位處理程序中使處理器進(jìn)入SVC模式并關(guān)閉中斷后加入如下代碼,便可根據當前運行時(shí)的地址進(jìn)行不同的復位處理:
上述代碼中的ADR指令讀取的_start標號地址由指令的執行地址決定。若是從SDRAM中的Bootloader啟動(dòng),則上述比較結果相等,程序直接跳轉到clear_bss標號地址處執行,這樣可以避免存儲系統的重新初始化和Bootloader的拷貝過(guò)程;若是上電或硬件復位,程序從ROM啟動(dòng),則上述比較結果不等,程序便進(jìn)行包括系統初始化和Bootloader拷貝等過(guò)程的全面復位處理操作。
?、?便于調試。Bootloader的調試通常也是一個(gè)繁瑣的過(guò)程,使用位置無(wú)關(guān)代碼,則可以將映像文件加載到SDRAM中進(jìn)行調試,這既能真實(shí)地反映程序從ROM中進(jìn)行系統引導的情況,又可以避免頻繁燒寫(xiě)程序存儲器。
3 結論
本文所介紹的基于位置無(wú)關(guān)的程序設計是通過(guò)基于PC或基址寄存器的符號引用規范來(lái)實(shí)現的。這種方法在實(shí)際系統開(kāi)發(fā)中應用廣泛,既能用于引導程序的設計,也可用于一般的應用程序或嵌入式共享庫的開(kāi)發(fā)。而在Bootloader的設計中引入位置無(wú)關(guān)代碼,可以使程序結構更為簡(jiǎn)單清晰,并能避免地址重映射并從SDRAM進(jìn)行快速系統引導;引用位置無(wú)關(guān)的設計方法使Bootloader的復位處理功能更為靈活,還使得在SDRAM中和在ROM中進(jìn)行程序調試具有相同的效果。
參考文獻
[1] 杜春雷. ARM體系結構與編程[M]. 北京:清華大學(xué)出版社,2003.
[2] 陳文智. 嵌入式系統開(kāi)發(fā)原理與實(shí)踐[M]. 北京:清華大學(xué)出版社,2005.
[3] ARM Limited. ARM Architecture Reference Manual. 2nd ed,2000.
[4] ARM Limited. ADS Developer Guide. Release 1.2,200111.
[5] ARM Limited. ADS Assembler Guide. Release 1.2,200111.
[6] Red Hat Inc. Using ld, the GNU linker. Version 2.14.
pid控制相關(guān)文章:pid控制原理
c語(yǔ)言相關(guān)文章:c語(yǔ)言教程
評論