一个程序,由代码段、只读数据段、数据段、bss段等组成。
程序一开始可以烧在Nor Flash上面,运行时代码段仍可以在Nor Flash运行,但对于数据段,就必须把数据段移到SDRAM中,因为只要在SDRAM里面,数据段的变量才能被写操作,把程序从一个位置移动到另一个位置,把这个过程就称为重定位。
先梳理下把整个程序复制到SDRAM需要哪些技术细节:
把程序从Flash复制到运行地址,链接脚本中就要指定运行地址(Runtime addr)为SDRAM地址;
编译链接生成的bin文件,需要在SDRAM地址上运行,但上电后却必须先在0地址运行,这就要求重定位之前的代码与位置无关(是位置无关码写成);
参考Uboot修改链接脚本:
SECTIONS
{
. = 0x30000000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
_end = .;
}
现在我们写的这个链接脚本,称为一体式链接脚本,对比前面的分体式链接脚本区别在于代码段和数据段的存放位置是否是分开的。
例如现在的一体式链接脚本的代码段后面依次就是只读数据段、数据段、bss段,都是连续在一起的。
分体式链接脚本则是代码段、只读数据段,中间相关很远之后才是数据段、bss段。
我们以后的代码更多的采用一体式链接脚本,原因如下:
分体式链接脚本适合单片机,单片机自带有flash,不需要再将代码复制到内存占用空间。而我们的嵌入式系统内存非常大,没必要节省这点空间,并且有些嵌入式系统没有Nor Flash等可以直接运行代码的Flash,就需要从Nand Flash或者SD卡复制整个代码到内存;
JTAG等调试器一般只支持一体式链接脚本;
修改start.S段
/* 重定位text, rodata, data段整个程序 */
mov r1, #0
ldr r2, =_start /* 第1条指令运行时的地址 */
ldr r3, =__bss_start /* bss段的起始地址 */
cpy:
ldr r4, [r1]
str r4, [r2]
add r1, r1, #4
add r2, r2, #4
cmp r2, r3
ble cpy
/* 清除BSS段 */
ldr r1, =__bss_start
ldr r2, =_end
mov r3, #0
clean:
str r3, [r1]
add r1, r1, #4
cmp r1, r2
ble clean
bl main
halt:
b halt
将修改后的代码重新编译烧写在Nor Flash上,上电运行。
对本代码的启动情况进行分析:
在生成的bin文件里,代码保存的位置是0x30000000。随后烧写到NOR Flash的0地址,但代码的结构没有变化。之后再重定位到SDRAM
查看反汇编:
3000005c: eb000106 bl 30000478 30000060: e3a01000 mov r1, #0 ; 0x0 30000064: e59f204c ldr r2, [pc, #76] ; 300000b8 <.text+0xb8> 30000068: e59f304c ldr r3, [pc, #76] ; 300000bc <.text+0xbc> 这里的bl 30000478不是跳转到30000478,这个时候sdram并未初始化; 为了验证,我们做另一个实验,修改连接脚本sdram.lds, 链接地址改为0x32000478,编译,查看反汇编: 3000005c: eb000106 bl 30000478 30000060: e3a01000 mov r1, #0 ; 0x0 30000064: e59f204c ldr r2, [pc, #76] ; 300000b8 <.text+0xb8> 30000068: e59f304c ldr r3, [pc, #76] ; 300000bc <.text+0xbc> 可以看到现在变成了bl 30000478,但两个的机器码eb000106都是一样的,机器码一样,执行的内容肯定都是一样的。 因此这里并不是跳转到显示的地址,而是跳转到: pc + offset,这个由链接器决定。 假设程序从0x30000000执行,当前指令地址:0x3000005c ,那么就是跳到0x30000478;如果程序从0运行,当前指令地址:0x5c 调到:0x00000478 跳转到某个地址并不是由bl指令所决定,而是由当前pc值决定。反汇编显示这个值只是为了方便读代码。 重点: 反汇编文件里, B或BL 某个值,只是起到方便查看的作用,并不是真的跳转。 怎么写位置无关码? 1、使用相对跳转命令 b或bl; 2、重定位之前,不可使用绝对地址,不可访问全局变量/静态变量,也不可访问有初始值的数组(因为初始值放在rodata里,使用绝对地址来访问); 3、重定位之后,使用ldr pc = xxx,跳转到/runtime地址; 写位置无关码,其实就是不使用绝对地址,判断有没有使用绝对地址,除了前面的几个规则,最根本的办法看反汇编。 因此,前面的例子程序使用bl命令相对跳转,程序仍在NOR/sram执行,要想让main函数在SDRAM执行,需要修改代码 //bl main /*bl相对跳转,程序仍在NOR/sram执行*/ ldr pc, =main/*绝对跳转,跳到SDRAM*/
上一篇:S3c2440代码重定位详解4---拷贝代码和链接脚本的改进
下一篇:S3c2440代码重定位详解6---重定位清除BSS段的C函数实现
推荐帖子
- 有在单片机上跑过指纹识别的没?
- 有一个朋友想在m0上实现指纹识别算法有没有好的建议或者成熟的方案有偿服务有在单片机上跑过指纹识别的没?m0是个什么鬼?以前倒是做过,貌似是同学做的大学毕业设计,设计的是指纹门锁,感觉跟你要的功能差不多吧 我之前用AS608指纹识别模块在STM32上面做过指纹识别的功能的我之前用AS608指纹识别模块在STM32上面做过指纹识别的 as608需要stm32做大量运算吗 不需要做大量运算
- littleshrimp 单片机
- 我该如何对MCU进行喂狗
- DotC2016今天与大家共同探讨一下如何对MCU进行喂狗。一孔之言,不一定正确。希望大家踊跃拍砖,共同探讨,相互提高。O(∩_∩)O~想必刚接触电子行业的一些人,都会对MCU需不要需要看门狗;为什么要喂狗;看门狗有没有用;用内部看门狗好还是外部看门狗好,如何对MUC进行喂狗……等诸多问题有疑惑。即便工
- 点创@布丁 测试/测量
- CCS5.2下CMD文件导致的DM648自启动问题
- 以前一直使用CCS3.3,因为软件太容易导致蓝屏、死机,用虚拟机又严重影响仿真器速度。所以改用了CCS5.2。CCS5使用的ECLIPS架构,跟以前的CCS3.3几乎完全接不上茬DSP/BIOS也升了级,好多函数都改了名字。甚至以前的gel文件也被包裹成别的样子好不容易把程序改到可以仿真运行了,等下载到SPIFLASH启动的时候又出现了问题,程序没能正常启动。后来测试波形发现程序已经完成了加载,但无法正确运行。经过几翻周折,终于找到了原因。发现编译后的.map文件有两个.c
- 灞波儿奔 微控制器 MCU
- 一种基于PWM的电压输出DAC电路设计
- 在电子和自动化技术的应用中,单片机和DAC(数模转换器)是经常需要同时使用的,然而许多单片机内部并没有集成DAC,即使有些单片机内部集成了DAC,DAC的精度也往往不高,在高精度的应用中还是需要外接DAC,这样增加了成本。但是,几乎所有的单片机都提供定时器或者PWM输出功能。如果能应用单片机的PWM输出(或者通过定时器和软件一起来实现PWM输出),经过简单的变换电路就可以实现DAC,这将大量降低成本电子设备的成本、减少体积,并容易提高精度。本文在对PWM到DAC转换关系
- mdreamj 模拟与混合信号
- 这个电路用得多吗
- 多问一个问题。n1n2是构成变压器吗,为什么不是凸的地方对凸的地方? 这个电路用得多吗【多问一个问题。n1n2是构成变压器吗】是。不过N1还起到Buck电路中电感的作用。【为什么不是凸的地方对凸的地方?】作者这么画,确实有些不妥,不过倒也无伤大雅,明白的人能看懂。【这个电路用得多吗】在需要一个小功率隔离附加直流电源时有用。谢谢大佬的解答!用mos管替换三极管,开关损耗与导通损耗会变小吗? 【用mos管替换三极管,开关损耗与导通损耗会变小吗?】
- yhhhy 电源技术
- VDD-VSS-VEE-VCC解释
- 一、解释 VCC:C=circuit表示电路的意思,即接入电路的电压; VDD:D=device表示器件的意思,即器件内部的工作电压; VSS:S=series表示公共连接的意思,通常指电路公共接地端电压; VEE:E=electron表示构成物质的基本粒子之一,因带负电,也写作e,通常指负电压供电; VDDH:H=high表示高压,即高压供电端。二、说明1、对于数字电路来说
- shezl 单片机