【问题标题】:Can the MIPS register $0 be used to store and retrieve values?MIPS 寄存器 $0 可以用于存储和检索值吗?
【发布时间】:2018-03-23 18:52:14
【问题描述】:

当我了解 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_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 美元?

【问题讨论】:

  • 这没有明显的意义,但 glibc 是开源的,所以也许检查一下它是从哪里来的?
  • 我有,但代码没有解释为什么要写入 $0 而不是其他一些临时寄存器。
  • 是glibc中手写的asm吗?有链接吗?
  • 如果没有源代码,这可能只是您正在反汇编的数据,就好像它是指令一样。需要确认来源
  • 我已将链接和副本添加到问题的来源。

标签: assembly mips glibc


【解决方案1】:

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


MIPS jal(与其他 j 指令一样)不是 PIC;它用imm26 << 2 替换了PC 的低28 位。 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。通过对该宏的使用进行特殊处理,他们可以省去将旧值写入$0 的无用写入,从而为每个可执行文件节省至少 4 个字节。但他们没有:/ 不过,代码只运行一次。

【讨论】:

  • 我理解bal 部分;它的 x86 对应物是 call $+5; pop ebx 序列,用于在 ebx 中构建全局指针。在 bal 之前对 $0 的看似毫无意义的写入让我感到困惑。
  • @JohnKällén:好吧,如果$0 并不总是读为0,那么bal 就不可能是bgezal $0 的伪指令。认为值得为未来的读者指出 $0 上的条件分支的意义是什么,作为 Jester 对您的实际问题的正确答案的补充。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-08
    • 2017-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多