s3c2440裸机-代码重定位-2-编程实现

发布时间:2024-07-05  

代码重定位(2.编程实现代码重定位)

1.引入链接脚本


我们上一节讲述了为什么要重定位代码,那么怎么去重定位代码呢?

上一节我们发现"arm-linux-ld -Ttext 0 -Tdata 0x30000000"这种方式编译出来的bin文件有800多M,这肯定是不行的,那么需要怎么把.data段重定位到sdram呢?

可以通过AT参数指定.data段在编译时的存放位置,我们发现这样指定太不方便了,而且不好确定要放在bin文件的哪个位置。这里就要引入链接脚本,它可以帮我们解决这个不必要的麻烦。

链接脚本格式

格式如下图:



我们来看一个具体的例子:

SECTIONS

{

    . = 0x00000000; //表示当前地址为0


    . = ALIGN(4);  //设置当前位置让4字节对齐

    .text  :   

    {

      cpu/arm920t/start.o   (.text)

      board/lyb2440/boot_init.o (.text)

      *(.text)

    }   //表示.text段从0x4开始存放,其中可以手动调整代码段的位置,

        //比如让start.o,boot_init.o中的函数放在最前面,然后存放剩余的代码段


    . = ALIGN(4); //设置当前位置让4字节对齐

    .rodata : { *(.rodata) } //从该位置开始存放所有的.rodata段


    . = ALIGN(4); //设置当前位置让4字节对齐

    .data : 0x30000000 : AT(0x800) { *(.data) } //从该位置开始存放所有的.data段 设置运行


    __bss_start = .; //设置.bss段的起始位置

    .bss : { *(.bss) } //从该位置开始存放所有的.bss段

    _end = .;//设置.bss段的结束位置(也就是整个链接脚本的结束为止)

}


这个是我从uboot中裁剪过来的链接脚本,从注释我已把链接脚本的结构讲解的差不多了。这里.data段指定了程序的运行(链接)地址为sdram的base_addr(0x30000000),通过AT指定加载(在bin文件的存放)地址0x800。

2.如何重定位代码

我们先编写一个链接脚本如下所示:

SECTIONS {
   .text   0  : { *(.text) }
   .rodata  : { *(.rodata) }
   .data 0x30000000 : AT(0x800) { *(.data) }
   .bss  : { *(.bss) *(.COMMON) }
}

我们上一节分析了nor启动时无法写全局变量,那么现在有两种重定位方法:

  1. 只重定位数据段
    只重定位数据段的过程用下图更直观:


对于nor启动时,我们可以直接从nor上取指令执行,所以可以只进行数据段的重定位,我们编写链接脚本relocate.lds如下所示:
SECTIONS { .text 0 : { *(.text) }//所有文件的.text .rodata : { *(.rodata) } //只读数据段 .data 0x30000000 : AT(0x800) { *(.data) } //放在0x800,但运行时在0x3000000 .bss : { *(.bss) *(.COMMON) }//所有文件的bss段,所有文件的.COMMON段 }
Makefile如下所示:
all: arm-linux-gcc -c -o led.o led.c arm-linux-gcc -c -o uart.o uart.c arm-linux-gcc -c -o init.o init.c arm-linux-gcc -c -o main.o main.c arm-linux-gcc -c -o start.o start.S #arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf arm-linux-objcopy -O binary -S sdram.elf sdram.bin arm-linux-objdump -D sdram.elf > sdram.dis
编译出sdram.bin,然后修改start.s进行.data段的重定位。我们需要将以0x800为.data段基地址的整个数据段copy到0x30000000处去,start.s修改如下:
.text .global _start _start: /* 关闭看门狗 */ /* 初始化时钟 */ /* 设置栈 */ /*初始化sdram*/ ... /* 重定位data段,把加载地址0x800(bin文件中在nor中)的数据段的内容重定位到sdram的baseaddr */ mov r1, #0x800 ldr r0, [r1] mov r1, #0x30000000 str r0, [r1] bl main halt: b halt
这里是用的相对跳转指令bl main,因为还没有重定位整个完整的代码,所以不能用ldr绝对跳转。前面的初始化时钟、sdram我就不写了,参考 时钟编程(二、配置时钟寄存器)、 (三、UART编程实现)、 内存控制器(五、SDRAM编程实现)
缺点: 这里只是人为的对.data段写死了,那么当我有多个全局变量时,还要计算重定位的次数,而且我们也不知道有多少个全局变量,所以这重定位方式有缺陷。那么我们对这种重定位.data断的方法做一个改进,将链接脚本修改如下:
SECTIONS { .text 0 : { *(.text) } .rodata : { *(.rodata) } .data 0x30000000 : AT(0x800) { data_load_addr = LOADADDR(.data); /* data段在bin文件中的地址, 加载地址 */ data_start = . ; /* data段在重定位地址, 运行时的地址 */ *(.data) data_end = . ; /* data段结束地址 */ } .bss : { *(.bss) *(.COMMON) } }
上面的链接脚本用一个变量data_load_addr指定了加载地址(data段在bin文件中的地址,即0x800),用变量data_start指定了运行地址(即为0x30000000),那么用data_end - data_start就是我们数据段的总长度。
对start.s做出如下修改:
/* 重定位data段 */ ldr r1, =data_load_addr /* data段在bin文件中的地址, 加载地址 */ ldr r2, =data_start /* data段在重定位地址, 运行时的地址 */ ldr r3, =data_end /* data段结束地址 */ cpy: ldrb r4, [r1] strb r4, [r2] add r1, r1, #1 add r2, r2, #1 cmp r2, r3 ble cpy bl main halt: b halt
注意,这里start.s中用到了链接脚本中的变量,r4作为临时变量,依次从data_load_addr读取出来后写入到data_start。
优点:可以不用计算有多少个全局变量,链接脚本自动帮我们弄好了。 缺点:由于我们的程序可能会大于SRAM或者nor的容量,那么就必须连代码段也一起进行重定位, 下面这种重定位方式更好,在实际应用中也是用的下面这种方式去做的重定位。

  1. 重定位整个程序
    重定位整个程序的过程用下图更直观:


我们修改链接脚本如下:
SECTIONS { . = 0x30000000; . = ALIGN(4); .text : { *(.text) } . = ALIGN(4); .rodata : { *(.rodata) } . = ALIGN(4); .data : { *(.data) } . = ALIGN(4); __bss_start = .; .bss : { *(.bss) *(.COMMON) } _end = .; }
在这里我们将代码段的地址设置为0x3000_0004,然后紧接着放.rodata段,然后再紧接着放.data段。这样我们的bin文件就不再有“空洞”了。
修改start.s如下:
.text .global _start _start: ... ... /* 重定位text, rodata, data段整个程序 */ mov r1, #0 ldr r2, =_start /* 第1条指令运行时的地址,也就是.text段的runtime addr,在这里是0x3000_0004*/ ldr r3, =__bss_start /* bss段的起始地址,也就是整个程序的结束地址 */ cpy: ldrb r4, [r1] strb r4, [r2] add r1, r1, #1 add r2, r2, #1 cmp r2, r3 ble cpy bl main halt: b halt
我们来分析下这段重定位:整个bin文件程序的长度(.text + .rodata + .data)为__bss_start - _start,那么我们是把bin文件从存储介质的0地址copy到程序的运行地址0x3000_0004,这样我们访问.data段时就是访问sdram中重定位后的数据段了。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>