国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

學(xué)習(xí)筆記:Cortex-A8的重定位

2019-11-08 03:07:13
字體:
供稿:網(wǎng)友

編譯器的工作過程

編譯器主要的工作過程: 預(yù)編譯階段:本階段由預(yù)編譯器執(zhí)行,主要是把宏定義替換掉、把頭文件包含進(jìn)來、處理掉注釋。 編譯階段:本階段由編譯器執(zhí)行,是把源代碼.c、.S后綴的文件編譯成對應(yīng)的.o二進(jìn)制文件。 鏈接階段:本階段由鏈接器執(zhí)行,把.o文件中的各函數(shù)(一段代碼)按照一定規(guī)則拼接起來,生成一個可執(zhí)行文件,至此這個可執(zhí)行文件就可以被系統(tǒng)執(zhí)行了。 以上過程是必要的,當(dāng)然還有一些其他非必要的過程: strip:把可執(zhí)行程序中的符號信息拿掉,以節(jié)省空間。 objcopy:由可執(zhí)行程序生成可燒錄的鏡像文件。 ……

鏈接腳本是什么?

程序經(jīng)過編譯階段后,雖然都是一段一段的二進(jìn)制,但這些段是不同的,并且每個段都是有名字來分類的。 段的名字分為兩種:一種是編譯器鏈接器內(nèi)部定好的,不可改變的,是先天性的。另一種則是程序員自定指定的名字,當(dāng)然段的屬性和特征也是由程序員自己定的,是后天性的。 先天性的段名: 代碼段(.text):又叫文本段,代碼段其實就是函數(shù)編譯后生成的段,這是純代碼組成的,沒有變量,筆者理解為是變量的加工機(jī)器。 數(shù)據(jù)段(.data):就是聲明并定義為非0的全局變量,如:int a = 1; bss段(.bss):又叫ZI(zero initial)段,零初始化段,如,int a =0;或int a;就會放在這里,所以變量被聲明了而沒有初始化也是0。 為什么要給段起名字呢?因為有名字了鏈接器才能找到這些段并按照鏈接腳本的規(guī)則進(jìn)行排列。 所以,鏈接腳本就是個規(guī)則文件,程序員可以寫個鏈接腳本來指揮鏈接器應(yīng)該怎樣排列代碼。而且,鏈接腳本不僅僅是簡單地命令鏈接器地把段按照某個順序排列,還可以命令鏈接器把段放在自己喜歡的內(nèi)存地址上,哪個地址開始,哪個地址結(jié)束,哪個段應(yīng)該放在哪個地址。所以,鏈接器執(zhí)行完畢后,每一個段中的每一個指令都有了其對應(yīng)的地址,因為這地址是鏈接器給的,所以叫做鏈接地址。如下是一個簡單的鏈接腳本示例:

SECTIONS //表示鏈接腳本的入口{. = 0xd0024000; //. 號代表當(dāng)前位置(地址),從這里可以看出代碼是從內(nèi)存地址0xd0024000開始的 //可以看出整個程序段的排列是.text+.data+.bss.text : {start.o //把start.o的.text段作為開頭* (.text) //其他文件的.text不用管,隨意排列}.data : { * (.data) //.data段順序無要求}bss_start = .;//這里是將當(dāng)前地址賦值給bss_start標(biāo)號,這樣其他文件就可以通過bss_start 調(diào)用這個地址了.bss : {* (.bss)}bss_end = .; //同理}

但是還有個問題,就是下載程序的時候要選擇的下載地址那個才是在內(nèi)存地址中真正的開始位置。不管在鏈接的時候?qū)懥四膫€地址,真正能加載到內(nèi)存中才是王道。然而在下載時選擇的地址是可以不同的,所以,程序?qū)嶋H上運行時的地址,是下載時指定的那個地址開頭的地址,這就是運行地址,也就是程序被實際運行時的地址。

位置無關(guān)代碼和位置相關(guān)代碼

顧名思義: 位置無關(guān)代碼:代碼運行時不用在乎運行在內(nèi)存的哪個地址上,也就是任何內(nèi)存地址都可以運行。 位置相關(guān)代碼:代碼要運行在特定的內(nèi)存地址上,否則無法正常運行。 為什么會有這兩種代碼的區(qū)別呢?筆者的理解是:函數(shù)調(diào)用和變量的調(diào)用,不管是函數(shù)名還是變量名,這個名字對于編譯器來說就是用來保存地址的,變量名保存的是變量本身在內(nèi)存的地址、函數(shù)名保存的是這個代碼段(函數(shù)代碼)的開頭的地址。所以調(diào)用函數(shù)和變量實際上就是跳轉(zhuǎn)到其保存的地址指向的內(nèi)存空間中的。那么這兩者的地址是誰給的呢?很明顯是鏈接器給的。也就說函數(shù)名和變量名保存的地址是鏈接地址來的。如果程序?qū)嶋H下載到內(nèi)存的地址和鏈接地址不一樣,那么這些函數(shù)名和變量名保存的地址與函數(shù)的代碼段、變量保存在內(nèi)存中的實際地址肯定不會相同,這樣程序在調(diào)用函數(shù)或變量的時候,地址不同了,然后跑錯了地址,那個地址全是亂的,進(jìn)而導(dǎo)致程序無法正常運行。而位置無關(guān)代碼應(yīng)該是因為沒有做這些調(diào)用所以對這些地址無所謂了。 總結(jié):

地址與運行地址不相同對位置無關(guān)代碼沒有影響,對位置相關(guān)代碼有影響。這樣看來匯編更容易實現(xiàn)位置無關(guān)代碼,因為匯編是直接對寄存器和內(nèi)存操作的,并且匯編的函數(shù)調(diào)用可以用相對地址來訪問,也就是基地址加上偏移量。這應(yīng)該也是重定位代碼要在匯編里實現(xiàn)的原因之一吧。

重定位

重定位就是為了解決這個問題的:內(nèi)存中的位置相關(guān)代碼的鏈接地址和運行地址不相同。 在實際運行時,在CPU運行位置相關(guān)代碼前把整個代碼(包括位置相關(guān)代碼和位置無關(guān)代碼)復(fù)制一份完全相同的代碼,再把復(fù)制的這一份代碼放在與鏈接地址相同的內(nèi)存地址上,再讓CPU跳轉(zhuǎn)到這份復(fù)制的代碼上去繼續(xù)執(zhí)行,此時CPU就可以執(zhí)行位置相關(guān)代碼了,因為這段復(fù)制代碼的鏈接地址與內(nèi)存地址是一樣的了。以上,就是重定位的解決方案。 重定位代碼是用匯編寫的,因為對于重定位來說匯編有兩個關(guān)鍵指令:ldr偽指令與adr偽指令。

ldr偽指令與adr偽指令的區(qū)別

按照字面的意思,ldr是長加載指令,adr是短加載指令。 更直接點就是:ldr指令在加載符號地址時,加載的是鏈接地址;adr指令在加載符號地址時,加載的是運行時地址。這一點很重要,在代碼中可以用于判斷鏈接地址與運行地址是否相等以此判斷是否需要重定位,另外在復(fù)制代碼時也是通過對比兩個鏈接地址是否相同來判斷是否復(fù)制完成的。 下面講解兩個指令大致的實現(xiàn)原理: 首先看下面的匯編代碼:

_start: adr r0, _start ldr r1, =_start

adr r0,_start在反匯編中對應(yīng)的代碼為:sub r0,pc,#n  //n為偏移量 也就是adr偽指令實際上會轉(zhuǎn)變?yōu)閟ub指令代替,而這句sub指令等價于:r0=pc-n,由于pc寄存器存儲的是代碼當(dāng)前的運行地址,所以adr加載的肯定是運行地址。 而ldr r1, =_start 在反匯編中對應(yīng)的代碼為:ldr r1, [pc,#n]  //n也是偏移量。 可以看出ldr即是指令也是偽指令,這句代碼等價于下面C代碼:

int r1;int *pc;r1 = *(pc+n);  //n為偏移量

也就是說,pc中存儲的運行地址,加上偏移量后得到新地址,取新地址指向的內(nèi)存地址存儲的內(nèi)容賦給r1。而這個新地址指向的內(nèi)存地址所存儲的內(nèi)容必定是一個鏈接地址,相當(dāng)于指向了一個指針變量,所以ldr加載的是鏈接地址。為什么指向的內(nèi)容一定存儲了鏈接地址呢?因為編譯器知道,鏈接執(zhí)行完后,對于編譯器來說,雖然整段代碼段的開始位置會改變,但不管代碼重哪里開始運行,每個指令互相之間的相對位置是不會改變的。這個相對位置就決定了偏移量。 如果要對應(yīng)反匯編文件來理解的話,還要注意有ARM指令流水線的存在。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 黑水县| 香港 | 修武县| 福安市| 稷山县| 商城县| 壶关县| 兴安盟| 上林县| 赤峰市| 齐齐哈尔市| 宜丰县| 夏邑县| 诸城市| 伊金霍洛旗| 宣威市| 德钦县| 石阡县| 庆云县| 无为县| 松原市| 叶城县| 大理市| 泗阳县| 凌源市| 新乡市| 新巴尔虎右旗| 正镶白旗| 郸城县| 浏阳市| 咸阳市| 阿勒泰市| 保定市| 新源县| 新营市| 当雄县| 汉沽区| 龙游县| 邛崃市| 屏东县| 南宫市|