IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> ARM裸机篇(三)——重定位和地址无关码 -> 正文阅读

[嵌入式]ARM裸机篇(三)——重定位和地址无关码

linux系列目录:

linux基础篇(一)——GCC和Makefile编译过程
linux基础篇(二)——静态和动态链接
ARM裸机篇(一)——i.MX6ULL启动过程
ARM裸机篇(二)——i.MX6ULL第一个裸机程序
ARM裸机篇(三)——重定位和地址无关码


一、将程序重映射到RAM

在上一节中,我们将第一个裸机程序编译后,然后将映像文件重定位到了DDR3内存上,其中.bin文件的起始地址为0x80000000。重定位结束后,CPU会从这个地址读取第一条指令开始执行程序。

在链接之前查看代码.text段的地址信息如下:可以发现所有段的起始地址都为0
在这里插入图片描述
链接之后的.text段地址:.text段的地址被重映射到了0x80000000.
在这里插入图片描述

在这里插入图片描述
这一节我们将代码重映射到RAM中执行.
在这里插入图片描述

  1. 修改链接脚本
SECTIONS {
    _load_addr = 0x80100000;
    
    . = 0x900000;

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

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

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

    . = ALIGN(4);
    __bss_start = .;
    .bss : { *(.bss) *(.COMMON) }
    __bss_end = .;
}


  1. 修改汇编部分:
	
.text
.global  _start
.thumb           // 使用thumb指令, 默认使用thumb指令
_start: 

	/* 设置栈 */
	ldr  sp,=0x80200000

	/* 重定位text, rodata, data段 */
	bl copy_data

	/* 清除bss段 */
	bl clean_bss

	/* 跳转到主函数 */
	// bl main		/* 相对跳转,程序仍在DDR3内存中执行 */
	ldr pc, =main 	/* 绝对跳转,程序在片内RAM中执行 */

halt:
	b  halt 

  1. 修改C部分
#define CCM_CCGR1 (volatile unsigned long*)0x20C406C          //时钟控制寄存器
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04 (volatile unsigned long*)0x20E006C//GPIO1_04复用功能选择寄存器
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04 (volatile unsigned long*)0x20E02F8 //PAD属性设置寄存器
#define GPIO1_GDIR (volatile unsigned long*)0x0209C004 //GPIO方向设置寄存器(输入或输出)
#define GPIO1_DR (volatile unsigned long*)0x0209C000   //GPIO输出状态寄存器


#define uint32_t  unsigned int 

/**********************************************************************
 * 函数名称: copy_data
 * 功能描述: 将整个程序(.text, .rodata, .data)从DDR3重定位到片内RAM
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 * 修改日期        版本号        修改人        修改内容
 * -------------------------------------------------
 * 2020/02/20	    V1.0         阿和            创建
 ***********************************************************************/
void copy_data (void)
{
	/* 从链接脚本中获得参数 _start, __bss_start, */
	extern int _load_addr, _start, __bss_start;

	volatile unsigned int *dest = (volatile unsigned int *)&_start;			//_start = 0x900000
	volatile unsigned int *end = (volatile unsigned int *)&__bss_start;		//__bss_start = 0x9xxxxx
	volatile unsigned int *src = (volatile unsigned int *)&_load_addr;		//_load_addr = 0x80100000

	/* 重定位数据 */	
	while (dest < end)
	{
		*dest++ = *src++;
	}
}
		  			 		  						  					  				 	   		  	  	 	  
/**********************************************************************
 * 函数名称: clean_bss
 * 功能描述: 清除.bss段
 * 输入参数: start, end
 * 输出参数: 无
 * 返 回 值: 无
 * 修改日期        版本号        修改人        修改内容
 * -------------------------------------------------
 * 2020/02/20	    V1.0         阿和            创建
 ***********************************************************************/
void clean_bss(void)
{
	/* 从lds文件中获得 __bss_start, __bss_end */
	extern int __bss_end, __bss_start;

	volatile unsigned int *start = (volatile unsigned int *)&__bss_start;
	volatile unsigned int *end = (volatile unsigned int *)&__bss_end;

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

/*简单延时函数*/
void delay(uint32_t count)
{
    volatile uint32_t i = 0;
    for (i = 0; i < count; ++i)
    {
        __asm("NOP"); /* 调用nop空指令 */
    }
}

int main()
{
    *(CCM_CCGR1) = 0xFFFFFFFF;   //开启GPIO1的时钟
    *(IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO04) = 0x5;//设置PAD复用功能为GPIO
    *(IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO04) = 0x1F838;//设置PAD属性
    *(GPIO1_GDIR) = 0x10;//设置GPIO为输出模式
    *(GPIO1_DR) = 0x0;   //设置输出电平为低电平

    while(1)
    {
        *(GPIO1_DR) = 0x0;
        delay(0xFFFFF);
        *(GPIO1_DR) = 1<<4;
        delay(0xFFFFF);
    }
    return 0;    
}
	
  1. 编译烧录
    可以发现程序定位到片内RAM后,LED的闪烁速度明显变快。因为CPU读取RAM的速度比读取DDR的速度快的多。

二、 代码分析

对led.elf进行反汇编,生成反汇编文件imx6ull.lds。

查看上述程序的反汇编发现,在重定位函数copy_data执行之前,已经涉及到了片内RAM上的地址,但此时片内RAM上并没有任何程序,那为什么程序还能正常运行呢?

在这里插入图片描述
dis文件中左边的90000xx是链接地址,表示程序运行“应该位于这里”。但是实际上,我们一上电,boot ROM把程序放到0x80100000去了。所以一开始运行这些指令时,它们是位于DDR里的。
第9行的blx命令,并不是跳到0x9005c8。这要根据当前的PC值来计算,在dis里写成9005c8,这只是表示“如果程序从0x900000开始运行的话,第9行就会跳到0x9005c8”。现在程序被boot ROM复制到0x80100000,从0x80100000开始运行,我们需要根据机器码来计算出实际跳转的地址。
blx是相对跳转指令,要跳到“pc + offset”这个地址去。程序从0x8010000运行,运行到第9行时,如下计算新地址:
PC=当前地址+8=0x8010004+8=0x801000C
offset=机器码“fa00016f”里的bit[23:0]4=0x16f4=0x5BC
新PC=PC + offset = 0x80105C8
在0x80105C8这个位置,确实存有copy_data函数,所以:即使程序并不在链接地址0x900000上,它也可以运行。因为blx是相对跳转指令,它用的不是链接地址,它是“位置无关”的。使用“位置无关码”写出的代码,它可以在任何位置上运行,不一定要在链接地址 上运行。

下面我们来分析一下实际板子上电后,程序是如何执行的:

  1. 程序被boot ROM重定位到0x80100000,并从这个地址开始执行第一条指令:此时pc = 0x80100000 + 8 = 0x80100008。
  2. 执行到第2条指令“fa00016f”时,根据上述算法,它跳到地址0x80105C8去执行copy_data函数
  3. 在执行完copy_data和clean_bss函数后,片内RAM 0x900000上已经有程序了。
  4. 执行绝对跳转命令“ldr pc, =main”,它是一条伪指令,真实指令是“ldr pc, [pc, #4] ; 900018 <halt+0x8>”:
    从dis文件里很容易看出,执行完这条指令后,pc等于dis文件中“900018”上的值“009001b3”,所以程序跳到片内RAM去执行main函数了。

注意: 在dis文件中,main函数的链接地址是0x009001b2,往pc寄存器里赋值0x009001b3时,bit0为1,表示main函数的代码是用Thumb指令写的。

  • 重定位之前,不可使用绝对地址
    a) 不可访问全局类变量(全局变量或static修饰的局部变量)
    b) 不可访问有初始值的数组(初始值放在rodata里,需要绝对地址来访问)
  • 重定位之后,使用ldr pc = xxx,跳转到绝对地址(runtime address)

三、 总结

重定位
编译器和汇编器生成从地址 0 开始的代码和数据段。链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些段,然后修改所有对这些符号的引用,使得它们指向这个内存位置 。

地址无关代码
利用相对跳转指令,根据当前PC值自动计算出跳转地址,而不是利用的链接地址,所以这类代码都是地址无关的,它可以在任何位置上运行。

编译器还利用代码段中任何指令和数据段中任何变量之间的距离都是一个运行时常量,与代码段和数据段的绝对内存位置无关这个事实,制作位置无关的共享库。

扩展阅读:linux基础篇(二)——静态和动态链接

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:58:47  更:2022-04-18 18:01:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:35:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码