浅析 Keil 中的 sct 文件

news2024/9/29 18:22:40

目录

  • 一、程序的存储与运行
    • 1、存储
    • 2、加载、运行
  • 二、sct 分散加载文件
    • 1、简介
    • 2、文件格式
      • 2.1 加载域
      • 2.2 执行域
      • 2.3 输入节区描述
    • 3、配置 sct 文件


一、程序的存储与运行

1、存储

程序编译后,应用程序中所有具有同一性质的数据(包括代码)被归到一个域,程序在存储或运行的时候,不同的域会呈现不同的状态,这些域的意义如下:

  • Code:即代码域,它指的是编译器生成的机器指令,这些内容被存储到 ROM 区。
  • RO-dataRead Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在 ROM 区,因而程序不能修改其内容。
    • 例如 C 语言中 const 关键字定义的变量就是典型的 RO-data。
  • RW-data:Read Write data,即可读写数据域,它指初始化为"非0值"的可读写数据,程序刚运行时,这些数据具有非0的初始值,且运行的时候它们会常驻在RAM区,因而应用程序可以修改其内容。
    • 例如 C 语言中使用定义的全局变量,且定义时赋予"非 0 值"给该变量进行初始化。
  • ZI-dataZero Initialie data,即 0 初始化数据,它指初始化为"0 值"的可读写数据域,它与 RW-data 的区别是程序刚运行时这些数据初始值全都为 0,而后续运行过程与 RW-data 的性质一样,它们也常驻在 RAM 区,因而应用程序可以更改其内容。
    • 例如 C 语言中使用定义的全局变量,且定义时赋予"0 值"给该变量进行初始化
    • 若定义该变量时没有赋予初始值,编译器会把它当 ZI-data 来对待,初始化为 0;
  • ZI-data 的栈空间(Stack)及堆空间(Heap):
    • 在 C 语言中,函数内部定义的局部变量属于栈空间,进入函数的时候从向栈空间申请内存给局部变量,退出时释放局部变量,归还内存空间。
    • 使用 malloc 动态分配的变量属于堆空间。
    • 在程序中的栈空间和堆空间都是属于 ZI-data 区域的,这些空间都会被初始值化为 0 值。编译器给出的 ZI-data 占用的空间值中包含了堆栈的大小(经实际测试,若程序中完全没有使用 malloc 动态申请堆空间,编译器会优化,不把堆空间计算在内)。

详细内容可以参考如下文章:
STM32 map 文件浅析 、单片机内存区域划分

总结如下:

程序组件所属类别
机器代码指令Code
常量RO-data
初值非0的全局变量RW-data
初值为0的全局变量ZI-data
局部变量ZI-data 栈空间
使用malloc动态分配的空间ZI-data 堆空间

2、加载、运行

RW-dataZI-data 它们仅仅是初始值不一样而已,为什么编译器非要把它们区分开?原因如下:

应用程序具有静止状态和运行状态。静止态的程序被存储在非易失存储器中,如 STM32 的内部 FLASH,因而系统掉电后也能正常保存。但是当程序在运行状态的时候,程序常常需要修改一些暂存数据,由于运行速度的要求,这些数据往往存放在内存中(RAM),掉电后这些数据会丢失。因此,程序在静止与运行的时候它在存储器中的表现是不一样的,见下图。


程序在存储状态时,RO sectionRW Section 都被保存在 ROM 区。当程序开始运行时,内核直接从 ROM 中读取代码,并且在执行主体代码前,会先执行一段加载代码,它把 RW Section 数据从 ROM 复制到 RAM,并且在 RAM 加入 ZI SectionZI Section 的数据都被初始化为 0。加载完后 RAM 区准备完毕,正式开始执行主体程序。

编译生成的 RW-data 的数据属于图中的 RW SectionZI-data 的数据属于图中的 ZI Section。是否需要掉电保存,这就是把 RW-dataZI-data 区别开来的原因:
- 因为在 RAM 创建数据的时候,默认值为 0,
- 但如果有的数据要求初值非 0,那就需要使用 ROM 记录该初始值,运行时再复制到 RAM

STM32 的 RO 区域不需要加载到 SRAM,内核直接从 FLASH 读取指令运行。计算机系统的应用程序运行过程很类似,不过计算机系统的程序在存储状态时位于硬盘,执行的时候甚至会把上述的 RO 区域(代码、只读数据)加载到内存,加快运行速度,还有虚拟内存管理单元(MMU)辅助加载数据,使得可以运行比物理内存还大的应用程序。而 STM32 没有 MMU,所以无法支持 Linux 系统。

当程序存储到 STM32 芯片的内部 FLASH 时(即 ROM 区),它占用的空间是 CodeRO-dataRW-data 的总和,所以如果这些内容比STM32 芯片的 FLASH 空间大,程序就无法被正常保存了。当程序在执行的时候,需要占用内部 SRAM 空间(即 RAM 区),占用的空间包括RW-dataZI-data。应用程序在各个状态时各区域的组成见下表。

程序状态与区域组成
程序执行时的只读区域(RO)Code + RO data
程序执行时的可读写区域(RW)RW data + ZI data
程序存储时占用的ROM区Code + RO data + RW data

而这些区域的起始地址和大小,以及各个函数变量应该放在哪个存储器区域中就是由本文要讲的 sct 文件定义的。

二、sct 分散加载文件

1、简介

当工程按默认配置构建时,MDK 会根据我们选择的芯片型号,获知芯片的内部 FLASH 及内部 SRAM 存储器概况,生成一个以工程名命名的后缀为 *.sct 的分散加载文件(Linker Control Filescatter loading),链接器根据该文件的配置分配各个节区地址,生成分散加载代码,因此我们通过修改该文件可以定制具体节区的存储位置。

  • 可以设置源文件中定义的所有变量自动按地址分配到外部 SDRAM,这样就不需要再使用关键字 __attribute__ 按具体地址来指定了;
  • 利用它还可以控制代码的加载区与执行区的位置,例如可以把程序代码存储到单位容量价格便宜的 NAND-FLASH 中,但在 NAND-FLASH 中的代码是不能像内部 FLASH 的代码那样直接提供给内核运行的,这时可通过修改分散加载文件,把代码加载区设定为 NAND-FLASH 的程序位置,而程序的执行区设定为 SDRAM 中的位置,这样链接器就会生成一个配套的分散加载代码,该代码会把 NAND-FLASH 中的代码加载到 SDRAM 中,内核再从 SDRAM 中运行主体代码,大部分运行 Linux 系统的代码都是这样加载的。

2、文件格式

下面是一个由 MDK 默认生成的 sct 文件:

我使用的是 STM32F407,不同的芯片型号内存不一样

LR_IROM1 0x08000000 0x00080000  {    ; load region size_region(加载域,基地址空间大小)
  ER_IROM1 0x08000000 0x00040000  {  ; load address = execution address(加载地址 = 执行地址)
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00020000  {  ; RW data(可读写数据)
   .ANY (+RW +ZI)
  }                             
}

在默认的 sct 文件配置中仅分配了 CodeRO-dataRW-dataZI-data 这些大区域的地址,链接时各个节区(函数、变量等)直接根据属性排列到具体的地址空间。

sct 文件中主要包含描述加载域及执行域的部分,一个文件中可包含有多个加载域,而一个加载域可由多个部分的执行域组成。同等级的域之间使用花括号"{}“分隔开,最外层的是加载域,第二层”{}"内的是执行域,其整体结构见下图。

2.1 加载域

sct 文件的加载域格式如下:

加载域名 (基地址 | ("+"地址偏移)) [属性列表] [最大容量]
"{"
	执行区域描述+
"}"

本例中为:
LR_IROM1 0x08000000 0x00080000  {
	...
}
  • 加载域名: 在 map 文件中的描述会使用名称 LR_IROM1 来标识空间。
  • 基地址 + 地址偏移:基地址为 STM32 内部 FLASH 的基地址 0x08000000,地址偏移可选
  • 属性列表: 属性列表说明了加载域的是否为绝对地址 N 字节对齐等属性。本例中没有描述加载域的属性。
  • 最大容量: 最大容量说明了这个加载域可使用的最大空间,该配置也是可选的,如果加上这个配置后,当链接器发现工程要分配到该区域的空间比容量还大,它会在工程构建过程给出提示。STM32 内部 FLASH 的大小 0x00080000(512KB)

2.2 执行域

执行域名 (基地址 | "+"地址偏移) [属性列表] [最大容量 ]
"{"
	输入节区描述
"}"

本例中为:
ER_IROM1 0x08000000 0x00040000  {  ; load address = execution address(加载地址 = 执行地址)
   ...
}
RW_IRAM1 0x20000000 0x00020000  {  ; RW data(可读写数据)
	...
}    

执行域的格式与加载域是类似的,区别只是输入节区的描述有所不同。

本例中包含了 ER_IROM1RW_IRAM1 两个执行域,它们分别对应描述了 STM32 的内部 FLASH 及内部 SRAM 的基地址及空间大小。而它们内部的“输入节区描述”说明了哪些节区要存储到这些空间,链接器会根据它来处理编排这些节区。

2.3 输入节区描述

模块选择样式 “(“输入节区样式”,” “+“输入节区属性”)”
模块选择样式 “(“输入节区样式”,” “+“节区特性”)”

模块选择样式 “(“输入符号样式”,” “+“输入节区属性”)”
模块选择样式 “(“输入符号样式”,” “+“节区特性”)”

本例中为:
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
.ANY (+RW +ZI)
  • 模块选择样式: 模块选择样式可用于选择 o 及 lib 目标文件作为输入节区,它可以直接使用目标文件名或“*”通配符,也可以使用“.ANY”。
    • 使用语句“.o”可以选择所有 o 文件,使用“.lib”可以选择所有 lib 文件,使用“*”或“.ANY”可以选择所有的 o 文件及 lib 文件。
    • 其中“.ANY”选择语句的优先级是最低的,所有其它选择语句选择完剩下的数据才会被“.ANY”语句选中。
  • 输入节区样式: 通过输入节区样式可以选择要控制的节区。“(RESET, +First)” 语句的 RESET 就是输入节区样式,它选择 RESET 的节区,并使用后面介绍的节区特性控制字“+First”表示它要存储到本区域的第一个地址。
  • (InRoot$$Sections)” 是一个链接器支持的特殊选择符号,它可以选择所有标准库里要求存储到 root 区域的节区。
  • 输入符号样式: 可以选择要控制的符号,符号样式需要使用“:gdef:”来修饰。例如可以使用“*(:gdef:Value_Test)”来控制选择符号“Value_Test”。
  • 输入节区属性: 通过在模块选择样式后面加入输入节区属性,可以选择样式中不同的内容,每个节区属性描述符前要写一个“+”号,使用空格或“,”号分隔开,可以使用的节区属性描述符见下表。
节区属性描述符说明
RO-CODE、CODE只读代码段
RO-DATA、CONST只读数据段
RO及TEXT包括 RO-CODE 和 RO-DATA
RW-DATA可读写数据段
RW-CODE可读写代码段
RW、DATA包括 RW-DATA 和 RW-CODE
ZI及BSS初始化为 0 的可读写数据段
XO只可执行的区域
ENTRY节区的入口点

例如,示例文件中使用".ANY(+RO)“选择剩余所有节区 RO 属性的内容都分配到执行域 ER_IROM1 中,使用”.ANY(+RW +ZI)"选择剩余所有节区 RW 及 ZI 属性的内容都分配到执行域 RW_IRAM1中。

  • 节区特性:节区特性可以使用"+FIRST"或"+LAST"选项配置它要存储到的位置,FIRST 存储到区域的头部,LAST 存储到尾部。通常重要的节区会放在头部,而 CheckSum(校验和)之类的数据会放在尾部。
    • 例如示例文件中使用"(RESET,+First)"选择了 RESET 节区,并要求把它放置到本区域第一个位置,而 RESET 是工程启动代码中定义的向量表,该向量表中定义的堆栈顶和复位向量指针必须要存储在内部 FLASH 的前两个地址,这样 STM32 才能正常启动(详见 STM32 芯片启动过程),所以必须使用 FIRST 控制它们存储到首地址。

总的来说,我们的 sct 示例文件配置如下:

  • 程序的加载域为内部 FLASH 的 0x08000000,最大空间为 0x00100000;程
  • 序的执行基地址与加载基地址相同,其中 RESET 节区定义的向量表要存储在内部 FLASH 的首地址,且所有 o 文件及 lib 文件的 RO 属性内容都存储在内部 FLASH 中;
  • 程序执行时 RW 及 ZI 区域都存储在以 0x20000000 为基地址,大小为 0x00020000 的空间。

链接器根据 sct 文件链接,链接后各个节区、符号的具体地址信息可以在 map 文件中查看。

3、配置 sct 文件

通过 Use Memory Layout from Target Dialog 选项可以选择是使用 MDK 生成还是使用用户自定义的 sct 文件。

取消选择后,即可自己设置 sct 文件,点击下面的 Edit 即可编辑 sct 文件:

可以看到,其地址、大小和如下设置是对应的:

现在尝试分配一个变量到 RAM 中:

uint32_t gTest __attribute__((section(".my_data")));

int main(void)
{
	...
	gTest = 10;
	printf("Value gTest == %d is in: %p\r\n", gTest, &gTest);

	...
}

sct 文件修改如下:

LR_IROM1 0x08000000 0x00080000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00040000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  MY_DATA 0x20000000 0x00005000   {
    .ANY(my_section)
  }
  RW_IRAM1 0x20010000 0x00010000  {  ; RW data
   .ANY (+RW +ZI)
  }                              
}

最终打印出来的结果为:

map 文件:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2177521.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java必修课——Spring框架

目录 一、Spring框架概述二、IOC概念和原理2.1、什么是IOC2.2、IOC接口 三、深入理解Java基础中的集合框架3.1、Collection3.2、Map3.3、集合工具类 四、练习写一个SpringMVC框架1、介绍2、程序实践3、总结 五、Java开发者必备10大数据工具和框架 一、Spring框架概述 Spring是…

从追随者到领跑者:suker书客如何粉碎技术障碍,成就行业传奇

护眼灯从小众需求逐渐走向了大众消费,一度引来许多品牌的相继入场,以吸顶灯、吊灯、台灯和落地灯等各大护眼品类为代表,均在悄然替代普通的台灯,这同时也揭示了关于护眼灯的发展潜力。 一些企业看到护眼灯市场前景后,…

煤矿厂智能化可视化:提升安全与效率

运用图扑可视化技术对煤矿厂进行实时监控与数据分析,提高安全管理水平和生产效率。

C语言基础之数组

上一篇讲述了C语言函数的使用,本文讲述数组的相关概念,通过一维数组、二维数组、数组越界等详细讲解数组相关的具体内容,以辅助读者了解并掌握数组相关概念。 一维数组 一维数组的定义与创建 若无数组,我们要存储一堆类型相同的…

osalTaskREC_t ‘REC’缩写的含义

osalTaskREC_t中‘REC’缩写的含义。 在osal中定义了 osalTimerRec_t, osalTaskRec_t 结构体,那么osal源码中类型名 osalTaskREC_t中‘REC’缩写的含义是什么? 查了下往上资料,rec应该是 Record(记录)’的…

AI大模型之旅-最强开源文生图工具Stable Diffusion WebUI 教程

1.1克隆 Automatic1111 的 GitHub 仓库 在你想安装 Web UI 的文件夹路径下执行 git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui 这将会克隆整个仓库到本地。 这里会默认访问 https://huggingface.co/ 下载 因此需要魔法 1.2 进入仓库目录 cd stable-di…

手机改IP地址怎么弄?全面解析与操作指南

在当今数字化时代,IP地址作为设备在网络中的唯一标识,其重要性不言而喻。有时候,出于隐私保护、网络访问需求或其他特定原因,我们可能需要更改手机的IP地址。然而,对于大多数普通用户来说,如何操作可能还是…

Xshell调用powershell连接Teleport

公司最近切换telport管理SSH的连接访问。配置完成后,通过powershell可能远程访问。但是powershell缺少session管理功能,每次连接都记不住远程IP,很是头疼。 利用Xshell的管理能力是否可行?那是相当可行。 首先,新建se…

环境变量配置文件

环境变量配置文件 系统级配置文件 /etc/profile 系统范围的配置文件,适用于所有用户的登录 shell。 定义了系统级别的环境变量,并调用其他配置文件(如 /etc/bash.bashrc)。 /etc/bash.bashrc 非登录 shell 的全局配置文件。 在系…

Java异步编程:初学者快速入门到精通指南

异步编程简介 异步编程允许程序在执行某些操作(如网络请求、文件I/O等)时不被阻塞,能够继续执行其他任务。这不仅可以提高程序的响应性,还能提升资源的利用率。 为什么选择Java进行异步编程? Java提供了多种异步编程…

Echarts折线图的末尾部分线条虚线

原理:等于画了两条线,一条实线一条虚线;把实线的最后的值给虚线;再将提示框进行过滤,防止多个点以及值为空的情况 初步实现参考: option {xAxis: {type: category,data: [Mon, Tue, Wed, Thu, Fri, Sat, …

《低空经济:文旅行业的新引擎 》

《低空经济:文旅行业的新引擎 》 一、低空经济与文旅行业的融合态势 低空经济作为新兴经济形态,正与文旅行业深度融合,为文旅发展带来新机遇。 近年来,随着科技的不断进步和人们对旅游体验的不断追求,低空经济与文旅…

js列表数据时间排序和取唯一值

1.取唯一值[...new Set(array)] const array [1, 2, 3, 2, 4, 5, 3, 5]; // 使用Set去除重复元素 const uniarray [...new Set(array)]; console.log(uniarray); // 输出: [1, 2, 3, 4, 5] 2.排序 var u [1,3,2,5,4]; var uu u.sort(); console.log(uu); var u [1,3…

ACL 2023--MetaAdapt: 通过元学习实现领域自适应的少量样本虚假信息检测

https://github.com/Yueeeeeeee/MetaAdapt 随着社交媒体上出现的新话题(例如COVID-19)成为虚假信息传播的来源,克服原始训练领域(即源领域)与这些目标领域之间的分布变化,仍然是虚假信息检测中的一项复杂任…

金三银四:20道前端手写面试题

文章目录 一、前言二、题目1. 防抖节流解读 2.一个正则题3. 不使用a标签,如何实现a标签的功能4. 不使用循环API 来删除数组中指定位置的元素(如:删除第三位) 写越多越好5. 深拷贝解读 6. 手写call bind applycall 解读apply 解读 …

Chrome无法拖入加载.crx扩展文件(以IDM为例)

问题原因:新版本的Chrome浏览器已不支持加载.crx文件 解决办法:将.crx文件压缩为.zip文件,解压缩后再加载到Chrome中 以IDM的.crx文件作为示例; IDM的.crx文件位于C:\Program Files (x86)\Internet Download Manager; 将IDMGCE…

计算机毕业设计 C语言学习辅导网站的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…

记一次实战中对Ruoyi系统的渗透

前言 最近碰到比较多Ruoyi的站,ruoyi的话漏洞还是比较多的,这里就分享一下自己渗透的一些案例吧,方便大家参考学习 首先声明 文章中涉及的敏感信息均已做打码处理,文章仅做经验分享用途,切勿当真,未授权…

解决:使用layui.treeTable.updateNode,更新表格数据后,done里面的事件丢失问题

1. 背景 在给树形表格添加行点击事件,并且只更新当前行数据。 treeTable.updateNode("SpeProjListId", result.LAY_DATA_INDEX, result);更新数据后,点击事件失效。 1. 给字段绑定事件: class"link_a link_style" , {…

基于Spring3.0实现AOP的小案例

前言 AOP(Aspect Oriented Programming)即面向切面编程,是一种通过预编译方式和运行期间动态代理实现程序功能统一维护的技术。针对功能增强的描述,可以理解为:“AOP允许在不修改源代码的情况下,通过定义切…