u-boot链接分析

作者:于连庆,华清远见嵌入式培训中心讲师。

一个典型的嵌入式系统中,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涉及到加载域(VMA)和运行域(LMA)两个概念。加载域是程序代码在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搬运到内存的高端地址.然后跳到内存中执行,提高速度。
嵌入式及3G相关资源及学习请点击:嵌入式开发视频 android开发视频 android培训 3G培训 QT培训 QT开发视频 物联网培训 物联网技术视频 嵌入式学习   

你可能感兴趣的:(u-boot链接分析)