When I learned about the MIPS processor, it was pounded into my head that reads of the $0 register always return 0, and writes to $0 are always discarded. From the MIPS Programmer's manual:
2.13.4.1 CPU General-Purpose Registers [...] r0 is hard-wired to a value of zero, and can be used as the target register for any instruction whose result is to be discarded. r0 can also be used as a source when a zero value is needed.
From this follows that the instructions or $0,$r31,$0 is a no-op.
Imagine my surprise when I was poking around in the startup code of an ELF MIPS binary, when I saw the following instruction sequence:
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
The instruction at address 0x610 is copying the value of $ra into $r0, which according to the paragraph above is tantamount to discarding it. Then, the instruction at address 0x628 is reading the value back from $0, but since $0 is hardwired to 0, it results in setting $ra to 0.
This all seems rather pointless: why execute statement 0x610 when it would be enough to just execute 0x628. The glibc folks clearly had some intent in mind when they wrote this code. It seems as if $0 is writeable and readable after all!
So under what circumstances can a program read / write to the $0 register as if it were any one of the other general purpose registers?
EDIT:
Looking at the glibc source code isn't helpful. The code for __start
uses a macro:
https://github.com/bminor/glibc/blob/master/sysdeps/mips/start.S#L80
ENTRY_POINT:
# ifdef __PIC__
SETUP_GPX($0)
...
Notice how $0 is deliberately being specified here. The SETUP_GPX macro is defined here:
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" clearly signals the intent of saving the register, but why $0?