原创 代码重定位的思考(3)----位置无关代码PIC

2011-2-25 14:13 1953 4 4 分类: MCU/ 嵌入式
来源:瑞萨单片机论坛

编译器在编译一段程序时,要经过三个步骤:编译,链接和加载。在链接时,要对所有目标文件进行重定位,建立符号引用规则,同时为变量,函数等分配地址。程序执行时,把代码加载到链接时指定的地址空间,以保证程序在执行过程中对变量,函数等符号的正确引用,是程序正常运行。
但是,在操作系统中,一个进程通常从硬盘等二级存储设备拷贝到内存中去执行,这两者的地址是不同的,因此操作系统要对这个进程进行重定位,才能正确运行该进程。
在设计系统引导程序如bootloader时,也要对代码进行重定位。因为我们为了提高速度,需要将bootloader从ROM拷贝到RAM中去执行,这两者的地址也不同。
拷贝bootloader的这一小段代码是上电后就开始执行的,这些代码即使不在链接时指定的地址空间也能正确运行,这就是位置无关代码(position independent code)。

PIC的特点是,它被加载到任意地址空间都可以正确的执行。其原理是PIC对常量和函数入口地址的操作都是基于PC+偏移量的寻址方式。即使程序被移动,但是PC也变化了,而偏移量是不变的,所以程序仍然可以找到正确的入口地址或者常量。
例如:SH里面的BRA指令就可以用来设计PIC
BRA _main
编译后:
_main * 2 + PC -> PC

在SH体系中,MOV指令操作一个常数或者函数入口地址,比如:
MOV.L #_main, R0
MOV.L #H'FF010010,R0
编译器通常将这个常数或者地址存放在(2)里面的literal pool中。
请注意:literal pool里面实际上只存放绝对地址或者常量!!
因此,我们可以得出一个结论:
使用literal pool并不能产生位置无关代码,因为在literal pool里面存放的是函数入口的绝对地址或者常数,移动程序后,这些内容并没有改变。
在设计bootloader时,一个可行的方法是,将bootloader链接到RAM里面的指定位置(0x1000000),然后将bootloader加载到ROM里面的地址0x0处,CPU上电从ROM地址0x0执行。此时的bootloader的运行地址为0x0,而链接地址为0x1000000。由于bootloader头部的一小段代码是位置无关代码,它仍然可以在地址0x0处运行,并将整个bootloader拷贝到RAM地址的0x1000000处,然后清除bss段并设置堆栈,最后将main()的绝对地址(链接时的地址),比如0x1000100装入PC,并跳转到RAM里去。
MOV.L #_main, R0
JSR @R0
NOP
执行到这里后,程序就从ROM跳转到RAM里面了。
指令MOV.L _main, R0之前的所有指令都是位置无关代码。由于MOV指令会把main的绝对地址0x1000100放入literal pool,因此执行JSR @R0之后,程序就跳转到RAM里面的0x1000100处了。
至此,bootloader就在链接时指定的地址处运行了,这时它就可以在RAM里使用literal pool里面的绝对地址来进行函数的跳转或者操作常数了。这与重定位之前的情况是一样的。

值得一提的是,在有操作系统的系统中,不可能把进程都用PIC来写(编译器不能将C程序完全编译成PIC),由于进程可以随意的从硬盘加载到内存中,因此必须从硬件上来实现重定位,比如重定位寄存器。x86提供代码段,数据段,堆栈段重定位寄存器,操作系统通过修改这些寄存器,来重定位内存中的代码,数据和栈。

文章评论0条评论)

登录后参与讨论
我要评论
0
4
关闭 站长推荐上一条 /3 下一条