U-boot链接地址的详解
1、運(yùn)行地址<--->鏈接地址:他們兩個(gè)是等價(jià)的,只是兩種不同的說法。
2、加載地址<--->存儲(chǔ)地址:他們兩個(gè)是等價(jià)的,也是兩種不同的說法。
運(yùn)行地址:程序在SRAM、SDRAM中執(zhí)行時(shí)的地址。就是執(zhí)行這條指令時(shí),PC應(yīng)該等于這個(gè)地址,換句話說,PC等于這個(gè)地址時(shí),這條指令應(yīng)該保存在這個(gè)地址內(nèi)。
加載地址:程序保存在Nand flash中的地址。
位置無關(guān)碼:B、BL、MOV都是位置位置無關(guān)碼。
位置有關(guān)碼:LDR PC,=LABEL等類似的代碼都是位置有關(guān)碼。
先看一個(gè)鏈接腳本:
SECTIONS{
?first 0x00000000 : {head.o init.o}
?second 0xB0004000 : AT(2048){leds.o}
}
鏈接腳本將程序分為兩個(gè)段:first和second。前者由head.o和init.o組成,它的加載地址和運(yùn)行地址都是0,所以在運(yùn)行時(shí)不需要移動(dòng)代碼,后者由leds.o組成,它的加載地址為2048,重定位地址為0xB0004000,這表明段second存放在編譯所得的映像文件的2048處,在運(yùn)行前需要將它復(fù)制到地址0xB0004000(MMU映射),將編譯所得的映像文件燒入到nand flash后,head.o和init.o依次從0x00000000處存放,而leds.o存放在2048處。從nand flash啟動(dòng)時(shí),cpu收件將nand flash的前4KB復(fù)制到cpu自身的ram(steppingstone)中去,這樣leds.o存放在地址為2048處,而運(yùn)行的時(shí)候需要將steppingstone中2048 - 4096的內(nèi)容復(fù)制到sdram中起始地址0xB0004000處,從而使用ldr跳轉(zhuǎn)時(shí)才會(huì)正確執(zhí)行下去。
許多腳本是相當(dāng)?shù)暮?jiǎn)單的.
可能的最簡(jiǎn)單的腳本只含有一個(gè)命令: 'SECTIONS'. 你可以使用'SECTIONS'來描述輸出文件的內(nèi)存布局.
'SECTIONS'是一個(gè)功能很強(qiáng)大的命令. 這里這們會(huì)描述一個(gè)很簡(jiǎn)單的使用. 讓我們假設(shè)你的程序只有代碼節(jié),初始化過的數(shù)據(jù)節(jié), 和未初始化過的數(shù)據(jù)節(jié). 這些會(huì)存在于'.text','.data'和'.bss'節(jié), 另外, 讓我們進(jìn)一步假設(shè)在你的輸入文件中只有這些節(jié).
對(duì)于這個(gè)例子, 我們說代碼應(yīng)當(dāng)被載入到地址'0x10000'處, 而數(shù)據(jù)應(yīng)當(dāng)從0x8000000處開始. 下面是一個(gè)實(shí)現(xiàn)這個(gè)功能的腳本:
? ? SECTIONS
? ? {
? ? ? . = 0x10000;
? ? ? .text : { *(.text) }
? ? ? . = 0x8000000;
? ? ? .data : { *(.data) }
? ? ? .bss : { *(.bss) }
? ? }
你使用關(guān)鍵字'SECTIONS'寫了這個(gè)SECTIONS命令, 后面跟有一串放在花括號(hào)中的符號(hào)賦值和輸出節(jié)描述的內(nèi)容.
上例中, 在'SECTIONS'命令中的第一行是對(duì)一個(gè)特殊的符號(hào)'.'賦值, 這是一個(gè)定位計(jì)數(shù)器. 如果你沒有以其它的方式指定輸出節(jié)的地址(其他方式在后面會(huì)描述), 那地址值就會(huì)被設(shè)為定位計(jì)數(shù)器的現(xiàn)有值. 定位計(jì)數(shù)器然后被加上輸出節(jié)的尺寸. 在'SECTIONS'命令的開始處, 定位計(jì)數(shù)器擁有值'0'.
第二行定義一個(gè)輸出節(jié),'.text'. 冒號(hào)是語法需要,現(xiàn)在可以被忽略. 節(jié)名后面的花括號(hào)中,你列出所有應(yīng)當(dāng)被放入到這個(gè)輸出節(jié)中的輸入節(jié)的名字. '*'是一個(gè)通配符,匹配任何文件名. 表達(dá)式'*(.text)'意思是所有的輸入文件中的'.text'輸入節(jié).
因?yàn)楫?dāng)輸出節(jié)'.text'定義的時(shí)候, 定位計(jì)數(shù)器的值是'0x10000',連接器會(huì)把輸出文件中的'.text'節(jié)的地址設(shè)為'0x10000'.
余下的內(nèi)容定義了輸出文件中的'.data'節(jié)和'.bss'節(jié). 連接器會(huì)把'.data'輸出節(jié)放到地址'0x8000000'處. 連接器放好'.data'輸出節(jié)之后, 定位計(jì)數(shù)器的值是'0x8000000'加上'.data'輸出節(jié)的長(zhǎng)度. 得到的結(jié)果是連接器會(huì)把'.bss'輸出節(jié)放到緊接'.data'節(jié)后面的位置.
連接器會(huì)通過在必要時(shí)增加定位計(jì)數(shù)器的值來保證每一個(gè)輸出節(jié)具有它所需的對(duì)齊. 在這個(gè)例子中, 為'.text'和'.data'節(jié)指定的地址會(huì)滿足對(duì)齊約束, 但是連接器可能會(huì)需要在'.data'和'.bss'節(jié)之間創(chuàng)建一個(gè)小的缺口.
一個(gè)典型的嵌入式系統(tǒng)中,bootloader代碼放在NOR Flash或NAND Flash里面,系統(tǒng)加電或復(fù)位后,首先運(yùn)行這段代碼。通常把bootloader代碼放在NOR Flash里面,NAND Flash由于硬件原因不能隨機(jī)訪問,需要特殊的硬件支持機(jī)制。
bootloader代碼除了初始化以外就是搬運(yùn)程序,即地址重定位(relocate)。我們?yōu)槭裁葱枰猺elocate?主要是經(jīng)濟(jì)方面和速度方面的原因。經(jīng)濟(jì)方面,NOR Flash和NAND Flash每兆價(jià)格相差懸殊,bootloader代碼一般在幾十到幾百K大小,而應(yīng)用程序通常都很大,幾M到幾十M的大小,所以用價(jià)格低廉的NAND Flash存儲(chǔ)。速度方面,程序在NOR Flash里執(zhí)行的速度遠(yuǎn)遠(yuǎn)小于在SDRAM中執(zhí)行的速度,為了追求更高的速度,也需要relocate,讓程序在SDRAM里面執(zhí)行。
relocate涉及到加載域(LMA)和運(yùn)行域(VMA)兩個(gè)概念。加載域是程序代碼在ROM、FLASH中的排列次序及地址安排,運(yùn)行域是程序運(yùn)行時(shí)代碼在SRAM、SDRAM中地址安排。存儲(chǔ)代碼時(shí)按照加載域存放在FLASH中,運(yùn)行時(shí)再?gòu)腇LASH中取出代碼到RAM運(yùn)行域運(yùn)行,一段代碼的加載域和存儲(chǔ)域可以不同。(可以參考杜春雷的《arm體系結(jié)構(gòu)與編程》一書的有關(guān)章節(jié))。
以smdk2410為例,密切相關(guān)的就兩個(gè)文件夾/board/smdk2410和/cpu/arm920t,里面核心文件就u-boot.lds 、config.mk 、start.S。
/cpu/arm920t/u-boot.lds
????????OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
????????OUTPUT_ARCH(arm)
????????ENTRY(_start)
????????SECTIONS
????????{
????????????????. = 0x00000000; // 從0地址起始
????????. = ALIGN(4);
????????????????.text :
????????????????{
????????????????????????cpu/arm920t/start.o (.text)
????????????????????????*(.text)
????????????????}
????????. = ALIGN(4);
????????????????.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
????????. = ALIGN(4);
????????????????.data : { *(.data) }
????????. = ALIGN(4);
????????????????.got : { *(.got) }
????????. = .;
????????????????__u_boot_cmd_start = .;
????????????????.u_boot_cmd : { *(.u_boot_cmd) }
????????????????__u_boot_cmd_end = .;
????????. = ALIGN(4);
????????????????__bss_start = .;
????????????????.bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
????????????????_end = .;
????????}
連接腳本文件lds中沒有設(shè)置LMA,只是設(shè)置了VMA。VMA的設(shè)置是通過頂層目錄下的config.mk文件中的LDFLAGS實(shí)現(xiàn)的,TEXT_BASE在/board/smdk2410/config.mk中定義為0x33F80000(SDRAM地址)。
LDFLAGS += -Bstatic -T $(obj)u-boot.lds $(PLATFORM_LDFLAGS)
????????ifneq ($(TEXT_BASE),)
????????LDFLAGS += -Ttext $(TEXT_BASE)
????????endif
查看u-boot.map文件,代碼的連接地址是從0x33F80000開始的。
167 .text???????? 0x33f80000????????0x232c8
????????168????????cpu/arm920t/start.o(.text)
????????169????????.text????????????????0x33f80000????????????????0x4a0 cpu/arm920t/start.o
????????170????????????????????????????????0x33f80048????????????????_bss_start
????????171????????????????????????????????0x33f8004c????????????????_bss_end
????????172????????????????????????????????0x33f80044????????????????_armboot_start
????????173????????????????????????????????0x33f80000????????????????_start
????????174????????board/samsung/fs2410/lowlevel_init.o(.text)
????????175????????.text????????????????0x33f804a0???????? 0x64 board/samsung/fs2410/lowlevel_init.o
????????176????????????????????????????????0x33f804a4????????????????lowlevel_init
????????177????????board/samsung/fs2410/nand_read.o(.text)
????????178????????.text????????????????0x33f80504????????0xe8 board/samsung/fs2410/nand_read.o
????????179????????????????????????????????0x33f80504????????????????wait_idle
????????180????????????????????????????????0x33f80518????????????????nand_read_ll
bootloader代碼上電之后之所以能夠正確執(zhí)行,有個(gè)很重要的原因,就是最初執(zhí)行的bootloader代碼是地址無關(guān)的,即這個(gè)映象文件可以被放在內(nèi)存中的任何一個(gè)地址上運(yùn)行。
對(duì)于地址無關(guān)的代碼, 尋址是基于pc值的, 在pc值上+/-一個(gè)偏移值得到運(yùn)行地址,如跳轉(zhuǎn)指令B。當(dāng)執(zhí)行完代碼搬運(yùn),就需要跳到和地址相關(guān)的地方去執(zhí)行,即RAM中。一般是跳轉(zhuǎn)到一個(gè)標(biāo)號(hào),這時(shí)地址相關(guān)代碼就開始運(yùn)行了,如:ldr pc,_start_armboot。
因?yàn)樵赽in映象生成的時(shí)候,就已經(jīng)把_start_armboot這個(gè)符號(hào)和實(shí)際地址綁定在一起,當(dāng)執(zhí)行l(wèi)dr pc,_start_armboot 語句時(shí),程序就從在ROM中執(zhí)行跳入到RAM中了,前提是進(jìn)行了代碼搬移。如果沒有代碼搬運(yùn)就執(zhí)行l(wèi)dr pc,_start_armboot,因?yàn)镽AM中沒有正確的可執(zhí)行代碼,程序就馬上飛掉了,所有在搬運(yùn)之前不能尋址絕對(duì)地址有關(guān)代碼,必須執(zhí)行代碼地址無關(guān).
下面的代碼是從NOR Flash向SDRAM搬運(yùn)的代碼:
relocate:
????????????????adr r0, _start
????????????????ldr r1, _TEXT_BASE
????????????????cmp r0, r1
????????????????beq stack_setup
????????????????ldr r2, _armboot_start
????????????????ldr r3, _bss_start
????????????????sub r2, r3, r2
????????????????add r2, r0, r2
????????copy_loop:
????????????????ldmia r0!, {r3-r10}
????????????????stmia r1!, {r3-r10}
????????????????cmp r0, r2
????????????????ble copy_loop
注意其中的?adr r0, _start,這是一條偽指令,一般被編譯器替換為sub r0, pc,#offset ,不要理解為讀取符合表中_start符號(hào)的地址(0x33F80000)。上電開始執(zhí)行時(shí),pc從0開始,所以現(xiàn)在r0值為0+offset,不等于_TEXT_BASE(0x33F80000)。接下來要用到鏈接時(shí)確定的符號(hào)地址_armboot_start(0x33F80044)了,把_start:0x0 (NOR Flash)里的.text、.data的代碼往SDRAM里_TEXT_BASE確定的地址: 0x33f80000搬運(yùn)。s3c2410的SDRAM基地址是0x3000_0000,由于uboot支持的這個(gè)board SDRAM64M(0x3000_0000-0x3400_0000),所以把u-boot.bin搬運(yùn)到內(nèi)存的高端地址.然后跳到內(nèi)存中執(zhí)行,提高速度。
總結(jié)
以上是生活随笔為你收集整理的U-boot链接地址的详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos7 安装 oracle 11
- 下一篇: 求Sn=a+aa+aaa+aaaa+aa