当我了解 MIPS 处理器时,我的脑海中牢记着读取 $0 寄存器总是返回 0,而写入 $0 总是被丢弃。来自 MIPS 程序员手册:
2.13.4.1 CPU 通用寄存器
[...]
r0 被硬连线到一个值
零,并且可以用作任何指令的目标寄存器
结果是被丢弃。 r0 也可以用作零时的源
需要价值。
由此可见,说明or $0,$r31,$0
是一个空操作。
想象一下,当我在查看 ELF MIPS 二进制文件的启动代码时,看到以下指令序列,我会感到多么惊讶:
00000610 03 E0 00 25 or $0,$ra,$0
00000614 04 11 00 01 bgezal $0,0000061C
00000618 00 00 00 00 nop
0000061C 3C 1C 00 02 lui $28,+0002
00000620 27 9C 84 64 addiu $28,$28,-00007B9C
00000624 03 9F E0 21 addu $28,$28,$ra
00000628 00 00 F8 25 or $ra,$0,$0
地址 0x610 处的指令将 $ra 的值复制到 $r0 中,根据上面的段落,这相当于丢弃它。然后,地址 0x628 处的指令从 $0 读回值,但由于 $0 硬连线为 0,因此导致将 $ra 设置为 0。
这一切看起来毫无意义:为什么要执行语句 0x610,而只执行 0x628 就足够了。 glibc 人员在编写这段代码时显然有一些意图。看来 $0 毕竟是可写和可读的!
那么在什么情况下程序可以像读取其他通用寄存器一样读取/写入 $0 寄存器呢?
编辑:
查看 glibc 源代码没有帮助。代码为__start
使用宏:
https://github.com/bminor/glibc/blob/master/sysdeps/mips/start.S#L80 https://github.com/bminor/glibc/blob/master/sysdeps/mips/start.S#L80
ENTRY_POINT:
# ifdef __PIC__
SETUP_GPX($0)
...
请注意这里是如何特意指定 $0 的。 SETUP_GPX 宏定义如下:
https://github.com/bminor/glibc/blob/master/sysdeps/mips/sys/asm.h#L75 https://github.com/bminor/glibc/blob/master/sysdeps/mips/sys/asm.h#L75
# define SETUP_GPX(r) \
.set noreorder; \
move r, $31; /* Save old ra. */ \
bal 10f; /* Find addr of cpload. */ \
nop; \
10: \
.cpload $31; \
move $31, r; \
.set reorder
“Save old ra”清楚地表明了保存寄存器的意图,但为什么是$0?