赞
踩
在ppc指令体系中,一次最多加载16位立即数。要加载32位立即数,必须分两次进行。很多资料上使用如下加载指令:
lis r0, 0x1122
addi r0, r0, 0x3344
这样做确实可以成功地把0x11223344加载到r0中。可是如果换成0x11118000呢?
lis r0, 0x1111
addi r0, r0, 0x8000
请注意, addi指令是r0 = r0 + EXTS(0x8000). EXTS(0x8000) = 0xffff8000。如此,加载到r0中的值就不是0x11118000,而是0x11108000。差之毫厘,谬以千里。
那gcc是如何实现加载32位立即数的指令的呢?
首先是加载0x11223344的指令, 这是对c代码反汇编得出的指令:
lis r0, 0x1122
ori r0, r0, 0x3344
gcc放弃了addi指令,转而使用ori指令。ori指令是r0 = r0 | (16'0 | 0x8000),加载到r0中的为0x11223344。
然后是加载0x11118000的指令,自然也是用ori指令:
lis r0, 0x1111
ori r0, r0, 0x8000
所以说,加载32位立即数时要小心,如果其第15位为1,用addi就会加载出错误的数值,还是用ori较为保险
其实,在ppc指令中,加载32位立即数并不是很常见,常见的是需要将一个数从一个32位地址中加载出来,或者将一个数保存到32位地址中。
我们来看通常的做法,假设是将地址0x11223344中的值加载到r0中:
lis r1, 0x1122
lwz r0, 0x3344(r1)
那如果是将地址0x11118000中的值加载到r0中呢?
lis r1, 0x1111
lwz r0, 0x8000(r1)
不好意思,lwz指令是指r0 = MEM[r1 + EXTS(0x8000)], 所以变成r0加载地址0x11108000处的值。
至于gcc的做法,自然是先用lis加ori指令将地址存入寄存器中,再读取内存内容。
但这样并不是说lwz/lhz/lbz等指令就没有用,它们用处很大,这些指令可以进行正负偏移2^15内的寻址。编译器在将高级代码翻译成汇编指令时,已知偏移的大小,可以酌情采用lwz/lhz/lbz等指令,而省去ori指令。除此之外,如果明确知道偏移是正的,也可以直接用lwz来组合地址,比如之前的地址0x11223344。但如果不太明确,保险起见,还是要用ori指令,不然会埋下很深的安全隐患!
转自:http://blog.csdn.net/qb_2008/article/details/7907398
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。