代码之家  ›  专栏  ›  技术社区  ›  John Källén

MIPS寄存器$0能否用于存储和检索值?

  •  4
  • John Källén  · 技术社区  · 7 年前

    当我了解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。

    这一切似乎都毫无意义:只要执行0x628就足够了,为什么还要执行语句0x610呢。glibc人员在编写这段代码时显然有一些意图。看起来0美元毕竟是可读写的!

    那么,在什么情况下,程序可以像读/写其他通用寄存器一样读/写$0寄存器呢?

    编辑: 查看glibc源代码没有帮助。的代码 __start 使用宏:

    https://github.com/bminor/glibc/blob/master/sysdeps/mips/start.S#L80

    ENTRY_POINT:
    # ifdef __PIC__
        SETUP_GPX($0)
    ...
    

    注意这里是如何故意指定$0的。SETUP\U GPX宏定义如下:

    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美元?

    2 回复  |  直到 7 年前
        1
  •  5
  •   Jester    7 年前

    它正在使用 $0 因为在入口点没有理由保存 $ra ,所以它被丢弃了。由于它是来自宏的手工编写的asm代码,因此没有像通常情况下那样进行优化。

        2
  •  2
  •   Peter Cordes    7 年前

    注意,glibc仅将其用于PIC。(参见 Is all MIPS code on Linux supposed to be PIC? )


    MIPS jal (与其他类似 j 说明)不是PIC;它将PC的低28位替换为 imm26 << 2 . It's an absolute call within that 1/16th of address space .

    但是 b 指令编码确实使用了相对位移,因此它仍然有效。 bal 是用于无条件PIC函数调用的伪指令:它设置 PC += imm16<<2 (参见同一链接)。这是一个用于测试的条件分支和链接的伪指令 $0 对于 >= 0 ,所以它总是被占用。正如反汇编所示,真正的指令是 "BGEZAL -- Branch on greater than or equal to zero and link" . 它只能在-2^17/+(2^17-4)字节内工作。

    “和链接”部分是这段代码想要的:它将PC带入 $ra 通过使用分支和链接,因为在PIC中,您在组装或链接时不知道自己的地址。

    无论如何,这解释了为什么 bgezal $0 正在阅读 $0 . 通过对该宏的这种使用进行特殊的外壳处理,他们可以通过省去对旧值的无用写入来为每个可执行文件节省至少4个字节 $0 . 但他们没有:/尽管代码只运行一次。