历史上的今天
今天是:2025年03月25日(星期二)
2020年03月25日 | tiny4412裸机程序之位置无关码
2020-03-25 来源:eefocus
在上篇文章中,将代码的.text、.data、.rodata段重定位到了0x02026400位置处去执行点亮LED的操作。但是,在链接脚本里指定的链接地址是0x02026400,那么为什么在重定位之前的代码能够在0x02023400地址处可以开始执行?就是因为前面使用的是位置无关码。写介绍介个概念:
链接地址:链接脚本里指定的,理论上程序运行时所处的地址。在编译时,编译器会根据链接地址来翻译位置有关码。
加载地址(运行地址):程序运行时,实际所处的地址。
位置无关码,位置有关码,是相对于一条指令的正常目的来说的。比如ldr r0, =xx,它的正常目的是取得标号处的地址,对于这个目的,它是位置有关码,运行的地址不对就获取不到正确的标号地址,其实它无论在哪都是获取的程序加载地址等于链接地址时,标号的地址,如果你就是想要这个值,那么用这条指令是非常正确的,就不用理会什么位置无关码,位置有关码的概念了,这一点非常重要。
因此,当加载地址不等于链接地址时,并不是不可以用位置无关码,而是要看你用位置无关码是否达到了你想要的目的。
位置无关码,依赖于程序当前运行的PC值,进行相对的跳转,导致的结果就是,无论代码在哪,总能达到指令的正常目的,因此是位置无关的。
位置有关码,不依赖当前PC值,是绝对跳转,只有程序运行在链接地址处时,才能达到指令的正常目的,因此是位置有关系的。
下面,我们来看常用的汇编指令以及C语言中哪些操作是位置有关码,哪些是位置无关码。
示例1:
借助前面介绍的uart的代码来做这个实验;
start.S文件内容如下:
.text
.global _start
_start:
ldr sp, =0x02027400 //调用C函数之前必须设置栈,栈用于保存运行环境,给局部变量分配空间;
//参考ROM手册P14,我们把栈指向BL2的最上方;
//即:0x02020000(iROM基地址)+5K(iROM代码用)+8K(BL1用)+16K(BL2用)
bl main //跳转到C函数中执行
mov r0, pc
bl printPc
halt: //死循环
b halt
首先,设置栈,然后调整到main函数执行,最后将pc的值保存到R0寄存器中,然后调用printPc函数将R0寄存器中的值打印出来。
mian.c的内容如下:
//main函数
int main()
{
//初始化串口操作
uart0_init();
//打印16进制数
puthex(100);
return 0;
}
int printPc(unsigned int value)
{
puthex(value);
}
在main()函数中调用了uart0_init()函数初始化串口。然后打印一个100的值;另一个函数printPc函数打印传入的值。
链接脚本如下:
SECTIONS {
. = 0x0;
.text : { *(.text) }
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : { *(.data) }
.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
其中,指定的链接地址是0x0,就是期望程序在0x0地址处开始运行。
编译烧写程序,查看输出如下:
![]()
第一个打印的数字是100;第二个打印的pc的值;大家可以发现,我们指定的链接地址是0x0,而程序运行在0x02023400地址处开始执行的,也能成功执行,为什么呢?就是因为我们使用的是位置无关码。查看反汇编:

已经在图中做了详细的注释。首先,就是将当前pc值加偏移量12处的值保存sp栈中。然后执行bl跳转到main()函数处执行。注意:bl 18这个指令并不是跳转到18地址处开始执行。bl是一条相对跳转指令,当前PC值加偏移量。其机器码是0xeb000003;我们将链接地址设置为0x02023400,看看这条指令的机器码是多少:

可以看到,将链接地址更改为0x02023400地址处,这个条指令的机器码还是0xeb000003。
可以得出结论:B/BL指令是位置无关码,是根据当前PC值加某个符合距离当前位置的offset值,不管链接地址是多少,此值是固定的。也就是不管链接地址是多少都能正常跳转。
还是看第一幅图片,我们将当前PC的值保存到了R0寄存器中了,然后跳转到printPc()函数执行,打印出的值是0x02023410。
也就是说,打印的值是加载地址,并不是链接地址。链接地址是期望程序运行的地址。加载地址是程序实际运行的地址。
结论:
1.程序的链接地址和加载地址不是一回事。
2.在重定位代码之前应该使用位置无关码,位置无关码的操作时根据当前PC值加上偏移量在进行执行。
怎么编写位置无关码:
1.使用相对跳转指令(B/BL);
2.重定位之前不可以使用绝对地址;不可以访问全局变量和静态变量;
3.重定位之后可以访问全局变量和静态变量;使用ldr pc, =xxx;指令跳转到某个地址开始执行;
4.不可访问有初始值的数组,数组中每个成员是位于栈中的,但是其初始值是保存在.rodata中的;
5.如果出了问题,最根本的办法是看反汇编。
在写位置无关码是不能使用全局变量和有初始值的数组:
全局变量(静态或非静态),如果有初始值则保存.data段中,没有初始值则保存到.bss段中。而取用.data段中的数据是根据链接地址来操作的。
有初始值的数组,数组中的每个变量是位于栈中的,但是其初始值是放在.rodata段中的,其地址是链接时就确定下来了。
本文完毕!
上一篇:ARM指令adr和ldr的区别
史海拾趣
|
如果用ARM的开发板,向超级终端发送文字,我知道有很多现成的头文件,里面有串口发送的函数,那有没有必要自己再编写一个呢?如果换作是其他的现有的函数呢? 现在学ARM9,用的是2440.因为是刚接触ARM,很多都不了解,请高人指点一下!!!!… 查看全部问答> |
|
ARM(Advanced RISC Machines)是微处理器行业的一家知名企业,设计了大量高性能、廉价、耗能低的RISC处理器、相关技术及软 件。ARM架构是面向低预算市场设计的第一款RISC微处理器,基本是32位单片机的行业标准,它提供一系列内核、体系扩展、微 ...… 查看全部问答> |
|
目前产品中已经有好几个芯片有类似情况,就是上电后,eth不能连接,换个cpu后就好了,不知道是什么情况。电路如下图所示:请有经验的大虾帮忙分析分析!CPU型号是LM3S9B92-C5 [ 本帖最后由 zxq6 于 2012-4-18 12:28 编辑 ]… 查看全部问答> |
|
《玩转TI MSP430 Launchpad》与大家见面了! TI-EEWORLD联手打造的《玩转TI MSP430 Launchpad》与广大工程师朋友们见面了!按照传统的观念,单片机入门教材或工具书之类都应是由资深专业人士编写,而与此不同的是,本书中大量精彩内容来自于网友们的经验和知识,这些都是来自于 ...… 查看全部问答> |
|
IAR 430编译出现这个警告是什么意思,怎么消除啊 Warning[Pa050]: non-native end of line sequence detected (this diagnostic is only issued once) F:\\Mixed Files\\msp430 files\\msp430g2452\\mcp2515_test\\main.c 2… 查看全部问答> |
|
TI 电源芯片和电源拓扑结构对应这个资料不错,推荐给大家,,, [ 本帖最后由 qwqwqw2088 于 2013-10-27 22:08 编辑 ]… 查看全部问答> |




