Skylar

无网无痕专栏

关于uboot.lds文件的分析

gcc等编译器内置有缺省的连接脚本。如果采用缺省脚本,则生成的目标代码需要操作系统才能加载运行。为了能在嵌入式系统上直接运行,需要编写自己的连接脚本文件。编写连接脚本,首先要对目标文件的格式有一定了解。GNU编译器生成的目标文件缺省为elf格式。elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:.text(正文段)包含程序的指令代码;.data(数据段)包含固定的数据,如常量、字符串;.bss(未初始化数据段)包含未初始化的变量、数组等。C++源程序生成的目标代码中还包括.fini(析构函数代码)和.init(构造函数代码)等。有关elf文件格式,读者可自行参考相关资料。连接器的任务就是将多个目标文件的.text、.data和.bss等段连接在一起,而连接脚本文件是告诉连接器从什么地址开始放置这些段。


 
先看一下GNU官方网站上对.lds文件形式的完整描述:

SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
  { contents } >region :phdr =fill
...
}

secname和contents是必须的,其他的都是可选的。下面挑几个常用的看看:1、secname:段名
2、contents:决定哪些内容放在本段,可以是整个目标文件,也可以是目标文件中的某段(代码段、数据段等)
3、start:本段连接(运行)的地址,如果没有使用AT(ldadr),本段存储的地址也是start。GNU网站上说start可以用任意一种描述地址的符号来描述。
4、AT(ldadr):定义本段存储(加载)的地址。


看一个简单的例子:(摘自《2410完全开发》)


SECTIONS {
firtst 0x00000000 : { head.o init.o }
second 0x30000000 : AT(4096) { main.o }
}

    以上,head.o放在0x00000000地址开始处,init.o放在head.o后面,他们的运行地址也是0x00000000,即连接和存储地址相同(没有AT指定);main.o放在4096(0x1000,是AT指定的,存储地址)开始处,但是它的运行地址在0x30000000,运行之前需要从0x1000(加载处)复制到0x30000000(运行处),此过程也就用到了读取Nand flash。
这就是存储地址和连接(运行)地址的不同,称为加载时域和运行时域,可以在.lds连接脚本文件中分别指定。

又如:
ENTRY(begin)
SECTION
{ .=0x00300000;
.text : { *(.text) }
.data: { *(.data) }
.bss: { *(.bss) }

  其中,ENTRY(begin)指明程序的入口点为begin标号;.=0x00300000指明目标代码的起始地址为0x00300000,这一段地址可以是SDRAM的起始地址;.text : { *(.text) }表示从0x00300000开始放置所有目标文件的代码段,随后的.data: { *(.data) }表示数据段从代码段的末尾开始,再后是.bss段。

用连接器生成最终目标文件    
   编写好的.lds文件,在用arm-linux-ld连接命令时带-T filename来调用执行,如:arm-linux-ld –T nand.lds x.o y.o –o xy.o。也可以用-T text参数直接指定连接地址,如:arm-linux-ld –T text 0x30000000 x.o y.o –o xy.o。
    例如:有了连接脚本文件,如下命令可生成最终的目标文件:
arm-linux-ld -nostadlib -o bootstrap.elf -T link.lds init.o xmrecever.o flash.o。其中,ostadlib表示不连接系统的运行库,而是直接从begin入口;-o指明目标文件的名称;-T指明采用的连接脚本文件;最后是需要连接的目标文件列表。

生成二进制代码 
  连接生成的elf文件还不能直接下载执行,通过objcopy工具可生成最终的二进制文件: arm-linux-objcopy -O binary bootstrap.elf bootstrap.bin。其中
-O binary指定生成为二进制格式文件。Objcopy还可以生成S格式的文件,只需将参数换成-O srec。如果想将生成的目标代码反汇编,还可以用objdump工具:
arm-linux-objdump -D bootstrap.elf
    至此,所生成的目标文件就可以直接写入Flash中运行了

---------------------------------------------------------------------------
下面,结合u-boot.lds看看一个正式的连接脚本文件。


OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm"")
    ;指定输出可执行文件是elf格式,32位ARM指令,小端
OUTPUT_ARCH(arm)
    ;指定输出可执行文件的平台为ARM
ENTRY(_start)
    ;指定输出可执行文件的起始代码段为_start.
SECTIONS
{
        . = 0x00000000 ; 指明目标代码的起始地址从0x0位置开始,"."代表的是当前位置
        . = ALIGN(4)   ; 代码以4字节对齐
        .text : ;指定代码段
        {
          cpu/arm920t/start.o (.text) ; 代码的第一个代码部分,指明start.s是入口程序代码,被放到代码段的开头
          *(.text) ;其它代码部分
        }
        . = ALIGN(4)
        .rodata : { *(.rodata) } ;指定只读数据段,RO段
        . = ALIGN(4);
        .data : { *(.data) }     ;指定读/写数据段,RW段
        . = ALIGN(4);
        .got : { *(.got) }       ;指定got段, got段式是uboot自定义的一个段, 非标准段
        __u_boot_cmd_start = .   ;把__u_boot_cmd_start赋值为当前位置, 即起始位置
        .u_boot_cmd : { *(.u_boot_cmd) } ;指定u_boot_cmd段, uboot把所有的uboot命令放在该段.
        __u_boot_cmd_end = .;把__u_boot_cmd_end赋值为当前位置,即结束位置
        . = ALIGN(4);
        __bss_start = .; 把__bss_start赋值为当前位置,即bss段的开始位置
        .bss : { *(.bss) }; 指定bss段
        _end = .; 把_end赋值为当前位置,即bss段的结束位置
}

====================================================================

 26 */
 27 OUTPUT_FORMAT("elf32-ntradbigmips", "elf32-ntradbigmips", "elf32-ntradbigmips")
 28 OUTPUT_ARCH(mips)
 29 ENTRY(_start)
 30 SECTIONS
 31 {
 32     . = 0x00000000;
 33
 34     . = ALIGN(4);
 35     .text       :
 36     {
 37       *(.text)
 38     }
 39
 40     . = ALIGN(4);
 41     .rodata  : { *(.rodata) }
 42
 43     . = ALIGN(4);
 44     .data  : { *(EXCLUDE_FILE (*product.o) .data) }
 45
 46     . = ALIGN(4);
 47     .sdata  : { *(.sdata) }
 48
 49     _gp = ALIGN(16);
 50
 51     . = ALIGN(16);
 52     __got_start = .;
 53     .got  : { *(.got) }
 54     __got_end = .;
 55
 56     .sdata  : { *(.sdata) }
 57
 58     . = ALIGN(32);
 59     __u_boot_cmd_start = .;
 60     .u_boot_cmd : { *(.u_boot_cmd) }
 61     __u_boot_cmd_end = .;
 62
 63     uboot_end_data = .;
 64     num_got_entries = (__got_end - __got_start) >> 2;
 65
 66     . = ALIGN(4);
 67     .sbss  : { *(.sbss) }
 68     .bss  : { *(.bss) }
 69     .data : {common/product.o (.data)}
 70     uboot_end = .;
 71    
 74    
 75     .debug          0 : { *(.debug) }
 76     .line           0 : { *(.line) }
 77    
 45
 46     . = ALIGN(4);
 47     .sdata  : { *(.sdata) }
 48
 49     _gp = ALIGN(16);
 50
 51     . = ALIGN(16);
 52     __got_start = .;
 53     .got  : { *(.got) }
 54     __got_end = .;
 55
 56     .sdata  : { *(.sdata) }
 57
 58     . = ALIGN(32);
 59     __u_boot_cmd_start = .;
 60     .u_boot_cmd : { *(.u_boot_cmd) }
 61     __u_boot_cmd_end = .;
 62
 63    uboot_end_data = .;
 64     num_got_entries = (__got_end - __got_start) >> 2;
 65
 66     . = ALIGN(4);
 67     .sbss  : { *(.sbss) }
 68     .bss  : { *(.bss) }
 69     .data : {common/product.o (.data)}
 70     uboot_end = .;
 71    
        la      t3, in_ram
        lw      t2, -12(t3)     

.word uboot_end_data
.word uboot_end
.word num_got_entries

in_ram:

==========================================================================

这段中,首先把代码段中in_ram符号 的位置,然后t3-12(3条指令的偏移),偏移后指向 代码中的 这条指令(.word uboot_end_data)的位置。然后从这个位置去一个值

LW指令的意思:load word 从这个位置,这里我理解成“把uboot_end_data这个变量的值赋给t2,这个值是在程序链接时按照uboot.lds的规则生成的。也就是上面我们说明的。”

看到这里,感觉在start.s代码中放入“ .word uboot_end_data ”其实就是为了搬移代码,因为 uboot_end_data的值是链接时根据uboot.lds生成的。所以这里 .word uboot_end_data ”的意思类似声明一个外部变量,放在这里,变量的值是以后确定的。(不知道对不对,先这样)

       

注意:

==================================================================================   

    
        addi    t0, a2, in_ram - _start
        synci  0($0) 
        j       t0
        nop

===================================================================================

这段代码中j到的地址实际是代码段中in_ram符号所指的地址,其后的指令对应了in_ram:符号后面的代码。

这些与u-boot.lds中的各种标号无关,in_ram这个标号在代码段!

in_ram符号是编译器编译uboot的时候生成的,放在uboot.bin文件的代码段,代码段的位置又是链接器根据uboot.lds文件决定的。

阅读更多
个人分类: ARM 相关
上一篇arm-linux-gcc: Command not found 解决方案
下一篇运行地址和加载地址
想对作者说点什么? 我来说一句

uboot的链接脚本的讲解

2011年12月16日 146KB 下载

三个有关质量的案例及分析

2009年01月02日 41KB 下载

关于PNG文件内部结构分析

2009年05月18日 504KB 下载

PNG文件内部结构分析(图片格式)2

2009年05月18日 151KB 下载

驾驭makefile

2012年11月07日 2.14MB 下载

ext.js核心类的介绍

2009年08月27日 12KB 下载

ANSYS的后笔筒有限元分析

2013年05月24日 197B 下载

图形图像文件综述.doc

2013年04月30日 93KB 下载

脑电信号分析的程序

2013年05月10日 2KB 下载

没有更多推荐了,返回首页

关闭
关闭