U-boot链接地址的详解
1、運行地址<--->鏈接地址:他們兩個是等價的,只是兩種不同的說法。
2、加載地址<--->存儲地址:他們兩個是等價的,也是兩種不同的說法。
運行地址:程序在SRAM、SDRAM中執行時的地址。就是執行這條指令時,PC應該等于這個地址,換句話說,PC等于這個地址時,這條指令應該保存在這個地址內。
加載地址:程序保存在Nand flash中的地址。
位置無關碼:B、BL、MOV都是位置位置無關碼。
位置有關碼:LDR PC,=LABEL等類似的代碼都是位置有關碼。
先看一個鏈接腳本:
SECTIONS{
?first 0x00000000 : {head.o init.o}
?second 0xB0004000 : AT(2048){leds.o}
}
鏈接腳本將程序分為兩個段:first和second。前者由head.o和init.o組成,它的加載地址和運行地址都是0,所以在運行時不需要移動代碼,后者由leds.o組成,它的加載地址為2048,重定位地址為0xB0004000,這表明段second存放在編譯所得的映像文件的2048處,在運行前需要將它復制到地址0xB0004000(MMU映射),將編譯所得的映像文件燒入到nand flash后,head.o和init.o依次從0x00000000處存放,而leds.o存放在2048處。從nand flash啟動時,cpu收件將nand flash的前4KB復制到cpu自身的ram(steppingstone)中去,這樣leds.o存放在地址為2048處,而運行的時候需要將steppingstone中2048 - 4096的內容復制到sdram中起始地址0xB0004000處,從而使用ldr跳轉時才會正確執行下去。
許多腳本是相當的簡單的.
可能的最簡單的腳本只含有一個命令: 'SECTIONS'. 你可以使用'SECTIONS'來描述輸出文件的內存布局.
'SECTIONS'是一個功能很強大的命令. 這里這們會描述一個很簡單的使用. 讓我們假設你的程序只有代碼節,初始化過的數據節, 和未初始化過的數據節. 這些會存在于'.text','.data'和'.bss'節, 另外, 讓我們進一步假設在你的輸入文件中只有這些節.
對于這個例子, 我們說代碼應當被載入到地址'0x10000'處, 而數據應當從0x8000000處開始. 下面是一個實現這個功能的腳本:
? ? SECTIONS
? ? {
? ? ? . = 0x10000;
? ? ? .text : { *(.text) }
? ? ? . = 0x8000000;
? ? ? .data : { *(.data) }
? ? ? .bss : { *(.bss) }
? ? }
你使用關鍵字'SECTIONS'寫了這個SECTIONS命令, 后面跟有一串放在花括號中的符號賦值和輸出節描述的內容.
上例中, 在'SECTIONS'命令中的第一行是對一個特殊的符號'.'賦值, 這是一個定位計數器. 如果你沒有以其它的方式指定輸出節的地址(其他方式在后面會描述), 那地址值就會被設為定位計數器的現有值. 定位計數器然后被加上輸出節的尺寸. 在'SECTIONS'命令的開始處, 定位計數器擁有值'0'.
第二行定義一個輸出節,'.text'. 冒號是語法需要,現在可以被忽略. 節名后面的花括號中,你列出所有應當被放入到這個輸出節中的輸入節的名字. '*'是一個通配符,匹配任何文件名. 表達式'*(.text)'意思是所有的輸入文件中的'.text'輸入節.
因為當輸出節'.text'定義的時候, 定位計數器的值是'0x10000',連接器會把輸出文件中的'.text'節的地址設為'0x10000'.
余下的內容定義了輸出文件中的'.data'節和'.bss'節. 連接器會把'.data'輸出節放到地址'0x8000000'處. 連接器放好'.data'輸出節之后, 定位計數器的值是'0x8000000'加上'.data'輸出節的長度. 得到的結果是連接器會把'.bss'輸出節放到緊接'.data'節后面的位置.
連接器會通過在必要時增加定位計數器的值來保證每一個輸出節具有它所需的對齊. 在這個例子中, 為'.text'和'.data'節指定的地址會滿足對齊約束, 但是連接器可能會需要在'.data'和'.bss'節之間創建一個小的缺口.
一個典型的嵌入式系統中,bootloader代碼放在NOR Flash或NAND Flash里面,系統加電或復位后,首先運行這段代碼。通常把bootloader代碼放在NOR Flash里面,NAND Flash由于硬件原因不能隨機訪問,需要特殊的硬件支持機制。
bootloader代碼除了初始化以外就是搬運程序,即地址重定位(relocate)。我們為什么需要relocate?主要是經濟方面和速度方面的原因。經濟方面,NOR Flash和NAND Flash每兆價格相差懸殊,bootloader代碼一般在幾十到幾百K大小,而應用程序通常都很大,幾M到幾十M的大小,所以用價格低廉的NAND Flash存儲。速度方面,程序在NOR Flash里執行的速度遠遠小于在SDRAM中執行的速度,為了追求更高的速度,也需要relocate,讓程序在SDRAM里面執行。
relocate涉及到加載域(LMA)和運行域(VMA)兩個概念。加載域是程序代碼在ROM、FLASH中的排列次序及地址安排,運行域是程序運行時代碼在SRAM、SDRAM中地址安排。存儲代碼時按照加載域存放在FLASH中,運行時再從FLASH中取出代碼到RAM運行域運行,一段代碼的加載域和存儲域可以不同。(可以參考杜春雷的《arm體系結構與編程》一書的有關章節)。
以smdk2410為例,密切相關的就兩個文件夾/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中沒有設置LMA,只是設置了VMA。VMA的設置是通過頂層目錄下的config.mk文件中的LDFLAGS實現的,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代碼上電之后之所以能夠正確執行,有個很重要的原因,就是最初執行的bootloader代碼是地址無關的,即這個映象文件可以被放在內存中的任何一個地址上運行。
對于地址無關的代碼, 尋址是基于pc值的, 在pc值上+/-一個偏移值得到運行地址,如跳轉指令B。當執行完代碼搬運,就需要跳到和地址相關的地方去執行,即RAM中。一般是跳轉到一個標號,這時地址相關代碼就開始運行了,如:ldr pc,_start_armboot。
因為在bin映象生成的時候,就已經把_start_armboot這個符號和實際地址綁定在一起,當執行ldr pc,_start_armboot 語句時,程序就從在ROM中執行跳入到RAM中了,前提是進行了代碼搬移。如果沒有代碼搬運就執行ldr pc,_start_armboot,因為RAM中沒有正確的可執行代碼,程序就馬上飛掉了,所有在搬運之前不能尋址絕對地址有關代碼,必須執行代碼地址無關.
下面的代碼是從NOR Flash向SDRAM搬運的代碼:
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符號的地址(0x33F80000)。上電開始執行時,pc從0開始,所以現在r0值為0+offset,不等于_TEXT_BASE(0x33F80000)。接下來要用到鏈接時確定的符號地址_armboot_start(0x33F80044)了,把_start:0x0 (NOR Flash)里的.text、.data的代碼往SDRAM里_TEXT_BASE確定的地址: 0x33f80000搬運。s3c2410的SDRAM基地址是0x3000_0000,由于uboot支持的這個board SDRAM64M(0x3000_0000-0x3400_0000),所以把u-boot.bin搬運到內存的高端地址.然后跳到內存中執行,提高速度。
總結
以上是生活随笔為你收集整理的U-boot链接地址的详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos7 安装 oracle 11
- 下一篇: 求Sn=a+aa+aaa+aaaa+aa