网站 建设 领导小组,设计师学校有哪些,企业电子商务网站设计的原则,厦门 网站建设 网站开发段的概念
代码段、只读数据段、可读可写数据段、BSS段。
char g_Char A; //可读可写#xff0c;不能放在ROM上#xff0c;应该放在RAM里
const char g_Char2 B; //只读变量#xff0c;可以放在ROM上
int g_A 0; //初始值为0#xff0c;没有必要浪费空间
int g_B; //没…段的概念
代码段、只读数据段、可读可写数据段、BSS段。
char g_Char A; //可读可写不能放在ROM上应该放在RAM里
const char g_Char2 B; //只读变量可以放在ROM上
int g_A 0; //初始值为0没有必要浪费空间
int g_B; //没有初始化没有必要浪费空间代码段RO-CODE就是程序本身不会被修改可读可写的数据段RW-DATA有初始值的全局变量、静态变量需要从ROM上复制到内存只读的数据段RO-DATA可以放在ROM上不需要复制到内存BSS段或ZI段初始值为0的全局变量或静态变量/未初始化的全局变量或静态变量没必要放在ROM上使用之前清零就可以局部变量保存在栈中运行时生成堆一块空闲空间使用malloc函数来管理它malloc函数可以自己写
重定位
保存在ROM上的全局变量在使用前需要复制到内存这就是数据重定位。 想把代码移动到其他位置这就是代码重定位。
程序中含有什么
代码段如果它不在链接地址上就需要重定位只读数据段如果它不在链接地址上就需要重定位可读可写的数据段如果它不在链接地址上就需要重定位BSS段不需要重定位因为程序里根本不保存BSS段使用前把BSS段对应的空间清零即可
谁来做重定位 程序本身它把自己复制到链接地址去
一开始程序可能并不位于它的链接地址为什么可以执行重定位的操作 因为重定位的代码是用位置无关码写的
什么叫位置无关码这段代码扔在任何位置都可以运行跟它所在的位置无关。
怎么写出位置无关码 跳转使用相对跳转指令不能使用绝对跳转指令。 只能使用branch指令比如bl main不能给PC直接赋值比如ldr pc,main 不要访问全局变量、静态变量 不使用字符串
怎么做重定位和清除BSS段
核心复制 复制的三要素源、目的、长度。
怎么知道代码段/数据段保存在哪加载地址怎么知道代码段/数据段被复制到哪链接地址怎么知道代码段/数据段的长度怎么知道BSS段的地址范围起始地址、长度
keil中使用散列文件Scatter File来描述 GCC中使用链接脚本Link Script来描述
加载地址和链接地址的区别
程序运行时应该位于它的链接地址处因为
使用函数地址时使用的是“函数的链接地址”所以代码段应该位于链接地址处。去访问全局变量、静态变量时用的是“变量的链接地址”所以数据段应该位于链接地址处
但是 程序一开始时可能并没有位于它的链接地址
比如对于STM32F103程序被烧录器烧写在Flash上这个地址称为加载地址比如对于IMX6ULL/STM32MP157片内ROM根据头部信息把程序读入内存这个地址称为“加载地址”
当加载地址链接地址时就需要重定位。
重定位的实质移动数据
把代码段、只读数据段和数据段移动到它的链接地址处。 也就是复制。
数据复制的三要素源、目的、长度。
数据保存在哪里加载地址数据复制到哪里链接地址长度
在keil中使用散列文件来描述。
在STM32F103这类资源紧缺的单片机芯片中
代码段保存在Flash上直接在Flash上运行当然也可以重定位到内存里数据段保存在Flash上使用前被复制到内存里
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************LR_IROM1 0x08000000 0x00040000 { ; load region size_regionER_IROM1 0x08000000 0x00040000 { ; load address execution address*.o (RESET, First)*(InRoot$$Sections).ANY (RO)}RW_IRAM1 0x20000000 0x0000C000 { ; RW data.ANY (RW ZI)}
}
一个散列文件由一个或多个加载域组成。 一个加载域里有一个或多个可执行域。 一个可执行域里有一个或多个输入段。 可执行域1源0x08000000目的0x08000000长度 可执行域1加载地址链接地址不需要重定位
*.o所有的.o文件抽取出RESET段放在文件最开始的位置所有objects文件和库在一个散列文件中只能使用一个.ANY等同于*优先级比*低在一个散列文件的一个可执行域里可以有多个.ANY
可执行域2源紧随可执行域1后目的0x20000000长度 需要重定位
获得region信息
可执行域的信息 加载域的信息
汇编代码里怎么使用这些信息
void memcpy(void *dest, void *src, unsigned int len)
{unsigned char *pcDest (unsigned char *)dest;unsigned char *pcSrc (unsigned char *)src;while(len--){*pcDest *pcSrc;pcDest;pcSrc;}
}IMPORT |Image$$RW_IRAM1$$Base|
IMPORT |Image$$RW_IRAM1$$Length|
IMPORT |Load$$RW_IRAM1$$Base|LDR R0, |Image$$RW_IRAM1$$Base| ;DEST
LDR R1, |Load$$RW_IRAM1$$Base| ;SOURCE
LDR R2, |Image$$RW_IRAM1$$Length| ;LENGTH
BL memcpyC语言中的BSS段
char g_Char A;
const char g_Char2 B;
int g_A 0; // 放在BSS段
int g_B; // 放在BSS段程序里的全局变量如果它的初始值为0或者没有设置初始值这些变量被放在BSS段里也叫ZI段。
BSS段并不会放入bin文件中否则浪费空间。
在使用BSS段里的变量之前把BSS段所占据的内存清零就可以了。
注意对于keil来说一个本该放到BSS段的变量如果它所占据的空间小于等于8字节keil仍然会放到data段里。只有当它所占据的空间大于8字节时才会放到BSS段。
int g_A[3] {0, 0}; //12个字节放在BSS段
char g_B[9]; //9个字节放在BSS段int g_A[2] {0, 0};//8个字节放在data段
char g_B[8]; //8个字节放在data段如何知道BSS段目的地址多大
在散列文件中BSS段ZI段在可执行域RW_IRAM1中描述
LR_IROM1 0x08000000 0x00080000 { ; load region size_regionER_IROM1 0x08000000 0x00080000 { ; load address execution address*.o (RESET, First)*(InRoot$$Sections).ANY (RO).ANY (XO)}RW_IRAM1 0x20000000 0x00010000 { ; RW data.ANY (RW ZI)}
}BSS段ZI段的链接地址基地址、长度使用下面的符号获得
代码段重定位-加载地址等于链接地址
在默认散列文件中代码段的load address execution address。 加载地址和执行地址链接地址一致无需重定位
LR_IROM1 0x08000000 0x00080000 { ; load region size_regionER_IROM1 0x08000000 0x00080000 { ; load address execution address*.o (RESET, First)*(InRoot$$Sections).ANY (RO).ANY (XO)}RW_IRAM1 0x20000000 0x00010000 { ; RW data.ANY (RW ZI)}
}加载地址不等于链接地址
有时候我们需要把程序复制到内存里里运行比如
想让程序执行得更快需要把代码段复制到内存里。程序很大保存在片外SPI Flash中SPI Flash上的代码无法直接执行需要复制到内存里。
这时候需要修改散列文件把代码段的可执行域放在内存里。 那么程序运行时需要尽快把代码段重定位到内存。
LR_IROM1 0x08000000 0x00080000 { ; load region size_regionER_IROM1 0x20000000 { ; load address ! execution address*.o (RESET, First).ANY (RO).ANY (XO)}RW_IRAM1 0 { ; RW data.ANY (RW ZI)}
}上面的散列文件中
可执行域ER_IROM1加载地址为0x08000000可执行地址为0x20000000两者不相等。 板子上电后从0x080000000处开始运行需要尽快把代码段复制到0x20000000可执行域RW_IRAM1加载地址紧跟着ER_IOM1的加载地址可执行地址紧跟着ER_IROM1的可执行地址。 需要尽快把数据复制到可执行地址处。
代码段不重定位的后果
ldr pc, main ;这样调用函数用到main函数的链接地址如果代码段没有重定位则跳转失败void (*funcptr)(const char *s, unsigned int val);
funcptr put_s_hex;
funcptr(hello,123);为什么重定位之前的代码也可以正常运行
因为重定位之前的代码是使用位置无关码写的 只使用相对跳转指令B、BL 不使用绝对跳转指令
LDR R0, main
BLX R0不访问全局变量、静态变量、字符串、数组 重定位完成后使用绝对跳转指令跳转到xxx函数的链接地址去
BL main;BL ;相对跳转程序仍在Flash上运行LDR R0,main ;绝对跳转跳转到链接地址上去就是跳去内存里执行
BLX R0重定位的纯C函数实现
难点在于怎么得到各个域的加载地址、链接地址、长度。
方法1 声明为外部变量使用时需要使用取址符
extern int Image$$ER_IROM1$$Base;
extern int Load$$ER_IROM1$$Base;
extern int Image$$ER_IROM1$$Length;memcpy(Image$$ER_IROM1$$Base, Image$$ER_IROM1$$Length, Load$$ER_IROM1$$Base);方法2 声明为外部数组使用时不需要使用取址符
extern char Image$$ER_IROM1$$Base[];
extern char Load$$ER_IROM1$$Base[];
extern int Image$$ER_IROM1$$Length;memcpy(Image$$ER_IROM1$$Base, Image$$ER_IROM1$$Length, Load$$ER_IROM1$$Base);