10.代码重定位

目录

1.为什么需要代码重定位?

2.Nand Flash 和Nor Flash 的区别 

3. 程序段的组成

4.代码重定位思路:

5.链接脚本

6.初始化bss段数据

7.定位代码和链接脚本的改进

8.重定位全部代码

9.使用C语言编写重定位代码

源代码:


1.为什么需要代码重定位?

首先以SOC芯片S3C2440为例,他的内部结构图以及外接内存设备简图如下:

CPU可以通过内存控制器直接访问SDRAM.NOR FLASH,片内SRAM但是没有办法直接访问NAND FLASH.

所以程序在SDRAM.NOR FLASH,片内SRAM,中可以直接运行,不能在NAND FALSH运行.

 但是我们可以发现,当把程序烧写到Nand Flash 的时候,为什么还可以设置为Nand启动呢?

这里有一个硬件机制?

1).如果设置为Nand启动,系统一上电,2240的内部硬件,会把Nand Flash前4K的数据直接拷贝到SRAM.

2).CPU从SRAM的0地址开始运行(因为CPU的地址可以直接发送到达SRAM,可以运行里面的bin文件)。

如果程序在Nand Flash的大小超过了4K,会怎样?

首先,把Nand Flash的前面4K复制到SRAM,那在Nand中大于4k的代码怎么办?

答:可以这样做,可以把Nand中前4k的代码作为一个功能代码,这个代码的功能就是把NAND FLASH的所有代码搬移到SDRAM运行(因为CPU的地址可以直接访问SDRAM),这个过程称之为重定位(重新确定程序的地址)。


2.Nand Flash 和Nor Flash 的区别 

NOR FLASH 和NAND FLASH 的区别
  NOR FLASH NAND FLASH
结构 NORflash采用内存的随机读取技术。各单元之间是并联的,对存储单元进行统一编址,所以可以随机访问任意一个字,应用程序可直接在flash内运行,而无需先拷贝到RAM。 NANDflash数据线和地址线共用I/O线,需额外联接一些控制的输入输出
读写速度 NOR flash有更快的读取速度 NAND flash有更快的写、擦除速度
可靠性 NOR的擦写次数是10万次

NAND的擦写次数是100万次

(NAND器件的坏块是随机分布的)

成本和容量 在面积和工艺相同的情况下,NAND的容量比NOR大的多,成本更低
易用性 NOR flash有专用的地址引脚来寻址,较容易和其他芯片联接,还支持本地执行 NAND flash的IO端口采用复用的数据线和地址线,必须先通过寄存器串行地进行数据存取。各厂商对信号的定义不同,增加了应用的难度
编程

NOR flash采用统一编址(有独立地址线),可随机读取每个“字”,但NOR flash不能像RAM以字节改写数据,只能按“页”写,故NOR flash不能代替RAM。擦除既可整页擦除,也可整块擦除

注意:

flash进行写操作时,只能将相应的位由1变0,而擦除才能把块内所有位由0变1。所有写入数据时,如果该页已经存在数据,必须先擦除再写。

 NAND flash共用地址线和数据线,页是读写数据的最小单元,块是擦除数据的最小单元

NOR FLASH 的特点是:

可以向内存一样的读取,但是不能直接写。

例子: 在Nor Flash中有以下指令:

mov R0,#0      --R0赋值为0

LDR  R1 ,[R0]    --读取地址R0的值到寄存器R1

STR  R1 ,[R0]    --把R1的值写入R0寄存器

当执行STR  R1 ,[R0] 不会成功,被视为无效操作。

这样子如果程序烧写在NOR FLASH 中会出现什么问题呢?

程序中含有需要更改的全局变量/静态变量(变量存储在栈中),但是如果现在bin文件烧写到NOR FLASH 上,那么意味着不能修改变量的值了,程序就会达不到我们预期的效果。所以如果想要的到想要的结果,就需要把这些全局变量/静态变量,重定位放到SDRAM(可读可写)中。

例子:

写一个程序,在程序中修改全局变量的值并打印出来,分别烧写到NOR Flash 和Nand Flash,观察效果:

编写的主函数如下所示:

#include "led.h"
#include "myprintf.h"

char global_char01 = 'A';

int main()
{
	
	uart0_init();
	myprintf(
"Hello world!\r\n");
	while(1)
	{
		putchar(global_char01);
		global_char01++;
		delay(1000000);
		
	}
	return 0;

}

首先打印出字符A,然后再加加一次,再次打印,我们预期的结果是打印出:ABCD.....

这里有一点需要注意:

修改Makefile,编译的时候指定数据段的起始位置。0x800,否则生成bin文件的大小就会大于4k,详情点我查看:点我

接着上传到Linux编译:

把bin文件传回window使用oflash分别烧写到NOR FLASH 和NAND FLASH.

烧写到Nand Flash 并从Nand Flash 启动:

连接开发版串口,打开串口,观察如下,和预测一样:

烧写到Nor  Flash 并从Nor Flash 启动:

连接开发版串口,打开串口,观察如下,一直打印出A,说明程序中的全局变量的值并没有被改变,一直打印出旧的数值:

而且打印的速度也明显比烧写在Nand Flash的慢:

小结:所以说Nor Flash能直接读取,但是是不能直接写的,这就导致了,虽然在函数中修改了全部变量的值,结果还是不变。


 分析原因:查看反汇编代码:


3. 程序段的组成

段的概念:段是程序的一部分,整个程序的所有组成部分,分成一个一个段,并且给每个段起一个名字,然后在链接时就可以用这个名字来指示这些段,使得这些段排布在合适的位置。

  • 编译器链接器内部段名,如:

代码段(.text) :又叫文本段就是函数编译后生成的东西;

数据段(.data):数据段在C语言中为显示初始化为非0的全局变量;

bss段                 :又叫zero initial段,就是零初始化段,对应C语言中初始化为0的全局变量(不保存在bin文件中);

rodata                 : const全局变量

comment             : 编译器注释信息(不保存在bin文件中)

注意:

(1)如何保证C语言中全局变量未显式初始化为0:本质上就是将这类全局变量放在了bss段当中,在每次设置栈时,都会先清除bss段,使得bss段的数据是干净的全部为0;

(2)C语言运行过程中如何保证data段的数据在main函数之前被赋值:因为data段会在main函数之前被处理。
 

修改上面的主函数,增加一些全局变量,如下:

再次编译程序,查看反汇编代码,可以发现在程序中多出了几个段:

可以查看一下,生成bin文件的大小并没有变化,说明只读段(.rodata)和bss段并不写入bin文件。


4.代码重定位思路:

在Makefile中指定了,data段的起始位置是  0x800

如果从NOR启动,那么CPU的0地址就是NOR Flash的0地址。片内SRAM的起始地址就是0x40000000

如果从Nand启动,那么CPU的0地址就是SRAM的0地址。此时NOR FLASH(大小为2M)为不可见状态

那么现在的思路是什么,修改Makefile,让Nor Flash 中的数据段(全局变量)放到SDRAM中去执行呢?

备注:SRAM的地址是0x30000000开始

如下放置代码行不行呢?

可以发现编译出来的bin文件很大,既然有几百M,Nor Flash的大小只有2M,根本不可能,而且不切实际。

解决方法思路:重定位代码,把全部变量重定位到SDRAM(可读可写)

1).只重定位数据段(全局变量)

在bin文件中,把代码段和数据段靠近,尽量减少bin文件大小。

接着烧写到NOR Flash,那么在NOR FALSH 中布局和上面一样:

从Nor Flash中启动,运行程序时,把数据段重定位:

2).重定位全部代码

在链接的时候,让代码段尽量靠近代码段的位置:

烧写到Nor Flash中

运行程序时,把整个程序进行重定位到SDRAM:


5.链接脚本

5.1.什么是链接脚本?

每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 也可以用连接命令做一些其他事情.

连接器有个默认的内置连接脚本, 可用ld –verbose查看. 连接选项-r和-N可以影响默认的连接脚本(如何影响?).

-T选项用以指定自己的链接脚本, 它将代替默认的连接脚本。你也可以使用以增加自定义的链接命令.

而我们的目的就是想通过自定义链接脚本来解决一些复杂的问题。

5.2.SECTIONS的作用:

SECTIONS命令告诉ld如何把输入文件的sections映射到输出文件的各个section: 如何将输入section合为输出section; 如何把输出section放入程序地址空间(VMA)和进程地址空间(LMA).

该命令格式如下:

SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
  { contents } >region :phdr =fill
...
}

备注:下划线一般不需要

secname   :段的名字,可以随意取

start         :起始地址,运行时地址(重定位地址)【Runtime Addr或Relocate Addr】

AT ( ldadr ): 加载地址[Load addr],不写这个选项时,加载地址等于运行时地址

contents   : 内容。格式举例:

1.可以指定在这个段只放置start.o的内容。

2.可以把所有的文件的数据段(*(.text))放置在这个段里面。

3.把start.o放在最前面,把所有文件的数据段放在start.o的后面。

5.3.VMA和LMA的区别

输出section通常包含两个地址: VMA(virtual memory address虚拟内存地址或程序地址空间地址)和LMA(load memory address加载内存地址或进程地址空间地址). 通常VMA和LMA是相同的.

在目标文件中, loadable或allocatable的输出section有两种地址: VMA(virtual Memory Address)和LMA(Load Memory Address). VMA是执行输出文件时section所在的地址, 而LMA是加载输出文件时section所在的地址. 一般而言, 某section的VMA == LMA. 但在嵌入式系统中, 经常存在加载地址和执行地址不同的情况: 比如将输出文件加载到开发板的flash中(由LMA指定), 而在运行时将位于flash中的输出文件复制到SDRAM中(由VMA指定)

5.4.链接脚本如何使用

在Makefile链接时使用自定义的链接脚本进行链接,如下

链接脚本例子:

以前的Makefile

现在需要很多参数的设定简单设定链接参数已经没有办法满足要求,所以需要引入链接脚本,新建一个文件sdram.lds,这个文件就是链接脚本文件,然后替换编译选项,如下:

备注:-T选项用以指定自己的链接脚本, 它将代替默认的连接脚本

编写sdram.lds内容

编译一下,生成了一个bin文件,但是可以发现这个文件很大:

这是因为在代码段和数据段之间有一个很大的空洞是没有放置东西的。

修改链接脚本把数据段放在0x800的位置,运行时位置在0x30000000

如下:

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

重新编译,可以发现文件bin文件变小了:

此时可以查看一下反汇编文件如下:

对应的main函数就是下面这个部分:

此时再次烧写到2440试一下,发现输出的是一些乱码,或者是我们不想要的结果,怎么回事呢?

分析原因:

bin文件的结构大致如下:

烧写到NOR Falsh 之后拷贝到SRAM中也是如下的结构,但是我们编写了链接脚本,main程序运行时,是去0x30000000位置来访问的。但是在进入main 函数之前,在0x30000000这个地址我们并没有去设置它的初始值,所以在这个地址是一个垃圾值,现在需要做的就是修改启动文件 start.S在跳转到main函数之前,把数据段的代码重定位到0x30000000的位置(也就是把g_Char变量初始值写到0x30000000的位置)

此时注意了:因为0x30000000位置使用到了SDRAM,所以在使用前一定要先初始化SDRAM,让我们能正常访问这些内存。所以修改Start.S启动文件。

在跳转到主函数之前,先初始化SDRAM,然后重定位:

上传到Linux系统进行编译,然后传回window系统烧写到2440,分别烧写这个bin文件到 Nand 和 Nor Flash,可以发现无论是从Nor或者Nand启动,输出结果都是一样的。烧写在Nor中程序正常运行了,说明重定位成功了。

备注:这部分源代码的命名为:重定位01


上面的例子算是重定位成功了,它的原理是什么呢?

在主函数开始之前,从0x800的位置,复制一个字节的数据到0x30000000地方,接着执行主函数,主函数运行时在读取g_Char(全部变量)的时候是去0x30000000读取的,这可以从反汇编代码中看出来,这是我们想要的结果。

问题来了?现在程序很简单只有一个变量,但是如果你不知道变量的个数呢,那么复制几个字节数据到SDRAM?

你是如何知道从0x800的地方复制?

那是因为我们设置了链接脚本设置的,但是这样就导致了一个程序的不灵活,而且修改程序重新编译的时候需要考虑很多的东西。所以如果让重定位代码变得灵活通用呢?

接着还是例子演示:

首先修改链接脚本:sdram.lds

1).必须灵活测量出数据段的长度,可以在数据段的开始记录,然后到数据段的结束再次记录,两次记录就可知道数据段的大小了

2).在重定位代码中,直接写0x800,显得不灵活,那就动态决定这个值:变量名为:data_load_addr

使用LOADADDR(段名) 这个函数来确定变量的值,修改如下:

3).修改启动文件start.S

4).修改mian函数,增加几个成员变量,测试然后编译测试程序:

修改如下:

数据段的变量共占用1+1+4=6个字节,但是因为4字节对齐所以占用8个字节。 

5).上传Linux系统编译:

也可以从反汇编代码看出来:

6).烧写运行程序,结果如下:

备注:这部分源代码的命名为:重定位02

备注:

1). 点的作用

.(点) 是一个特殊的符号,它是定位器,一个位置指针,指向程序地址空间内的某位置(或某section内的偏移,如果它在SECTIONS命令内的某section描述内),该符号只能在SECTIONS命令内使用。

2).内建函数 LOADADDR()

LOADADDR(SECTION) :返回SECTION的LMA(load memory address加载内存地址或进程地址空间地址)

例子:

如下返回的就是 AT(LMA)里面的LMA


6.初始化bss段数据

在上面程序的基础之上修改代码。

我们试着以16进制的方式打印一下bss段代码的数值。

在uart.c文件中,编写一个函数以16进制的方式打印出变量的值,main函数修改如下:

分别打印初始值为0的变量和初始值为10的变量。

编译程序烧写到Nand或Nor Flash上,观察程序运行结果:

这是因为初始化为0的值存在于bss段,并没有在bin文件中出现,所以其值并没有进行初始化,所以我们在程序还没运行之前应该手动将他们进行初始化。

思路:

我们现在需要将bss段的起始到结束的位置进行清零,那么就需要知道bss段起始地址,这当然是使用链接脚本来做啦,然后再启动文件,还没有进入main函数之前就对bss段进行清零:

1).修改sdram.lds 链接脚本:

2).修改start.S ,清除bss段的数据

3).编译烧写,观察一下在main中初始化为0的值是否变成0了。

备注:这部分源代码的命名为:重定位03


7.定位代码和链接脚本的改进

再次回忆一个重定位的概念,代码重定位就是把全局变量(数据段)的代码在拷贝到SDRAM运行,在这里Nor Flash是16位的,而Nand Flash是32bit的,但是我们却每次只传输一个字节,这样做无疑增加了硬件和软件的负担,尤其是硬件。

比如我们需要把20字节的数据从NOR Flash 传输到SDRAM ,以字节访问时如何运作的。

从Nor Flash 取出数据,需要访问硬件20次,把数据写入SDRAM,也需要访问硬件20次,而且在SDRAM中还需要通过控制四个DQM引脚选择要写哪个字节。这就是资源利用不到位,带来的负担。

改进:

使用 ldr 和str 命令一次性读取四个字节和写四个字节数据。

这样一来,Nor 操作的次数是5次,访问硬件的次数是10次(一次两个字节),SDRAM,访问硬件的次数是5次(一次4字节),总共访问硬件次数是15次。


例子:修改重定位代码的传输字节大小

1).修改如下:

cpy:
	  ldr r4, [r1]  /*把r1的地址加载到r4,即r4=data_load_addr*/
	  str r4, [r2]  /*把r4=data_load_addr的值写入[r2=data_start]的位置*/
	  add  r1,r1, #4 /*r1地址自增*/
	  add  r2,r2, #4 /*r2地址自增*/
	  cmp  r2,r3	 /*比较r2和r3的值,如果r2小于r3,还没完成*/
	  ble  cpy		 /*否则继续执行拷贝命令*/
	  /*清除bss段数据*/
	  ldr r1,=bss_start  /*r1记录bss起始位置*/
	  ldr r2,=bss_end	 /*r2记录bss结束位置*/
	  mov r3,#0			 /*对bss写的值*/
clean:
	  str r3,[r1]		/*把1字节的数据r3写到bss段起始位置*/	
	  add r1,r1,#4		/*清除下一个字节*/
	  cmp r1,r2			/*判断是否清楚完毕*/
	  ble clean			/*未清除完毕,继续执行clean*/

2).上传编译,烧写,调试:

可以发现程序还是正常运行:


在上面的基础上把一个变量给注释掉,然后重新烧写试一下,如下:

重新上传编译烧写:

发现只输出了g_Int变量的值?

怎么回事?一个变量的有无会影响一个程序的运行?

因为在链接脚本里,bss是紧跟数据段的,在清除bss段的时候误把data段的数据也清楚了。看一下反汇编文件即可明白:

这时候问题老了,好像g_Char 和g_Char3全部变量存放在0x30000000的位置啊,看一下:

这样一来即使我们在start.S重定位了代码,最后还是在清除bss段的时候被清除了。

现在问题找到了,如何解决?

可以修改链接脚本,是清除的数据段向四字节对齐,比如本来需要从0x30000002开始清除,但是加入向四字节对齐之后0x30000002不是四字节对齐,四字节对齐之后就是0x30000004了,这样就就不会破坏代码段的数据了。

1).修改链接脚本

2).编译,烧写:

现在就可以正常的执行了,可以查看反汇编文件。

地址向4对齐了之后就不会破坏到data段的数据了

备注:这部分源代码的命名为:重定位04 


8.重定位全部代码

如何写位置无关码?

1.使用相对跳转命令。

2.重定位之前,不可使用绝对地址,不可以访问全局变量/静态变量。也不可有初始值的数组(因为初始值放置在rodata段,使用绝对地址来访问)

3.重定位之后,使用命令 ldr pc , =  xxx ,进行绝对跳转。

例子:

1).修改链接脚本:

2).修改启动文件

3).编译,烧写,运行:

结果正常:

分析程序:

1)bin文件的构成:

2).烧写到Nor Flash 上

3).从NOR FLASH启动,从0地址开始运行,在Nor Flash代码段的代码功能,需要把整段代码复制到SDRAM,其实就是在启动代码里面来写这段代码。

其中主要代码是以下这段:

4).查看反汇编代码

可以看到程序的地址是从0x30000000开始运行的,这也是我们在链接脚本中指定的运行时地址

这里有一个问题需要注意:

其中bl 30000490 不是跳转到地址0x30000490,可以反推一下,此时SDRAM还没有初始化完成,肯定是不能跳转到这个地址里去呀。

可以做一个实验,比如把链接脚本里面的0x30000000改成0x20000000,然后重新编译一下,查看反汇编代码:

现在变成bl 20000490了,难道是跳转到0x20000490这个地址吗?

把0x20000000改成0x22000000,重新编译,查看反汇编代码:

这时候可以发现一些规律:

他们的机器码都是:0xeb00010b,如果说此处的bl是地址跳转,那么他们的机器码就应该是不一样的。

那么bl跳转是,当前pc指针的值+ offset(偏移)。而offset是链接器算出来的。不是一个固定值

其中当前pc值就是该指令的地址。

比如设置链接脚本的重定位地址是0x30000000,运行到0x3000005c,执行完这个地址的命令,跳转到0x30000490.

如果程序0x20000000开始运行,运行到0x2000005c, 执行完,跳转到0x20000490.

如果程序0x22000000开始运行,运行到0x2200005c, 执行完,跳转到0x22000490.

此时,这个offset的值就是0x490-0x5c = 1076

因此,bl 后面跟一个地址,它并不是跳转到这个地址,而是取决于当前指令的位置,在反汇编写出这些值,只是为了让人更容易分析代码。

总结:

1.在反汇编文件(.dis)中 B 或者BL指令只是起一个提示作用。不是地址的跳转

2.从这个例子可以知道,虽然我们指定了运行时地址是0x30000000,但是我们把它烧写到NOR Flash的0地址仍然可以运行,因为它进行函数跳转的时候,使用的是偏移地址,他可以跳转到正确的函数执行。

3.写位置无关码的时候,跳转的时候需要使用 B 或者 BL命令

5).相对跳转和绝对跳转的问题

现在有一个问题,当我们把bin文件烧写到NOR FLASH 时候,开始运行时就对整段代码进行重定位,但是跳转到main函数时使用的还是bl 命令,也就是相对地址,那么他是跳转到NOR  Flash 的main函数,如下简图:

在启动文件中,跳转到mian函数的是相对跳转

简图如下,所以跳转到main函数还是在NOR FLASH 运行

所以需要使用绝对跳转命令,跳转到0x300005d8

6).修改启动文件,在重定位之后,main函数使用绝对跳转,在SDRAM运行。

7).编译烧写运行,正常运行,现在程序就完全定位到了SDRAM了程序在SDRAM中运行。

备注:这部分源代码的命名为:重定位05 


9.使用C语言编写重定位代码

9.1.编写两个c语言的函数,用于代码重定位,和清除bss段,分别如下:

/*需要的参数:1.复制的原地址
*		     2.复制的目的地址
*		     3.复制的长度
*/
void copy_to_sdram(volatile unsigned int *src,volatile unsigned int *des,unsigned int len)
{
	unsigned int i=0;
	while(i<len)
	{
		*des++ = *src++;
		i+=4;
	}	
}
/*需要的参数
    1.清除的起始地址
*	2.清除的结束地址
*/


void clean_bss(volatile unsigned int *start,volatile unsigned int *end)
{
	while(start<=end)
	{
		*start++=0;
	}
}

9.2.在启动文件中首先为这两个函数提供参数,然后调用函数:

如下:

9.3.编译烧写运行,程序正常执行

备注:这部分源代码的命名为:重定位06


既然能在汇编语言获得源地址,目的地址,等参数,那么在C语言中应该也能获得这些参数啊?如何做?

1).修改C语言程序如下:

void copy_to_sdram(void)
{

	/*从sdran.lds链接脚本的到代码的运行地址
	 *从外部得到_code_start,_bss_start
	 *接着从0地址复制数据到运行时地址
	**/
	//定义两个变量,_code_start 用于获取链接脚本的代码起始位置,_bss_star
	//用于获取代码结束位置,也是bss段的起始位置
	extern int _code_start,_bss_star;  
	//定义三个指针变量,分别是重定位代码的位置(运行时地址)
	//代码段结束位置
	//需要重定位的代码
	volatile unsigned int *des =(volatile unsigned int *)&_code_start;
	volatile unsigned int *end =(volatile unsigned int *)&_bss_star;
	volatile unsigned int *src =(volatile unsigned int *)0;
	//进行复制,把原地址代码复制到目的地址,代码重定位
	while(des<end)
	{
		*des++ = *src++;
	}
	
}
//清除bss段
void clean_bss(void)
{
	extern int _bss_star,_bss_end;
	volatile unsigned int *start =(volatile unsigned int *)&_bss_star;
	volatile unsigned int *end =(volatile unsigned int *)&_bss_end;
	
	while(start<=end)
	{
		*start++=0;
	}
	
}

注意点:

1.需要把 _code_start,_bss_star,_bss_end 声明为外部变量

2.使用变量时需要加上取地址的符号

3.为什么在汇编语言使用的时候不需要取地址,在C函数中需要取地址

在c函数中,定义一个全局变量 int g_A;

 程序中必然有四字节控件来保存这个变量g_A;

假设lds里面有很多变量(1万个),我们C程序可以不用去保存它,所以在lds中有一个取巧的办法就是,它有一个symbol table ,它保存了这些常量(在链接时得到)。这个符号表只在编译的时候辅助链接,不会存放到程序里面,所以不会影响到程序的大小。

  • 结论:

1.c程序中不保存lds文件中的变量

2.怎么使用lds的变量,lds里面的是一些固定的值,编译程序时,有一个symbol table 符号表

例如有多个变量:g_A,g_A.... ,则有一个表保存

然而在lds中使用symbol table 来保存lds 变量,此时应该称为常量,其来源于链接脚本,在链接时候确定

在c语言中如何使用呢?

想要的到变量的值,那么应该是取地址了

2).修改链接脚本,为_code_start,_bss_star,_bss_end 这几个变量提供信息:修改如下:

SECTIONS
{
	. = 0x30000000;
	_code_start = . ;
	. = ALIGN(4);
	.text      :
	{
	  *(.text)
	}

	. = ALIGN(4);
	.rodata : { *(.rodata) }

	. = ALIGN(4);
	.data : { *(.data) }

	. = ALIGN(4);
	_bss_star = .;
	.bss : { *(.bss) *(.COMMON) }
	_bss_end = .;
}

3).修改启动文件start.S,只需要调用重定位代码和bss段清除代码即可

4).编译烧写运行:

备注:这部分源代码的命名为:重定位07


源代码:

重定位01:https://download.csdn.net/download/qq_36243942/10902995

重定位02:https://download.csdn.net/download/qq_36243942/10903276

重定位03:https://download.csdn.net/download/qq_36243942/10904127

重定位04:https://download.csdn.net/download/qq_36243942/10904311

重定位05:https://download.csdn.net/download/qq_36243942/10906851

重定位06:https://download.csdn.net/download/qq_36243942/10906959

重定位07:


发布了91 篇原创文章 · 获赞 247 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_36243942/article/details/85935662
10.