姓名:張碩 學(xué)院:電子工程學(xué)院 學(xué)號(hào):19020100006
轉(zhuǎn)自:https://blog.csdn.net/qq_38612145/article/details/108272435
嵌牛導(dǎo)讀:本文主要講述了鏈接地址和運(yùn)行地址商模、鏈接腳本作用
嵌牛鼻子:嵌入式贝次、SDRAM护蝶、重定位
嵌牛提問:從源碼到可執(zhí)行程序的步驟有哪些料饥?
嵌牛正文:
? ? ? ?SDRAM和重定位
1.位置有關(guān)編碼
位置有關(guān)編碼:匯編源碼編碼成二進(jìn)制可執(zhí)行程序后和內(nèi)存地址是有關(guān)的
在設(shè)計(jì)一個(gè)程序時(shí)践盼,會(huì)給這個(gè)程序指定一個(gè)運(yùn)行地址(鏈接地址)掠兄。就是說我們?cè)诰幾g程序時(shí)其實(shí)心里是知道我們程序?qū)肀贿\(yùn)行時(shí)的地址(運(yùn)行地址)的鄙币,而且必須給編譯器鏈接器指定這個(gè)地址(鏈接地址)才行傅物。最后得到的二進(jìn)制程序理論上是和你指定的運(yùn)行地址有關(guān)的麦乞,將來這個(gè)程序被執(zhí)行時(shí)必須放在當(dāng)時(shí)編譯鏈接時(shí)給定的那個(gè)地址(鏈接地址)下才行蕴茴,否則不能運(yùn)行(就叫位置有關(guān)代碼)。
位置無關(guān)編碼(PIC姐直,position independent code):匯編源文件被編碼成二進(jìn)制可執(zhí)行程序時(shí)編碼方式與位置(內(nèi)存地址)無關(guān)倦淀。
有個(gè)別特別的指令他可以跟指定的地址(鏈接地址)沒有關(guān)系,也就是說這些代碼實(shí)際運(yùn)行時(shí)不管放在哪里都能正常運(yùn)行声畏。
對(duì)比:位置無關(guān)代碼要好一些晃听,適應(yīng)性強(qiáng),放在哪里都能正常運(yùn)行砰识;位置有關(guān)代碼就必須運(yùn)行在鏈接時(shí)指定的地址上能扒,適應(yīng)性差。位置無關(guān)碼有一些限制辫狼,不能完成所有功能初斑,有時(shí)候不得不使用位置有關(guān)代碼。
2.鏈接地址和運(yùn)行地址
對(duì)于位置有關(guān)代碼來說:最終執(zhí)行時(shí)的運(yùn)行地址和編譯鏈接時(shí)給定的鏈接地址必須相同膨处,否則一定出錯(cuò)见秤。
Makefile中用-Ttext 0x0來指定鏈接地址是0x0砂竖。這意味著我們認(rèn)為這個(gè)程序?qū)頃?huì)放在0x0這個(gè)內(nèi)存地址去運(yùn)行。但是實(shí)際上我們運(yùn)行時(shí)的地址是0xd0020010(我們用dnw下載時(shí)指定的下載地址)鹃答。這兩個(gè)地址看似不同乎澄,但是實(shí)際相同。這是因?yàn)镾5PV210內(nèi)部做了映射测摔,把SRAM映射到了0x0地址去置济。
鏈接地址:鏈接地址是由程序員在編譯鏈接的過程中,通過Makefile中-Ttext xxx或者在鏈接腳本中指定的锋八。程序員事先會(huì)預(yù)知自己的程序的執(zhí)行要求浙于,并且有一個(gè)期望的執(zhí)行地址,并且會(huì)用這個(gè)地址來做鏈接地址挟纱。
運(yùn)行地址:程序?qū)嶋H運(yùn)行時(shí)地址(指定方式:由實(shí)際運(yùn)行時(shí)被加載到內(nèi)存的哪個(gè)位置說了算)羞酗,編譯鏈接時(shí)是無法絕對(duì)確定運(yùn)行時(shí)地址的。
舉例:210中的裸機(jī)程序紊服。運(yùn)行地址由我們下載時(shí)確定檀轨,下載時(shí)下載到0xd0020010,所以就從這里開始運(yùn)行欺嗤。這個(gè)下載地址也不是隨意定的参萄,是iROM中的BL0加載BL1時(shí)事先指定好的地址,這是由CPU的設(shè)計(jì)決定的)剂府。所以理論上編譯鏈接時(shí)應(yīng)該將地址指定到0xd0020010拧揽,但是實(shí)際上我們?cè)谥奥銠C(jī)程序中都是使用位置無關(guān)碼PIC,所以鏈接地址可以是0腺占。
3. S5PV210的啟動(dòng)過程
bootloader必須小于96KB并大于16KB淤袜,假定bootloader為80KB,啟動(dòng)過程是這樣子:先開機(jī)上電后BL0運(yùn)行衰伯,BL0會(huì)加載外部啟動(dòng)設(shè)備中的bootloader的前16KB(BL1)到SRAM中去運(yùn)行铡羡,BL1運(yùn)行時(shí)會(huì)加載BL2(bootloader中80-16=64KB)到SRAM中(從SRAM的16KB處開始用)去運(yùn)行;BL2運(yùn)行時(shí)會(huì)初始化DDR并且將OS搬運(yùn)到DDR去執(zhí)行OS意鲸,啟動(dòng)完成烦周。
uboot實(shí)際使用的方式
uboot大小隨意,假定為200KB怎顾。啟動(dòng)過程是這樣子:先開機(jī)上電后BL0運(yùn)行读慎,BL0會(huì)加載外部啟動(dòng)設(shè)備中的uboot的前16KB(BL1)到SRAM中去運(yùn)行,BL1運(yùn)行時(shí)會(huì)初始化DDR槐雾,然后將整個(gè)uboot搬運(yùn)到DDR中夭委,然后用一句長(zhǎng)跳轉(zhuǎn)(從SRAM跳轉(zhuǎn)到DDR)指令從SRAM中直接跳轉(zhuǎn)到DDR中繼續(xù)執(zhí)行uboot直到uboot完全啟動(dòng)。uboot啟動(dòng)后在uboot命令行中去啟動(dòng)OS募强。
4. 為什么需要重定位
鏈接地址和運(yùn)行地址有時(shí)候必須不相同株灸,而且還不能全部用位置無關(guān)碼崇摄,這時(shí)候只能重定位。
5. 從源碼到可執(zhí)行程序的步驟
預(yù)編譯:預(yù)編譯器執(zhí)行慌烧。譬如C中的宏定義就是由預(yù)編譯器處理逐抑,注釋等也是由預(yù)編譯器處理的。
編譯:編譯器來執(zhí)行屹蚊。把源碼.c .S編成機(jī)器碼.o文件厕氨。
鏈接: 鏈接器來執(zhí)行。把.o文件中的各函數(shù)(段)按照一定規(guī)則(鏈接腳本來指定)累積在一起淑翼,形成可執(zhí)行文件腐巢。
strip: strip是把可執(zhí)行程序中的符號(hào)信息給拿掉品追,以節(jié)省空間玄括。(Debug版本和Release版本)
objcopy:由可執(zhí)行程序生成可燒錄的鏡像bin文件。
6.程序段
段就是程序的一部分肉瓦,我們把整個(gè)程序的所有東西分成了一個(gè)一個(gè)的段遭京,給每個(gè)段起個(gè)名字,然后在鏈接時(shí)就可以用這個(gè)名字來指示這些段泞莉。也就是說給段命名就是為了在鏈接腳本中用段名來讓段站在合適的位置哪雕。
段名分為2種:一種是編譯器鏈接器內(nèi)部定好的,先天性的名字鲫趁;一種是程序員自己指定的斯嚎、自定義的段名。
先天性段名:
代碼段:(.text)挨厚,又叫文本段堡僻,代碼段其實(shí)就是函數(shù)編譯后生成的東西
數(shù)據(jù)段:(.data),數(shù)據(jù)段就是C語言中有顯式初始化為非0的全局變量
bss段:(.bss)疫剃,又叫ZI(zero initial)段钉疫,就是零初始化段,對(duì)應(yīng)C語言中未初始化的全局變量巢价。
后天性段名:
段名由程序員自己定義牲阁,段的屬性和特征也由程序員自己定義。
舉例
C語言中全局變量如果未顯式初始化壤躲,值是0城菊。本質(zhì)就是C語言把這類全局變量放在了bss段,從而保證了為0碉克。
C運(yùn)行時(shí)環(huán)境如何保證顯式初始化為非0的全局變量的值在main之前就被賦值了凌唬?就是因?yàn)樗堰@類變量放在了.data段中,而.data段會(huì)在main執(zhí)行之前被處理(初始化)棉胀。
7. 鏈接腳本作用
鏈接腳本其實(shí)是個(gè)規(guī)則文件法瑟,他是程序員用來指揮鏈接器工作的冀膝。鏈接器會(huì)參考鏈接腳本,并且使用其中規(guī)定的規(guī)則來處理.o文件中那些段霎挟,將其鏈接成一個(gè)可執(zhí)行程序窝剖。
鏈接腳本的關(guān)鍵內(nèi)容有2部分:段名 + 地址(作為鏈接地址的內(nèi)存地址)。鏈接腳本的理解:
SECTIONS {} 這個(gè)是整個(gè)鏈接腳本
. 點(diǎn)號(hào)在鏈接腳本中代表當(dāng)前位置酥夭。
= 等號(hào)代表賦值
8. 重定位
重定位實(shí)際就是在運(yùn)行地址處執(zhí)行一段位置無關(guān)碼PIC赐纱,讓這段PIC(也就是重定位代碼)從運(yùn)行地址處把整個(gè)程序鏡像拷貝一份到鏈接地址處,完了之后使用一句長(zhǎng)跳轉(zhuǎn)指令從運(yùn)行地址處直接跳轉(zhuǎn)到鏈接地址處去執(zhí)行同一個(gè)函數(shù)(led_blink)熬北,這樣就實(shí)現(xiàn)了重定位之后的無縫連接疙描。
假如在SRAM中將代碼從0xd0020010重定位到0xd0024000。當(dāng)我們把代碼鏈接地址設(shè)置為0xd0024000時(shí)讶隐,實(shí)際隱含意思就是我這個(gè)代碼將來必須放在0xd0024000位置才能正確執(zhí)行起胰。如果實(shí)際運(yùn)行地址不是這個(gè)地址就要出事(除非代碼是PIC位置無關(guān)碼)。所以代碼執(zhí)行時(shí)通過代碼前段的少量位置無關(guān)碼將整個(gè)代碼搬移到0xd0024000巫延,再使用一個(gè)長(zhǎng)跳轉(zhuǎn)跳轉(zhuǎn)到0xd0024000處的代碼繼續(xù)執(zhí)行效五,重定位完成
長(zhǎng)跳轉(zhuǎn):首先這句代碼是一句跳轉(zhuǎn)指令(ARM中的跳轉(zhuǎn)指令就是類似于分支指令B、BL等作用的指令)炉峰,跳轉(zhuǎn)指令通過給PC(r15)賦一個(gè)新值來完成代碼段的跳轉(zhuǎn)執(zhí)行畏妖。長(zhǎng)跳轉(zhuǎn)指的是跳轉(zhuǎn)到的地址和當(dāng)前地址差異比較大,跳轉(zhuǎn)的范圍比較寬廣疼阔。即使用ldr pc, =led_blink這句長(zhǎng)跳轉(zhuǎn)直接從0xd0020010處代碼跳轉(zhuǎn)到0xd0024000開頭的那一份代碼的led_blink函數(shù)處去執(zhí)行戒劫。如果短跳轉(zhuǎn)bl led_blink則執(zhí)行的就是0xd0020010開頭的這一份,如果長(zhǎng)跳轉(zhuǎn)ldr pc, =led_blink則執(zhí)行的是0xd0024000開頭處的這一份)婆廊。這就是短跳轉(zhuǎn)和長(zhǎng)跳轉(zhuǎn)的區(qū)別迅细。
執(zhí)行完代碼重定位后,實(shí)際上在SRAM中有2份代碼的鏡像(一份是我們下載到0xd0020010處開頭的否彩,另一份是重定位代碼復(fù)制到0xd0024000處開頭的)疯攒,這兩份內(nèi)容完全相同,僅僅地址不同列荔。如果短跳轉(zhuǎn)bl led_blink則執(zhí)行的就是0xd0020010開頭的這一份敬尺,如果長(zhǎng)跳轉(zhuǎn)ldr pc, =led_blink則執(zhí)行的是0xd0024000開頭處的這一份)。這就是短跳轉(zhuǎn)和長(zhǎng)跳轉(zhuǎn)的區(qū)別贴浙。