新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > ARM的位置無關程序設計在Bootloader中的應用

ARM的位置無關程序設計在Bootloader中的應用

作者:黃振華 時間:2008-05-12 來源:單片機與嵌入式系統(tǒng)應用 收藏

  引言

本文引用地址:http://m.butianyuan.cn/article/82416.htm

  基于位置無關代碼(PositionIndependent Code)的程序設計在嵌入式應用系統(tǒng)開發(fā)中具有重要的作用,尤其在裸機狀態(tài)下開發(fā)程序及進行內(nèi)核初始化設計;利用位置無關的程序設計方法還可以在具體應用中用于構(gòu)建高效率動態(tài)鏈接庫,因而深入理解和熟練掌握位置無關的程序設計方法,有助于開發(fā)人員設計出結(jié)構(gòu)簡單、清晰的應用程序。本文首先介紹位置無關代碼的基本概念和實現(xiàn)原理,然后闡述基于匯編位置無關的程序設計方法和實現(xiàn)過程,最后以程序設計為例,介紹了程序設計中的作用。

  1 位置無關代碼及程序設計方法

  1.1 基本概念與實現(xiàn)原理

  應用程序必須經(jīng)過編譯、匯編和鏈接后才變成可執(zhí)行文件,在鏈接時,要對所有目標文件進行重定位(relocation),建立符號引用規(guī)則,同時為變量、函數(shù)等分配運行地址。當程序執(zhí)行時,系統(tǒng)必須把代碼加載到鏈接時所指定的地址空間,以保證程序在執(zhí)行過程中對變量、函數(shù)等符號的正確引用,使程序正常運行。在具有操作系統(tǒng)的系統(tǒng)中,重定位過程由操作系統(tǒng)自動完成。

  在設計Bootloader程序時,必須在裸機環(huán)境中進行,這時Bootloader映像文件的運行地址必須由程序員設定。通常情況下,將Bootloader程序下載到ROM的0x0地址進行啟動,而在大多數(shù)應用系統(tǒng)中,為了快速啟動,首先將Bootloader程序拷貝到SDRAM中再運行。一般情況下,這兩者的地址并不相同,程序在SDRAM中的地址重定位過程必須由程序員完成。實際上,由于Bootloader是系統(tǒng)上電后要執(zhí)行的第一段程序,Bootloader程序的拷貝和在這之前的所有工作都必須由其自身來完成,而這些指令都是在ROM中執(zhí)行的。也就是說,這些代碼即使不在鏈接時所指定的運行時地址空間,也可以正確執(zhí)行。這就是位置無關代碼,它是一段加載到任意地址空間都能正常執(zhí)行的特殊代碼。

  位置無關代碼常用于以下場合:

  程序在運行期間動態(tài)加載到內(nèi)存;
  程序在不同場合與不同程序組合后加載到內(nèi)存(如共享的動態(tài)鏈接庫);
  在運行期間不同地址相互之間的映射(如Bootloader程序)。
  雖然在用GCC編譯時,使用-f選項可為C語言產(chǎn)生位置無關代碼,但這并不能修正程序設計中固有的位置相關性缺陷。特別是匯編語言代碼,必須由程序員遵循一定的程序設計準則,才能保證程序的位置無關性。

  1.2 處理器的要點

  程序的位置無關可執(zhí)行文件PIE(PositionIndependent Executable)包括位置無關代碼和位置無關數(shù)據(jù)(PositionIndependent Data)兩部分。

  主要針對可讀寫數(shù)據(jù)段(.data段),其中保存已賦初值的全局變量。為實現(xiàn)其位置無關性,通常使用寄存器R9作為靜態(tài)基址寄存器,使其指向該可讀寫段的首地址,并使用相對于基址寄存器的偏移量來對該段的變量進行尋址。這種方法常用于為可重入程序的多個實例產(chǎn)生多個獨立的數(shù)據(jù)段。在程序設計中,一般不必考慮可讀寫段的位置無關性,這主要是因為可讀寫數(shù)據(jù)主要分配在SDRAM中。

  PIC包括程序中的代碼和只讀數(shù)據(jù)(.text段),為保證程序能在ROM和SDRAM空間都能正確運行(如裸機狀態(tài)下的Bootloader程序),必須采用位置無關代碼程序設計。下面重點介紹PIC的程序設計要點。

  PIC遵循只讀段位置無關ROPI(ReadOnly Position Independence)的ATPCS(ARMThumb Procedure Call Standard)的程序設計規(guī)范:

  (1) 程序設計規(guī)范1

  引用同一ROPI段或相對位置固定的另一ROPI段中的符號時,必須是基于PC的符號引用,即使用相對于當前PC的偏移量來實現(xiàn)跳轉(zhuǎn)或進行常量訪問。

 ?、?位置無關的程序跳轉(zhuǎn)。在ARM匯編程序中,使用相對跳轉(zhuǎn)指令B/BL實現(xiàn)程序跳轉(zhuǎn)。指令中所跳轉(zhuǎn)的目標地址用基于當前PC的偏移量來表示,與鏈接時分配給地址標號的絕對地址值無關,因而代碼可以在任何位置進行跳轉(zhuǎn),實現(xiàn)位置無關性。

  另外,還可使用ADR或ADRL偽指令將地址標號值讀取到PC中實現(xiàn)程序跳轉(zhuǎn)。這是因為ADR或ADRL等偽指令會被編譯器替換為對基于PC的地址值進行操作,但這種方式所能讀取的地址范圍較小,并且會因地址值是否為字對齊而異?! 〉贏RM程序中,使用LDR等指令直接將地址標號值讀取到PC中實現(xiàn)程序跳轉(zhuǎn)不是位置無關的。例如:

  LDRPC, =main

  上面的LDR匯編偽指令編譯后的結(jié)果為:

  LDRPC, [PC, OFFSET_TO_LPOOL]

  LPOOLDCD main

  可見,雖然LDR是把基于PC的一個存儲單元LPOOL的內(nèi)容加載到PC中,但該存儲單元中保存的卻是鏈接時所決定的main函數(shù)入口的絕對地址,所以main函數(shù)實際所在的段不是位置無關。
 
 ?、?位置無關的常量訪問。在應用程序中,經(jīng)常要讀寫相關寄存器以完成必要的硬件初始化。為增強程序的可讀性,利用EQU偽指令對一些常量進行賦值,但在訪問過程中,必須實現(xiàn)位置無關性。下面以PXA270的GPIO初始化介紹位置無關的常量訪問方法。

       

  可見,LDR偽指令實際上使用基于PC的偏移量來對符號常量GPIO_BASE和init_GPDR0進行引用,因而是位置無關的。由此可以得出如下結(jié)論:使用LDR偽指令將一個常量讀取到非PC的其他通用寄存器中可實現(xiàn)位置無關的常量訪問;但將一個地址值讀取到PC中進行程序跳轉(zhuǎn)時,跳轉(zhuǎn)目標則是位置相關的。

 ?。?) 程序設計規(guī)范2

  其他被ROPI段中的代碼引用的必須是絕對地址,或者是基于可讀寫位置無關(RWPI)段的靜態(tài)基址寄存器的可寫數(shù)據(jù)。

  使用絕對地址只能引用被重定位到特定位置的代碼段中的符號,通過在位置無關代碼中引入絕對地址,可以讓程序跳轉(zhuǎn)到指定位置。例如,假設Bootloader的階段1將其自身代碼拷貝到鏈接時所指定的SDRAM地址空間后,當要跳轉(zhuǎn)到階段2的C程序入口時,可以使用指令“LDRPC, =main”跳轉(zhuǎn)到程序在SDRAM中的main函數(shù)入口地址開始執(zhí)行。這是因為程序在編譯鏈接時給main函數(shù)分派絕對地址,系統(tǒng)通過將main函數(shù)的絕對地址直接賦給PC實現(xiàn)程序跳轉(zhuǎn)。如果使用相對跳轉(zhuǎn)指令“Bmain”,那么只會跳轉(zhuǎn)到啟動ROM內(nèi)部的main函數(shù)入口。

  2 位置無關代碼在Bootloader設計中的應用

  在使用GNU工具開發(fā)Bootloader時,程序在鏈接時會通過一個鏈接腳本(linker script)來設定映像文件的內(nèi)存映射。一個簡單的鏈接腳本結(jié)構(gòu)如下:

       

  這里不再介紹鏈接腳本的語法。需要指出的是,鏈接腳本中所描述的輸出段地址為虛擬地址VMA(Virtual Memory Address)。這里的“虛擬地址”僅指映像文件執(zhí)行時,各輸出段所重定位到相應的存儲地址空間,與內(nèi)存管理無關。因此,上面的鏈接腳本實際上指定了Bootloader映像在執(zhí)行時,將被重定位到BOOTADDR開始的存儲地址空間,以保證在相關位置對符號進行正確引用,使程序正常運行。

  ARM處理器復位后總是從0x0地址取第1條指令,因此只需把BOOTADDR設置為0,再把編譯后生成的可執(zhí)行二進制文件下載到ROM的0x0地址開始的存儲空間,程序便可正常引導;但是,一旦在鏈接時指定映像文件從0x0地址開始,那么Bootloader就只能在0x0地址開始的ROM空間內(nèi)運行,而無法拷貝到SDRAM空間運行實現(xiàn)快速引導。當然,對PXA270等具有MMU功能的微處理器來說,雖然可以先將Bootloader映像整個拷貝到SDRAM中,再使用MMU功能將SDRAM空間映射到0x0地址,進而繼續(xù)在SDRAM中運行;但這樣一方面會使得Bootloader的設計與實現(xiàn)復雜化,另一方面在一些必須屏蔽MMU功能的應用中(例如引導armlinux系統(tǒng)),無法使用MMU進行地址重映射。

  利用ARM的基于位置無關的程序設計可以解決上述問題。只需在程序鏈接時,將BOOTADDR設置為SDRAM空間的地址(一般情況下利用SDRAM中最高的1 MB存儲空間作為起始地址),這樣ARM處理器上電復位后Bootloader仍然可以從地址0開始執(zhí)行,并將自身拷貝到指定的__boot_start起始的SDRAM中運行。實現(xiàn)上述功能的鏈接腳本所對應的啟動代碼架構(gòu)如下:

       

  程序入口為_start,即復位異常,所有其他異常向量都使用相對跳轉(zhuǎn)指令B來實現(xiàn),以保證位置無關特性。在完成基本的硬件初始化后,利用鏈接腳本傳遞過來__boot_start和__boot_end的參數(shù),將Bootloader映像整個拷貝到指定的SDRAM空間,并清零.bss段,初始化堆棧后,程序?qū)ain函數(shù)入口的絕對地址賦給PC,進而跳轉(zhuǎn)到SDRAM中繼續(xù)運行。程序在跳轉(zhuǎn)到main函數(shù)之前,所有的代碼都在ROM中運行,因而必須要保證代碼的位置無關性,所以在調(diào)用初始化GPIO、存儲系統(tǒng)和堆棧等子程序時,都使用相對跳轉(zhuǎn)指令來完成。

  使用位置無關設計Bootloader程序有如下優(yōu)點:

 ?、?簡化設計,方便實現(xiàn)系統(tǒng)的快速引導。位置無關代碼可以避免在引導時進行地址映射,并方便地跳轉(zhuǎn)到SDRAM中實現(xiàn)快速引導。

 ?、?實現(xiàn)復位處理智能化。由于位置無關代碼可以被加載到任意地址空間運行,因此其運行時的當前地址與鏈接時所指派的地址并不一定相同。利用這一特性,可以在復位處理程序中使處理器進入SVC模式并關閉中斷后加入如下代碼,便可根據(jù)當前運行時的地址進行不同的復位處理:

        

  上述代碼中的ADR指令讀取的_start標號地址由指令的執(zhí)行地址決定。若是從SDRAM中的Bootloader啟動,則上述比較結(jié)果相等,程序直接跳轉(zhuǎn)到clear_bss標號地址處執(zhí)行,這樣可以避免存儲系統(tǒng)的重新初始化和Bootloader的拷貝過程;若是上電或硬件復位,程序從ROM啟動,則上述比較結(jié)果不等,程序便進行包括系統(tǒng)初始化和Bootloader拷貝等過程的全面復位處理操作。

 ?、?便于調(diào)試。Bootloader的調(diào)試通常也是一個繁瑣的過程,使用位置無關代碼,則可以將映像文件加載到SDRAM中進行調(diào)試,這既能真實地反映程序從ROM中進行系統(tǒng)引導的情況,又可以避免頻繁燒寫程序存儲器。

  3 結(jié)論

  本文所介紹的基于位置無關的程序設計是通過基于PC或基址寄存器的符號引用規(guī)范來實現(xiàn)的。這種方法在實際系統(tǒng)開發(fā)中應用廣泛,既能用于引導程序的設計,也可用于一般的應用程序或嵌入式共享庫的開發(fā)。而在Bootloader的設計中引入位置無關代碼,可以使程序結(jié)構(gòu)更為簡單清晰,并能避免地址重映射并從SDRAM進行快速系統(tǒng)引導;引用位置無關的設計方法使Bootloader的復位處理功能更為靈活,還使得在SDRAM中和在ROM中進行程序調(diào)試具有相同的效果。

  參考文獻

  [1] 杜春雷. ARM體系結(jié)構(gòu)與編程[M]. 北京:清華大學出版社,2003.

  [2] 陳文智. 嵌入式系統(tǒng)開發(fā)原理與實踐[M]. 北京:清華大學出版社,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控制相關文章:pid控制原理


c語言相關文章:c語言教程




評論


相關推薦

技術專區(qū)

關閉