• 链接脚本(Linker Script)用法解析(二) clear_table & copy_table


    可执行文件中的.bss段和.data段分别存放未赋初值的全局变量和已赋初值的全局变量,两者的特点分别为:

    (1).bss段:①无初值,所以不占ROM空间;②运行时存储于RAM;③默认初值为0

    (2).data段:①占用ROM空间,用于存放初值;②运行时存储于RAM;③程序启动时将其初值从ROM载入到RAM

    (ps:两者与.rodata及局部变量的区别:.rodata段存放只读变量即声明为static的变量,存储于ROM中;局部变量是在程序运行时才产生的,存储于栈——stack中。)

    根据.bss段的性质,需要对其执行如下操作:①通过linker script给.bss段分配RAM空间;②在启动过程中将.bss段所占RAM空间初始化——由于未赋初值的全局变量默认值为0,因此将此RAM地址段的值全部设为0

    根据.data段的性质,需要这样处理:①通过linker script给.data段分配RAM空间和ROM空间;②在启动过程中将.data段所占RAM空间初始化——将存储在ROM中的全局变量初值复制到其RAM空间从而完成全局变量的初始化

    以上工作一般在Cpu的启动过程中完成,如英飞凌TC297的启动代码如下所示:

    void _Core0_start(void)
    {
       ......
    Ifx_C_Init(); /*Initialization of C runtime variables */
       ...... /*Call main function of Cpu0 */ __non_return_call(core0_main); }

    其中,Ifx_C_Init()函数用于完成上述工作,具体实现如下:

    extern uint32 __clear_table[];  /**< clear table entry */
    extern uint32 __copy_table[];   /**< copy table entry */
    
    typedef volatile union
    {
        uint8              *ucPtr;
        uint16             *usPtr;
        uint32             *uiPtr;
        unsigned long long *ullPtr;
    } IfxStart_CTablePtr;
    
    /*!
     * rief Initializes C variables.
     *
     * This function is called in the startup. This function initialize the all variables in .data section
     * and clears the .bss section
     */
    void Ifx_C_Init(void)
    {
        IfxStart_CTablePtr pBlockDest, pBlockSrc;
        uint32             uiLength, uiCnt;
        uint32            *pTable;
        /* clear table */
        pTable = (uint32 *)&__clear_table;
    
        while (pTable)
        {
            pBlockDest.uiPtr = (uint32 *)*pTable++;
            uiLength         = *pTable++;
    
            /* we are finished when length == -1 */
            if (uiLength == 0xFFFFFFFF)
            {
                break;
            }
    
            uiCnt = uiLength / 8;
    
            while (uiCnt--)
            {
                *pBlockDest.ullPtr++ = 0;
            }
    
            if (uiLength & 0x4)
            {
                *pBlockDest.uiPtr++ = 0;
            }
    
            if (uiLength & 0x2)
            {
                *pBlockDest.usPtr++ = 0;
            }
    
            if (uiLength & 0x1)
            {
                *pBlockDest.ucPtr = 0;
            }
        }
    
        /* copy table */
        pTable = (uint32 *)&__copy_table;
    
        while (pTable)
        {
            pBlockSrc.uiPtr  = (uint32 *)*pTable++;
            pBlockDest.uiPtr = (uint32 *)*pTable++;
            uiLength         = *pTable++;
    
            /* we are finished when length == -1 */
            if (uiLength == 0xFFFFFFFF)
            {
                break;
            }
    
            uiCnt = uiLength / 8;
    
            while (uiCnt--)
            {
                *pBlockDest.ullPtr++ = *pBlockSrc.ullPtr++;
            }
    
            if (uiLength & 0x4)
            {
                *pBlockDest.uiPtr++ = *pBlockSrc.uiPtr++;
            }
    
            if (uiLength & 0x2)
            {
                *pBlockDest.usPtr++ = *pBlockSrc.usPtr++;
            }
    
            if (uiLength & 0x1)
            {
                *pBlockDest.ucPtr = *pBlockSrc.ucPtr;
            }
        }
    }

    以上代码的主要工作包括——以copy table的工作为例:(1) 获取源地址(pBlockSrc)、目标地址(pBlockDest)和长度(uLength);(2)从源地址(ROM地址)复制长度为uLength字节的数据到目标地址(RAM地址)。

    那么源地址、目标地址和长度是如何得到的呢? ——我们可以看到在Ifx_C_Init()函数之前引用了变量__copy_table作为起始地址,从而获得了源地址、目标地址和长度。

    但变量__copy_table是在哪里定义的?又是如何定义的呢?为何根据该变量可以获得地址和长度? ——问题的答案在于链接脚本(linker script)中定义的copy table(蓝色字体):

    ...................
          
           .rodata : FLAGS(arl)
        {
            *(.rodata)
            *(.rodata.*)
            *(.gnu.linkonce.r.*)    
            /*
              * Create the clear and copy tables that tell the startup code
              * which memory areas to clear and to copy, respectively.
              */
            . = ALIGN(4) ;
            PROVIDE(__clear_table = .) ;
            LONG(0 + ADDR(.CPU2.zbss));     LONG(SIZEOF(.CPU2.zbss));
            LONG(0 + ADDR(.CPU2.bss));    LONG(SIZEOF(.CPU2.bss));
            LONG(0 + ADDR(.CPU1.zbss));     LONG(SIZEOF(.CPU1.zbss));
            LONG(0 + ADDR(.CPU1.bss));    LONG(SIZEOF(.CPU1.bss));
            LONG(0 + ADDR(.CPU0.zbss));     LONG(SIZEOF(.CPU0.zbss));
            LONG(0 + ADDR(.CPU0.bss));    LONG(SIZEOF(.CPU0.bss));
            LONG(0 + ADDR(.zbss));     LONG(SIZEOF(.zbss));
            LONG(0 + ADDR(.sbss));     LONG(SIZEOF(.sbss));
            LONG(0 + ADDR(.bss));    LONG(SIZEOF(.bss));
            LONG(0 + ADDR(.sbss4));    LONG(SIZEOF(.sbss4));
            LONG(0 + ADDR(.bss_emem));    LONG(SIZEOF(.bss_emem));
            LONG(-1);                 LONG(-1);
            PROVIDE(__copy_table = .) ;
            LONG(LOADADDR(.CPU2.zdata));    LONG(0 + ADDR(.CPU2.zdata));    LONG(SIZEOF(.CPU2.zdata));
            LONG(LOADADDR(.CPU2.data));    LONG(0 + ADDR(.CPU2.data));    LONG(SIZEOF(.CPU2.data));
            LONG(LOADADDR(.CPU1.zdata));    LONG(0 + ADDR(.CPU1.zdata));    LONG(SIZEOF(.CPU1.zdata));
            LONG(LOADADDR(.CPU1.data));    LONG(0 + ADDR(.CPU1.data));    LONG(SIZEOF(.CPU1.data));
            LONG(LOADADDR(.CPU0.zdata));    LONG(0 + ADDR(.CPU0.zdata));    LONG(SIZEOF(.CPU0.zdata));
            LONG(LOADADDR(.CPU0.data));    LONG(0 + ADDR(.CPU0.data));    LONG(SIZEOF(.CPU0.data));
            LONG(LOADADDR(.zdata));    LONG(0 + ADDR(.zdata));    LONG(SIZEOF(.zdata));
            LONG(LOADADDR(.sdata));    LONG(0 + ADDR(.sdata));    LONG(SIZEOF(.sdata));
            LONG(LOADADDR(.data));    LONG(0 + ADDR(.data));    LONG(SIZEOF(.data));
            LONG(LOADADDR(.data_emem));    LONG(0 + ADDR(.data_emem));    LONG(SIZEOF(.data_emem));
            LONG(LOADADDR(.data_lmu));    LONG(0 + ADDR(.data_lmu));    LONG(SIZEOF(.data_lmu));
            LONG(LOADADDR(.sdata4));    LONG(0 + ADDR(.sdata4));    LONG(SIZEOF(.sdata4));
            LONG(LOADADDR(.CPU0.psram_text));    LONG(0 + ADDR(.CPU0.psram_text));    LONG(SIZEOF(.CPU0.psram_text));
            LONG(LOADADDR(.CPU1.psram_text));    LONG(0 + ADDR(.CPU1.psram_text));    LONG(SIZEOF(.CPU1.psram_text));
            LONG(LOADADDR(.CPU2.psram_text));    LONG(0 + ADDR(.CPU2.psram_text));    LONG(SIZEOF(.CPU2.psram_text));
            LONG(-1);                 LONG(-1);                 LONG(-1);  //load -1 as end flag of copy_table(used by startup code)
            . = ALIGN(8);
          } > pfls0

    首先定义符号常量__copy_table = .用来记录copy table的起始地址(注:链接脚本中定义的符号常量相当于一个全局变量,可以在工程中的.c和.h中使用,只需用extern引用即可);接下来定义copy table的内容——通过关键字LONG将各个.data段的VMA、LMA及段长度(单位:字节)从起始地址__copy_table开始,依序写入到ROM中。由于copy table的性质与.rodata段类似,因此该链接脚本将copy table接在.rodata段后面存储在ROM中。(注:LOADADDR可以读取段的LMA,ADDR读取段的VMA,SIZEOF读取段的长度)

    注意,高亮部分可用于将程序从ROM拷贝到RAM运行,具体做法如下:

    (1)首先用#pragma section命令将程序存储在自定义的程序段中

    #pragma section ".my_ram_fun" ax
    ... user functions
    #pragma section

     (2)在链接脚本中加载该输入段(input section)并为其输出段(output section)分配存储地址,包括LMA和VMA。注意,这里输出段的名字必须后缀为.CPU0.psram_text,这样才能与copy table结合起来。当然,copy table中的段名可以自己定义,不必非得命名为.CPU0.psram_text

    /* Allocate space for internal code sections. */
    .CPU0.psram_text :
    {
    *(.my_ram_fun)
    *(.my_ram_fun.*)
    . = ALIGN(8) ;
    } > PMI_PSPR AT> PMU_PFLASH0 =0

    通过以上两步,程序启动时启动代码便会将user function自动复制到RAM中。由copy table可类推clear table的原理,较为简单,在此不做赘述。

    总结:Clear_table和Copy_table是什么? Clear_table和Copy_table是链接脚本中用于初始化.bss段和.data段的RAM空间的一段语句。

  • 相关阅读:
    Java-集合类源码List篇(二)
    规范输入
    Java 构造方法
    c语言函数实现交换两个数的值
    2015北京宇信易诚科技面试题
    Java中的访问控制
    2015大连华信校园招聘面试题--堆栈
    2015大连华信面试题OSI七层模型
    2015大连华信面试题二叉树、遍历
    2015大连华信面试题MVC框架
  • 原文地址:https://www.cnblogs.com/uestcliming666/p/10881649.html
Copyright © 2020-2023  润新知