Skip to content

[Bug] MIPS N32 ABI has 32-bit registers #1226

Description

@Legoclones

GEF+GDB version

GEF: (Standalone)
Blob Hash(/home/justin/.gef-2026.01.py): b87f503eb86896f7ae452e886619cc6d7adbd594
SHA256(/home/justin/.gef-2026.01.py): c2afbb821ce8e1b4909a70b2aff33eb526f114e0cfb3340fb9b7dbf8b1da3f1c
GDB: 17.1
GDB-Python: 3.13

Operating System

Kali Linux WSL

Describe the issue you encountered

MIPS64 has two main ABIs used - N32 and N64. Both have 64-bit registers, but N32 has a 32-bit address space. When using gef-remote with the proper QEMU options, the N64 ABI is treated correctly, and it's detected as MIPS64. However, binaries with the N32 ABI is detected as MIPS32, and thus 32-bit registers are incorrectly used.

Do you read the docs and look at previously closed issues/PRs for similar cases?

Yes

Architecture impacted

  • X86
  • X64
  • ARM
  • ARM64
  • MIPS
  • MIPS64
  • PPC
  • PPC64
  • RISCV

Describe your issue. Without a proper reproduction step-by-step, your issue will be ignored.

(qemu) gef➤  show architecture
The target architecture is set to "auto" (currently "mips:isa64r2").
(qemu) gef➤  set $s0 = 0x1122334455667788
(qemu) gef➤  i r $s0
s0: 0x1122334455667788
(qemu) gef➤  context
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────── registers ────
$zero: 0x0
$at  : 0x0
$v0  : 0x0
$v1  : 0x0
$a0  : 0x0
$a1  : 0x0
$a2  : 0x0
$a3  : 0x0
$t0  : 0x0
$t1  : 0x0
$t2  : 0x0
$t3  : 0x0
$s0  : 0x55667788 # only last 4 bytes are shown
$s1  : 0x0
$s2  : 0x0
$s3  : 0x0
$s4  : 0x0
$s5  : 0x0
$s6  : 0x0
$s7  : 0x0
$t8  : 0x0
$t9  : 0x0
$k0  : 0x0
$k1  : 0x0
$s8  : 0x0
$pc  : 0x013abd10  →  <__start+0000> move t9, ra
$sp  : 0x0138aea0  →  0x00000001
$hi  : 0x0
$lo  : 0x0
$fir : 0x7f0000
$ra  : 0x0
$gp  : 0x0
───────────────────────────────────────────────────────────────────────────────── stack ────
[!] Command 'dereference' failed to execute properly, reason: Bad register
────────────────────────────────────────────────────────────────────── code:mips:MIPS32 ────
    0x13abd04                  nop
    0x13abd08                  nop
    0x13abd0c                  nop
 →  0x13abd10 <__start+0000>   move   t9, ra
    0x13abd14 <__start+0004>   bal    0x13abd1c <__start+12>
    0x13abd18 <__start+0008>   nop
    0x13abd1c <__start+000c>   move   s2, gp
    0x13abd20 <__start+0010>   lui    gp, 0x3
    0x13abd24 <__start+0014>   addiu  gp, gp, -15612
─────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, stopped 0x13abd10 in __start (), reason: SIGTRAP
───────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x13abd10 → __start()
────────────────────────────────────────────────────────────────────────────────────────────

The register output only displays the least significant 32 bits of each register (s0 should be 0x1122334455667788, which i r correctly displays). I'm not sure if the stack dereferencing failing is related to it or not, but I'm pretty sure GEF treats it as MIPS32 due to the code:mips:MIPS32 title.

I'm fairly sure this is a bug in GEF, as following the same steps for a MIPS64 N64 binary correctly shows the 64-bit register

Minimalist test case

My preferred method of compiling, hosting, and connecting to these MIPS binaries with QEMU is through some containers I've posted on DockerHub (mips-compile and mips-pwn). For example, use the following C code (poc.c):

int main(){ return 0; }

Compile it with the commands (uses MIPS64, big endian, Release 2, with both ABIs):

# for the n32 ABI
docker run -it --rm -v ${PWD}:/workdir legoclones/mips-compile:mips64r2-glibc-n32 mips64-linux-gcc poc.c -o poc_n32

# for the n64 ABI
docker run -it --rm -v ${PWD}:/workdir legoclones/mips-compile:mips64r2-glibc-n64 mips64-linux-gcc poc.c -o poc_n64

To emulate it with QEMU and expose the user mode GDB stub:

# n32 ABI
docker run -i --rm -v ./:/target/ctf -p 1234:1234 --name poc_n32 legoclones/mips-pwn:mips64r2-glibc-n32 chroot /target /qemu -g 1234 /ctf/poc_n32

# n64 ABI
docker run -i --rm -v ./:/target/ctf -p 1234:1234 --name poc_n64 legoclones/mips-pwn:mips64r2-glibc-n64 chroot /target /qemu -g 1234 /ctf/poc_n64

To connect to the GDB stub in another terminal:

# n32 ABI
gdb -q ./poc_n32 -ex "gef-remote localhost 1234 --qemu-user --qemu-binary ./poc_n32"

# n64 ABI
gdb -q ./poc_n64 -ex "gef-remote localhost 1234 --qemu-user --qemu-binary ./poc_n64"

I've also attached the compiled binaries if you'd like: gef_poc.zip

Additional context?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions