两年前开始接触MIPS,那时候觉得好多问题不是很理解,当然有些问题至今还是一知半解的。在后来的学习中经常回过头来看之前碰到的那些问题,每次看都会有新的心得,也许这正应了那句古语“温故而知新”。唉,古人很早就明白了这个道理,不禁感慨自己落后了不是一点半点!
下面回顾一下MIPS中RELOC的这段描述。首次接触RELOC是在vxWorks中的一段MIPS汇编,RELOC是一个宏定义,具体如下:
- #define RELOC(toreg,address) \
- bal 9f; \
- 9:; \
- la toreg,address; \
- addu toreg,ra; \
- la ra,9b; \
- subu toreg,ra
当时觉得这段的意思是在进行地址的转换,也许你会说我从RELOC这个字面上就可以理解到这个意思了。呵呵,如果这样的话那么你是个高手了,小弟自叹不如!
那么这一段究竟讲什么意思呢?在具体讲解之前,我们还是来看看这个宏具体是如何使用的。我们学一个东西往往是从模仿开始的,实践证明这是学习的最最有效的途径。RELOC的使用大致是这样的:
- RELOC(t0, romStart)
- jal t0
- nop
这样我们就可以具体来了解这一段的具体意思了:romStart的连接地址是在ram所在的地址,而实际运行是在flash上(这是一段bootloader启动代码,上电运行是在flash中运行的),我们需要将romStart的地址换成在flash中的位置,所以该段代码先加上当前运行位置的地址,然后减去链接时生成的当前位置的地址,这样就得到实际运行时romStart的相对地址,这样就可以直接跳转过去了。
为什么是这样子的一个功能呢?我记得当时上网搜到了这样一段描述:
- RELOC(t0,romStart) \
- 0Xbfc01004: bal 9f; \
- 9: ;\
- 0Xbfc01008:la t0, romStart; \
- 0Xbfc01010:addu t0, ra; \
- 0Xbfc01014:la ra, 9b; \
- 0Xbfc0101c:subu t0, ra \
- jal t0
- nop
1. 执行bal之后,ra寄存器的值为0Xbfc0100c;
2. 执行la之后,t0寄存器存放的是romStart的链接地址。
3. 执行addu之后,t0寄存器存放的是0Xbfc0100c+romStart的链接地址。
4. 执行la之后,ra寄存器存放的是0xbfc01008的地址对应的链接地址。
5. 执行subu之后,t0寄存器存放的是0xbfc01008的地址对应的链接地址-(0Xbfc0100c+romStart),此时t0寄存器存放的即是romStart对应的ROM地址。
看了上面这段解释,总感觉有那么点的奇怪。难道最终计算出来的romStart在flash中的地址没有偏移4字节吗?
后来,我有幸接触到了Broadcom的CFE。它里面关于reloc的做法比较合理,很清晰,新手很容易就看明白。
- /* Check if we booted from SDRAM */
- bal 1f
- nop
- 1: li t0,PHYSADDR_MASK
- and t0,t0,ra
- li t1,SI_FLASH1
- blt t0,t1,2f
- move s5,zero
- /* If we are in flashcompute reloc for text addresses */
- la t0,1b
- sub s5,ra,t0 # s5: Relocation factor
- 2:
- /* jyh 2009.12 led_out=1 */
- li a0,1
- la t2,led_out
- add t2,t2,s5
- jalr t2
- nop
参照上面的发法试着分析一下,是不是很容易?没有一点歧义吧?
对于vxWorks中存在的歧义,至今仍未参透,期待个中高手的指点了!
附:
何谓链接地址?凡是标号的值都是标号所在指令的链接地址(即RAM地址)。在分支指令中的标号所表示的值是一个相对于本分支指令地址的16bit的偏移量。汇编器会将标号所表示的有效地址(目标地址)转换成合适的偏移值放入指令中。在跳转指令中的标号所表示的值也是由汇编器将标号所在的有效地址(目标地址)转换成合适的偏移值,转换规则详见指令说明。
本文是在学习网络上一位博友的日志之后,有感所写,在此感谢一下他。
http://blog.sina.com.cn/s/blog_3f7cc7420100p62j.html
阅读(3315) | 评论(0) | 转发(0) |