MIPS emulation goes into “Branch in delay / forbidden slot” scenario when virtual tlb is enabled
Author: palantCreated Dec 12, 2025Updated Aug 28, 2026
The application I tried to emulate was producing Unhandled CPU exception consistently, this turned out to be unhandled interrupt 20, meaning EXCP_RI. I traced it back to this condition:
Now I am no MIPS expert but the code in question doesn’t seem to be putting a branch in a delay slot. Here is the reduced code:
import unicorn
uc = unicorn.Uc(
unicorn.UC_ARCH_MIPS,
unicorn.UC_MODE_MIPS32 | unicorn.UC_MODE_LITTLE_ENDIAN
)
uc.ctl_set_tlb_mode(unicorn.UC_TLB_VIRTUAL)
BASE = 0x1000
# b 0x1008
# lb $v0, ($s8)
# b 0x1000
# addiu $s0, $s0, 1
CODE = b'\x01\x00\x00\x10\x00\x00\xc2\x83\xFD\xFF\x00\x10\x01\x00\x10\x26'
uc.mem_map(BASE, 0x1000, perms=unicorn.UC_PROT_ALL)
uc.mem_write(BASE, CODE)
def hook_code(uc, addr, size, user_data):
print(f'At address 0x{addr:x}')
uc.reg_write(unicorn.mips_const.UC_MIPS_REG_S8, BASE)
uc.hook_add(unicorn.UC_HOOK_CODE, hook_code)
uc.emu_start(BASE, BASE + len(CODE), count=20)If I don’t enable virtual tlb this code goes into an endless loop as expected (unfortunately not an option for the real application, it has to be loaded at a high memory address). Same happens if the memory access in the delay slot of the first jump is replaced by nop or some other instruction that doesn’t access memory. But as written here the second jump triggers “reserved instruction” exception.
Source: unicorn-engine/unicorn