韦东山嵌入式Linux学习——009 数据段重定位

数据段重定位

  • 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
  • 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
  • 参考资料:Using LD, the GNU linker:http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.htm,EM63A165TS(SDRAM)datasheet,S3C2440datasheet,开发版原理图
  • 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-1


一、基础知识点

  1. 程序由以下五个段组成
    {
    .text(代码段),
    .data(数据段),
    .rodata(只读数据段),
    .bss(.bss,未初始化或初始化为0的全局变量),
    .COMMON(注释段),
    }

  2. 汇编指令解析
    2.1 ldr:把数据从内存加载到寄存器
    {
     LDR指令的格式为:
     LDR{条件} 目的寄存器,<存储器地址>
     LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。
    }
    2.2 str:把数据从寄存器保存到内存
    {
     STR指令的格式为:
     STR{条件} 源寄存器,<存储器地址>
     STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。
    }
    2.3 bne:是“不相等或不为0跳转指令”

      cmp同bne搭配理解
      如:cmp r0,r1
      bne clean_bss//如果r0!=r1,就执行bne,跳转到clean_bss函数处执行,否则向下执行。

  3. C函数怎么使用lds文件中的变量abc?

    3.1 在C函数中声明改变量为extern类型, 比如:
      extern int abc;
    3.2. 使用时, 要加取址符&, 比如:
       int *p = &abc; // p的值即为lds文件中abc的值

二、什么是重定位?

  重定位就是把代码段搬移到自身想要的地址。本来程序是运行在运行地址处的,你可以通过重定位搬移到链接地址处。

三、为什么要重定位?

问题:

  • 首先:CPU能直接访问的地方有:NOR FLASH、SDRAM、SRAM和各种控制器(包括NAND flash控制器)。所以当我们的程序烧写到SDRAM或者NOR FALSH的时候,程序能直接运行
  • 如果烧写到NAND FLASH,芯片会把程序的头4K先拷贝到SRAM中执行,如果NAND flash中的程序小于4K的话,程序还能正常运行,如果大于4K,那大于4K的这部分就运行不了

解决:

  • 所以我们就引入了重定位,NAND FLASH的代码中的前4K的代码需要把整个代码拷贝到SDRAM去执行。

  • 另外,对于NOR FLASH来说,我们无法简单的去写NOR FLASH,所以一旦程序中有需要写的变量,比如全局变量和静态变量,我们在无法在NOR FLASH上直接修改它们的值。因此,我们还是需要将NOR FLASH代码重定位到SDRAM中去执行。

四、怎么重定位?

!在东山嵌入式Linxu开发板(S3C2440.v3)进行学习!
  第一步
  判断bin文件所要烧写到NOR FLASH还是NAND FLASH
  第二步
  实验一:烧写到NOR FLASH
  目的:需要把data段重定位到SDRAM
  原因:NOR FLASH中的数据只可读不可写,修改其中的数据是无效的
*************************************************************
  实验二:烧写到NAND FLASH
  目的:需要把data段重定位到SDRAM
  原因:NAND FLASH中的内存大小为4k,若程序的bin文件>4K时,前4K的代码需要把整个程序读出,放到SDRAM
  第三步
  进行代码的编写工作

四、怎么在代码中实现数据段重定位?

Makefile文件:
添加脚本文件

arm-linux-ld -T sdram.lds start.o led.o uart.o sdram_init.o  main.o -o sdram.elf

sdram.lds脚本文件:

根据《Using LD, the GNU linker》中的
SECTIONS {
	...
	secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
		{ contents } >region :phdr =fill
	...
	}
编译出:
SECTIONS {
   .text   0  : { *(.text) }   //所有文件的代码段放到起始地址为0的代码段
   .rodata  : { *(.rodata) }   //所有文件的只读数据段放到紧接着代码段的只读数据段
   .data 0x30000000 : AT(0x800)   //把data的加载地址和data的长度放到起始代码为0x800的数据段中
	{ 
		data_load_addr = LOADADDR(.data);   //data_load_addr = 加载地址
		. = ALIGN(4);                       //起到保证4字节对齐的作用
		data_start = . ;                    //data_start = 当前地址即当前地址
		*(.data)                            //添加数据段
		data_end = . ;                      //data_start = 当前地址即添加数据段后地址的地址
	}
   . = ALIGN(4);                          //起到保证4字节对齐的作用
   	bss_start = .;
	.bss      :{*(.bss),*(.COMMON)}
	bss_end = .;
	}
!0x30000000与0x800需要看反汇编代码与NOR FLASH ,SRAM,NAND FLASH的内存大小确定!

start.S汇编文件数据段重定位和清除bss,COMMON段功能实现:

bl sdram_init
	
	/* 重定位data段 */
	ldr r1, =data_load_addr   //把data_loda_addr的值传送到r1寄存器中,data段在bin文件中的地址, 加载地址
	ldr r2, =data_start       //把data_start的值传送到r2寄存器中,data段在重定位地址, 运行时的地址
	ldr r3, =data_end         //把data_end的值传送到r3寄存器中
	
cpy:
	ldr r4, [r1]      //从r1存储器中传送数据到r4寄存器中,r4 = [r1]
	str r4, [r2]      //把r4寄存器中的数据写入到r2存储器中,r4 ->[r2]
	add r1, r1,#4     
	add r2, r2,#4     
	cmp r2, r3        //比较r2-r3 =0?,!=则继续执行cpy程序
	ble cpy

	/* 清除BSS段 */
	ldr r1, =bss_start
	ldr r2, =bss_end
	mov r3, #0
clean:
	str r3, [r1]
	add r1, r1, #4
	cmp r1, r2
	ble clean

五、怎么在代码中实现全部重定位?

Makefile文件:
添加脚本文件

arm-linux-ld -T sdram.lds start.o led.o uart.o sdram_init.o  main.o -o sdram.elf

sdram.lds脚本文件:

SECTIONS {
   .text   0  : { *(.text) }   //所有文件的代码段放到起始地址为0的代码段
   .rodata  : { *(.rodata) }   //所有文件的只读数据段放到紧接着代码段的只读数据段
   .data 0x30000000 : AT(0x800)   //把data的加载地址和data的长度放到起始代码为0x800的数据段中
	{ 
		data_load_addr = LOADADDR(.data);   //data_load_addr = 加载地址
		. = ALIGN(4);                       //起到保证4字节对齐的作用
		data_start = . ;                    //data_start = 当前地址即当前地址
		*(.data)                            //添加数据段
		data_end = . ;                      //data_start = 当前地址即添加数据段后地址的地址
	}
   . = ALIGN(4);                          //起到保证4字节对齐的作用
   	bss_start = .;
	.bss      :{*(.bss),*(.COMMON)}
	bss_end = .;
	}
!0x30000000与0x800需要看反汇编代码与NOR FLASH ,SRAM,NAND FLASH的内存大小确定!

start.S汇编文件代码重定位和清除bss,COMMON段功能实现:

bl sdram_init
	
	/* 重定位data段 */
	ldr r1, =data_load_addr   //把data_loda_addr的值传送到r1寄存器中,data段在bin文件中的地址, 加载地址
	ldr r2, =data_start       //把data_start的值传送到r2寄存器中,data段在重定位地址, 运行时的地址
	ldr r3, =data_end         //把data_end的值传送到r3寄存器中
	
cpy:
	ldr r4, [r1]      //从r1存储器中传送数据到r4寄存器中,r4 = [r1]
	str r4, [r2]      //把r4寄存器中的数据写入到r2存储器中,r4 ->[r2]
	add r1, r1,#4     
	add r2, r2,#4     
	cmp r2, r3        //比较r2-r3 =0?,!=则继续执行cpy程序
	ble cpy

	/* 清除BSS段 */
	ldr r1, =bss_start
	ldr r2, =bss_end
	mov r3, #0
clean:
	str r3, [r1]
	add r1, r1, #4
	cmp r1, r2
	ble clean

提问:既然在汇编文件中可以使用链接脚本的变量,那么在C文件中可以吗,如果可以的话在C文件中实现代码重定位,清除数据段功能。
回答:是可以的,3.1小点有介绍。

下面进行代码的编译:
start.S汇编文件代码重定位和清除bss,COMMON段功能实现:

bl sdram_init

bl copy_to_sdram

bl clean_bss

在sdram.c文件中实现上述函数功能:

/*功能:复制整个text,rodata,data段到SDRAM中*/


void copy_to_sdram(void)
{
	extern int start,__bss_start;    //建立外部变量,方便获取lds文件中的量
	
	volatile unsigned int *text =(volatile unsigned int *) &start;      //text指向程序开头地址
	volatile unsigned int *end = (volatile unsigned int *)&__bss_start;    //end指向bss段开头的地址
	volatile unsigned int *src = (volatile unsigned int *)0;       //src指向0地址.即FLASH的开头地址

	while(text < end)
	{
		*text++ = *src++; 		
	}
}

/*功能:清楚全部bss段*/
void clean_bss(void)
{

	extern int __bss_start,_end;     //建立外部变量,方便获取lds文件中的量
	volatile unsigned int *_start = (volatile unsigned int *)&__bss_start;
	volatile unsigned int *end = (volatile unsigned int *)&_end;

	while(_start <= end)
	{
		*(_start)++ =0; 
	}
}

int sdram_test(void)
{
		
	int i;
	volatile unsigned char *p = (volatile unsigned char *)0x30000000; //p指针指向sdram的地址

	//写数据到sdram,从0x30000000到0x30001000都写入0x22
	for(i=0;i<=1000;i++)
	{
		p[i] = 0x22;
	}

	//从sdram读数据
	for(i=0;i<=1000;i++)
	{
		if(p[i] != 0x22)
			return -1;
	}
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嵌入内存压力测试是为了确保硬件上的内存没有问题,并且方便以后定位内存相关的问题。在这里,我们可以使用memtester软件进行嵌入设备上的内存压力测试。 要进行嵌入内存压力测试,可以按照以下步骤进行操作: 1. 首先,从memtester的官网(http://pyropus.ca/software/memtester/)上下载并解压软件。 2. 接下来,需要配置和编译memtester。由于是在嵌入设备上运行,所以需要指定相应的交叉工具编译链。修改根目录下的“conf-cc”和“conf-ld”文件,将默认编译器改为目标编译器,然后使用make命令进行编译。 3. 将编译好的可执行文件导出到目标板上。可以通过nfs服务器挂载或者将可执行文件放到根文件系统中重新烧录。推荐使用nfs服务器挂载的方进行导出。 4. 最后,运行memtester程序。使用命令"memtester [-p 内存物理起始地址] [测试内存大小B/K/M/G] [测试次数]"进行测试。其中,测试内存大小要指定,并且不要超过可用内存大小。物理起始地址和测试次数可以省略,如果省略物理地址,memtester会随机选择空闲内存进行测试;如果省略测试次数,memtester会一直进行测试,直到手动结束。在测试之前,可以使用free命令查看可用内存大小。 需要注意的是,memtester会对内存进行与运算、或运算等操作,以校验数据是否出错。它会将要测试的内存分成两部分,并赋予相同的数据。然后,对其中一部分进行运算,与另一部分进行对比。如果内存没有出错,两部分对应地址的数据应该相等,否则会报错。值得一提的是,在没有指定物理地址时,报错会打印出0x开头的地址,这是出错内存单元相对于起始内存地址的偏移量,而不是实际的物理地址。 综上所述,嵌入内存压力测试使用memtester软件能够帮助我们确保内存的稳定性,并为以后的问题定位提供便利。以下是进行嵌入内存压力测试的步骤: 1. 下载并解压memtester软件。 2. 配置和编译memtester,指定交叉工具编译链。 3. 导出编译好的可执行文件到目标板。 4. 运行memtester程序,使用合适的命令参数进行内存压力测试。 记得在测试之前,检查可用内存大小,以便指定合适的测试内存大小。 http://pyropus.ca/software/memtester/ 引用 引用

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值