当前位置: 首页 > news >正文

重定位(2)

一、链接脚本实例:

我们来句句分析这些链接脚本的实例:

. 代表当前地址

SECTIONS{
    .= 0xC0200000;    //*对于STM32MP157设置链接地址为0xC0200000,对于IMX6ULL设为0x80200000*/
    
    .= ALIGN(4);//将当前位置向四取整
    .text     //开始摆放代码段数据,当前地址往上增长
{
    (.text)//代码段取出来
}

.= ALIGN(4):    //当前位置向四取整
.rodata : { *(.rodata) }//只读数据段取出来,当前地址往上继续增长

·= ALIGN(4);
.data : { *(.data) } //数据段取出来

.= ALIGN(4):
__bss_start =.;    //用它存当前地址作为bss起始地址
.bss : { *(.bss) *(.COMMON) }//bss段取出来
__bss_end =.;    //用它存当前地址作为bss结束地址
}
注:到时候吧bss起始地址和结束地址取出来,直接清零就可以清零bss段了 

二、完整的语法

SECTIONS {
...
secname start BLocK(align)(NOLOAD) : AT( ldadr )  //secname是段名
    { contents }>region :phdr =fill
...
}

//secname是段名,我们随便命名、start是起始地址(不写默认当前地址)、 BLocK(align)指定怎么对齐、
//AT( ldadr ) 是加载地址,不填就是等于他的链接地址、 { contents }段的内容,一般写成*(.test)

三、重定位数据段:

1、框架:

2、概述

由图可知:将A处代码重定位带B处

具体思路:A处的只读数据的结束地址减去起始地址通过memcy函数重定位到B

代码如下:


.text
.globle	_start
_start

/* 设置sp */
ldr sp = (0xc0000000 + 0x100000)   //栈的起始地址

/* rodata/data的重定位 */

ldr r0, = __rodata_start  //读出只读数据段的起始地址
//只读数据段的结束地址 = 只读数据段的起始地址 - (B - A)
ldr r2, =_start 	/* link addr*/
adr r3, _start		/* load addr */
sub r2, r2, r3		//B - A 
sub r1, r0 ,r2		/* 只读数据段的结束地址 */

ldr r3 = __bss_start
sub r2, r3, r0
	
bl memcpy		/* r0: 目的, r1:源, r2:长度 */

/* 调用main函数 */
bl main




void memcpy(void *dest, void *Src, unsigned int len)
{

	unsigned char* pcDest = dest;
	unsigned char* pcSrc = Src;

	while(led--)
	{ 
		
*pcDest = *pcSrc;//目的 = 源
		pcSrc++;
		pcDest++;
	}
}

四、清除bss段

1、概述:

由于没有初始值或者初始值为零的全局变量,太浪费内存,我们把它放在bss段,只记录他的起始地址和结束地址,使用之前把那些段清零就可以

2、代码实现:


.text
.globle	_start
_start

/* 设置sp */
ldr sp = (0xc0000000 + 0x100000)   //栈的起始地址

/* rodata/data的重定位 */

ldr r0, = __rodata_start  //读出只读数据段的起始地址
//只读数据段的结束地址 = 只读数据段的起始地址 - (B - A)
ldr r2, =_start 	/* link addr*/
adr r3, _start		/* load addr */
sub r2, r2, r3		//B - A 
sub r1, r0 ,r2		/* 只读数据段的结束地址 */

ldr r3 = __bss_start
sub r2, r3, r0
	
bl memcpy		/* r0: 目的, r1:源, r2:长度 */

/* clear_bss */
ldr r0, = __bss_start
mov r1, #0
ldr r2, = __bss_end
sub r2, r2, r0
bl memset		/* r0: dest, r1: val(0), r2: len */

/* 调用main函数 */
bl main




void memcpy(void *dest, void *Src, unsigned int len)
{

	unsigned char* pcDest = dest;
	unsigned char* pcSrc = Src;

	while(led--)
	{ 
		
*pcDest = *pcSrc;//目的 = 源
		pcSrc++;
		pcDest++;
	}
}
void memset(void *dest, unsigned char val, unsigned int len)
{

	unsigned char* pcDest = dest;

	while(led--)
	{ 
		
*pcDest = val;//目的 = 源
		pcDest++;
	}
}


五、重定位代码段

为什么我们重定向了数据段、清除了bss,执行main程序可以打印出来呢?

因为现在还在A加载地址附近运行程序,没去B链接地址运行

代码实现:


.text
.globle	_start
_start

/* 设置sp */
ldr sp = (0xc0000000 + 0x100000)   //栈的起始地址

/* text/rodata/data的重定位 */
ldr r0, = _start
adr r1, _start

ldr r3, = __bss_start

sub r2, r3, r0

bl memcpy		/* r0: 目的, r1:源, r2:长度 */

/* clear_bss */
ldr r0, = __bss_start
mov r1, #0
ldr r2, = __bss_end
sub r2, r2, r0
bl memset		/* r0: dest, r1: val(0), r2: len */

/* 调用main函数 */
bl main




void memcpy(void *dest, void *Src, unsigned int len)
{

	unsigned char* pcDest = dest;
	unsigned char* pcSrc = Src;

	while(led--)
	{ 
		
*pcDest = *pcSrc;//目的 = 源
		pcSrc++;
		pcDest++;
	}
}
void memset(void *dest, unsigned char val, unsigned int len)
{

	unsigned char* pcDest = dest;

	while(led--)
	{ 
		
*pcDest = val;//目的 = 源
		pcDest++;
	}
}


相关文章:

  • Qt的QDateTimeEdit控件的使用
  • 并发编程(线程安全)面试题及原理
  • 华为云IAM 用户名和IAM ID
  • 在Blender中给SP分ID通道图
  • 后路式编程
  • Java进阶-SpringCloud设计模式-工厂模式的设计与详解
  • Linux软件包管理
  • 2025最新Transformer模型及深度学习前沿技术应用
  • go语言因为前端跨域导致无法访问到后端解决方案
  • React + TypeScript 实战指南:用类型守护你的组件
  • MVTec HDevelopEVO 24.11体验
  • MsBox.Avalonia 全局修改弹框样式
  • Gradle 配置 Lombok 项目并发布到私有 Maven 仓库的完整指南
  • ​DeepSeek:如何通过自然语言生成HTML文件与原型图?
  • Calico-基本安装、IPIP同节点通信和Proxy_ARP实践 Day01
  • CentOS 7 aarch64上制作kernel rpm二进制包 —— 筑梦之路
  • leetcode day25 28 KMP算法
  • 【ESP-ADF】在 VSCode 安装 ESP-ADF 注意事项
  • 统计建模小贴士
  • Maven 中 SNAPSHOT 版本与 RELEASE 版本的区别
  • 杭州钱塘区3宗涉宅用地均以底价成交,共计成交金额25.73亿元
  • 王毅谈中拉论坛十年成果
  • 从普通人经历中发现历史,王笛解读《线索与痕迹》
  • 夜读丨取稿费的乐趣
  • 央行设立服务消费与养老再贷款,额度5000亿元
  • 江西省直机关工委副书记熊亮华履新宜春市委常委、宣传部部长