Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can the MIPS register $0 be used to store and retrieve values?

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?

like image 977
John Källén Avatar asked Mar 07 '23 20:03

John Källén


2 Answers

It's using $0 because at the entry point there is no reason to save $ra, so it's just discarded. Since it's hand written asm code coming from a macro, it's not optimized away as would normally be the case.

like image 132
Jester Avatar answered Mar 22 '23 07:03

Jester


Note that glibc only uses this for PIC. (See Is all MIPS code on Linux supposed to be PIC?)


MIPS jal (like other j instructions) is not PIC; it replaces the low 28 bits of PC with imm26 << 2. It's an absolute call within that 1/16th of address space.

But the b instruction encoding does use a relative displacement, so it still works. bal is a pseudo-instruction for an unconditional PIC function call: It sets PC += imm16<<2 (see that same link). It's a pseudo-instruction for a conditional branch-and-link that tests $0 for >= 0, so it's always taken. As your disassembly shows, the real instruction is "BGEZAL -- Branch on greater than or equal to zero and link". It only works within -2^17 / +(2^17 - 4) bytes.

The "and link" part is what this code wants: It's getting PC into $ra by using a branch-and-link, because in PIC you don't know your own address at assemble or link time.

Anyway, this explains why bgezal $0 is reading $0. By special-casing this use of that macro, they could have saved at least 4 bytes per executable by leaving out the useless write of the old value into $0. But they didn't :/ The code only runs once, though.

like image 35
Peter Cordes Avatar answered Mar 22 '23 08:03

Peter Cordes