在上一章中我们引入了为什么要使用代码重定位。
那么本章我们要讲解代码重定位的具体实现。
在实现的重定位的时候,我们需要引入链接脚本的概念。
也就是我们把代码重定位的起始和结束,具体内容的放置等等这些配置放到一个文件中。
然后由Makefile在编译的过程中来调用这个 *.lds 文件,然后执行代码的各个段的位置配置。
首先我们做了一个实验,在不实用lds链接脚本的情况下,直接在Makefile中做代码重定位。
然后发现生成的bin文件多达800M之多。

如上图,将 -Tdata 段(全局变量和static变量)设置放到SDRAM中,0x30001000处。
编译后可以看到:

有800MB+的内存占用。
查一下反汇编的dis文件可以看到:
我们实际代码结束段和我们重定向data的地址差

中间跨过的地址空间占0x30000000-0x3cc = 30000C34 (这中间的巨大空白脚 Hole)
所以为了缩小bin文件的大小我们才使用了lds文件。
将Makefile修改如下:

然后设置 sdram.lds文件:

在这里为什么我们的lds文件要写成这样的格式:
请参考链接:http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html
这里有规定链接脚本的格式,如何定义变量等等规则。
再看下我们的代码分布:

然后我们分析一下这个链接脚本的具体配置:(后面写这个链接脚本的时候我把重定位地址改成了0x30000000而不是前面的0x30001000, 当然这个地址只要是在SDRAM范围内都可以)
//首先这个Section是固定格式
SECTIONS{
// text存放的是我们的代码段
.text 0 : { *(.text) }
// rodata存放的是我们的只读数据段
.rodata : {*(.rodata)}
//0x30000000 这里是我们声明全局变量和静态变量存放的地址
//0x800是全局变量和静态变量实际的存放地址
.data 0x30000000 : AT(0x800)
{
//LOADADDR(.data):data段在bin文件的地址。 data_load_addr是我们定义的一个变量
data_load_addr = LOADADDR(.data);
//CPU去数据的地址, 这里的 "."表示当前地址,
data_start = .;
//存放全局变量和静态变量数据的长度
*(.data)
//当前地址,也就是存放全局和静态变量后的地址
data_end = .;
}
//存放bss段和注释段
.bss : {*(.bss) *(.COMMON)}
}
完成后我们还需要初始化sdram,并且把data段从bin文件中拷贝到SDRAM。只有当这些准备工作都完成无误后,CPU在指定的重定位地方才能读到全局变量和静态变量。
这部分工作需要在start.S中还没进入到main之前完成。
.text
.global _start
_start:
/*stop watchdog*/
ldr r0, =0x53000000
ldr r1, =0x00
str r1,[r0]
/* config MPLL FCLK : HCLK : PCLK = 400MHz : 100MHz : 50MHz */
/* LOCKTIME = 0xFFFFFFFF */
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFF
str r1, [r0]
/* CLKDIVN(0x4C000014) = 0x5
* tFCLK : tHCLK : tPCLK = 1:4:8
*/
ldr r0, =0x4C000014
ldr r1, =0x5
str r1, [r0]
/* config CPU mode asynchronization */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* config MPLL(0x4C000004) = (92<<12) | (1<<4) | (1<<0)
* m = MDIV+8 = 92+8=100
* p = PDIV+2 = 1+2 = 3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
*/
ldr r0, =0x4C000004
ldr r1, =(92<<12) | (1<<4) | (1<<0)
str r1, [r0]
/* notice: once config valid,
* CPU will work at new FCLK
*/
/* config sp, estimate whether initial
* from NOR flash or NAND flash
* test method:
* write 0 to start address
* read data from 0 address,
* if read data is 0, then initial from NAND flash
* if read data not equal to 0, then initial from NOR flash
*/
mov r1, #0
ldr r0, [r1] /*read out src value for backup*/
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2 = 0 */
cmp r1, r2 /* if r1==r2 nand initial, else nor initial*/
ldr sp, =0x40000000 + 1024*4 /* assumption initial from nor */
moveq sp, #1024*4 /* initial from nand */
streq r0, [r1] /* recovery src value */
/* init sdram */
bl sdram_init
/* relocate data */
ldr r1, =data_load_addr /* data段在bin文件中的地址, 加载地址 */
ldr r2, =data_start /* data段在重定位地址, 运行时的地址 */
ldr r3, =data_end /* data段结束地址 */
/* 把data段从r1的地方拷贝到r2的地方 */
cpy:
ldrb r4, [r1]
strb r4, [r2]
add r1, r1, #1
add r2, r2, #1
cmp r2, r3
bne cpy
bl main
halt:
b halt
从sdram_init到 跳转至main之前的部分都是在做重定向工作。
这个时候我们再次编译code。可以看到:

bin文件从800MB+一下变成2KB+了。
烧录到NorFlash后看下串口的现象:

全局变量的值可以改变了。
说明我们重定位成功了。全局变量在SDRAM中,所以在执行自增的时候,变量值可以改变。
main代码如下:
/**
******************************************************************************
* @file : main.c
* @version : none
* @date : 2019-09-02
* @brief : test led & uart
******************************************************************************
* @attention : JZ2440
* @author : shao15232
* @personal : shao15232 work stdio
******************************************************************************
*/
#include "led.h"
#include "uart.h"
char g_char = 'A';
const char g_char2 = 'B';
int g_A = 0;
int g_B;
void delay(unsigned int count)
{
while(count--);
}
int main(void)
{
unsigned char c;
uart_init();
puts("Hello, Welcome to JZ2440 world!\r\n");
while(1)
{
putchar(g_char);
g_char++;
delay(1000000);
}
return 0;
}
/* This experiment is test for nand flash and nor flash distinction
* if initial from nand flash, then g_char variable will increase by itself
* if initial from nor flash, then nor flash can't write. then g_char can,t increase itself.
* it's will always putchar AAAA....
*/
再看下反汇编dis中g_A、g_B的地址:

在我们重定向的0x30000000开始的地方
本文详细介绍了在S3C2440和JZ2440平台进行代码重定位的过程,通过lds链接脚本实现更高效的内存管理。内容包括为何使用lds脚本,如何通过Makefile调用lds文件,以及如何分析和配置链接脚本,以减少bin文件大小。在完成重定位后,还需在start.S中初始化SDRAM并复制data段,确保全局变量和静态变量在SDRAM中正确工作。

3万+

被折叠的 条评论
为什么被折叠?



