nuc972 u-boot 2020移植 -u-boot.lds链接文件

GNU编译器生成的目标文件缺省为elf格式,elf文件由若干段(section)组成,如不特殊指明,由C源程序生成的目标代码中包含如下段:

  • .text(正文段)包含程序的指令代码;
  • .data(数据段)包含固定的数据,如常量、字符串;
  • .bss(未初始化数据段)包含未初始化的变量、数组等。

C++源程序生成的目标代码中还包括

  • .fini(析构函数代码)
  • .init(构造函数代码)等.
 

链接脚本的作用

链接器的任务就是将多个目标文件的.text、.data和.bss等段链接在一起,而链接脚本文件是告诉链接器从什么地址开始放置这些段.简而言之,由于一个工程中有多个.c文件,当它们生成.o文件后如何安排它们在可执行文件中的顺序,这就是链接脚本的作用。
 
OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm)    #指定输出可执行文件是elf格式,32位ARM指令,小端
OUTPUT_ARCH(arm)                    #指定输出可执行文件的平台为ARM
ENTRY(_start)                       #指定输出可执行文件的起始代码段为_start.
SECTIONS
{
    . = 0x00000000;                 #定位当前地址为0地址
    . = ALIGN(4);                   #代码以4字节对齐
    .text :                         #指定代码段:必须将start.o文件放在代码段的开始位置,其它文件可任意放
    {
        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段的结束位置
}

uboot 编译出来的第一个链接脚本就是执行 u-boot.lds 链接脚本,去掉里面无用的和没有定义的,进行分析。
    1. /* 配置头文件,自动生成的,包含芯片SOC 相关的头文件 */
    2. #include <config.h>
    3. /* 主要是做一些 32位 和64 位的适配定义 */
    4. #include <asm/psci.h>
    5. /* 输出格式为 elf32-littlearm, */
    6. OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
    7. OUTPUT_ARCH(arm) /* 输出架构为 ARM */
    8. /* 用来指定整个程序的入口地址,所谓入口地址就是整个程序的开头地址,可以认为就是整个程序的第一句指令。有点像C语言中的main。 */
    9. ENTRY(_start) /* _start 就是汇编的起始函数 */
    10. /* SECTIONS 就是整个链接脚本的指定 */
    11. SECTIONS
    12. {
    13. /* 指定程序的链接地址有2种方法:一种是在Makefile中ld的flags用-Ttext 0x20000000来指定;
    14. 第二种是在链接脚本的SECTIONS开头用.=0x20000000来指定。
    15. 两种都可以实现相同效果。这两种技巧是可以共同配合使用的,也就是说既在链接脚本中指定也在ld flags中用-Ttext来指定。两个都指定以后以-Ttext指定的为准。
    16. uboot的最终链接起始地址就是在Makefile中用-Ttext 来指定的,注意 TEXT_BASE 变量。最终来源是 Makefile 中配置对应的命令中,在make xxx_config时得到的。
    17. 若没有配置,则由此处指定*/
    18. . = 0x00000000;
    19. . = ALIGN(); /* 4字节对齐 */
    20. /* 代码段 */
    21. /* 在代码段中,必须注意文件的排列顺序,这些顺序会影响编译的时候这些 .o 文件在生成的u-boot.bin 中的排列顺序 */
    22. /* 指定必须放在前面部分的那些文件就是那些必须安排在前4KB内的文件,这些文件中的函数在前4KB会被调用。在后面第二部分(4KB之后)中调用的程序,前后顺序就无所谓了。 */
    23. .text :
    24. {
    25. /* 映像文件赋值起始地址,它在文件 arch/arm/lib/sections.c 中定义:
    26. * char __image_copy_start[0] __attribute__((section(".__image_copy_start")));*/
    27. *(.__image_copy_start)
    28. /* arch/arm/lib/vectors.S 里有一句:.section ".vectors" */
    29. /* 这里的 vectors 是让 vector.S 链接的二进制文件的开头部分 */
    30. *(.vectors)
    31. CPUDIR/start.o (.text*) /* 执行 start.S */
    32. *(.text*) /* 其他代码 */
    33. }
    34. . = ALIGN();
    35. .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } /* 只读数据段 */
    36. . = ALIGN();
    37. .data : { /* 普通数据段,即可读写数据段 */
    38. *(.data*)
    39. }
    40. . = ALIGN();
    41. . = .;
    42. /* 在u-boot的linker_list.h中通过宏定义,让编译器在编译阶段生成了一些顺序链表.u_boot_list*,链接阶段顺序存放到这个.u_boot_list节中。 */
    43. /* u-boot启动过程中,会从这个节读取模块驱动,命令行支持的命令等。 */
    44. . = ALIGN();
    45. .u_boot_list : {
    46. KEEP(*(SORT(.u_boot_list*)));
    47. }
    48. . = ALIGN();
    49. /* UEFI 段 */
    50. .__efi_runtime_start : {
    51. *(.__efi_runtime_start)
    52. }
    53. .efi_runtime : {
    54. *(efi_runtime_text)
    55. *(efi_runtime_data)
    56. }
    57. .__efi_runtime_stop : {
    58. *(.__efi_runtime_stop)
    59. }
    60. .efi_runtime_rel_start :
    61. {
    62. *(.__efi_runtime_rel_start)
    63. }
    64. .efi_runtime_rel : {
    65. *(.relefi_runtime_text)
    66. *(.relefi_runtime_data)
    67. }
    68. .efi_runtime_rel_stop :
    69. {
    70. *(.__efi_runtime_rel_stop)
    71. }
    72. /* UEFI 段结束地方 */
    73. . = ALIGN();
    74. .image_copy_end :
    75. {
    76. *(.__image_copy_end)
    77. }
    78. /* .rel_dyn* 段 */
    79. /* .rel_dyn_start,.rel.dyn和.rel_dyn_end提供了程序的重定位支持 */
    80. /* 重定位:
    81.     在老的uboot中,如果我们想要uboot启动后把自己拷贝到内存中的某个地方,只要把要拷贝的地址写给TEXT_BASE即可,
    82.     然后boot启动后就会把自己拷贝到TEXT_BASE内的地址处运行,在拷贝之前的代码都是相对的,不能出现绝对的跳转,否则会跑飞。
    83.     在新版的uboot里,TEXT_BASE的含义改变了。它表示用户要把这段代码加载到哪里,通常是通过串口等工具。
    84.     然后搬移的时候由uboot自己计算一个地址来进行搬移。
    85.     新版的uboot采用了动态链接技术,在lds文件中有__rel_dyn_start和__rel_dyn_end,这两个符号之间的区域存放着动态链接符号,
    86.     只要给这里面的符号加上一定的偏移,拷贝到内存中代码的后面相应的位置处,就可以在绝对跳转中找到正确的函数。*/
    87. .rel_dyn_start :
    88. {
    89. *(.__rel_dyn_start)
    90. }
    91. .rel.dyn : {
    92. *(.rel*)
    93. }
    94. .rel_dyn_end :
    95. {
    96. *(.__rel_dyn_end)
    97. }
    98. .end :
    99. {
    100. *(.__end)
    101. }
    102. _image_binary_end = .;
    103. /*
    104. * Deprecated: this MMU section is used by pxa at present but
    105. * should not be used by new boards/CPUs.
    106. */
    107. /* MMU 表项 */
    108. . = ALIGN();
    109. .mmutable : {
    110. *(.mmutable)
    111. }
    112. /*
    113. * Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c
    114. * __bss_base and __bss_limit are for linker only (overlay ordering)
    115. */
    116. /* bss 段,.bss节包含了程序中所有未初始化的全局变量 */
    117. /* 由链接指令(OVERLAY)可见,.bss_start与__rel_dyn_start,.bss与__bss_base,.bss_end与__bss_limit是重叠的。*/
    118. .bss_start __rel_dyn_start (OVERLAY) : {
    119. KEEP(*(.__bss_start));
    120. __bss_base = .;
    121. }
    122. .bss __bss_base (OVERLAY) : {
    123. *(.bss*)
    124. . = ALIGN();
    125. __bss_limit = .;
    126. }
    127. .bss_end __bss_limit (OVERLAY) : {
    128. KEEP(*(.__bss_end));
    129. }
    130. /* 其他段,这些节都是在编译链接时自动生成的,主要用于动态链接或调试使用: */
    131. .dynsym _image_binary_end : { *(.dynsym) } /* 动态符号表`dynamic symbol`,但与`.symtab`不同,`.dynsym`只保存动态链接相关的符号,而`.symtab`通常保存了所有的符号; */
    132. .dynbss : { *(.dynbss) } /* 动态未初始化数据表`dynamic bss`; */
    133. .dynstr : { *(.dynstr*) } /* 动态字符串表`dynamic string`,用于保存符号名的字符串表; */
    134. .dynamic : { *(.dynamic*) } /* 保存了动态链接所需要的基本信息,例如依赖哪些共享对象,动态链接符号表的位置,动态链接重定位表的位置,共享对象初始化代码的地址等; */
    135. .plt : { *(.plt*) } /* 程序连接表`Procddure Linkage Table`,是实现动态链接的必要数据; */
    136. .interp : { *(.interp*) } /* 解释器`interpreter`的缩写 */
    137. .gnu.hash : { *(.gnu.hash) }
    138. .gnu : { *(.gnu*) }
    139. .ARM.exidx : { *(.ARM.exidx*) }
    140. .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
    141. /*.gnu.hash .gnu .ARM.exidx`和`.gnu.linkonce.armexidx`是针对`arm`体系专门生成的段,用于调试时函数调用的`backtrace`,如果不需要调试,则可以不用这两段。 */
    142. }



posted @ 2020-06-19 17:19  liujunhuasd  阅读(201)  评论(0编辑  收藏  举报