链接脚本(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; /*!
* \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 / ; while (uiCnt--)
{
*pBlockDest.ullPtr++ = ;
} if (uiLength & 0x4)
{
*pBlockDest.uiPtr++ = ;
} if (uiLength & 0x2)
{
*pBlockDest.usPtr++ = ;
} if (uiLength & 0x1)
{
*pBlockDest.ucPtr = ;
}
} /* 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 / ; 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() ;
PROVIDE(__clear_table = .) ;
LONG( + ADDR(.CPU2.zbss)); LONG(SIZEOF(.CPU2.zbss));
LONG( + ADDR(.CPU2.bss)); LONG(SIZEOF(.CPU2.bss));
LONG( + ADDR(.CPU1.zbss)); LONG(SIZEOF(.CPU1.zbss));
LONG( + ADDR(.CPU1.bss)); LONG(SIZEOF(.CPU1.bss));
LONG( + ADDR(.CPU0.zbss)); LONG(SIZEOF(.CPU0.zbss));
LONG( + ADDR(.CPU0.bss)); LONG(SIZEOF(.CPU0.bss));
LONG( + ADDR(.zbss)); LONG(SIZEOF(.zbss));
LONG( + ADDR(.sbss)); LONG(SIZEOF(.sbss));
LONG( + ADDR(.bss)); LONG(SIZEOF(.bss));
LONG( + ADDR(.sbss4)); LONG(SIZEOF(.sbss4));
LONG( + ADDR(.bss_emem)); LONG(SIZEOF(.bss_emem));
LONG(-); LONG(-);
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() ;
} > PMI_PSPR AT> PMU_PFLASH0 =
通过以上两步,程序启动时启动代码便会将user function自动复制到RAM中。由copy table可类推clear table的原理,较为简单,在此不做赘述。
总结:Clear_table和Copy_table是什么? Clear_table和Copy_table是链接脚本中用于初始化.bss段和.data段的RAM空间的一段语句。
链接脚本(Linker Script)用法解析(二) clear_table & copy_table的更多相关文章
- 脚本kafka-configs.sh用法解析
引用博客来自李志涛:https://www.cnblogs.com/lizherui/p/12275193.html 前言介绍 网络上针对脚本kafka-configs.sh用法,也有一些各种文章,但 ...
- [转]Linux下的lds链接脚本详解
转载自:http://linux.chinaunix.net/techdoc/beginner/2009/08/12/1129972.shtml 一. 概论 每一个链接过程都由链接脚本(lin ...
- Linux下的lds链接脚本简介
转载:http://hubingforever.blog.163.com/blog/static/171040579201192472552886/ 一. 概论 每一个链接过程都由链接脚本(lin ...
- Linux下的lds链接脚本详解【转】
转自:http://www.cnblogs.com/li-hao/p/4107964.html 转载自:http://linux.chinaunix.net/techdoc/beginner/2009 ...
- Linux下的lds链接脚本详解
1. 概论2. 基本概念3. 脚本格式4. 简单例子5. 简单脚本命令6. 对符号的赋值7. SECTIONS命令8. MEMORY命令9. PHDRS命令10. VERSION命令11. 脚本内的表 ...
- [转]Linux下的链接脚本基础
[转]http://linux.chinaunix.net/techdoc/beginner/2009/08/12/1129972.shtml 1. 前言 (1)每一个链接过程都由链接脚本(linke ...
- Linux下的lds链接脚本简介(一)
转载自:http://linux.chinaunix.net/techdoc/beginner/2009/08/12/1129972.shtml 一. 概论 每一个链接过程都由链接脚本(linker ...
- Linux下的lds链接脚本基础
转载:http://soft.chinabyte.com/os/104/12255104.shtml 今天在看uboot引导Linux部分,发现要对链接脚本深入了解,才能知道各个目标文件的内存分布 ...
- 链接脚本(Linker Script)用法解析(一) 关键字SECTIONS与MEMORY
1.MEMORY关键字用于描述一个MCU ROM和RAM的内存地址分布(Memory Map),MEMORY中所做的内存描述主要用于SECTIONS中LMA和VMA的定义. 2.SECTIONS关键字 ...
随机推荐
- 原生JS实现call,apply,bind函数
1. 前言 使用原生JS实现call和apply函数,充分了解其内部原理.call和apply都是为了解决改变this的指向.作用都相同,只是传参的方式不同.除了第一个参数外,call可以接受一个参数 ...
- 原生JS实现单向链表
1.前言 用JS实现一个简单的单向链表,并完成相关的功能 2.功能说明 push(value):从链表尾部添加一个新的节点 insertAfer(value,item):向链表中的item节点之后插入 ...
- wangkoala杂题总集(根据个人进度选更)
CQOI2014 数三角形 首先一看题,先容斥一波,求出网格内选三个点所有的情况,也就是C(n*m,3);然后抛出行里三点共线的方案数:C(n,3)*m; 同理就有列中三点共线的方案数:n*C(m,3 ...
- 可保图片不变形的object-fit
Object-fit 我们有时候浏览一些网站的时候,偶尔会遇到这种情况: 明显它喵的形变了,尤其是这种这么业余的失误,还是出现在一个专门做图片的网站上. 产生这种现象的原因是:图片写了固定的宽高,这 ...
- Linux centos5.6版本下mysql5.6主从环境安装配置
MySQL数据库支持数据库的主从复制功能,因此在集群方面具有其独特的优势,国内外大型网站架构体系中,均采用了MySQL的主从数据库配置来实现查询负载.数据库热备等功能.本人在此将如何配置实现做了个简单 ...
- Asp.net Core 系列之--4.事务、日志及错误处理
ChuanGoing 2019-11-17 这篇原本时想把事务处理.日志处理.错误处理.授权于鉴权一并介绍完的,授权和鉴权我想结合自定义权限来介绍,全部放到这里篇幅可能太长,因此权限部分将会在下篇来介 ...
- 接口测试专题(Java & jmeter & Linux基础)
以下是我和两个朋友原创文章合集,主题是接口测试,有Java接口测试案例和jmeter的案例,还有接口测试相关服务器操作基础.欢迎点赞.关注和转发. 接口测试 httpclient处理多用户同时在线 h ...
- [ISE使用] 使用ISE的过程中,遇到过的一些“软件上的问题”
1.planahead打不开了. PlanAhead替代文件rdiArgs.bat的下载链接如下: http://www.eevblog.com/forum/microcontrollers/guid ...
- nyoj 63-小猴子下落 (模拟)
63-小猴子下落 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:2 submit:5 题目描述: 有一颗二叉树,最大深度为D,且所有叶子的深度都相同 ...
- nyoj 263-精 挑 细 选 (sort(P, P+m, cmp); bool cmp(node a, node b)...)
263-精 挑 细 选 内存限制:64MB 时间限制:3000ms 特判: No 通过数:14 提交数:26 难度:1 题目描述: 小王是公司的仓库管理员,一天,他接到了这样一个任务:从仓库中找出一根 ...