来源于网络并稍加创作:https://blog.csdn.net/u012308586/article/details/123918444
在了解keil的链接脚本之前需要了解几个重要概念:
RO(ReadOnly):表示程序中的指令和常量
RW(Read/Write):表示程序中已初始化的变量
ZI(Zero):表示程序中未初始化的变量
我们在编译keil中的工程项目时生成的目标文件时叫做镜像文件(Image)或bin文件,image文件中只包含RO,RW段,这是因为ZI数据段都是零,没有必要包含,只需要把ZI所在的区域清零即可。而其他两个数据段是被赋值了的,所以image文件需要包含已初始化变量的初值。如果我们将image文件烧录到ROM中,那么其必须包含有将RW段移动到RAM中以及将ZI段所在的地址清零的功能。这是保证烧录程序能运行起来的基本要求。
知道基本的定义后,我们来了解链接脚本,也叫程序加载文件。在生成image文件时如何来分配相关数据的存放基址呢?这个就有链接脚本决定,如果不指定特定的链接脚本,连接器就会自动采用默认的链接脚本来生成镜像。本文主要讲解分散加载文件:分散加载文件主要由一个加载时域,多个运行时域组成。
加载时域格式如下所示:
load_region_name base_address max_size
{
execution_region_description+
}
load_region_name为加载时域的名字,长度不超过31个字节
base_address为加载时域的起始地址,即从该地址开始加载相关代码
max_size为加载时域的最大范围,若实际大小超过该大小,链接器将会报错
execution_region_description+是对执行时域的描述
同理,运行时域格式如下:
exec_region_name base_address max_size
{
input_section_description*
}
应用实例:
LR_IROM1 0x08020000 0x00040000 { ; 定义一个加载时域,域基址:0x08020000,域大
; 小为 0x00040000,对应实际 Flash 的大小
ER_IROM1 0x08000000 0x00040000 { ; 定义一个运行时域,第一个运行时域必须和加载
; 时域起始地址相同,否则库不能加载到该时域的
; 错误,其域大小一般也和加载时域大小相同
*.o (RESET, +First) ; 将 RESET 段最先加载到本域的起始地址外,即
; RESET 的起始地址为 0,RESET 存储的是向量表
*(InRoot$$Section)
.ANY (+RO) ; 加载所有匹配目标文件的只读属性数据,包含:
; Code、RW-Code、RO-Data。
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00040000 { ; 定义一个运行时域,域基址:0x20000000,域大
; 小为 0x00040000,对应实际 RAM 大小
* (+RW +ZI) ; 加载所有区配目标文件的 RW-Data、ZI-Data
; 这里也可以用.ANY 替代*号
}
ARM_LIB_HEP 0x20040000 EMPTY 0x70000 { ; 定义一个运行时域,域基址:0x20040000,域大小为0x70000,预留给启动代码里的堆用
}
ARM_LIB_STACK 0x200BFFC0 EMPTY -0x BFFC0 { ; 定义一个运行时域,域基址:0x200BFFC0,域大小为0x BFFC0,预留给启动代码里的栈用
;启动代码汇编使用__
;IMPORT |Image$$ARM_LIB_STACK$$ZI$$Linit|
;Vector DCD |Image$$ARM_LIB_STACK$$ZI$$Linit| ;Top of Stack
}
BOOT_INFO 0x200BFFC0 EMPTY 0x10 { ; 定义一个运行时域,域基址:0x200BFFC0,域大小为0 x10,预留引导信息用
;C/C++使用:
;extern unsigned int Image$$BOOT_INFO$$ZI$$Base;//起始
;extern unsigned int Image$$BOOT_INFO$$ZI$$Limit; //结束
;#define BOOT_INFO_BASE_ADDR ((unsigned int)(&Image$$BOOT_INFO$$ZI$$Base))//0x200BFFC0
;#define BOOT_INFO_END_ADDR ((unsigned int)(&Image$$BOOT_INFO$$ZI$$Limit))//0x200BFFD0
}
MCU_SELF_TEST_RESULT 0x200BFFD0 EMPTY 0x10 { ; 定义一个运行时域,域基址:0x200BFFD0,域大小为0 x10,预留自检信息用
}
}
sct文件的编写与使用
参考资源
1. 分散加载文件浅释.pdf。(周立功工程技术笔记)
1. DUI0377G_02_mdk_armlink_user_guide.pdf(在keil的帮助文档中可以找到)
分散加载文件.sct语法
一个分散加载文件由一个或者多个加载域(load regions)组成。每个加载域由一个或者多个执行域(execution regions)组成。执行域中又包含很多个输入段描述(Input section description)。关系图如下.
分散加载文件使用;作为注释符。
什么是加载域
加载域的组成如下:
语法格式
load_region_name (base_address | (+offset)) [attribute_list] [max_size]
{
execution_region
}
名称(name): 加载域的名称,用户自定义。
起始地址: 本加载域的起始地址。分两种表达格式
base_address:直接指明本加载域的起始地址。
+offset:表示此加载域的起始地址为前一个加载域的结束地址+offset字节。offset必须字对齐。
属性(attribute_list)(可选): 用于指定加载域的特征。具体值如下
ABSOLUTE: 绝对地址。默认属性。
PI: 与位置无关。
RELOC: 可重定位。
OVERLAY: 覆盖。
NOCOMPRESS: 不能进行压缩。
加载域大小(max_size)(可选)
指定加载域的最大范围。默认取值0xFFFFFFFF.
一个或者多个执行域.
执行域的组成见执行域的介绍。
什么是执行域
执行域描述了可执行文件在执行时内存(RAM+ROM)的分配。
执行域的组成类似加载域,如下:
语法格式
exec_region_name (base_address | (+offset)) [attribute_list] [max_size | Length]
{
input section descriptions
}
名称(name): 执行域的名称,用户自定义。
起始地址: 本执行域的起始地址。分两种表达格式
base_address:直接指明本执行域的起始地址。
+offset:表示此执行域的起始地址为前一个执行域的结束地址+offset字节。offset必须字对齐。
属性(attribute_list)(可选): 用于指定加载域的特征。具体值如下
ABSOLUTE: 绝对地址。默认属性。
PI: 与位置无关。
RELOC: 可重定位。
OVERLAY: 覆盖。
NOCOMPRESS: 不能进行压缩。
加载域大小(max_size)(可选)
指定加载域的最大范围。默认取值0xFFFFFFFF.
一个或者多个输入段描述.
输入段描述的组成见输入段描述的介绍。
什么是输入段描述
语法格式
目标文件过滤器 [+属性]
输入段描述的组成如下:
目标文件过滤器。
支持通配符*、?。不区分大小写。如main.o,user*.o等。可匹配的文件可以为目标文件名、库名(不带前导路径名)、库完整路径名。
可以使用*.o匹配所有的目标文件。用*匹配所有文件,包括目标文件和库。
可以使用.ANY进行任意的匹配。它的优先级低于*.
属性,不区分大小写:
RO-CODE,同义词有CODE
RO-DATA,同义词有CONST
RO 即RO-CODE + RO-DATA,同义词有TEXT
RW-DATA
RW-CODE
RW 即RW-DATA + RW-CODE,同义词有DATA
ZI,同义词有BSS
ENTRY 即包含ENTRY的段。
FIRST
LAST
example:
*.o (RESET, +First) 任何目标文件中的RESET段放在起始位置。
.ANY (+RW +ZI)匹配任意文件的数据段。
.ANY (+RO) 匹配任意文件的代码段。