s3c2440裸机-代码重定位-4-清bss的优化和位置无关码

发布者:tau29最新更新时间:2024-07-05 来源: elecfans关键字:代码重定位  位置无关码 手机看文章 扫描二维码
随时随地手机看文章

1.代码重定位的改进

用ldr、str代替ldrb, strb加快代码重定位的速度。

前面重定位时,我们使用的是ldrb命令从的Nor Flash读取1字节数据,再用strb命令将1字节数据写到SDRAM里面。 我们2440开发板的Nor Flash是16位,SDRAM是32位。 假设现在需要复制16byte数据。

不同的读写指令cpu读取nor的次数cpu写入sdram的次数
ldrb、strb1616
ldr、str84

可以看出我们更换读写指令后读写次数变少了,提升了cpu的访问效率。

修改后的start.s代码如下图所示,这里我只简单的列出了重定位的实现:

... cpy: ldr r4, [r1] str r4, [r2] add r1, r1, #4 //r1加4 add r2, r2, #4 //r2加4 cmp r2, r3 //如果r2 =< r3继续拷贝 ble cpy ...

c语言实现重定位

添加如下链接脚本:


SECTIONS

{

    . = 0x30000000;


    __code_start = .;


    . = ALIGN(4);

    .text      :

    {

      *(.text)

    }


    . = ALIGN(4);

    .rodata : { *(.rodata) }


    . = ALIGN(4);

    .data : { *(.data) }


    . = ALIGN(4);

    __bss_start = .;

    .bss : { *(.bss) *(.COMMON) }

    _end = .;

}

在main.c中添加如下函数实现:


void copy2sdram(void)

{

        //要从lds文件中获得 __code_start, __bss_start

        //然后从0地址把数据复制到__code_start


    extern int __code_start, __bss_start;


    volatile unsigned int *dest = (volatile unsigned int *)&__code_start;

    volatile unsigned int *end = (volatile unsigned int *)&__bss_start;

    volatile unsigned int *src = (volatile unsigned int *)0;


    while (dest < end)

    {

        *dest++ = *src++; //从0地址依次copy到__code_start(代码段的运行地址)

    }

}

然后在start.s中设置栈指针sp后,即可执行bl copy2sdram进行重定位代码。如何设置栈指针请参考 时钟编程(二、配置时钟寄存器)中有实现,重复代码我就不贴上来了。


2.清bss的改进

用ldr、str代替ldrb, strb加快清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

c语言实现清bss

和上面重定位的代码实现一样,就是往bss段全部写0. 执行完bl copy2sdram, 然后再bl clean_bss即可完成清除bss段。

void clean_bss(void) { /* 从lds文件中获得 __bss_start, _end*/ extern int _end, __bss_start; volatile unsigned int *start = (volatile unsigned int *)&__bss_start; volatile unsigned int *end = (volatile unsigned int *)&_end; while (start <= end) { *start++ = 0; } }

注意:汇编代码获取的是链接脚本中的变量的地址,而C语言代码中获取的是链接脚本中的变量的值,所以这里的用C语言改进重定位还是清bss都是要加取址符。

保证所有段的起始地址以4字节对齐

我们前面为了加快重定位和清bss的速度,用到了ldr,str这样以4字节为单位进行读写,但是还可能导致一个问题,假设现在链接脚本没有进行用ALIGN(4)让不同的段以4字节对齐,那么就会出现访问错乱的情况。

我举个例子:

#include 's3c2440_soc.h' #include 'uart.h' #include 'init.h' char g_Char = 'A'; //.data char g_Char3 = 'a'; const char g_Char2 = 'B'; //.rodata int g_A = 0; //bss int g_B; //bss int main(void) { uart0_init(); puts('nrg_A = '); printHex(g_A); puts('nr'); putchar(g_Char); return 0; }

将链接脚本中.data段和.bss之间的ALIGN(4)去掉。那么我们会发现程序执行的时候输出的g_A=0,为什么呢,我们明明初始化g_A=‘A’呀?

我们分析下反汇编看看:

我们的.bss段紧接着.data段后面,可知在对bss段进行清除的时候,由于我们是以4字节为单位操作的,所以我们清除g_A的时候,连带g_Char,g_Char的值也一起清除了。所以data段和数据段之间添加ALIGN(4)。修改后就会发现bss段的地址以0x30000248开始了,如下图:

3.位置无关码

我们对‘bl sdram_init’指令进行分析: 查看反汇编:(代码段的链接地址为0x3000,0000)

这里的bl 3000036c不是跳转到3000036c,这个时候sdram并未初始化,那么这个物理地址是无法访问的; 为了验证,我们做另一个实验,修改连接脚本sdram.lds, 链接地址改为0x3000,0800,编译,查看反汇编:

可以看到现在变成了bl 300003ec,但两个的机器码e1a0c00d都是一样的,机器码一样,执行的内容肯定都是一样的。 因此这里并不是跳转到显示的地址,而是跳转到: pc + offset,这个由链接器决定。


假设程序从0x30000000执行,当前指令地址:0x3000005c ,那么就是跳到0x3000036c;如果程序从0运行,当前指令地址:0x5c 调到:0x000003ec


跳转到某个地址并不是由bl指令所决定,而是由当前pc值决定。反汇编显示这个值只是为了方便读代码。


重点: 反汇编文件里, B或BL 某个值,只是起到方便查看的作用,并不是真的跳转。


怎么写位置无关码?


使用相对跳转命令 b或bl; 重定位之前,不可使用绝对地址,不可访问全局变量/静态变量,也不可访问有初始值的数组(因为初始值放在rodata里,使用绝对地址来访问); 重定位之后,使用ldr pc = xxx,跳转到/runtime地址; 写位置无关码,其实就是不使用绝对地址,判断有没有使用绝对地址,除了前面的几个规则,最根本的办法看反汇编。


因此,前面的例子程序使用bl命令相对跳转,程序仍在NOR/sram执行,要想让main函数在SDRAM执行,需要修改代码:


//bl main  /*bl相对跳转,程序仍在NOR/sram执行*/

  ldr pc, =main/*绝对跳转,跳到SDRAM*/


关键字:代码重定位  位置无关码 引用地址:s3c2440裸机-代码重定位-4-清bss的优化和位置无关码

上一篇:s3c2440裸机-异常中断1-异常中断的原理与流程
下一篇:s3c2440裸机-代码重定位-3-清bss原理及实现

推荐阅读最新更新时间:2026-03-21 04:37

s3c2440裸机-代码定位-4-清bss的优化和位置无关
1.代码重定位的改进 用ldr、str代替ldrb, strb加快代码重定位的速度。 前面重定位时,我们使用的是ldrb命令从的Nor Flash读取1字节数据,再用strb命令将1字节数据写到SDRAM里面。 我们2440开发板的Nor Flash是16位,SDRAM是32位。 假设现在需要复制16byte数据。 不同的读写指令 cpu读取nor的次数 cpu写入sdram的次数 ldrb、strb 16 16 ldr、str 8 4 可以看出我们更换读写指令后读写次数变少了,提升了cpu的访问效率。 修改后的start.s代码如下图所示,这里我只简单的列出了重定位的实现: ... cpy: ldr r4, str
[单片机]
s3c2440裸机-代码定位(1.定位的引入,为什么要代码定位)
1.重定位的引入(为什么要代码重定位) 我们知道s3c2440的cpu从0地址开始取指令执行,当从nor启动时,0地址对应nor,nor可以像内存一样读,但不能像内存一样写。我们能够从nor上取指令执行。 例子1:当nand启动的时候,我们nand中的前4K指令会变自动加载到sram中去,这时的0地址对应sram。 那么我们的程序如果大于4K,要从nand启动,sram只拷贝了nand中的前4K代码,那么如何解决这个问题呢? 那么就需要重定位代码到sdram中去,sdram的容量较大,又可以直接被cpu访问。 例子2:我们知道,程序含有: 代码段(.text) 数据段(.data):存放初始值不为0的全局变量/静态变量
[单片机]
<font color='red'>s3c2440</font><font color='red'>裸机</font>-<font color='red'>代码</font><font color='red'>重</font><font color='red'>定位</font>(1.<font color='red'>重</font><font color='red'>定位</font>的引入,为什么要<font color='red'>代码</font><font color='red'>重</font><font color='red'>定位</font>)
ARM下的位置无关和相关
为什么需要位置无关码?   见 :   U-BOOT详解(什么是《编译地址》?什么是《运行地址》?) http://bbs.21ic.com/forum.php?mod=viewthread&tid=857037&typeid=114 ARM位置无关代码设计规范 http://wenku.baidu.com/view/5ef25b890b4c2e3f562763a8.html 位置无关可执行文件PIE包括位置无关代码PIC和位置无关数据PID两部分。 通常情况下,将bootloader程序下载到ROM的0x0地址进行启动(比如固化到NorFlash中)。然而在很多的设计中,比如将bootloader固化在NAND中,在系统
[单片机]
总结:代码定位
什么是重定位?为什么要代码重定位? 要弄清楚上面的这两个问题,首先要理解下面这几个概念 一、编码 (1)位置无关编码:PIC,可执行程序运行时与代码在内存中的地址无关,代码中没有使用绝对地址,而是使用的相对地址。(例如:B、BL、MOV等指令) (2)位置有关编码:可执行程序运行时与代码在内存中的地址有关系。(例如:LDR PC, =MAIN等指令) 二、地址 (1)链接地址:程序编译链接时指定的地址(使用makefile或者链接脚本可以指定链接地址) (2)运行地址:程序在内存中实际运行的地址。 参考资料:http://blog.csdn.net/linux_103/article/details/8888427 三、S
[单片机]
tiny4412 裸机程序 六、定位代码到IRAM+0x8000【转】
一、重定向 对于程序而言,我们需要理解两个概念,一是程序当前所处的地址,即程序在运行时,所处的当前地址;二是程序的链接地址,即程序运行时应该位于的运行地址。编译程序时,可以指定程序的链接地址。对于Tiny4412而言,启动时只会从MMC/sd等启动设备中拷贝前16K的代码到IRAM中,那么当我们的程序超过16K怎么办?那就需要我们在前16K的代码中将整个程序完完整整地拷贝到DRAM等其他更大存储空间,然后再跳转到DRAM中继续运行我们的代码,这个拷贝然后跳转的过程就叫重定位。 本章中我们主要学习如何重定位,但是并不会涉如何使用到DRAM,而是简单地将代码从IRAM的0x02020010处拷贝到IRAM的0x02028000处,
[单片机]
S3c2440代码定位详解1---段的概念定位的引入
S3C2440的CPU可以直接给SDRAM发送命令、给Nor Flash发送命令、给4K的片上SRAM发送命令,但是不能直接给Nand Flash发送命令 NAND启动过程 假如把程序烧写到Nand Flash上,即向Nand Flash烧入* bin* 文件,CPU是无法从Nand Flash中取代码执行的。 为什还可以使用NAND启动? 上电后,Nand启动硬件会自动把Nand Flash前4K复制到SRAM; CPU从0地址运行SRAM; 如果我的程序大于4K怎么办? 前4K的代码需要把整个程序读出来放到SDRAM(即代码重定位) NOR启动过程 如果从Nor Flash启动,会出现什么问题? 将拨动开关
[单片机]
<font color='red'>S3c2440</font><font color='red'>代码</font><font color='red'>重</font><font color='red'>定位</font>详解1---段的概念<font color='red'>重</font><font color='red'>定位</font>的引入
S3C2440 代码定位实验(一)
实验环境 本实验基于这样的开发环境:在Ubuntu18.04.3发行版上使用编译器arm-linux-gcc(arm-none-linux-gnueabi, 4.3.2 Sourcery G++ Lite 2008q3-72)编译,并运行于S3C2440 ARM硬件平台上。S3C2440是一款32位的ARM SoC。 链接(linking) 所谓链接,就是将各种代码和数据部分收集起来并组合成一个单一文件的过程,这个单一文件可以被加载到存储器内并被执行。链接既可以执行于编译时,也可以执行于加载时,也可以执行于运行时。若执行于编译时,则是由编译器在将源代码翻译成机器码时负责;若执行于加载时,则是由加载器(loader)把可执行程序
[单片机]
<font color='red'>S3C2440</font> <font color='red'>代码</font><font color='red'>重</font><font color='red'>定位</font>实验(一)
Exynos4412裸机程序之代码定位初体验
从前面一节 Exynos 4412的启动过程分析 ,我们知道:一上电,exynos4412首先执行固化在IROM中的代码,iROM首先设置程序运行环境 (比如关看门狗、关中断、关MMU 、设置栈 、设置栈 、启动PLL等 ),然后根据OM引脚确定启动设备 (NAND Flash/SD 卡/其他 ),把 BL1从里面读出存入iRAM的0x02021400地址处,最后启动 BL1; BL1从SD卡适当的位置读入14K 字节的数据,存在iRAM地址0x02023400处,所以BL2不能大于(14K – 4) 字节,这里引出了为什么写这一节的原因:如果我们的程序很大,大于14K怎么办???? 下面我们先来介绍两个概念: 一是程序当前所处的
[单片机]
Exynos4412<font color='red'>裸机</font>程序之<font color='red'>代码</font><font color='red'>重</font><font color='red'>定位</font>初体验
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved