痞子衡嵌入式:深扒IAR启动函数流程之段初始化函数__iar_data_init3实现
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是IAR启动函数流程里的段初始化函数__iar_data_init3实现。
本篇是 《IAR启动函数流程及其__low_level_init设计对函数重定向的影响》 一文的后续,在上篇文章里我们在 IAR 软件安装目录下找到了标准启动函数 __iar_program_start() 相关源文件,并且分析了 __iar_program_start() 函数里的全部动作。我们知道了其中负责 .data/.bss/.textrw 段初始化工作的是 __iar_data_init3() 函数,但是这个函数的具体实现并没有详细介绍,今天我们就仔细说说这个 __iar_data_init3() 函数:
- Note 1: 阅读本文前需要对 《IAR链接文件(.icf)》 有所了解。
- Note 2: 本文使用的 IAR EWARM 软件版本是 v9.10.2。
一、为什么有些段需要初始化?
《IAR链接文件(.icf)》 一文第一小节列出了 IAR 工程里定义的全部系统段(Section)名,其中 .data/.bss/.textrw 段是需要初始化的,因为这些段是链接在 RAM 里,而 RAM 上电其内容都是随机值,所以需要一段启动代码将 .data/.bss/.textrw 段所在的 RAM 区填上对应的初值(初值来自于下载了程序镜像文件的 Flash 区),然后应用程序才能正常运行。
- Note: 除了 .data/.bss/.textrw 之外,还有一些段(.noinit/CSTACK/HEAP等)也链接在 RAM 区,但这些段对初值没有依赖,所以不需要初始化。
.bss // 初值为 0 的静态/全局变量(RAM)
.data // 初值为非 0 的全局变量(RAM)
.data_init // .data 段的初值(Flash)
.textrw // __ramfunc 修饰的重定向函数实际执行区(RAM)
.textrw_init // .textrw 段的机器码存储区(Flash)
二、RW/ZI段初始化的一般实现
应用程序工程在编译链接结束后,.data/.bss/.textrw 段实际链接地址就确定了(这里指默认由 IAR 链接器自由分配具体链接地址,而不是用户在链接文件中指明具体链接地址的情况),我们知道了这些段的链接地址,就可以完成对应初始化工作(说白了,就是初值数据从 Flash 到 RAM 的拷贝工作),实际链接地址可以通过如下 IAR 链接器提供的接口来获取,具体拷贝过程可参看 《IAR下将关键函数重定向到RAM中执行的方法》 一文最后一节里的代码。
- Note: IAR 链接器为了后续初始化的方便,都是将程序中全部的全局变量紧挨着放到一块连续的 RAM 区域(.data),然后其全部初值也一一对应紧挨着放一起(.data_init,下载到一块连续的 Flash 区);对于 .textrw 的处理也类似。
#pragma section = ".data"
#pragma section = ".data_init"
#pragma section = ".bss"
#pragma section = ".textrw"
#pragma section = ".textrw_init"
uint8_t *data_ram = __section_begin(".data");
uint8_t *data_rom = __section_begin(".data_init");
uint8_t *data_rom_end = __section_end(".data_init");
uint8_t *bss_start = __section_begin(".bss");
uint8_t *bss_end = __section_end(".bss");
uint8_t *code_relocate_ram = __section_begin(".textrw");
uint8_t *code_relocate_rom = __section_begin(".textrw_init");
uint8_t *code_relocate_rom_end = __section_end(".textrw_init");
段初始化的一般实现虽然简单,但有个缺点,就是对于用户自定义 RW/ZI 段或者多个分散的 RW/ZI 段无法自动适应,需要根据实际情况不断调整代码实现。
三、__iar_data_init3() 函数实现细节
前面铺垫了这么多,终于到了围观 IAR 标准段初始化函数 __iar_data_init3() 实现的时候了,跟这个函数相关的源文件在如下路径下,核心代码在 data_init.c 文件中:
\IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\data_init.c
\IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\zero_init3.c - 存放 __iar_zero_init3 函数
\IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\copy_init3.c - 存放 __iar_copy_init3 函数
在 data_init.c 文件中有一个叫 IAR_DATA_INIT 的函数,其实它就是 __iar_data_init3,光看这个函数里的代码会让人有点摸不着头脑,因为用了 IAR 链接器里的接口及一些特殊定义,我们结合一个具体应用程序工程来讲解会更清晰。
// 在 IAR 目录 \arm\inc\c\DLib_Product.h 中宏定义
#define _DLIB_ELF_INIT_INTERFACE_VERSION 3
// 在 IAR 目录 \arm\src\lib\init\data_init.h 中的宏定义
#define IAR_DATA_INIT _GLUE(__iar_data_init, _DLIB_ELF_INIT_INTERFACE_VERSION)
#pragma section = "Region$$Table" const TABLE_MEM
void IAR_DATA_INIT(void)
{
FAddr TABLE_MEM const * pi = __section_begin("Region$$Table");
table_ptr_t pe = __section_end ("Region$$Table");
while (pi != pe)
{
init_fun_t * fun = FAddr_GetPtr(pi);
++pi;
pi = fun(pi);
}
}
我们现在随便编译一个 SDK 例程(痞子衡选择的是 \SDK_2.11.0_MIMXRT1170-EVK\boards\evkmimxrt1170\demo_apps\hello_world\cm7\iar,切到 flexspi_nor_debug build,即代码 RO 段链接在 0x30000000 开始的 Flash 区,代码 RW 段链接在 0x20000000 开始的 DTCM 区),查看其对应映射文件(.map),摘出其中跟段初始化相关的一些内容如下,初始化工作包含:利用 __iar_zero_init3 函数清零起始地址为 0x20000040 长度为 0x4c 字节的 ZI 段空间,利用 __iar_copy_init3 函数拷贝 0x40 字节 RW 段数据(从 0x300060fc 到 0x20000000):
*******************************************************************************
*** INIT TABLE
***
Address Size
------- ----
Zero (__iar_zero_init3)
1 destination range, total size 0x4c:
0x2000'0040 0x4c
Copy (__iar_copy_init3)
1 source range, total size 0x40:
0x3000'60fc 0x40
1 destination range, total size 0x40:
0x2000'0000 0x40
*******************************************************************************
*** ENTRY LIST
***
Entry Address Size Type Object
---- ------- ---- ---- ------
.iar.init_table$$Base 0x3000'63d4 -- Gb - Linker created -
.iar.init_table$$Limit 0x3000'63f8 -- Gb - Linker created -
Region$$Table$$Base 0x3000'63d4 -- Gb - Linker created -
Region$$Table$$Limit 0x3000'63f8 -- Gb - Linker created -
__iar_copy_init3 0x3000'630d 0x2c Code Gb copy_init3.o [6]
__iar_zero_init3 0x3000'613d 0x3c Code Gb zero_init3.o [6]
在映射文件里,我们知道了 Region$$Table 区域的起止地址 [0x300063d4 - 0x300063f8),打开镜像文件或者在线调试找到这段区域里的内容,你会发现段初始化工作所需的全部信息(操作函数地址、操作数据长度、操作源地址、操作目标地址)都记录在里面,其中特别注意的是涉及 Flash 区的地址都是以相对地址来存放的:
- Note:FAddr_GetPtr 函数负责地址转换,0x300063d4 地址处的值是 0xfffffd69,那么 0x300063d4 + 0xfffffd69 = 0x13000613d,保留低 32bit 即是 __iar_zero_init3 函数地址。
现在我们就很好理解 __iar_data_init3 函数里的代码了,它就是从 Region$$Table 区域里按序取出初始化工作所需的信息,并去一一执行完成段初始化的工作,这种实现方法的优点在于拓展性强,IAR 链接器可根据实际应用程序工程的链接情况自由拓展 Region$$Table 区域里的内容,而 __iar_data_init3 函数本身则不需要做任何修改。
至此,IAR启动函数流程里的段初始化函数__iar_data_init3实现痞子衡便介绍完毕了,掌声在哪里~~~
欢迎订阅
文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

痞子衡嵌入式:深扒IAR启动函数流程之段初始化函数__iar_data_init3实现的更多相关文章
- 痞子衡嵌入式:深扒IAR启动函数流程之段初始化实现中可用的压缩选项
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR启动函数流程里段初始化实现中可用的压缩选项. 接着 <IAR启动函数流程之段初始化函数__iar_data_init3实现& ...
- 痞子衡嵌入式:深扒IAR启动函数流程及其__low_level_init设计对函数重定向的影响
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR启动函数流程及其__low_level_init设计对函数重定向的影响. 上一篇文章 <IAR下RT-Thread工程自定义 ...
- 痞子衡嵌入式:在IAR开发环境下将关键函数重定向到RAM中执行的三种方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将关键函数重定向到RAM中执行的三种方法. 嵌入式项目里应用程序代码正常是放在 Flash 中执行的,但有时候也需要将 ...
- 痞子衡嵌入式:在IAR开发环境下RT-Thread工程函数重定向失效分析
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下RT-Thread工程函数重定向失效分析. 痞子衡旧文 <在IAR下将关键函数重定向到RAM中执行的方法> ...
- 痞子衡嵌入式:在IAR开发环境下将整个源文件代码重定向到任意RAM中的方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将整个源文件代码重定向到任意RAM中的方法. 痞子衡旧文 <在IAR下将关键函数重定向到RAM中执行的方法> ...
- 痞子衡嵌入式:在IAR开发环境下为工程开启CRC完整性校验功能的方法
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下为工程开启CRC完整性校验功能的方法. CRC校验在嵌入式领域里的应用非常广,比如在通信领域,CRC检验值可以作为数据 ...
- 痞子衡嵌入式:浅析IAR下调试信息输出机制之硬件UART外设
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR下调试信息输出机制之硬件UART外设. 在嵌入式世界里,输出打印信息是一种非常常用的辅助调试手段,借助打印信息,我们可以比较容易地 ...
- 痞子衡嵌入式:浅析IAR下调试信息输出机制之半主机(Semihosting)
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是IAR下调试信息输出机制之半主机(Semihosting). 在嵌入式世界里,输出打印信息是一种非常常用的辅助调试手段,借助打印信息,我 ...
- 痞子衡嵌入式:深扒i.MXRTxxx系列ROM中集成的串行NOR Flash启动SW Reset功能及其应用场合
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRTxxx系列ROM中集成的串行NOR Flash启动SW Reset功能及其应用场合. 在串行 NOR Flash 热启动过程 ...
随机推荐
- 2020.5.16-ICPC Central Europe Regional Contest 2019
A. ABB #include <bits/stdc++.h> using namespace std; #define PB push_back #define ZERO (1e-10) ...
- 02Prism WPF 入门实战 - 建项
1.概要 Prism介绍 Github: https://github.com/PrismLibrary/Prism 开发文档:https://prismlibrary.com/docs/ Prism ...
- AES解密尾部出现乱码问题
说明 在使用AES解密的时候我发现解密出来的字符串尾部一直都有乱码 解决方案 尾部字符串的ascii码就是删除位索引 具体代码: cryptor = AES.new('AES_KEY'.encode( ...
- perl打开读取文件(open)
在Perl中可以用open或者sysopen函数来打开文件进行操作,这两个函数都需要通过一个文件句柄(即文件指针)来对文件进行读写定位等操作.下面以open函数为例:1:读:open(文件句柄,&qu ...
- 项目实战:Qt文件改名工具 v1.2.0(支持递归检索,搜索:模糊匹配,前缀匹配,后缀匹配;重命名:模糊替换,前缀追加,后缀追加)
需求 在整理文件和一些其他头文件的时候,需要对其名称进行整理和修改,此工具很早就应该写了,创业后,非常忙,今天抽空写了一个顺便提供给学习. 工具和源码下载地址 本篇文章的应用包和源码包可在 ...
- 关于takin-data,你想知道的都在这里(一)启动命令篇
通过docker部署体验takin的小伙伴都应该知道,在安装部署手册中有提到:在启动surge-deploy任务前,需要将启动命令中的ip参数替换为docker容器所在宿主机的ip,很多小伙伴都在这里 ...
- nssm.exe使用方法
nssm no-sucking service manager 1. 安装服务命令 nssm install <servicename> nssm install <servicen ...
- segyio库的使用
最近在使用segyio库读取segy文件的时候默认读取总是出现问题,经过分析发现是我们通常所用的segy格式与本库的默认格式略有不同,修改参数就可以读取: 1) with segyio.open(fi ...
- C语言中都有哪些常见的数据结构你都知道几个??
上次在面试时被面试官问到学了哪些数据结构,那时简单答了栈.队列/(ㄒoㄒ)/~~其它就都想不起来了,今天有空整理了一下几种常见的数据结构,原来我们学过的数据结构有这么多~ 首先,先来回顾下C语言中常见 ...
- Python中根据时间自动创建文件夹
导语 电脑桌面文件太多查找起来比较花费时间,并且凌乱的电脑桌面也会影响工作心情,于是利用python根据时间自动建立当日文件夹,这样就可以把桌面上文件按时间进行存放. 代码实现 # _*_codi ...