可执行文件中的.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;/*!* \brief 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空间的一段语句。

转载于:https://www.cnblogs.com/uestcliming666/p/10881649.html

链接脚本(Linker Script)用法解析(二) clear_table copy_table相关推荐

  1. 链接脚本(Linker Scripts)语法和规则解析(翻译自官方手册)

    原链接:链接脚本(Linker Scripts)语法和规则解析(翻译自官方手册)_BSP-路人甲的博客-CSDN博客_链接脚本语法 为了便于与英文原文对照学习与理解(部分翻译可能不准确),本文中的每个 ...

  2. 链接脚本(Linker Script)解析

    每一个嵌入式开发工程师,在工作越来越深入后,都会慢慢接触到链接脚本,那么什么是链接脚本,我们又该如何结合实际去做自己的链接脚本呢? 一.什么是链接脚本 链接脚本实质上是一个规则文件,程序员用来只会链接 ...

  3. 【Kafka】kafka 脚本kafka-configs.sh用法解析

    1.概述 引用博客来自李志涛:https://www.cnblogs.com/lizherui/p/12275193.html 参考:https://www.cnblogs.com/lvcisco/p ...

  4. Linux下的lds链接脚本详解

    一. 概论 每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分 ...

  5. 编译链接脚本lds文件

    转载自:http://linux.chinaunix.net/techdoc/beginner/2009/08/12/1129972.shtml 一. 概论 每一个链接过程都由链接脚本(linker ...

  6. linux下lds链接脚本详解

    转载自:http://linux.chinaunix.net/techdoc/beginner/2009/08/12/1129972.shtml 一. 概论 每一个链接过程都由链接脚本(linker ...

  7. linux 链接脚本,Linux下的lds链接脚本简介(一)

    每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空 ...

  8. 链接脚本文件-Linker script file

    连接器(Linker)会将一个或多个目标文件(.0 file)整合成一个可执行文件(.hex file). 如单个源文件test.c经过编译后会生成一个目标文件test.o,test.o文件内容会细分 ...

  9. Linker Scripts3--简单的链接脚本命令2-Assigning Values to Symbols

    1.前言 本章继续讲述简单脚本命令的后半部分 2.Assigning Values to Symbols 你可以给一个符号(symbol)赋值,它会把这些定义的符号放入全局符号表(symbols ta ...

  10. Linux Kernel系列三:Kernel编译和链接中的linker script语法详解

    先要讲讲这个问题是怎么来的.(咱们在分析一个技术的时候,先要考虑它是想解决什么问题,或者学习新知识的时候,要清楚这个知识的目的是什么). 我在编译内核的时候,发现arch/arm/kernel目录下有 ...

最新文章

  1. vue 公用页面引用_vue中实现部分页面引入公共组件
  2. Java时间对比------实际时间和设置时间对比
  3. Consumer group理解深入
  4. Oracle Grid Infrastructure: Understanding Split-Brain Node Eviction (文档 ID 1546004.1)
  5. python获取计算机信息系统数据罪_使用 python 收集获取 Linux 系统主机信息
  6. html5用在什么领域,html5技术有哪些新特性(全方面了解html5特点)
  7. [转载] Python中NumPy简介及使用举例
  8. 简单解决 WIN10更新后 远程桌面提示 CredSSP加密Oracle修正的问题
  9. PHP设计模式——代理模式
  10. [洛谷P3613]睡觉困难综合征
  11. 黑马程序员-android视频播放器
  12. lingo入门教程之一 初识lingo
  13. Filenet 周报(12.02-12.08)
  14. 【远距离无线模块】WDS3及SI4338使用步骤及配置说明
  15. python美化excel_python3哪个库可以实现格式美化?
  16. 人工智能的主要优势是什么?
  17. Microsoft VBScript 编译器错误 #x27;800a0408#x27; 无效字符 高手来来来,感激不尽
  18. GWAS数据分析流程—SNP、Indel提取
  19. C语言有大约40个运算符,最常用的有这些
  20. Python代码实现md5加密

热门文章

  1. Java修改图片大小尺寸图片缩放
  2. 计算机图形学(二)——画一个三角形1.0
  3. python设置休眠时间_如何在Python中实现时间睡眠?
  4. Access restriction: The type 'BASE64Decoder' is not API (restriction on required library xxx)
  5. psql计算环比和同比
  6. UM2 3D 打印机 DIY 实践 ( 3) Marlin 固件配置篇
  7. System.InvalidOperationException: Failed to deploy distro docker-desktop......
  8. Android 7 soter,安卓首发!OPPO Find X全面支持微信人脸支付功能
  9. 2020 年 9 月程序员平均工资 14469 元,你拖后腿了没?
  10. matlab中变量类型