u-boot链接脚本分析

这段时间在读 u-boot makefile ,看到链接的时候,发现 make 会调用 board/Samsung/smdk6410 下的 u-boot.lds 链接脚本,于是看了点关于链接器和加载器方面的东西,所以写下来,以防以后忘了。

         在看链接脚本前,我们先来了解一些关于目标文件的知识。在我们将 c 源程序编译为可执行文件 ( ELF) 时,实际上需要先经过编译器实现预处理生成 .i 或者 .ii 文件,再由汇编器编译生成目标文件,最后由链接器将各个目标文件和各种库文件连接成可执行文件。目标文件包含如下五类信息:

         > 头信息             关于文件的整体信息,诸如代码大小,翻译成该目标文件的源文件名称和创建日期等。

         > 目标代码        :  由编译器或者汇编器产生的二进制指令和数据

         > 重定位信息 目标代码中的一个位置列表,链接器在修改目标代码的地址时会对他进行调整

         > 符号               :该模块中定义的全局符号,以及从其他模块导入的或者由链接器定义的符号

         > 调试信息        :目标代码中与链接无关但会被调试器使用到的其他信息,包括源代码文件和行号信息,本地符号,被目标代码使用的数据结构描述信息

        

         并不是所有的目标格式都包含这几类信息,一个很有用的目标格式很少或者不包含任何信息都有可能。

         下面是以前 unix 下使用比较多的 a.out 目标文件的格式 :

                                               u-boot链接脚本分析_第1张图片

 

a.out header:

a.out 的头部根据 unix 版本的不同而略有变化, BSD 中的格式如下

       int   a_magic ;        // 幻数

           int    a_text;                        // 文本段的大小

           int   a_data;                       // 初始化的数据段的大小

           int   a_bss;                         // 未初始化的数据段的大小

           int  a_syms;                     // 符号表的大小

           int   a_entry;                     // 入口点

           int   a_trsize;                     // 文本重定位段的大小

           int   a_drsize;                    // 数据重定位段的大小

 

text section:    存放程序代码

Data section:   存放数据

Bss section :   存放未初始化的数据,在镜像文件中,是不为 bss 段分配空间的,所以如果你开一个很大的全局的未初始化的数组,镜像文件的大小不会相应的变大。而只是在加载器将镜像加载进内存时,才会为 bss 段分配空间

         对于哪些变量放入 bss 段,哪些数据放入 data 段的文件的问题,经过测试发现,对于为初始化的静态数据都是放入 bss 段的,而初始化过的全局数据和静态数据是放进 data 段的,但未初始化的全局数据这既不在 bss 段中,也不在 data 段中,只是在符号表中有一项指向该全局变量 :  

         #include<stdio.h>

         Char  a_test[0x100] ;

         Char  b_test[0x100] = {1};

         Static char c_test[0x100];

         Int main(void)

         {

                   Static  char   a[0x100] ;

                   Static  char   b[0x100] = {1};

}

如以上程序中, c_test, b_test, a 中的数据是存放在 bss 段中的,而 b_test b 所分配的数据是存放在 data 段中的,用 objdump 可以分析出 .o 文件的信息:

        

 u-boot链接脚本分析_第2张图片

所以如果有朋友做裸机程序,并且用 gcc 编译时,定义未初始化的全局数组可能会出现问题,要小心使用。

 

介绍完目标文件的格式,我们开始切入正题:链接器。何为链接器呢,其实说的简单点就是把各个目标文件的各种段进行重新组合:

u-boot链接脚本分析_第3张图片


而链接脚本的作用就是程序员可以通过有限的控制命令,来指示链接器如何来进行工作:

/* 输出文件的运行环境,因为我们用的是 6410 ,是 arm 平台的 */

OUTPUT_ARCH(arm)

 

/* 程序入口点 */

ENTRY(_start)

 

/*section 命令用来设置段 */

SECTIONS

{

         /* 将当前地址定位到 0x00000000 处, . 操作符用来表示当前地址 */

         . = 0x00000000;

 

         /* 将当前地址设为 4 个字节对齐处 , 即: . = (. + arg-1)&~(arg-1)*/

         . = ALIGN(4);

 

         /* 定义 text , 定义段的格式为:

         *SECTION[ADDRESS][(TYPE)]:[AT(LMA)]

         *{

         *       .......

         *       OUTPUT-SECTION-COMMAND

         *       OUTPUT_SECTION_COMMAND

         *       .......

         * }[>REGION][AT>LMA_REGION][:PHDR:PHDR....][=FILLEXP]

  [] 内的内容通常是可选的

         *  SECTION: 段名,这个是必须的

         *  ADDRESS: 一个表达式,用来对 VMA 进行设置

         *  AT(LMA): 指定加载地址

         */

 

        /*

         * 定义 .text

         */  

.text      :
    {
    /*段由start.o,cpu_init.o,onenand_cp.o,nand_cp.o,movi.o中的.text段和div0.o中所有段组成*/
      cpu/s3c64xx/start.o    (.text)
      cpu/s3c64xx/s3c6410/cpu_init.o    (.text)
      cpu/s3c64xx/onenand_cp.o    (.text)
      cpu/s3c64xx/nand_cp.o    (.text)
      cpu/s3c64xx/movi.o (.text)
      *(.text)
      lib_arm/div0.o
    }
    /*.rodata段由所有输入文件的.rodata段组成*/
    . = ALIGN(4);
    .rodata : { *(.rodata) }

    /*.data段由所有输入文件的.data段组成*/
    . = ALIGN(4);
    .data : { *(.data) }

    /*.got段由所有输入文件的.
vi@linux  13:43:01
got段组成*/
    . = ALIGN(4);
    .got : { *(.got) }

    /*__u_boot_cmd_start的值被设置为了当前地址*/
    __u_boot_cmd_start = .;

    /*.u_boot_cmd段由所有文件的.u_boot_cmd段组成*/
    .u_boot_cmd : { *(.u_boot_cmd) }

    /*__u_boot_cmd_end变量的值被赋值为了当前地址*/
    __u_boot_cmd_end = .;

    /*.mmudata段由所有输入文件的.mmudata段组成 */
    . = ALIGN(4);
    .mmudata : { *(.mmudata) }

     = ALIGN(4);

    /*__bss_start变量被设置为了当前地址*/
    __bss_start = .;

    /*.bss段由所有输入文件的.bss段组成*/
    .bss : { *(.bss) }
    _end = .;

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