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

发布者:CelestialLight最新更新时间:2023-08-09 来源: elecfans关键字:s3c2440  裸机  代码重定位 手机看文章 扫描二维码
随时随地手机看文章

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启动时无法写全局变量,那么现在有两种重定位方法:


只重定位数据段


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

对于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的容量,那么就必须连代码段也一起进行重定位,

下面这种重定位方式更好,在实际应用中也是用的下面这种方式去做的重定位。


重定位整个程序


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

我们修改链接脚本如下:


 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中重定位后的数据段了。


关键字:s3c2440  裸机  代码重定位 引用地址:s3c2440裸机-代码重定位(2.编程实现代码重定位)

上一篇:s3c2440裸机-清bss原理及实现
下一篇:s3c2440裸机-代码重定位(1.重定位的引入,为什么要代码重定位)

推荐阅读最新更新时间:2024-11-08 18:39

tq2440JLink烧写裸机程序的问题
首先回顾一下编写裸机程序的步骤: 1 .使用ADS1.2创建工程,在DebugRel setting中的Language setting中要选中ARM920T,因为用的是S3C2440的CPU。ARM Linker中的起始地址是0x30000000,首先执行的文件为2440init.o,入口函数式init。 2 .添加所需要的文件到工程中,编写Main.c文件。当然入门的话只需要写这个文件就可以了。 3 .编译文件 4 .打开JLink,连接到开发板 5 .打开调试器AXD,下载编译好的axf文件到开发板上(文件在工程内的DebugRel文件夹下) 6 .运行。。。 但是在最后运行的时候却运行不起来,调
[单片机]
tq2440JLink烧写<font color='red'>裸机</font>程序的问题
lwIP+ucos2移植于S3C2440过程
一般采用的步骤: 1.深入了解所采用的系统核心 2.分析所采用的C语言开发工具的特点. 3.编写移植代码. 4.进行移植的测试 5.针对项目的开发平台,封装服务函数. 首先,是芯片的中断处理机制,如何开启,屏蔽中断,可否保存前一次中断状态,芯片是否有软中断或陷阱指令 已经将图片移植成功,是一件很快乐的事情. 现在要将网络也加进去,看一看,想什么办法能够办到. 将lwip组织起来: lwipopts.h cc.h timer.h Timer0_Init(void) Timer1_Init(void) Delay10ms(unsigned short T) ; Undefine_Init(void) ; volatil
[单片机]
s3c2440的IIC控制
在tq2440和mini2440上都连接着EEPROM 它们作用也不过測试I2C总线能否用。 当中在mini2440上EEPROM型号是 AT24C08,在tq2440上这个型号是 AT24C02A。 它们之间容量不同。地址线也不一样。 S3C2440A RISC 微处理器能够支持一个多主控 IIC 总线串行接口。一条串行数据线(SDA)和一条专用时钟线(SCL) 连接到 IIC 总线的总线主控和外设之间。SDA 和 SCL 线都为双向的。都连接到GPE14(SCL) GPE15(SDA)。 为了控制多主控 IIC 总线操作,必须写入值到下面寄存器中: – 多主控 IIC 总线控制寄存器,IICCON – 多主控
[单片机]
<font color='red'>s3c2440</font>的IIC控制
uboot启动后在内存中运行裸机程序hello
如题,实现过程中发现3额问题,先写下来,待解答: 1、uboot启动后再dnw上打印许多信息,我想改变其中的打印信息或加上自己的打印信息以证明程序运行到何处。修改完后重新编译uboot.bin。 在DNW下执行dnw 50008000 USB下载uboot.bin到内存50008000处, go 50008000,从内存50008000处运行我刚下载的程序,发现我修改的内容并未显示。 重新执行dnw 50008000,nand erase 0 100000,nand write.uboot 50008000 0 100000,后从nand直接启动,发现我修改的内容被打印出来了。 总结:可能的原因:1、程序下载到50008000
[单片机]
openocd 命令行烧写ARM裸机程序
以前是用RVDS 的IDE来烧写调试ARM程序的,不过RVDS虽然是集成化的调试工具调试起来方便,但是有的时候只知其一,不知其二,只知道按部就班的来点击按钮,忽略了一些本质性的东西。而且RVDS还有一个不好的地方是它只能在windows平台下运行,不支持Linux OS。为了便于学习Linux,使用Openocd会是个不错的选择,可以学习gnu 汇编,Makefile编写,工具链命令行使用。 如果这些命令搞熟悉了,你还可以利用Qt 来做个自己的图形化界面烧写调试工具。(不过这只是个壳子而已,精髓在于openocd ,所以如果有时间你还可以分析一下Openocd的源码,因为它是开源的,开源的东西就是好,它可以满足你的好奇心,虽然有些
[单片机]
移植u-boot-2010.09到S3C2440(二)——ARM汇编中的LDR及ADR的区别
我在看U-BOOT的lowlevel_init.S文件时看到以下代码: lowlevel_init: ldr r0, =SMRDATA ldr r1, _TEXT_BASE sub r0, r0, r1 ldr r1, =BWSCON add r2, r0, #13*4 0: ldr r3, , #4 str r3, , #4 cmp r2, r0 bne 0b mov pc, lr 这段代码实现了U-BOOT的内存控制器部分的寄存器初始化,一共13个寄存器,对U-BOOT来最重要的就是SDRAM的初始化,显然没有这部分代码,当U-BOOT从NAND FLASH中启动的时候,START.S文件是无法完成代码的relocate的
[单片机]
S3C2440上LCD驱动(FrameBuffer)开发(一)
一、开发环境 主 机:VMWare--Fedora 9 开发板:Mini2440--64MB Nand, Kernel:2.6.30.4 编译器:arm-linux-gcc-4.3.2 二、背景知识 1. LCD工作的硬件需求: 要使一块LCD正常的显示文字或图像,不仅需要LCD驱动器,而且还需要相应的LCD控制器。在通常情况下,生产厂商把LCD驱动器会以COF/COG的形式与LCD玻璃基板制作在一起,而LCD控制器则是由外部的电路来实现,现在很多的MCU内部都集成了LCD控制器,如S3C2410/2440等。通过LCD控制器就可以产生LCD驱动器所需要的控制信号来控制STN/TFT屏了。 2. S3C2440内部LC
[单片机]
【JZ2440笔记】裸机实验点亮LED
一、前言 最近在学韦东山的JZ2440开发板,于是记录下学习过程中的笔记。一般学程序写的第一个例子是打印“Hello World”,而学单片机的第一个例子一般都是点亮LED,学ARM的话如果从裸机开始学,也跟玩单片机差不多,从点亮LED开始。 二、实验步骤 1、目标 点亮开发板上的3个LED灯。 2、硬件连线 点亮LED需要S3C2440芯片的IO引脚GPF4、GPF5、GPF6输出低电平即可。 3、寄存器配置 和玩单片机一个套路,配置IO模式(GPFCON寄存器)和要输出的电平(GPFDAT)就行了。即GPFCON寄存器给0x00001500把GPF4、5、6配置成输出模式,GPFDAT给0x00
[单片机]
【JZ2440笔记】<font color='red'>裸机</font>实验点亮LED
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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