N64Recomp / N64Recomp

Tool to statically recompile N64 games into native executables

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Missing jump label in decompile output

lizardman0 opened this issue · comments

Hi, I'm trying out this tool by recompiling a demo from the official SDK (seemed like a better idea than a full blown commercial game)

[input]
entrypoint = 0x80200060
elf_path = "teapot.elf"
output_func_path = "RecompiledFuncs"

[patches]
ignored = ["gspF3DEX2_xbusTextStart"]  # ucode should be ignored

I got some ambiguity warnings, but the expected functions were found.

Sections
Num symbols: 903
Found entrypoint, original name: boot
Function count: 766
Working dir: /home/lizardman/projects/n64
[Warn] Potential jal resolution ambiguity
  gcc2_compiled._80206570
  sqrtf_recomp
[Warn] Potential jal resolution ambiguity
  gcc2_compiled._80206570
  sqrtf_recomp
[Warn] Potential jal resolution ambiguity
  gcc2_compiled._80206570
  sqrtf_recomp
[Warn] Potential jal resolution ambiguity
  gcc2_compiled._80206570
  sqrtf_recomp
[Warn] Potential jal resolution ambiguity
  gcc2_compiled._80206570
  sqrtf_recomp
[Warn] Potential jal resolution ambiguity
  gcc2_compiled._80206570
  sqrtf_recomp
[Warn] Potential jal resolution ambiguity
  gcc2_compiled._80206570
  sqrtf_recomp
[Warn] Potential jal resolution ambiguity
  gcc2_compiled._80206EA0
  gcc2_compiled._80206EA0
  gcc2_compiled._80206EA0
  guNormalize
[Warn] Potential jal resolution ambiguity
  gcc2_compiled._80206EA0
  gcc2_compiled._80206EA0
  gcc2_compiled._80206EA0
  guNormalize

The main problem is that the recomp_entrypoint is missing some things:

#include "recomp.h"
#include "disable_warnings.h"

void recomp_entrypoint(uint8_t* rdram, recomp_context* ctx) {
    uint64_t hi = 0, lo = 0, result = 0;
    unsigned int rounding_mode = DEFAULT_ROUNDING_MODE;
    int c1cs = 0; 
    // addiu       $sp, $sp, -0x20
    ctx->r29 = ADD32(ctx->r29, -0X20);
    // sw          $ra, 0x1C($sp)
    MEM_W(0X1C, ctx->r29) = ctx->r31;
    // jal         0x802051D8
    // sw          $s0, 0x18($sp)
    MEM_W(0X18, ctx->r29) = ctx->r16;
    __osInitialize_common_recomp(rdram, ctx);
    goto after_0;
    // sw          $s0, 0x18($sp)
    MEM_W(0X18, ctx->r29) = ctx->r16;
    after_0:
    // jal         0x802015B0
    // nop

    osCartRomInit_recomp(rdram, ctx);
    goto after_1;
    // nop

    after_1:
    // lui         $s0, 0x8021
    ctx->r16 = S32(0X8021 << 16);
    // addiu       $s0, $s0, -0x4570
    ctx->r16 = ADD32(ctx->r16, -0X4570);
    // addu        $a0, $s0, $zero
    ctx->r4 = ADD32(ctx->r16, 0);
    // lui         $at, 0x8022
    ctx->r1 = S32(0X8022 << 16);
    // sw          $v0, -0x14F4($at)
    MEM_W(-0X14F4, ctx->r1) = ctx->r2;
    // lui         $v0, 0x8021
    ctx->r2 = S32(0X8021 << 16);
    // addiu       $v0, $v0, -0x23C0
    ctx->r2 = ADD32(ctx->r2, -0X23C0);
    // sw          $v0, 0x10($sp)
    MEM_W(0X10, ctx->r29) = ctx->r2;
    // addiu       $v0, $zero, 0xA
    ctx->r2 = ADD32(0, 0XA);
    // addiu       $a1, $zero, 0x1
    ctx->r5 = ADD32(0, 0X1);
    // lui         $a2, 0x8020
    ctx->r6 = S32(0X8020 << 16);
    // addiu       $a2, $a2, 0xCC
    ctx->r6 = ADD32(ctx->r6, 0XCC);
    // addu        $a3, $zero, $zero
    ctx->r7 = ADD32(0, 0);
    // jal         0x802039E0
    osCreateThread_recomp(rdram, ctx);
    goto after_2;
;}

it's trying to jump to a undefined label after_2, but the the disassembly shows that it was supposed to call osStartThread with the idle thread address:

80200060 <boot> (File Offset: 0x50060):
80200060:	27bdffe0 	addiu	sp,sp,-32
80200064:	afbf001c 	sw	ra,28(sp)
80200068:	0c081476 	jal	802051d8 <__osInitialize_common> (File Offset: 0x551d8)
8020006c:	afb00018 	sw	s0,24(sp)
80200070:	0c08056c 	jal	802015b0 <osCartRomInit> (File Offset: 0x515b0)
80200074:	00000000 	nop
80200078:	3c108021 	lui	s0,0x8021
8020007c:	2610ba90 	addiu	s0,s0,-17776
80200080:	02002021 	move	a0,s0
80200084:	3c018022 	lui	at,0x8022
80200088:	ac22eb0c 	sw	v0,-5364(at)
8020008c:	3c028021 	lui	v0,0x8021
80200090:	2442dc40 	addiu	v0,v0,-9152
80200094:	afa20010 	sw	v0,16(sp)
80200098:	2402000a 	li	v0,10
8020009c:	24050001 	li	a1,1
802000a0:	3c068020 	lui	a2,0x8020
802000a4:	24c600cc 	addiu	a2,a2,204
802000a8:	00003821 	move	a3,zero
802000ac:	0c080e78 	jal	802039e0 <osCreateThread> (File Offset: 0x539e0)
802000b0:	afa20014 	sw	v0,20(sp)
802000b4:	0c080ee8 	jal	80203ba0 <osStartThread> (File Offset: 0x53ba0)
802000b8:	02002021 	move	a0,s0
802000bc:	8fbf001c 	lw	ra,28(sp)
802000c0:	8fb00018 	lw	s0,24(sp)
802000c4:	03e00008 	jr	ra
802000c8:	27bd0020 	addiu	sp,sp,32

I'm not really sure if this is really a bug (it's probably my fault). Should I set the entrypoint to something else? I wasn't sure if boot should be bypassed (it felt important because some functions are not stubbed in the mm runtime)

This doesn't seem to be the entrypoint, as the entrypoint will be a handwritten asm function and is generally the value at offset 0x8 in the ROM header. Generally it's just a loop that clears the boot segment's BSS and jumps to the bootproc (the one you marked as the entrypoint). In this case I'd guess it's probably 0x80200000.

Thanks! That makes sense. The linker in the official SDK (mild.exe right?) didn't include the actual boot in the elf file and that tripped me up. Instead of doing some elf magic and injecting the boot in the original elf, I decided to just reverse my own demo ROM and it was a pretty good learning experience. Anyways, I finally got it to run

Screenshot from 2024-06-02 18-28-27

I had to patch the msgqueue logic

diff --git a/ultramodern/mesgqueue.cpp b/ultramodern/mesgqueue.cpp
index 2821c87..6d99979 100644
--- a/ultramodern/mesgqueue.cpp
+++ b/ultramodern/mesgqueue.cpp
@@ -15,7 +15,9 @@ struct QueuedMessage {
 static moodycamel::BlockingConcurrentQueue<QueuedMessage> external_messages {};
 
 void enqueue_external_message(PTR(OSMesgQueue) mq, OSMesg msg, bool jam) {
-    external_messages.enqueue({mq, msg, jam});
+    if (mq != 0) {
+        external_messages.enqueue({mq, msg, jam});
+    }
 }
 
 bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block);

the idle thread was segfaulting due to a NULL mq being enqueued. Please let me know if you need more info

That's awesome to see, congratulations! Good catch on the message queue issue, I'm guessing the game doesn't have an event handler set up for every event message so one of them was still zero. We're working on moving the recomp runtime into a separate repo currently, so we can add this change or fix the root cause there.