s3c2440裸机-代码重定位(2.编程实现代码重定位)
代码重定位(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启动时无法写全局变量,那么现在有两种重定位方法:
只重定位数据段
只重定位数据段的过程用下图更直观:
对于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裸机-代码重定位(2.编程实现代码重定位)的更多相关文章
- Windows PE 重定位表编程(枚举重定位地址)
原理之前单独总结过,在这里: http://blog.csdn.net/u013761036/article/details/54051347 下面是枚举重定位信息的代码: // ReLocation ...
- s3c2440裸机-代码重定位、清bss的改进和位置无关码
1.代码重定位的改进 用ldr.str代替ldrb, strb加快代码重定位的速度. 前面重定位时,我们使用的是ldrb命令从的Nor Flash读取1字节数据,再用strb命令将1字节数据写到SDR ...
- s3c2440裸机-时钟编程(二、配置时钟寄存器)
s3c2440裸机编程-时钟编程(二.配置时钟寄存器) 1.2440时钟时序 下图是2440时钟配置时序: 1.上电后,nRESET复位信号拉低,此时cpu还无法取指令工作. 2.nRESET复位信号 ...
- 十三、S3C2440 裸机 — 初始化代码及MMU
13.1 NOR FLASH 搬运 把程序从 nor flash 上搬运到 SDRAM 中 程序存储在 nor flash 上,运行时将程序搬运到 SDRAM 中运行 nor flash 启动:nor ...
- linux上使用J-Link调试S3C2440裸机代码
linux上使用J-Link调试S3C2440裸机代码 工具: segger的jlink仿真器 segger的jlink for linux 交叉编译工具链里面的arm-xx-linux-xx-gdb ...
- 【转载】s3c2440裸机开发调试环境(MDK4.6,Jlink v8,mini2440)
用于arm裸机程序开发的IDE基本有 以下3个:MDK,IAR,还有ADS.具体它们的具体情况在这里我就不多说了,百度一下就明白了.由于之前开发c51,stm32时候都使用了MDK开发环境,而且MDK ...
- iOS6定位服务编程详解
现在的移动设备很多都提供定位服务,使用iOS系统的iPhone.iPod Touch和iPad都可以提供位置服务,iOS设备能提供3种不同途径进行定位:Wifi, 蜂窝式移动电话基站, GPS卫星 i ...
- iOS定位服务编程详解
现在的移动设备很多都提供定位服务,使用iOS系统的iPhone.iPod Touch和iPad都可以提供位置服务,iOS设备能提供3种不同途径进行定位:Wifi, 蜂窝式移动电话基站, GPS卫星 i ...
- FastMM 定位内存泄露的代码位置
FastMM 定位内存泄露的代码位置 开源的FastMM,使用很简单,在工程的第一行引用FastMM4即可(注意,一定要在第一个Uses的位置),可以在调试程序时提示内存泄露情况,还可以生成报告. 在 ...
随机推荐
- ELK filebeat的安装
安装参考官方文档: https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-installation.html 注意事项: Fr ...
- HDU1907 Jhon
Little John is playing very funny game with his younger brother. There is one big box filled with M& ...
- Yii2框架那些折磨人的坑
说点闲话 距离上次写博客,已经有一年了.在动手写之前,总是带着深深的罪恶感.被它折磨许久,终于,还是,动手了. 值得庆祝的一件事:最近开始健身了.每天动感单车45分钟,游泳45分钟,真的是(生)爽(不 ...
- Crush 算法以及PG和PGP调整经验
PG和PGP调整经验调整前准备为了降低对业务的影响,需要调整以下参数ceph tell osd.* injectargs ‘–osd-max-backfills 1’ceph tell osd.* i ...
- Python3 数据结构之词频统计(英文)
import string path = r'C:\Users\Black\Desktop\Walden.txt' with open(path, 'r', encoding='utf-8') as ...
- JS中&&和||的理解
运算符可以从三个不同的层次进行理解. 第一层理解 当操作数都是布尔值时,"&&"对两个值执行布尔与(AND)操作. 复制代码代码如下: x==0 && ...
- android之 xml文件一般用到的属性
android:layout_above 将该控件的底部至于给定ID的控件之上android:layout_below 将该控件的顶部至于给定ID的控件之下android:layout_toLeftO ...
- prometheus动态刷新rule
Prometheus的target是支持动态更新的,例如通过file_sd_configs配置将target放置到yaml文件中,当yaml文件中的内容发生变化时,Prometheus会自动更新自身的 ...
- linux指令-date
1.在linux中要显示日期,则可以直接输入指令date 2.如果想以这样2016/12/26的方式输出呢,那就是,Y是年份,m是月份,d是日 date +%Y/%m/%d 3.如果要显示时间,则da ...
- Ansible自动化部署入门到进阶笔记
目录 一.基本部署 安装Ansible Ansible配置文件 定义Inventory 使用秘钥方式连接 使用帮助 Ansible命令应用基础 二.常见模块 三.Ansible playbook 四. ...