sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

recomp.cpp (147146B)


      1 #include <cassert>
      2 #include <cstdint>
      3 #include <cstdlib>
      4 #include <cstring>
      5 #include <cinttypes>
      6 #include <unistd.h>
      7 
      8 #include <map>
      9 #include <set>
     10 #include <vector>
     11 #include <string>
     12 #include <string_view>
     13 
     14 #include "rabbitizer.hpp"
     15 #include "rabbitizer.h"
     16 
     17 #include "elf.h"
     18 
     19 #if defined(_WIN32) && !defined(__CYGWIN__)
     20 #include <windows.h>
     21 #endif /* _WIN32 && !__CYGWIN__ */
     22 
     23 #if !defined(_MSC_VER) && !defined(__CYGWIN__) && !defined(_WIN32)
     24 #define UNIX_PLATFORM
     25 #endif
     26 
     27 #ifdef UNIX_PLATFORM
     28 // TODO: determine if any of those headers are not required
     29 #include <csignal>
     30 #include <ctime>
     31 #include <cxxabi.h> // for __cxa_demangle
     32 #include <dlfcn.h>  // for dladdr
     33 #include <execinfo.h>
     34 #include <unistd.h>
     35 #endif
     36 
     37 #ifndef FULL_TRACEBACK
     38 // Change to non-zero to have full traceback, including names not exported
     39 #define FULL_TRACEBACK 0
     40 #endif
     41 
     42 // set this to 1 when testing a new program, to verify that no false function pointers are found
     43 #define INSPECT_FUNCTION_POINTERS 0
     44 
     45 #ifndef TRACE
     46 #define TRACE 0
     47 #endif
     48 
     49 #define LABELS_64_BIT 1
     50 
     51 #ifndef DUMP_INSTRUCTIONS
     52 // Set to non-zero to dump actual disassembly when dumping C code
     53 #define DUMP_INSTRUCTIONS 0
     54 #endif
     55 
     56 #define u32be(x) (uint32_t)(((x & 0xff) << 24) + ((x & 0xff00) << 8) + ((x & 0xff0000) >> 8) + ((uint32_t)(x) >> 24))
     57 #define u16be(x) (uint16_t)(((x & 0xff) << 8) + ((x & 0xff00) >> 8))
     58 #define read_u32_be(buf) (uint32_t)(((buf)[0] << 24) + ((buf)[1] << 16) + ((buf)[2] << 8) + ((buf)[3]))
     59 
     60 #define UniqueId_cpu_li rabbitizer::InstrId::UniqueId::cpu_USERDEF_00
     61 #define UniqueId_cpu_la rabbitizer::InstrId::UniqueId::cpu_USERDEF_01
     62 
     63 using namespace std;
     64 
     65 struct Edge {
     66     uint32_t i;
     67     uint8_t function_entry : 1;
     68     uint8_t function_exit : 1;
     69     uint8_t extern_function : 1;
     70     uint8_t function_pointer : 1;
     71 };
     72 
     73 struct Insn {
     74     // base instruction
     75     rabbitizer::InstructionCpu instruction;
     76 
     77     //
     78     bool is_global_got_memop;
     79     bool no_following_successor;
     80 
     81     // patching instructions
     82     bool patched;
     83     // lui pairs
     84     uint32_t patched_addr;
     85     // immediates are 16 bits wide, but they can be either signed or unsigned
     86     // a 32 bits signed member can hold all those possible values
     87     int32_t patched_imms;
     88     rabbitizer::Registers::Cpu::GprO32 lila_dst_reg;
     89     int linked_insn;
     90     union {
     91         uint32_t linked_value;
     92         float linked_float;
     93     };
     94 
     95     // jumptable instructions
     96     uint32_t jtbl_addr;
     97     uint32_t num_cases;
     98     rabbitizer::Registers::Cpu::GprO32 index_reg;
     99 
    100     // graph
    101     vector<Edge> successors;
    102     vector<Edge> predecessors;
    103     uint64_t b_liveout;
    104     uint64_t b_livein;
    105     uint64_t f_livein;
    106     uint64_t f_liveout;
    107 
    108     Insn(uint32_t word, uint32_t vram) : instruction(word, vram) {
    109         this->is_global_got_memop = false;
    110         this->no_following_successor = false;
    111 
    112         this->patched = false;
    113         this->patched_addr = 0;
    114         this->patched_imms = 0;
    115         this->lila_dst_reg = rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero;
    116         this->linked_insn = -1;
    117         this->linked_value = 0;
    118 
    119         this->jtbl_addr = 0;
    120         this->num_cases = 0;
    121         this->index_reg = rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero;
    122 
    123         this->b_liveout = 0;
    124         this->b_livein = 0;
    125         this->f_livein = 0;
    126         this->f_liveout = 0;
    127     }
    128 
    129     void patchInstruction(rabbitizer::InstrId::UniqueId instructionId) {
    130         this->patched = true;
    131         RabbitizerInstruction* innerInstr = this->instruction.getCPtr();
    132         innerInstr->uniqueId = (RabbitizerInstrId)(instructionId);
    133         innerInstr->descriptor = &RabbitizerInstrDescriptor_Descriptors[innerInstr->uniqueId];
    134     }
    135 
    136     void patchAddress(rabbitizer::InstrId::UniqueId instructionId, uint32_t newAddress) {
    137         this->patchInstruction(instructionId);
    138         this->patched_addr = newAddress;
    139     }
    140 
    141     uint32_t getAddress() const {
    142         if (this->patched && this->patched_addr != 0) {
    143             return this->patched_addr;
    144         }
    145 
    146         if (this->instruction.hasOperandAlias(rabbitizer::OperandType::cpu_label)) {
    147             return this->instruction.getInstrIndexAsVram();
    148         }
    149 
    150         if (this->instruction.isBranch()) {
    151             return this->instruction.getVram() + this->instruction.getBranchOffset();
    152         }
    153 
    154         assert(!"unreachable code");
    155     }
    156 
    157     void patchImmediate(int32_t newImmediate) {
    158         this->patched = true;
    159         this->patched_imms = newImmediate;
    160     }
    161 
    162     int32_t getImmediate() const {
    163         if (this->patched) {
    164             return this->patched_imms;
    165         }
    166 
    167         return this->instruction.getProcessedImmediate();
    168     }
    169 
    170     std::string disassemble() const {
    171         char buffer[0x1000];
    172         int32_t imm;
    173 
    174         switch (this->instruction.getUniqueId()) {
    175             case UniqueId_cpu_li:
    176                 imm = this->getImmediate();
    177                 if (imm >= 0) {
    178                     sprintf(buffer, "li          %s, 0x%X", RabbitizerRegister_getNameGpr((int)this->lila_dst_reg),
    179                             imm);
    180                 } else {
    181                     sprintf(buffer, "li          %s, %i", RabbitizerRegister_getNameGpr((int)this->lila_dst_reg), imm);
    182                 }
    183                 return buffer;
    184 
    185             case UniqueId_cpu_la:
    186                 sprintf(buffer, "la          %s, 0x%X", RabbitizerRegister_getNameGpr((int)this->lila_dst_reg),
    187                         this->getAddress());
    188                 return buffer;
    189 
    190             default:
    191                 return this->instruction.disassembleInstruction(0);
    192         }
    193     }
    194 };
    195 
    196 struct Function {
    197     vector<uint32_t> returns; // points to delay slots
    198     uint32_t end_addr;        // address after end
    199     uint32_t nargs;
    200     uint32_t nret;
    201     bool v0_in;
    202     bool referenced_by_function_pointer;
    203 };
    204 
    205 bool conservative;
    206 
    207 const uint8_t* text_section;
    208 uint32_t text_section_len;
    209 uint32_t text_vaddr;
    210 
    211 const uint8_t* rodata_section;
    212 uint32_t rodata_section_len;
    213 uint32_t rodata_vaddr;
    214 
    215 const uint8_t* data_section;
    216 uint32_t data_section_len;
    217 uint32_t data_vaddr;
    218 
    219 uint32_t bss_section_len;
    220 uint32_t bss_vaddr;
    221 
    222 vector<Insn> insns;
    223 set<uint32_t> label_addresses;
    224 vector<uint32_t> got_globals;
    225 vector<uint32_t> got_locals;
    226 uint32_t gp_value;
    227 uint32_t gp_value_adj;
    228 
    229 map<uint32_t, string> symbol_names;
    230 
    231 vector<pair<uint32_t, uint32_t>> data_function_pointers;
    232 set<uint32_t> la_function_pointers;
    233 map<uint32_t, Function> functions;
    234 uint32_t main_addr;
    235 uint32_t mcount_addr;
    236 uint32_t procedure_table_start;
    237 uint32_t procedure_table_len;
    238 
    239 #define FLAG_NO_MEM 1
    240 #define FLAG_VARARG 2
    241 
    242 /**
    243  * Struct containing information on external functions that are called using the wrappers in `libc_impl.c`.
    244  *
    245  * name:    function name
    246  * params:  first char is return type, subsequent chars are argument types. Key to chars used:
    247  *          - 'v' void
    248  *          - 'i' signed int (int32_t)
    249  *          - 'u' unsigned int (uint32_t)
    250  *          - 'p' pointer (uintptr_t)
    251  *          - 'f' float
    252  *          - 'd' double
    253  *          - 'l' signed long long (int64_t)
    254  *          - 'j' unsigned long long (uint64_t)
    255  *          - 't' trampoline
    256  *
    257  * flags:   use defines above
    258  */
    259 const struct ExternFunction {
    260     const char* name;
    261     const char* params;
    262     int flags;
    263 } extern_functions[] = {
    264     { "exit", "vi", 0 }, // override exit from application
    265     { "abort", "v", 0 },
    266     { "sbrk", "pi", 0 },
    267     { "malloc", "pu", 0 },
    268     { "calloc", "puu", 0 },
    269     { "realloc", "ppu", 0 },
    270     { "free", "vp", 0 },
    271     { "fscanf", "ipp", FLAG_VARARG },
    272     { "printf", "ip", FLAG_VARARG },
    273     { "sprintf", "ipp", FLAG_VARARG },
    274     { "fprintf", "ipp", FLAG_VARARG },
    275     { "_doprnt", "ippp", 0 },
    276     { "strlen", "up", 0 },
    277     { "open", "ipii", 0 },
    278     { "creat", "ipi", 0 },
    279     { "access", "ipi", 0 },
    280     { "rename", "ipp", 0 },
    281     { "utime", "ipp", 0 },
    282     { "flock", "iii", 0 },
    283     { "chmod", "ipu", 0 },
    284     { "umask", "ii", FLAG_NO_MEM },
    285     { "ecvt", "pdipp", 0 },
    286     { "fcvt", "pdipp", 0 },
    287     { "sqrt", "dd", FLAG_NO_MEM },
    288     { "sqrtf", "ff", FLAG_NO_MEM },
    289     { "atoi", "ip", 0 },
    290     { "atol", "ip", 0 },
    291     { "atof", "dp", 0 },
    292     { "strtol", "ippi", 0 },
    293     { "strtoul", "uppi", 0 },
    294     { "strtoll", "lppi", 0 },
    295     { "strtoull", "jppi", 0 },
    296     { "strtod", "dpp", 0 },
    297     { "strchr", "ppi", 0 },
    298     { "strrchr", "ppi", 0 },
    299     { "strcspn", "upp", 0 },
    300     { "strpbrk", "ppp", 0 },
    301     { "fstat", "iip", 0 },
    302     { "stat", "ipp", 0 },
    303     { "ftruncate", "iii", 0 },
    304     { "truncate", "ipi", 0},
    305     { "bcopy", "vppu", 0 },
    306     { "memcpy", "pppu", 0 },
    307     { "memccpy", "pppiu", 0 },
    308     { "read", "iipu", 0 },
    309     { "write", "iipu", 0 },
    310     { "fopen", "ppp", 0 },
    311     { "freopen", "pppp", 0 },
    312     { "fclose", "ip", 0 },
    313     { "ftell", "ip", 0 },
    314     { "rewind", "vp", 0 },
    315     { "fseek", "ipii", 0 },
    316     { "lseek", "iiii", 0 },
    317     { "fflush", "ip", 0 },
    318     { "fchown", "iiii", 0 },
    319     { "dup", "ii", 0 },
    320     { "dup2", "iii", 0 },
    321     { "pipe", "ip", 0 },
    322     { "perror", "vp", 0 },
    323     { "fdopen", "iip", 0 },
    324     { "memset", "ppiu", 0 },
    325     { "bcmp", "ippu", 0 },
    326     { "memcmp", "ippu", 0 },
    327     { "getpid", "i", FLAG_NO_MEM },
    328     { "getpgrp", "i", 0 },
    329     { "remove", "ip", 0 },
    330     { "unlink", "ip", 0 },
    331     { "close", "ii", 0 },
    332     { "strcmp", "ipp", 0 },
    333     { "strncmp", "ippu", 0 },
    334     { "strcpy", "ppp", 0 },
    335     { "strncpy", "pppu", 0 },
    336     { "strcat", "ppp", 0 },
    337     { "strncat", "pppu", 0 },
    338     { "strtok", "ppp", 0 },
    339     { "strstr", "ppp", 0 },
    340     { "strdup", "pp", 0 },
    341     { "toupper", "ii", FLAG_NO_MEM },
    342     { "tolower", "ii", FLAG_NO_MEM },
    343     { "gethostname", "ipu", 0 },
    344     { "isatty", "ii", 0 },
    345     { "strftime", "upupp", 0 },
    346     { "times", "ip", 0 },
    347     { "clock", "i", FLAG_NO_MEM },
    348     { "ctime", "pp", 0 },
    349     { "localtime", "pp", 0 },
    350     { "setvbuf", "ippiu", 0 },
    351     { "__semgetc", "ip", 0 },
    352     { "__semputc", "iip", 0 },
    353     { "fgetc", "ip", 0 },
    354     { "fgets", "ipip", 0 },
    355     { "__filbuf", "ip", 0 },
    356     { "__flsbuf", "iip", 0 },
    357     { "ungetc", "iip", 0 },
    358     { "gets", "pp", 0 },
    359     { "fread", "upuup", 0 },
    360     { "fwrite", "upuup", 0 },
    361     { "fputs", "ipp", 0 },
    362     { "puts", "ip", 0 },
    363     { "getcwd", "ppu", 0 },
    364     { "time", "ip", 0 },
    365     { "bzero", "vpu", 0 },
    366     { "fp_class_d", "id", FLAG_NO_MEM },
    367     { "ldexp", "ddi", FLAG_NO_MEM },
    368     { "__ll_mul", "lll", FLAG_NO_MEM },
    369     { "__ll_div", "lll", FLAG_NO_MEM },
    370     { "__ll_rem", "ljl", FLAG_NO_MEM },
    371     { "__ll_lshift", "llj", FLAG_NO_MEM },
    372     { "__ll_rshift", "llj", FLAG_NO_MEM },
    373     { "__ull_div", "jjj", FLAG_NO_MEM },
    374     { "__ull_rem", "jjj", FLAG_NO_MEM },
    375     { "__ull_rshift", "jjj", FLAG_NO_MEM },
    376     { "__d_to_ull", "jd", FLAG_NO_MEM },
    377     { "__d_to_ll", "ld", FLAG_NO_MEM },
    378     { "__f_to_ull", "jf", FLAG_NO_MEM },
    379     { "__f_to_ll", "lf", FLAG_NO_MEM },
    380     { "__ull_to_f", "fj", FLAG_NO_MEM },
    381     { "__ll_to_f", "fl", FLAG_NO_MEM },
    382     { "__ull_to_d", "dj", FLAG_NO_MEM },
    383     { "__ll_to_d", "dl", FLAG_NO_MEM },
    384     { "_exit", "vi", 0 },
    385     { "_cleanup", "v", 0 },
    386     { "_rld_new_interface", "pu", FLAG_VARARG },
    387     { "_exithandle", "v", 0 },
    388     { "_prctl", "ii", FLAG_VARARG },
    389     { "_atod", "dpii", 0 },
    390     { "pathconf", "ipi", 0 },
    391     { "getenv", "pp", 0 },
    392     { "gettxt", "ppp", 0 },
    393     { "setlocale", "pip", 0 },
    394     { "mmap", "ppuiiii", 0 },
    395     { "munmap", "ipu", 0 },
    396     { "mprotect", "ipui", 0 },
    397     { "sysconf", "ii", 0 },
    398     { "getpagesize", "i", 0 },
    399     { "strerror", "pi", 0 },
    400     { "ioctl", "iiu", FLAG_VARARG },
    401     { "fcntl", "iii", FLAG_VARARG },
    402     { "signal", "pit", 0 },
    403     { "sigset", "pit", 0 },
    404     { "get_fpc_csr", "i", 0 },
    405     { "set_fpc_csr", "ii", 0 },
    406     { "setjmp", "ip", 0 },
    407     { "longjmp", "vpi", 0 },
    408     { "tempnam", "ppp", 0 },
    409     { "tmpnam", "pp", 0 },
    410     { "mktemp", "pp", 0 },
    411     { "mkstemp", "ip", 0 },
    412     { "tmpfile", "p", 0 },
    413     { "wait", "ip", 0 },
    414     { "kill", "iii", 0 },
    415     { "execlp", "ip", FLAG_VARARG },
    416     { "execv", "ipp", 0 },
    417     { "execvp", "ipp", 0 },
    418     { "fork", "i", 0 },
    419     { "system", "ip", 0 },
    420     { "tsearch", "pppp", 0 },
    421     { "tfind", "pppp", 0 },
    422     { "qsort", "vpuut", 0 },
    423     { "regcmp", "pp", FLAG_VARARG },
    424     { "regex", "ppp", FLAG_VARARG },
    425     { "__assert", "vppi", 0 },
    426 };
    427 
    428 void disassemble(void) {
    429     uint32_t i;
    430 
    431     RabbitizerConfig_Cfg.misc.omit0XOnSmallImm = true;
    432     RabbitizerConfig_Cfg.misc.opcodeLJust -= 8;
    433     RabbitizerConfig_Cfg.misc.upperCaseImm = false;
    434     insns.reserve(1 + text_section_len / sizeof(uint32_t)); // +1 for dummy instruction
    435 
    436     for (i = 0; i < text_section_len; i += sizeof(uint32_t)) {
    437         uint32_t word = read_u32_be(&text_section[i]);
    438         Insn insn(word, text_vaddr + i);
    439         insns.push_back(insn);
    440     }
    441 
    442     {
    443         // Add dummy NOP instruction to avoid out of bounds
    444         Insn insn(0x00000000, text_vaddr + i);
    445         insn.no_following_successor = true;
    446         insns.push_back(insn);
    447     }
    448 }
    449 
    450 void add_function(uint32_t addr) {
    451     if (addr >= text_vaddr && addr < text_vaddr + text_section_len) {
    452         functions[addr];
    453     }
    454 }
    455 
    456 map<uint32_t, Function>::iterator find_function(uint32_t addr) {
    457     if (functions.size() == 0) {
    458         return functions.end();
    459     }
    460 
    461     auto it = functions.upper_bound(addr);
    462 
    463     if (it == functions.begin()) {
    464         return functions.end();
    465     }
    466 
    467     --it;
    468     return it;
    469 }
    470 
    471 rabbitizer::Registers::Cpu::GprO32 get_dest_reg(const Insn& insn) {
    472     switch (insn.instruction.getUniqueId()) {
    473         case rabbitizer::InstrId::UniqueId::cpu_jalr:
    474             // jalr technically modifies rd, so an empty case is here to avoid crashing
    475             break;
    476 
    477         case UniqueId_cpu_li:
    478         case UniqueId_cpu_la:
    479             return insn.lila_dst_reg;
    480 
    481         default:
    482             if (insn.instruction.modifiesRt()) {
    483                 return insn.instruction.GetO32_rt();
    484             } else if (insn.instruction.modifiesRd()) {
    485                 return insn.instruction.GetO32_rd();
    486             }
    487             break;
    488     }
    489 
    490     return rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero;
    491 }
    492 
    493 // try to find a matching LUI for a given register
    494 void link_with_lui(int offset, rabbitizer::Registers::Cpu::GprO32 reg, int mem_imm) {
    495 #define MAX_LOOKBACK 128
    496     // don't attempt to compute addresses for zero offset
    497     // end search after some sane max number of instructions
    498     int end_search = std::max(0, offset - MAX_LOOKBACK);
    499 
    500     for (int search = offset - 1; search >= end_search; search--) {
    501         switch (insns[search].instruction.getUniqueId()) {
    502             case rabbitizer::InstrId::UniqueId::cpu_lui:
    503                 if (reg == insns[search].instruction.GetO32_rt()) {
    504                     goto loop_end;
    505                 }
    506                 continue;
    507 
    508             case rabbitizer::InstrId::UniqueId::cpu_lw:
    509             case rabbitizer::InstrId::UniqueId::cpu_ld:
    510             case rabbitizer::InstrId::UniqueId::cpu_addiu:
    511             case rabbitizer::InstrId::UniqueId::cpu_add:
    512             case rabbitizer::InstrId::UniqueId::cpu_sub:
    513             case rabbitizer::InstrId::UniqueId::cpu_subu:
    514                 if (reg == get_dest_reg(insns[search])) {
    515                     if ((insns[search].instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_lw) &&
    516                         insns[search].instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_gp) {
    517                         int mem_imm0 = insns[search].instruction.getProcessedImmediate();
    518                         uint32_t got_entry = (mem_imm0 + gp_value_adj) / sizeof(uint32_t);
    519 
    520                         if (got_entry < got_locals.size()) {
    521                             // used for static functions
    522                             uint32_t addr = got_locals[got_entry] + mem_imm;
    523                             insns[search].linked_insn = offset;
    524                             insns[search].linked_value = addr;
    525                             insns[offset].linked_insn = search;
    526                             insns[offset].linked_value = addr;
    527 
    528                             // Patch instruction to contain full address
    529                             insns[search].lila_dst_reg = get_dest_reg(insns[search]);
    530                             insns[search].patchAddress(UniqueId_cpu_la, addr);
    531 
    532                             // Patch instruction to have offset 0
    533                             switch (insns[offset].instruction.getUniqueId()) {
    534                                 case rabbitizer::InstrId::UniqueId::cpu_addiu: {
    535                                     rabbitizer::Registers::Cpu::GprO32 dst_reg = insns[offset].instruction.GetO32_rt();
    536                                     insns[offset].patchInstruction(rabbitizer::InstrId::UniqueId::cpu_move);
    537                                     // Patch the destination register too
    538                                     insns[offset].instruction.Set_rd(dst_reg);
    539                                 }
    540 
    541                                     if (addr >= text_vaddr && addr < text_vaddr + text_section_len) {
    542                                         add_function(addr);
    543                                     }
    544                                     goto loop_end;
    545 
    546                                 case rabbitizer::InstrId::UniqueId::cpu_lb:
    547                                 case rabbitizer::InstrId::UniqueId::cpu_lbu:
    548                                 case rabbitizer::InstrId::UniqueId::cpu_sb:
    549                                 case rabbitizer::InstrId::UniqueId::cpu_lh:
    550                                 case rabbitizer::InstrId::UniqueId::cpu_lhu:
    551                                 case rabbitizer::InstrId::UniqueId::cpu_sh:
    552                                 case rabbitizer::InstrId::UniqueId::cpu_lw:
    553                                 case rabbitizer::InstrId::UniqueId::cpu_sw:
    554                                 case rabbitizer::InstrId::UniqueId::cpu_ldc1:
    555                                 case rabbitizer::InstrId::UniqueId::cpu_lwc1:
    556                                 case rabbitizer::InstrId::UniqueId::cpu_swc1:
    557                                     insns[offset].patchImmediate(0);
    558                                     goto loop_end;
    559 
    560                                 default:
    561                                     assert(0 && "Unsupported instruction type");
    562                             }
    563                         }
    564                         goto loop_end;
    565                     } else {
    566                         // ignore: reg is pointer, offset is probably struct data member
    567                         goto loop_end;
    568                     }
    569                 }
    570 
    571                 continue;
    572 
    573             case rabbitizer::InstrId::UniqueId::cpu_jr:
    574                 if ((insns[search].instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) &&
    575                     (offset - search >= 2)) {
    576                     // stop looking when previous `jr ra` is hit,
    577                     // but ignore if `offset` is branch delay slot for this `jr ra`
    578                     goto loop_end;
    579                 }
    580                 continue;
    581 
    582             default:
    583                 continue;
    584         }
    585     }
    586 loop_end:;
    587 }
    588 
    589 // for a given `jalr t9`, find the matching t9 load
    590 void link_with_jalr(int offset) {
    591     // end search after some sane max number of instructions
    592     int end_search = std::max(0, offset - MAX_LOOKBACK);
    593 
    594     for (int search = offset - 1; search >= end_search; search--) {
    595         if (get_dest_reg(insns[search]) == rabbitizer::Registers::Cpu::GprO32::GPR_O32_t9) {
    596             // should be a switch with returns
    597             switch (insns[search].instruction.getUniqueId()) {
    598                 case rabbitizer::InstrId::UniqueId::cpu_lw:
    599                 case UniqueId_cpu_la:
    600                     if (insns[search].is_global_got_memop ||
    601                         (insns[search].instruction.getUniqueId() == UniqueId_cpu_la)) {
    602                         insns[search].linked_insn = offset;
    603                         insns[offset].linked_insn = search;
    604                         insns[offset].linked_value = insns[search].linked_value;
    605 
    606                         insns[offset].patchAddress(rabbitizer::InstrId::UniqueId::cpu_jal, insns[search].linked_value);
    607 
    608                         insns[search].patchInstruction(rabbitizer::InstrId::UniqueId::cpu_nop);
    609                         insns[search].is_global_got_memop = false;
    610 
    611                         add_function(insns[search].linked_value);
    612                     }
    613                     return;
    614 
    615                 case rabbitizer::InstrId::UniqueId::cpu_addiu:
    616                     if (insns[search].linked_insn != -1) {
    617                         uint32_t first = insns[search].linked_insn;
    618 
    619                         // not describing as patched since instruction not edited
    620                         insns[search].linked_insn = offset;
    621                         insns[offset].linked_insn = first;
    622                         insns[offset].linked_value = insns[search].linked_value;
    623                     }
    624                     return;
    625 
    626                 case rabbitizer::InstrId::UniqueId::cpu_ld:
    627                 case rabbitizer::InstrId::UniqueId::cpu_addu:
    628                 case rabbitizer::InstrId::UniqueId::cpu_add:
    629                 case rabbitizer::InstrId::UniqueId::cpu_sub:
    630                 case rabbitizer::InstrId::UniqueId::cpu_subu:
    631                     return;
    632 
    633                 default:
    634                     break;
    635             }
    636         } else if ((insns[search].instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_jr) &&
    637                    (insns[search].instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra)) {
    638             // stop looking when previous `jr ra` is hit
    639             return;
    640         }
    641     }
    642 }
    643 
    644 // TODO: uniformise use of insn vs insns[i]
    645 void pass1(void) {
    646     for (size_t i = 0; i < insns.size(); i++) {
    647         Insn& insn = insns[i];
    648 
    649         // TODO: replace with BAL. Or just fix properly
    650         if (insn.instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_bal) {
    651             insn.patchAddress(rabbitizer::InstrId::UniqueId::cpu_jal,
    652                               insn.instruction.getVram() + insn.instruction.getBranchOffset());
    653         }
    654 
    655         if (insn.instruction.isJump()) {
    656             if (insn.instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_jal ||
    657                 insn.instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_j) {
    658                 uint32_t target = insn.getAddress();
    659 
    660                 label_addresses.insert(target);
    661                 add_function(target);
    662             } else if (insn.instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_jr) {
    663                 // sltiu $at, $ty, z
    664                 // sw    $reg, offset($sp)   (very seldom, one or more, usually in func entry)
    665                 // lw    $gp, offset($sp)    (if PIC, and very seldom)
    666                 // beqz  $at, .L
    667                 // some other instruction    (not always)
    668                 // lui   $at, %hi(jtbl)
    669                 // sll   $tx, $ty, 2
    670                 // addu  $at, $at, $tx
    671                 // lw    $tx, %lo(jtbl)($at)
    672                 // nop                       (code compiled with 5.3)
    673                 // addu  $tx, $tx, $gp       (if PIC)
    674                 // jr    $tx
    675 
    676                 // IDO 7.1:
    677                 // lw      at,offset(gp)
    678                 // andi    t9,t8,0x3f
    679                 // sll     t9,t9,0x2
    680                 // addu    at,at,t9
    681                 // lw      t9,offset(at)
    682                 // addu    t9,t9,gp
    683                 // jr      t9
    684 
    685                 // IDO 5.3:
    686                 // lw      at,offset(gp)
    687                 // andi    t3,t2,0x3f
    688                 // sll     t3,t3,0x2
    689                 // addu    at,at,t3
    690                 // something
    691                 // lw      t3,offset(at)
    692                 // something
    693                 // addu    t3,t3,gp
    694                 // jr      t3
    695                 if (i >= 7 && rodata_section != NULL) {
    696                     bool is_pic =
    697                         (insns[i - 1].instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_addu) &&
    698                         (insns[i - 1].instruction.GetO32_rt() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_gp);
    699                     bool has_nop =
    700                         insns[i - is_pic - 1].instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_nop;
    701                     bool has_extra = insns[i - is_pic - has_nop - 5].instruction.getUniqueId() !=
    702                                      rabbitizer::InstrId::UniqueId::cpu_beqz;
    703                     int lw = i - (int)is_pic - (int)has_nop - 1;
    704 
    705                     if (insns[lw].instruction.getUniqueId() != rabbitizer::InstrId::UniqueId::cpu_lw) {
    706                         --lw;
    707                     }
    708 
    709                     if ((insns[lw].instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_lw) &&
    710                         (insns[lw].linked_insn != -1)) {
    711                         int sltiu_index = -1;
    712                         int andi_index = -1;
    713                         uint32_t addu_index = lw - 1;
    714                         uint32_t num_cases;
    715                         bool found = false;
    716                         bool and_variant = false;
    717                         int end = 14;
    718 
    719                         if (insns[addu_index].instruction.getUniqueId() != rabbitizer::InstrId::UniqueId::cpu_addu) {
    720                             --addu_index;
    721                         }
    722 
    723                         if (insns[addu_index].instruction.getUniqueId() != rabbitizer::InstrId::UniqueId::cpu_addu) {
    724                             goto skip;
    725                         }
    726 
    727                         if (insns[addu_index - 1].instruction.getUniqueId() != rabbitizer::InstrId::UniqueId::cpu_sll) {
    728                             goto skip;
    729                         }
    730 
    731                         if (get_dest_reg(insns[addu_index - 1]) != insn.instruction.GetO32_rs()) {
    732                             goto skip;
    733                         }
    734 
    735                         for (int j = 3; j <= 4; j++) {
    736                             if (insns[lw - j].instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_andi) {
    737                                 andi_index = lw - j;
    738                                 break;
    739                             }
    740                         }
    741 
    742                         if (i == 368393) {
    743                             // In copt
    744                             end = 18;
    745                         }
    746 
    747                         for (int j = 5; j <= end; j++) {
    748                             if ((insns[lw - has_extra - j].instruction.getUniqueId() ==
    749                                  rabbitizer::InstrId::UniqueId::cpu_sltiu) &&
    750                                 (insns[lw - has_extra - j].instruction.GetO32_rt() ==
    751                                  rabbitizer::Registers::Cpu::GprO32::GPR_O32_at)) {
    752                                 sltiu_index = j;
    753                                 break;
    754                             }
    755 
    756                             if (insns[lw - has_extra - j].instruction.getUniqueId() ==
    757                                 rabbitizer::InstrId::UniqueId::cpu_jr) {
    758                                 // Prevent going into a previous switch
    759                                 break;
    760                             }
    761                         }
    762 
    763                         if (sltiu_index != -1) {
    764                             andi_index = -1;
    765                         }
    766 
    767                         if (sltiu_index != -1 && insns[lw - has_extra - sltiu_index].instruction.getUniqueId() ==
    768                                                      rabbitizer::InstrId::UniqueId::cpu_sltiu) {
    769                             num_cases = insns[lw - has_extra - sltiu_index].instruction.getProcessedImmediate();
    770                             found = true;
    771                         } else if (andi_index != -1) {
    772                             num_cases = insns[andi_index].instruction.getProcessedImmediate() + 1;
    773                             found = true;
    774                             and_variant = true;
    775                         } else if (i == 219382) {
    776                             // Special hard case in copt where the initial sltiu is in another basic block
    777                             found = true;
    778                             num_cases = 13;
    779                         } else if (i == 370995) {
    780                             // Special hard case in copt where the initial sltiu is in another basic block
    781                             found = true;
    782                             num_cases = 12;
    783                         }
    784 
    785                         if (found) {
    786                             uint32_t jtbl_addr = insns[lw].linked_value;
    787 
    788                             if (is_pic) {
    789                                 insns[i - 1].patchInstruction(rabbitizer::InstrId::UniqueId::cpu_nop);
    790                             }
    791 
    792                             insn.jtbl_addr = jtbl_addr;
    793                             insn.num_cases = num_cases;
    794                             insn.index_reg = insns[addu_index - 1].instruction.GetO32_rt();
    795                             insns[lw].patchInstruction(rabbitizer::InstrId::UniqueId::cpu_nop);
    796 
    797                             insns[addu_index].patchInstruction(rabbitizer::InstrId::UniqueId::cpu_nop);
    798 
    799                             insns[addu_index - 1].patchInstruction(rabbitizer::InstrId::UniqueId::cpu_nop);
    800 
    801                             if (!and_variant) {
    802                                 insns[addu_index - 2].patchInstruction(rabbitizer::InstrId::UniqueId::cpu_nop);
    803                             }
    804 
    805                             if (jtbl_addr < rodata_vaddr ||
    806                                 jtbl_addr + num_cases * sizeof(uint32_t) > rodata_vaddr + rodata_section_len) {
    807                                 fprintf(stderr, "jump table outside rodata\n");
    808                                 exit(EXIT_FAILURE);
    809                             }
    810 
    811                             for (uint32_t i = 0; i < num_cases; i++) {
    812                                 uint32_t target_addr =
    813                                     read_u32_be(rodata_section + (jtbl_addr - rodata_vaddr) + i * sizeof(uint32_t));
    814 
    815                                 target_addr += gp_value;
    816                                 // printf("%08X\n", target_addr);
    817                                 label_addresses.insert(target_addr);
    818                             }
    819                         }
    820                     skip:;
    821                     }
    822                 }
    823             } else if (insn.instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_jalr) {
    824                 // empty
    825             } else {
    826                 assert(!"Unreachable code");
    827             }
    828         } else if (insn.instruction.isBranch()) {
    829             uint32_t target = insn.getAddress();
    830 
    831             label_addresses.insert(target);
    832         }
    833 
    834         switch (insns[i].instruction.getUniqueId()) {
    835             // find floating point LI
    836             case rabbitizer::InstrId::UniqueId::cpu_mtc1: {
    837                 rabbitizer::Registers::Cpu::GprO32 rt = insns[i].instruction.GetO32_rt();
    838 
    839                 for (int s = i - 1; s >= 0; s--) {
    840                     switch (insns[s].instruction.getUniqueId()) {
    841                         case rabbitizer::InstrId::UniqueId::cpu_lui:
    842                             if (insns[s].instruction.GetO32_rt() == rt) {
    843                                 float f;
    844                                 uint32_t lui_imm = insns[s].instruction.getProcessedImmediate() << 16;
    845 
    846                                 memcpy(&f, &lui_imm, sizeof(f));
    847                                 // link up the LUI with this instruction and the float
    848                                 insns[s].linked_insn = i;
    849                                 insns[s].linked_float = f;
    850                                 // rewrite LUI instruction to be LI
    851                                 insns[s].lila_dst_reg = get_dest_reg(insns[s]);
    852                                 insns[s].patchInstruction(UniqueId_cpu_li);
    853                                 insns[s].patchImmediate(lui_imm);
    854                             }
    855                             goto loop_end;
    856 
    857                         case rabbitizer::InstrId::UniqueId::cpu_lw:
    858                         case rabbitizer::InstrId::UniqueId::cpu_ld:
    859                         case rabbitizer::InstrId::UniqueId::cpu_lh:
    860                         case rabbitizer::InstrId::UniqueId::cpu_lhu:
    861                         case rabbitizer::InstrId::UniqueId::cpu_lb:
    862                         case rabbitizer::InstrId::UniqueId::cpu_lbu:
    863                         case rabbitizer::InstrId::UniqueId::cpu_addiu:
    864                             if (rt == insns[s].instruction.GetO32_rt()) {
    865                                 goto loop_end;
    866                             }
    867                             continue;
    868 
    869                         case rabbitizer::InstrId::UniqueId::cpu_add:
    870                         case rabbitizer::InstrId::UniqueId::cpu_sub:
    871                         case rabbitizer::InstrId::UniqueId::cpu_subu:
    872                             if (rt == insns[s].instruction.GetO32_rd()) {
    873                                 goto loop_end;
    874                             }
    875                             continue;
    876 
    877                         case rabbitizer::InstrId::UniqueId::cpu_jr:
    878                             if (insns[s].instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
    879                                 goto loop_end;
    880                             }
    881                             continue;
    882 
    883                         default:
    884                             continue;
    885                     }
    886                 }
    887             loop_end:;
    888             } break;
    889 
    890             case rabbitizer::InstrId::UniqueId::cpu_sd:
    891             case rabbitizer::InstrId::UniqueId::cpu_sw:
    892             case rabbitizer::InstrId::UniqueId::cpu_sh:
    893             case rabbitizer::InstrId::UniqueId::cpu_sb:
    894             case rabbitizer::InstrId::UniqueId::cpu_lb:
    895             case rabbitizer::InstrId::UniqueId::cpu_lbu:
    896             case rabbitizer::InstrId::UniqueId::cpu_ld:
    897             case rabbitizer::InstrId::UniqueId::cpu_ldl:
    898             case rabbitizer::InstrId::UniqueId::cpu_ldr:
    899             case rabbitizer::InstrId::UniqueId::cpu_lh:
    900             case rabbitizer::InstrId::UniqueId::cpu_lhu:
    901             case rabbitizer::InstrId::UniqueId::cpu_lw:
    902             case rabbitizer::InstrId::UniqueId::cpu_lwu:
    903             case rabbitizer::InstrId::UniqueId::cpu_ldc1:
    904             case rabbitizer::InstrId::UniqueId::cpu_lwc1:
    905             case rabbitizer::InstrId::UniqueId::cpu_lwc2:
    906             case rabbitizer::InstrId::UniqueId::cpu_swc1:
    907             case rabbitizer::InstrId::UniqueId::cpu_swc2: {
    908                 rabbitizer::Registers::Cpu::GprO32 mem_rs = insns[i].instruction.GetO32_rs();
    909                 int32_t mem_imm = insns[i].instruction.getProcessedImmediate();
    910 
    911                 if (mem_rs == rabbitizer::Registers::Cpu::GprO32::GPR_O32_gp) {
    912                     unsigned int got_entry = (mem_imm + gp_value_adj) / sizeof(unsigned int);
    913 
    914                     if (got_entry >= got_locals.size()) {
    915                         got_entry -= got_locals.size();
    916                         if (got_entry < got_globals.size()) {
    917                             assert(insn.instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_lw);
    918                             unsigned int dest_vaddr = got_globals[got_entry];
    919 
    920                             insns[i].is_global_got_memop = true;
    921                             insns[i].linked_value = dest_vaddr;
    922 
    923                             // patch to LA
    924                             insns[i].lila_dst_reg = get_dest_reg(insns[i]);
    925                             insns[i].patchAddress(UniqueId_cpu_la, dest_vaddr);
    926                         }
    927                     }
    928                 } else {
    929                     link_with_lui(i, mem_rs, mem_imm);
    930                 }
    931             } break;
    932 
    933             case rabbitizer::InstrId::UniqueId::cpu_addiu:
    934             case rabbitizer::InstrId::UniqueId::cpu_ori: {
    935                 // could be insn?
    936                 rabbitizer::Registers::Cpu::GprO32 rt = insns[i].instruction.GetO32_rt();
    937                 rabbitizer::Registers::Cpu::GprO32 rs = insns[i].instruction.GetO32_rs();
    938                 int32_t imm = insns[i].instruction.getProcessedImmediate();
    939 
    940                 if (rs == rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero) { // becomes LI
    941                     insns[i].lila_dst_reg = get_dest_reg(insns[i]);
    942                     insns[i].patchInstruction(UniqueId_cpu_li);
    943                     insns[i].patchImmediate(imm);
    944                 } else if (rt != rabbitizer::Registers::Cpu::GprO32::GPR_O32_gp) { // only look for LUI if rt and rs are
    945                                                                                    // the same
    946                     link_with_lui(i, rs, imm);
    947                 }
    948             } break;
    949 
    950             case rabbitizer::InstrId::UniqueId::cpu_jalr: {
    951                 rabbitizer::Registers::Cpu::GprO32 rs = insn.instruction.GetO32_rs();
    952 
    953                 if (rs == rabbitizer::Registers::Cpu::GprO32::GPR_O32_t9) {
    954                     link_with_jalr(i);
    955                     if (insn.linked_insn != -1) {
    956                         insn.patchAddress(rabbitizer::InstrId::UniqueId::cpu_jal, insn.linked_value);
    957 
    958                         label_addresses.insert(insn.linked_value);
    959                         add_function(insn.linked_value);
    960                     }
    961                 }
    962             } break;
    963 
    964             default:
    965                 break;
    966         }
    967 
    968         if ((insn.instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_addu) &&
    969             (insn.instruction.GetO32_rd() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_gp) &&
    970             (insn.instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_gp) &&
    971             (insn.instruction.GetO32_rt() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_t9) && i >= 2) {
    972             for (size_t j = i - 2; j <= i; j++) {
    973                 insns[j].patchInstruction(rabbitizer::InstrId::UniqueId::cpu_nop);
    974             }
    975         }
    976     }
    977 }
    978 
    979 uint32_t addr_to_i(uint32_t addr) {
    980     return (addr - text_vaddr) / 4;
    981 }
    982 
    983 void pass2(void) {
    984     // Find returns in each function
    985     for (size_t i = 0; i < insns.size(); i++) {
    986         uint32_t addr = text_vaddr + i * 4;
    987         Insn& insn = insns[i];
    988 
    989         if ((insn.instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_jr) &&
    990             (insn.instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra)) {
    991             auto it = find_function(addr);
    992             assert(it != functions.end());
    993 
    994             it->second.returns.push_back(addr + 4);
    995         }
    996 
    997         if (insn.instruction.getUniqueId() == UniqueId_cpu_la) {
    998             uint32_t faddr = insn.getAddress();
    999 
   1000             if ((text_vaddr <= faddr) && (faddr < text_vaddr + text_section_len)) {
   1001                 la_function_pointers.insert(faddr);
   1002                 functions[faddr].referenced_by_function_pointer = true;
   1003 #if INSPECT_FUNCTION_POINTERS
   1004                 fprintf(stderr, "la function pointer: 0x%x at 0x%x\n", faddr, addr);
   1005 #endif
   1006             }
   1007         }
   1008     }
   1009 
   1010     for (auto it = functions.begin(); it != functions.end(); ++it) {
   1011         if (it->second.returns.size() == 0) {
   1012             uint32_t i = addr_to_i(it->first);
   1013             auto str_it = symbol_names.find(it->first);
   1014 
   1015             if (str_it != symbol_names.end() && str_it->second == "__start") {
   1016 
   1017             } else if (str_it != symbol_names.end() && str_it->second == "xmalloc") {
   1018                 // orig 5.3:
   1019                 /*
   1020                 496bf4:       3c1c0fb9        lui     gp,0xfb9
   1021                 496bf8:       279c366c        addiu   gp,gp,13932
   1022                 496bfc:       0399e021        addu    gp,gp,t9
   1023                 496c00:       27bdffd8        addiu   sp,sp,-40
   1024                 496c04:       8f858de8        lw      a1,-29208(gp)
   1025                 496c08:       10000006        b       496c24 <alloc_new+0x14>
   1026                 496c0c:       afbf0020        sw      ra,32(sp)
   1027                 */
   1028 
   1029                 // jal   alloc_new
   1030                 //  lui  $a1, malloc_scb
   1031                 // jr    $ra
   1032                 //  nop
   1033                 uint32_t alloc_new_addr = text_vaddr + (i + 7) * 4;
   1034 
   1035                 insns[i].patchAddress(rabbitizer::InstrId::UniqueId::cpu_jal, alloc_new_addr);
   1036 
   1037                 assert(symbol_names.count(alloc_new_addr) && symbol_names[alloc_new_addr] == "alloc_new");
   1038                 i++;
   1039 
   1040                 // LA
   1041                 if (insns[i + 5].instruction.getUniqueId() == UniqueId_cpu_la) {
   1042                     // 7.1
   1043                     insns[i] = insns[i + 5];
   1044                 } else {
   1045                     // 5.3
   1046                     insns[i] = insns[i + 3];
   1047                 }
   1048                 i++;
   1049 
   1050                 // JR $RA
   1051                 insns[i].patched = true;
   1052                 insns[i].instruction = rabbitizer::InstructionCpu(0x03E00008, insns[i].instruction.getVram());
   1053                 it->second.returns.push_back(text_vaddr + i * 4 + 4);
   1054                 i++;
   1055 
   1056                 for (uint32_t j = 0; j < 4; j++) {
   1057                     // NOP
   1058                     insns[i].patched = true;
   1059                     insns[i].instruction = rabbitizer::InstructionCpu(0, insns[i].instruction.getVram());
   1060                     i++;
   1061                 }
   1062             } else if (str_it != symbol_names.end() && str_it->second == "xfree") {
   1063                 // jal   alloc_dispose
   1064                 //  lui  $a1, malloc_scb
   1065                 // jr    $ra
   1066                 //  nop
   1067                 uint32_t alloc_dispose_addr = text_vaddr + (i + 4) * 4;
   1068 
   1069                 if (symbol_names.count(alloc_dispose_addr + 4) &&
   1070                     symbol_names[alloc_dispose_addr + 4] == "alloc_dispose") {
   1071                     alloc_dispose_addr += 4;
   1072                 }
   1073 
   1074                 insns[i].patchAddress(rabbitizer::InstrId::UniqueId::cpu_jal, alloc_dispose_addr);
   1075                 assert(symbol_names.count(alloc_dispose_addr) && symbol_names[alloc_dispose_addr] == "alloc_dispose");
   1076                 i++;
   1077 
   1078                 insns[i] = insns[i + 2];
   1079                 i++;
   1080 
   1081                 // JR $RA
   1082                 insns[i].patched = true;
   1083                 insns[i].instruction = rabbitizer::InstructionCpu(0x03E00008, insns[i].instruction.getVram());
   1084                 it->second.returns.push_back(text_vaddr + i * 4 + 4);
   1085                 i++;
   1086 
   1087                 // NOP
   1088                 insns[i].patched = true;
   1089                 insns[i].instruction = rabbitizer::InstructionCpu(0, insns[i].instruction.getVram());
   1090             } else if ((insns[i].instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_lw) &&
   1091                        (insns[i + 1].instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_move) &&
   1092                        (insns[i + 2].instruction.getUniqueId() == rabbitizer::InstrId::UniqueId::cpu_jalr)) {
   1093                 /*
   1094                 408f50:       8f998010        lw      t9,-32752(gp)
   1095                 408f54:       03e07821        move    t7,ra
   1096                 408f58:       0320f809        jalr    t9
   1097                 */
   1098             } else if (it->first > mcount_addr) {
   1099                 fprintf(stderr, "no ret: 0x%x\n", it->first);
   1100                 abort();
   1101             }
   1102         }
   1103 
   1104         auto next = it;
   1105 
   1106         ++next;
   1107         if (next == functions.end()) {
   1108             it->second.end_addr = text_vaddr + text_section_len;
   1109         } else {
   1110             it->second.end_addr = next->first;
   1111         }
   1112     }
   1113 }
   1114 
   1115 void add_edge(uint32_t from, uint32_t to, bool function_entry = false, bool function_exit = false,
   1116               bool extern_function = false, bool function_pointer = false) {
   1117     Edge fe = Edge(), be = Edge();
   1118 
   1119     fe.i = to;
   1120     be.i = from;
   1121     fe.function_entry = function_entry;
   1122     be.function_entry = function_entry;
   1123     fe.function_exit = function_exit;
   1124     be.function_exit = function_exit;
   1125     fe.extern_function = extern_function;
   1126     be.extern_function = extern_function;
   1127     fe.function_pointer = function_pointer;
   1128     be.function_pointer = function_pointer;
   1129     insns[from].successors.push_back(fe);
   1130     insns[to].predecessors.push_back(be);
   1131 }
   1132 
   1133 void pass3(void) {
   1134     // Build graph
   1135     for (size_t i = 0; i < insns.size(); i++) {
   1136         uint32_t addr = text_vaddr + i * 4;
   1137         Insn& insn = insns[i];
   1138 
   1139         if (insn.no_following_successor) {
   1140             continue;
   1141         }
   1142 
   1143         switch (insn.instruction.getUniqueId()) {
   1144             case rabbitizer::InstrId::UniqueId::cpu_beq:
   1145             case rabbitizer::InstrId::UniqueId::cpu_bgez:
   1146             case rabbitizer::InstrId::UniqueId::cpu_bgtz:
   1147             case rabbitizer::InstrId::UniqueId::cpu_blez:
   1148             case rabbitizer::InstrId::UniqueId::cpu_bltz:
   1149             case rabbitizer::InstrId::UniqueId::cpu_bne:
   1150             case rabbitizer::InstrId::UniqueId::cpu_beqz:
   1151             case rabbitizer::InstrId::UniqueId::cpu_bnez:
   1152             case rabbitizer::InstrId::UniqueId::cpu_bc1f:
   1153             case rabbitizer::InstrId::UniqueId::cpu_bc1t:
   1154                 add_edge(i, i + 1);
   1155                 add_edge(i + 1, addr_to_i(insn.getAddress()));
   1156                 break;
   1157 
   1158             case rabbitizer::InstrId::UniqueId::cpu_beql:
   1159             case rabbitizer::InstrId::UniqueId::cpu_bgezl:
   1160             case rabbitizer::InstrId::UniqueId::cpu_bgtzl:
   1161             case rabbitizer::InstrId::UniqueId::cpu_blezl:
   1162             case rabbitizer::InstrId::UniqueId::cpu_bltzl:
   1163             case rabbitizer::InstrId::UniqueId::cpu_bnel:
   1164             case rabbitizer::InstrId::UniqueId::cpu_bc1fl:
   1165             case rabbitizer::InstrId::UniqueId::cpu_bc1tl:
   1166                 add_edge(i, i + 1);
   1167                 add_edge(i, i + 2);
   1168                 add_edge(i + 1, addr_to_i(insn.getAddress()));
   1169                 insns[i + 1].no_following_successor = true; // don't inspect delay slot
   1170                 break;
   1171 
   1172             case rabbitizer::InstrId::UniqueId::cpu_b:
   1173             case rabbitizer::InstrId::UniqueId::cpu_j:
   1174                 add_edge(i, i + 1);
   1175                 add_edge(i + 1, addr_to_i(insn.getAddress()));
   1176                 insns[i + 1].no_following_successor = true; // don't inspect delay slot
   1177                 break;
   1178 
   1179             case rabbitizer::InstrId::UniqueId::cpu_jr: {
   1180                 add_edge(i, i + 1);
   1181 
   1182                 if (insn.jtbl_addr != 0) {
   1183                     uint32_t jtbl_pos = insn.jtbl_addr - rodata_vaddr;
   1184 
   1185                     assert(jtbl_pos < rodata_section_len &&
   1186                            jtbl_pos + insn.num_cases * sizeof(uint32_t) <= rodata_section_len);
   1187 
   1188                     for (uint32_t j = 0; j < insn.num_cases; j++) {
   1189                         uint32_t dest_addr = read_u32_be(rodata_section + jtbl_pos + j * sizeof(uint32_t)) + gp_value;
   1190 
   1191                         add_edge(i + 1, addr_to_i(dest_addr));
   1192                     }
   1193                 } else {
   1194                     assert(insn.instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra &&
   1195                            "jump to address in register not supported");
   1196                 }
   1197 
   1198                 insns[i + 1].no_following_successor = true; // don't inspect delay slot
   1199                 break;
   1200             }
   1201 
   1202             case rabbitizer::InstrId::UniqueId::cpu_jal: {
   1203                 add_edge(i, i + 1);
   1204 
   1205                 uint32_t dest = insn.getAddress();
   1206 
   1207                 if (dest > mcount_addr && dest >= text_vaddr && dest < text_vaddr + text_section_len) {
   1208                     add_edge(i + 1, addr_to_i(dest), true);
   1209 
   1210                     auto it = functions.find(dest);
   1211                     assert(it != functions.end());
   1212 
   1213                     for (uint32_t ret_instr : it->second.returns) {
   1214                         add_edge(addr_to_i(ret_instr), i + 2, false, true);
   1215                     }
   1216                 } else {
   1217                     add_edge(i + 1, i + 2, false, false, true);
   1218                 }
   1219 
   1220                 insns[i + 1].no_following_successor = true; // don't inspect delay slot
   1221                 break;
   1222             }
   1223 
   1224             case rabbitizer::InstrId::UniqueId::cpu_jalr:
   1225                 // function pointer
   1226                 add_edge(i, i + 1);
   1227                 add_edge(i + 1, i + 2, false, false, false, true);
   1228                 insns[i + 1].no_following_successor = true; // don't inspect delay slot
   1229                 break;
   1230 
   1231             default:
   1232                 add_edge(i, i + 1);
   1233                 break;
   1234         }
   1235     }
   1236 }
   1237 
   1238 #define GPR_O32_hi (rabbitizer::Registers::Cpu::GprO32)((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra + 1)
   1239 #define GPR_O32_lo (rabbitizer::Registers::Cpu::GprO32)((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra + 2)
   1240 
   1241 uint64_t map_reg(rabbitizer::Registers::Cpu::GprO32 reg) {
   1242     return (uint64_t)1 << ((int)reg - (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero + 1);
   1243 }
   1244 
   1245 uint64_t temporary_regs(void) {
   1246     // clang-format off
   1247     return
   1248         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t0) |
   1249         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t1) |
   1250         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t2) |
   1251         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t3) |
   1252         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t4) |
   1253         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t5) |
   1254         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t6) |
   1255         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t7) |
   1256         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t8) |
   1257         map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_t9);
   1258     // clang-format on
   1259 }
   1260 
   1261 typedef enum {
   1262     /* 0 */ TYPE_NOP, // No arguments
   1263     /* 1 */ TYPE_S,   // in
   1264     /* 2 */ TYPE_D,   // 1 out
   1265     /* 3 */ TYPE_D_S, // out, in
   1266 } TYPE;
   1267 
   1268 TYPE insn_to_type(Insn& insn) {
   1269     switch (insn.instruction.getUniqueId()) {
   1270         case rabbitizer::InstrId::UniqueId::cpu_add_s:
   1271         case rabbitizer::InstrId::UniqueId::cpu_add_d:
   1272             return TYPE_NOP;
   1273 
   1274         case rabbitizer::InstrId::UniqueId::cpu_add:
   1275         case rabbitizer::InstrId::UniqueId::cpu_addu:
   1276             return TYPE_D_S;
   1277 
   1278         case rabbitizer::InstrId::UniqueId::cpu_addi:
   1279         case rabbitizer::InstrId::UniqueId::cpu_addiu:
   1280         case rabbitizer::InstrId::UniqueId::cpu_andi:
   1281         case rabbitizer::InstrId::UniqueId::cpu_ori:
   1282         case rabbitizer::InstrId::UniqueId::cpu_lb:
   1283         case rabbitizer::InstrId::UniqueId::cpu_lbu:
   1284         case rabbitizer::InstrId::UniqueId::cpu_lh:
   1285         case rabbitizer::InstrId::UniqueId::cpu_lhu:
   1286         case rabbitizer::InstrId::UniqueId::cpu_lw:
   1287         case rabbitizer::InstrId::UniqueId::cpu_lwl:
   1288         // case rabbitizer::InstrId::UniqueId::cpu_lwr:
   1289         case rabbitizer::InstrId::UniqueId::cpu_move:
   1290         case rabbitizer::InstrId::UniqueId::cpu_negu:
   1291         case rabbitizer::InstrId::UniqueId::cpu_not:
   1292         case rabbitizer::InstrId::UniqueId::cpu_sll:
   1293         case rabbitizer::InstrId::UniqueId::cpu_slti:
   1294         case rabbitizer::InstrId::UniqueId::cpu_sltiu:
   1295         case rabbitizer::InstrId::UniqueId::cpu_sra:
   1296         case rabbitizer::InstrId::UniqueId::cpu_srl:
   1297         case rabbitizer::InstrId::UniqueId::cpu_xori:
   1298             return TYPE_D_S;
   1299 
   1300         case rabbitizer::InstrId::UniqueId::cpu_mfhi:
   1301             return TYPE_D_S;
   1302 
   1303         case rabbitizer::InstrId::UniqueId::cpu_mflo:
   1304             return TYPE_D_S;
   1305 
   1306         case rabbitizer::InstrId::UniqueId::cpu_and:
   1307         case rabbitizer::InstrId::UniqueId::cpu_or:
   1308         case rabbitizer::InstrId::UniqueId::cpu_nor:
   1309         case rabbitizer::InstrId::UniqueId::cpu_sllv:
   1310         case rabbitizer::InstrId::UniqueId::cpu_slt:
   1311         case rabbitizer::InstrId::UniqueId::cpu_sltu:
   1312         case rabbitizer::InstrId::UniqueId::cpu_srav:
   1313         case rabbitizer::InstrId::UniqueId::cpu_srlv:
   1314         case rabbitizer::InstrId::UniqueId::cpu_subu:
   1315         case rabbitizer::InstrId::UniqueId::cpu_xor:
   1316             return TYPE_D_S;
   1317 
   1318         case rabbitizer::InstrId::UniqueId::cpu_cfc1:
   1319         case rabbitizer::InstrId::UniqueId::cpu_mfc1:
   1320         case UniqueId_cpu_li:
   1321         case UniqueId_cpu_la:
   1322         case rabbitizer::InstrId::UniqueId::cpu_lui:
   1323             return TYPE_D;
   1324 
   1325         case rabbitizer::InstrId::UniqueId::cpu_ctc1:
   1326         case rabbitizer::InstrId::UniqueId::cpu_bgez:
   1327         case rabbitizer::InstrId::UniqueId::cpu_bgezl:
   1328         case rabbitizer::InstrId::UniqueId::cpu_bgtz:
   1329         case rabbitizer::InstrId::UniqueId::cpu_bgtzl:
   1330         case rabbitizer::InstrId::UniqueId::cpu_blez:
   1331         case rabbitizer::InstrId::UniqueId::cpu_blezl:
   1332         case rabbitizer::InstrId::UniqueId::cpu_bltz:
   1333         case rabbitizer::InstrId::UniqueId::cpu_bltzl:
   1334         case rabbitizer::InstrId::UniqueId::cpu_beqz:
   1335         case rabbitizer::InstrId::UniqueId::cpu_bnez:
   1336         case rabbitizer::InstrId::UniqueId::cpu_mtc1:
   1337             return TYPE_S;
   1338 
   1339         case rabbitizer::InstrId::UniqueId::cpu_beq:
   1340         case rabbitizer::InstrId::UniqueId::cpu_beql:
   1341         case rabbitizer::InstrId::UniqueId::cpu_bne:
   1342         case rabbitizer::InstrId::UniqueId::cpu_bnel:
   1343         case rabbitizer::InstrId::UniqueId::cpu_sb:
   1344         case rabbitizer::InstrId::UniqueId::cpu_sh:
   1345         case rabbitizer::InstrId::UniqueId::cpu_sw:
   1346         case rabbitizer::InstrId::UniqueId::cpu_swl:
   1347         // case rabbitizer::InstrId::UniqueId::cpu_swr:
   1348         case rabbitizer::InstrId::UniqueId::cpu_tne:
   1349         case rabbitizer::InstrId::UniqueId::cpu_teq:
   1350         case rabbitizer::InstrId::UniqueId::cpu_tge:
   1351         case rabbitizer::InstrId::UniqueId::cpu_tgeu:
   1352         case rabbitizer::InstrId::UniqueId::cpu_tlt:
   1353             return TYPE_S;
   1354 
   1355         case rabbitizer::InstrId::UniqueId::cpu_div:
   1356             return TYPE_D_S;
   1357 
   1358         case rabbitizer::InstrId::UniqueId::cpu_div_s:
   1359         case rabbitizer::InstrId::UniqueId::cpu_div_d:
   1360             return TYPE_NOP;
   1361 
   1362         case rabbitizer::InstrId::UniqueId::cpu_divu:
   1363         case rabbitizer::InstrId::UniqueId::cpu_mult:
   1364         case rabbitizer::InstrId::UniqueId::cpu_multu:
   1365             return TYPE_D_S;
   1366 
   1367         case rabbitizer::InstrId::UniqueId::cpu_neg_s:
   1368         case rabbitizer::InstrId::UniqueId::cpu_neg_d:
   1369             return TYPE_NOP;
   1370 
   1371         case rabbitizer::InstrId::UniqueId::cpu_jalr:
   1372             return TYPE_S;
   1373 
   1374         case rabbitizer::InstrId::UniqueId::cpu_jr:
   1375             if (insn.jtbl_addr != 0) {
   1376                 insn.instruction.Set_rs(insn.index_reg);
   1377             }
   1378             if (insn.instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
   1379                 return TYPE_NOP;
   1380             }
   1381             return TYPE_S;
   1382 
   1383         case rabbitizer::InstrId::UniqueId::cpu_lwc1:
   1384         case rabbitizer::InstrId::UniqueId::cpu_ldc1:
   1385         case rabbitizer::InstrId::UniqueId::cpu_swc1:
   1386         case rabbitizer::InstrId::UniqueId::cpu_sdc1:
   1387             return TYPE_S;
   1388 
   1389         default:
   1390             return TYPE_NOP;
   1391     }
   1392 }
   1393 
   1394 uint64_t get_dest_reg_mask(const Insn& insn) {
   1395     switch (insn.instruction.getUniqueId()) {
   1396         case rabbitizer::InstrId::UniqueId::cpu_div:
   1397         case rabbitizer::InstrId::UniqueId::cpu_divu:
   1398         case rabbitizer::InstrId::UniqueId::cpu_mult:
   1399         case rabbitizer::InstrId::UniqueId::cpu_multu:
   1400             return map_reg(GPR_O32_lo) | map_reg(GPR_O32_hi);
   1401 
   1402         default:
   1403             return map_reg(get_dest_reg(insn));
   1404     }
   1405 }
   1406 
   1407 uint64_t get_single_source_reg_mask(const rabbitizer::InstructionCpu& instr) {
   1408     switch (instr.getUniqueId()) {
   1409         case rabbitizer::InstrId::UniqueId::cpu_mflo:
   1410             return map_reg(GPR_O32_lo);
   1411         case rabbitizer::InstrId::UniqueId::cpu_mfhi:
   1412             return map_reg(GPR_O32_hi);
   1413 
   1414         default:
   1415             break;
   1416     }
   1417     if (instr.hasOperandAlias(rabbitizer::OperandType::cpu_rs)) {
   1418         return map_reg(instr.GetO32_rs());
   1419     } else if (instr.hasOperandAlias(rabbitizer::OperandType::cpu_rt)) {
   1420         return map_reg(instr.GetO32_rt());
   1421     } else {
   1422         return 0;
   1423     }
   1424 }
   1425 
   1426 uint64_t get_all_source_reg_mask(const rabbitizer::InstructionCpu& instr) {
   1427     uint64_t ret = 0;
   1428 
   1429     switch (instr.getUniqueId()) {
   1430         case rabbitizer::InstrId::UniqueId::cpu_mflo:
   1431             ret |= map_reg(GPR_O32_lo);
   1432         case rabbitizer::InstrId::UniqueId::cpu_mfhi:
   1433             ret |= map_reg(GPR_O32_hi);
   1434 
   1435         default:
   1436             break;
   1437     }
   1438 
   1439     if (instr.hasOperandAlias(rabbitizer::OperandType::cpu_rs)) {
   1440         ret |= map_reg(instr.GetO32_rs());
   1441     }
   1442     if (instr.hasOperandAlias(rabbitizer::OperandType::cpu_rt) && !instr.modifiesRt()) {
   1443         ret |= map_reg(instr.GetO32_rt());
   1444     }
   1445     return ret;
   1446 }
   1447 
   1448 void pass4(void) {
   1449     vector<uint32_t> q; // TODO: Why is this called q?
   1450     uint64_t livein_func_start = 1U | map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1451                                  map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1452                                  map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_sp) |
   1453                                  map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero);
   1454 
   1455     q.push_back(main_addr);
   1456     insns[addr_to_i(main_addr)].f_livein = livein_func_start;
   1457 
   1458     for (auto& it : data_function_pointers) {
   1459         q.push_back(it.second);
   1460         insns[addr_to_i(it.second)].f_livein = livein_func_start |
   1461                                                map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1462                                                map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3);
   1463     }
   1464 
   1465     for (auto& addr : la_function_pointers) {
   1466         q.push_back(addr);
   1467         insns[addr_to_i(addr)].f_livein = livein_func_start | map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1468                                           map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3);
   1469     }
   1470 
   1471     while (!q.empty()) {
   1472         uint32_t addr = q.back();
   1473         q.pop_back();
   1474         uint32_t i = addr_to_i(addr);
   1475         Insn& insn = insns[i];
   1476         uint64_t live = insn.f_livein | 1U;
   1477         uint64_t src_regs_map;
   1478 
   1479         switch (insn_to_type(insn)) {
   1480             case TYPE_D:
   1481                 live |= get_dest_reg_mask(insn);
   1482                 break;
   1483 
   1484             case TYPE_D_S:
   1485                 src_regs_map = get_all_source_reg_mask(insn.instruction);
   1486                 if ((live & src_regs_map) == src_regs_map) {
   1487                     live |= get_dest_reg_mask(insn);
   1488                 }
   1489                 break;
   1490 
   1491             case TYPE_S:
   1492             case TYPE_NOP:
   1493                 break;
   1494         }
   1495 
   1496         if ((insn.f_liveout | live) == insn.f_liveout) {
   1497             // No new bits
   1498             continue;
   1499         }
   1500 
   1501         live |= insn.f_liveout;
   1502         insn.f_liveout = live;
   1503 
   1504         bool function_entry = false;
   1505 
   1506         for (Edge& e : insn.successors) {
   1507             uint64_t new_live = live;
   1508 
   1509             if (e.function_exit) {
   1510                 new_live &= 1U | map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1511                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1) |
   1512                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero);
   1513             } else if (e.function_entry) {
   1514                 new_live &= 1U | map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1515                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1516                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1517                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1518                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3) |
   1519                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_sp) |
   1520                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero);
   1521                 function_entry = true;
   1522             } else if (e.extern_function) {
   1523                 string_view name;
   1524                 size_t extern_function_id;
   1525                 uint32_t address = insns[i - 1].getAddress();
   1526 
   1527                 // TODO: Can this only ever be a J-type instruction?
   1528                 auto it = symbol_names.find(address);
   1529                 const ExternFunction* found_fn = nullptr;
   1530 
   1531                 if (it != symbol_names.end()) {
   1532                     name = it->second;
   1533 
   1534                     for (auto& fn : extern_functions) {
   1535                         if (name == fn.name) {
   1536                             found_fn = &fn;
   1537                             break;
   1538                         }
   1539                     }
   1540 
   1541                     if (found_fn == nullptr) {
   1542                         fprintf(stderr, "missing extern function: %s\n", string(name).c_str());
   1543                     }
   1544                 }
   1545 
   1546                 assert(found_fn);
   1547 
   1548                 char ret_type = found_fn->params[0];
   1549 
   1550                 new_live &= ~(map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1551                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1552                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1553                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1554                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3) |
   1555                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1) | temporary_regs());
   1556 
   1557                 switch (ret_type) {
   1558                     case 'i':
   1559                     case 'u':
   1560                     case 'p':
   1561                         new_live |= map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0);
   1562                         break;
   1563 
   1564                     case 'f':
   1565                         break;
   1566 
   1567                     case 'd':
   1568                         break;
   1569 
   1570                     case 'v':
   1571                         break;
   1572 
   1573                     case 'l':
   1574                     case 'j':
   1575                         new_live |= map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1576                                     map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1);
   1577                         break;
   1578                 }
   1579             } else if (e.function_pointer) {
   1580                 new_live &= ~(map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1581                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1582                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1583                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1584                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3) |
   1585                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1) | temporary_regs());
   1586                 new_live |= map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1587                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1);
   1588             }
   1589 
   1590             if ((insns[e.i].f_livein | new_live) != insns[e.i].f_livein) {
   1591                 insns[e.i].f_livein |= new_live;
   1592                 q.push_back(text_vaddr + e.i * sizeof(uint32_t));
   1593             }
   1594         }
   1595 
   1596         if (function_entry) {
   1597             // add one edge that skips the function call, for callee-saved register liveness propagation
   1598             live &= ~(map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1599                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1600                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1601                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1602                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3) |
   1603                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1) | temporary_regs());
   1604 
   1605             if ((insns[i + 1].f_livein | live) != insns[i + 1].f_livein) {
   1606                 insns[i + 1].f_livein |= live;
   1607                 q.push_back(text_vaddr + (i + 1) * sizeof(uint32_t));
   1608             }
   1609         }
   1610     }
   1611 }
   1612 
   1613 void pass5(void) {
   1614     vector<uint32_t> q;
   1615 
   1616     assert(functions.count(main_addr));
   1617 
   1618     q = functions[main_addr].returns;
   1619     for (auto addr : q) {
   1620         insns[addr_to_i(addr)].b_liveout = 1U | map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0);
   1621     }
   1622 
   1623     for (auto& it : data_function_pointers) {
   1624         for (auto addr : functions[it.second].returns) {
   1625             q.push_back(addr);
   1626             insns[addr_to_i(addr)].b_liveout = 1U | map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1627                                                map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1);
   1628         }
   1629     }
   1630 
   1631     for (auto& func_addr : la_function_pointers) {
   1632         for (auto addr : functions[func_addr].returns) {
   1633             q.push_back(addr);
   1634             insns[addr_to_i(addr)].b_liveout = 1U | map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1635                                                map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1);
   1636         }
   1637     }
   1638 
   1639     for (auto& insn : insns) {
   1640         if (insn.f_livein != 0) {
   1641             q.push_back(insn.instruction.getVram());
   1642         }
   1643     }
   1644 
   1645     while (!q.empty()) {
   1646         uint32_t addr = q.back();
   1647 
   1648         q.pop_back();
   1649 
   1650         uint32_t i = addr_to_i(addr);
   1651         Insn& insn = insns[i];
   1652         uint64_t live = insn.b_liveout | 1;
   1653 
   1654         switch (insn_to_type(insn)) {
   1655             case TYPE_S:
   1656                 live |= get_all_source_reg_mask(insn.instruction);
   1657                 break;
   1658 
   1659             case TYPE_D:
   1660                 live &= ~get_dest_reg_mask(insn);
   1661                 break;
   1662 
   1663             case TYPE_D_S:
   1664                 if (live & get_dest_reg_mask(insn)) {
   1665                     live &= ~get_dest_reg_mask(insn);
   1666                     live |= get_all_source_reg_mask(insn.instruction);
   1667                 }
   1668                 break;
   1669 
   1670             case TYPE_NOP:
   1671                 break;
   1672         }
   1673 
   1674         if ((insn.b_livein | live) == insn.b_livein) {
   1675             // No new bits
   1676             continue;
   1677         }
   1678 
   1679         live |= insn.b_livein;
   1680         insn.b_livein = live;
   1681 
   1682         bool function_exit = false;
   1683 
   1684         for (Edge& e : insn.predecessors) {
   1685             uint64_t new_live = live;
   1686 
   1687             if (e.function_exit) {
   1688                 new_live &= 1U | map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1689                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1);
   1690                 function_exit = true;
   1691             } else if (e.function_entry) {
   1692                 new_live &= 1U | map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1693                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1694                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1695                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1696                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3) |
   1697                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_sp);
   1698             } else if (e.extern_function) {
   1699                 string_view name;
   1700                 size_t extern_function_id;
   1701                 const ExternFunction* found_fn = nullptr;
   1702                 uint32_t address = insns[i - 2].getAddress();
   1703                 // TODO: Can this only ever be a J-type instruction?
   1704                 auto it = symbol_names.find(address);
   1705 
   1706                 if (it != symbol_names.end()) {
   1707                     name = it->second;
   1708                     for (auto& fn : extern_functions) {
   1709                         if (name == fn.name) {
   1710                             found_fn = &fn;
   1711                             break;
   1712                         }
   1713                     }
   1714                 }
   1715 
   1716                 assert(found_fn);
   1717 
   1718                 uint64_t args = 1U;
   1719 
   1720                 if (found_fn->flags & FLAG_VARARG) {
   1721                     // Assume the worst, that all four registers are used
   1722                     for (int j = 0; j < 4; j++) {
   1723                         args |= map_reg((rabbitizer::Registers::Cpu::GprO32)(
   1724                             (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + j));
   1725                     }
   1726                 }
   1727 
   1728                 int pos = 0;
   1729                 int pos_float = 0;
   1730                 bool only_floats_so_far = true;
   1731 
   1732                 for (const char* p = found_fn->params + 1; *p != '\0'; ++p) {
   1733                     switch (*p) {
   1734                         case 'i':
   1735                         case 'u':
   1736                         case 'p':
   1737                         case 't':
   1738                             only_floats_so_far = false;
   1739                             if (pos < 4) {
   1740                                 args |= map_reg((rabbitizer::Registers::Cpu::GprO32)(
   1741                                     (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos));
   1742                             }
   1743                             ++pos;
   1744                             break;
   1745 
   1746                         case 'f':
   1747                             if (only_floats_so_far && pos_float < 4) {
   1748                                 pos_float += 2;
   1749                             } else if (pos < 4) {
   1750                                 args |= map_reg((rabbitizer::Registers::Cpu::GprO32)(
   1751                                     (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos));
   1752                             }
   1753                             ++pos;
   1754                             break;
   1755 
   1756                         case 'd':
   1757                             // !!!
   1758                             if (pos % 1 != 0) {
   1759                                 ++pos;
   1760                             }
   1761                             if (only_floats_so_far && pos_float < 4) {
   1762                                 pos_float += 2;
   1763                             } else if (pos < 4) {
   1764                                 args |= map_reg((rabbitizer::Registers::Cpu::GprO32)(
   1765                                             (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos)) |
   1766                                         map_reg((rabbitizer::Registers::Cpu::GprO32)(
   1767                                             (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos + 1));
   1768                             }
   1769                             pos += 2;
   1770                             break;
   1771 
   1772                         case 'l':
   1773                         case 'j':
   1774                             if (pos % 1 != 0) {
   1775                                 ++pos;
   1776                             }
   1777                             only_floats_so_far = false;
   1778                             if (pos < 4) {
   1779                                 args |= map_reg((rabbitizer::Registers::Cpu::GprO32)(
   1780                                             (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos)) |
   1781                                         map_reg((rabbitizer::Registers::Cpu::GprO32)(
   1782                                             (int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos + 1));
   1783                             }
   1784                             pos += 2;
   1785                             break;
   1786                     }
   1787                 }
   1788                 args |= map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_sp);
   1789                 new_live &= ~(map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1790                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1791                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1792                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1793                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3) |
   1794                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1) | temporary_regs());
   1795                 new_live |= args;
   1796             } else if (e.function_pointer) {
   1797                 new_live &= ~(map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1798                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1799                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1800                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1801                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3) |
   1802                               map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1) | temporary_regs());
   1803                 new_live |= map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1804                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1805                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1806                             map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3);
   1807             }
   1808 
   1809             if ((insns[e.i].b_liveout | new_live) != insns[e.i].b_liveout) {
   1810                 insns[e.i].b_liveout |= new_live;
   1811                 q.push_back(text_vaddr + e.i * sizeof(uint32_t));
   1812             }
   1813         }
   1814 
   1815         if (function_exit) {
   1816             // add one edge that skips the function call, for callee-saved register liveness propagation
   1817             live &= ~(map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0) |
   1818                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0) |
   1819                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1) |
   1820                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2) |
   1821                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3) |
   1822                       map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1) | temporary_regs());
   1823 
   1824             if ((insns[i - 1].b_liveout | live) != insns[i - 1].b_liveout) {
   1825                 insns[i - 1].b_liveout |= live;
   1826                 q.push_back(text_vaddr + (i - 1) * sizeof(uint32_t));
   1827             }
   1828         }
   1829     }
   1830 }
   1831 
   1832 void pass6(void) {
   1833     for (auto& it : functions) {
   1834         uint32_t addr = it.first;
   1835         Function& f = it.second;
   1836 
   1837         for (uint32_t ret : f.returns) {
   1838             Insn& i = insns[addr_to_i(ret)];
   1839 
   1840             if (i.f_liveout & i.b_liveout & map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1)) {
   1841                 f.nret = 2;
   1842             } else if ((i.f_liveout & i.b_liveout & map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0)) &&
   1843                        f.nret == 0) {
   1844                 f.nret = 1;
   1845             }
   1846         }
   1847 
   1848         Insn& insn = insns.at(addr_to_i(addr));
   1849 
   1850         for (int i = 0; i < 4; i++) {
   1851             if (insn.f_livein & insn.b_livein &
   1852                 map_reg(
   1853                     (rabbitizer::Registers::Cpu::GprO32)((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + i))) {
   1854                 f.nargs = 1 + i;
   1855             }
   1856         }
   1857         f.v0_in = (insn.f_livein & insn.b_livein & map_reg(rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0)) != 0 &&
   1858                   !f.referenced_by_function_pointer;
   1859     }
   1860 }
   1861 
   1862 void dump(void) {
   1863     for (size_t i = 0; i < insns.size(); i++) {
   1864         Insn& insn = insns[i];
   1865         uint32_t vaddr = text_vaddr + i * sizeof(uint32_t);
   1866         if (label_addresses.count(vaddr)) {
   1867             if (symbol_names.count(vaddr)) {
   1868                 printf("L%08x: //%s\n", vaddr, symbol_names[vaddr].c_str());
   1869             } else {
   1870                 printf("L%08x:\n", vaddr);
   1871             }
   1872         }
   1873 
   1874         // TODO: construct an immediate override for the instructions
   1875         printf("\t%s", insn.disassemble().c_str());
   1876         if (insn.patched) {
   1877             printf("\t[patched, immediate now 0x%X]", insn.patched_addr);
   1878         }
   1879         printf("\n");
   1880     }
   1881 }
   1882 
   1883 const char* r(uint32_t reg) {
   1884     static const char* regs[] = {
   1885         /*  */ "zero", "at", "v0", "v1",
   1886         /*  */ "a0",   "a1", "a2", "a3",
   1887         /*  */ "t0",   "t1", "t2", "t3", "t4", "t5", "t6", "t7",
   1888         /*  */ "s0",   "s1", "s2", "s3", "s4", "s5", "s6", "s7",
   1889         /*  */ "t8",   "t9", "k0", "k1", "gp", "sp", "fp", "ra",
   1890     };
   1891 
   1892     assert(reg < std::size(regs));
   1893     return regs[reg];
   1894 }
   1895 
   1896 const char* wr(uint32_t reg) {
   1897     // clang-format off
   1898     static const char *regs[] = {
   1899         "f0.w[0]", "f0.w[1]",
   1900         "f2.w[0]", "f2.w[1]",
   1901         "f4.w[0]", "f4.w[1]",
   1902         "f6.w[0]", "f6.w[1]",
   1903         "f8.w[0]", "f8.w[1]",
   1904         "f10.w[0]", "f10.w[1]",
   1905         "f12.w[0]", "f12.w[1]",
   1906         "f14.w[0]", "f14.w[1]",
   1907         "f16.w[0]", "f16.w[1]",
   1908         "f18.w[0]", "f18.w[1]",
   1909         "f20.w[0]", "f20.w[1]",
   1910         "f22.w[0]", "f22.w[1]",
   1911         "f24.w[0]", "f24.w[1]",
   1912         "f26.w[0]", "f26.w[1]",
   1913         "f28.w[0]", "f28.w[1]",
   1914         "f30.w[0]", "f30.w[1]"
   1915     };
   1916     // clang-format on
   1917 
   1918     size_t index = reg - (int)rabbitizer::Registers::Cpu::Cop1O32::COP1_O32_fv0;
   1919 
   1920     assert(index < std::size(regs));
   1921     return regs[index];
   1922 }
   1923 
   1924 const char* fr(uint32_t reg) {
   1925     // clang-format off
   1926     static const char *regs[] = {
   1927         "f0.f[0]", "f0.f[1]",
   1928         "f2.f[0]", "f2.f[1]",
   1929         "f4.f[0]", "f4.f[1]",
   1930         "f6.f[0]", "f6.f[1]",
   1931         "f8.f[0]", "f8.f[1]",
   1932         "f10.f[0]", "f10.f[1]",
   1933         "f12.f[0]", "f12.f[1]",
   1934         "f14.f[0]", "f14.f[1]",
   1935         "f16.f[0]", "f16.f[1]",
   1936         "f18.f[0]", "f18.f[1]",
   1937         "f20.f[0]", "f20.f[1]",
   1938         "f22.f[0]", "f22.f[1]",
   1939         "f24.f[0]", "f24.f[1]",
   1940         "f26.f[0]", "f26.f[1]",
   1941         "f28.f[0]", "f28.f[1]",
   1942         "f30.f[0]", "f30.f[1]",
   1943     };
   1944     // clang-format on
   1945 
   1946     size_t index = reg - (int)rabbitizer::Registers::Cpu::Cop1O32::COP1_O32_fv0;
   1947 
   1948     assert(index < std::size(regs));
   1949     return regs[index];
   1950 }
   1951 
   1952 const char* dr(uint32_t reg) {
   1953     // clang-format off
   1954     static const char *regs[] = {
   1955         "f0",
   1956         "f2",
   1957         "f4",
   1958         "f6",
   1959         "f8",
   1960         "f10",
   1961         "f12",
   1962         "f14",
   1963         "f16",
   1964         "f18",
   1965         "f20",
   1966         "f22",
   1967         "f24",
   1968         "f26",
   1969         "f28",
   1970         "f30"
   1971     };
   1972     // clang-format on
   1973 
   1974     size_t index = reg - (int)rabbitizer::Registers::Cpu::Cop1O32::COP1_O32_fv0;
   1975 
   1976     assert(index % 2 == 0);
   1977     index /= 2;
   1978     assert(index < std::size(regs));
   1979     return regs[index];
   1980 }
   1981 
   1982 void dump_instr(int i);
   1983 
   1984 void dump_cond_branch(int i, const char* lhs, const char* op, const char* rhs) {
   1985     Insn& insn = insns[i];
   1986     const char* cast1 = "";
   1987     const char* cast2 = "";
   1988 
   1989     if (strcmp(op, "==") && strcmp(op, "!=")) {
   1990         cast1 = "(int)";
   1991         if (strcmp(rhs, "0")) {
   1992             cast2 = "(int)";
   1993         }
   1994     }
   1995     printf("if (%s%s %s %s%s) {\n", cast1, lhs, op, cast2, rhs);
   1996     dump_instr(i + 1);
   1997 
   1998     uint32_t addr = insn.getAddress();
   1999 
   2000     printf("goto L%x;}\n", addr);
   2001 }
   2002 
   2003 void dump_cond_branch_likely(int i, const char* lhs, const char* op, const char* rhs) {
   2004     uint32_t target = text_vaddr + (i + 2) * sizeof(uint32_t);
   2005 
   2006     dump_cond_branch(i, lhs, op, rhs);
   2007     if (!TRACE) {
   2008         printf("else goto L%x;\n", target);
   2009     } else {
   2010         printf("else {printf(\"pc=0x%08x (ignored)\\n\"); goto L%x;}\n", text_vaddr + (i + 1) * 4, target);
   2011     }
   2012     label_addresses.insert(target);
   2013 }
   2014 
   2015 void dump_jal(int i, uint32_t imm) {
   2016     string_view name;
   2017     auto it = symbol_names.find(imm);
   2018     const ExternFunction* found_fn = nullptr;
   2019 
   2020     // Check for an external function at the address in the immediate. If it does not exist, function is internal
   2021     if (it != symbol_names.end()) {
   2022         name = it->second;
   2023         for (auto& fn : extern_functions) {
   2024             if (name == fn.name) {
   2025                 found_fn = &fn;
   2026                 break;
   2027             }
   2028         }
   2029     }
   2030 
   2031     dump_instr(i + 1);
   2032 
   2033     if (found_fn != nullptr) {
   2034         if (found_fn->flags & FLAG_VARARG) {
   2035             for (int j = 0; j < 4; j++) {
   2036                 printf("MEM_U32(sp + %d) = %s;\n", j * 4, r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + j));
   2037             }
   2038         }
   2039 
   2040         const char ret_type = found_fn->params[0];
   2041 
   2042         switch (ret_type) {
   2043             case 'v':
   2044                 break;
   2045 
   2046             case 'i':
   2047             case 'u':
   2048             case 'p':
   2049                 printf("%s = ", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0));
   2050                 break;
   2051 
   2052             case 'f':
   2053                 printf("%s = ", fr((int)rabbitizer::Registers::Cpu::Cop1O32::COP1_O32_fv0));
   2054                 break;
   2055 
   2056             case 'd':
   2057                 printf("tempf64 = ");
   2058                 break;
   2059 
   2060             case 'l':
   2061             case 'j':
   2062                 printf("temp64 = ");
   2063                 break;
   2064         }
   2065 
   2066         printf("wrapper_%s(", string(name).c_str());
   2067 
   2068         bool first = true;
   2069 
   2070         if (!(found_fn->flags & FLAG_NO_MEM)) {
   2071             printf("mem");
   2072             first = false;
   2073         }
   2074 
   2075         int pos = 0;
   2076         int pos_float = 0;
   2077         bool only_floats_so_far = true;
   2078         bool needs_sp = false;
   2079 
   2080         for (const char* p = &found_fn->params[1]; *p != '\0'; ++p) {
   2081             if (!first) {
   2082                 printf(", ");
   2083             }
   2084 
   2085             first = false;
   2086 
   2087             switch (*p) {
   2088                 case 't':
   2089                     printf("trampoline, ");
   2090                     needs_sp = true;
   2091                     // fallthrough
   2092                 case 'i':
   2093                 case 'u':
   2094                 case 'p':
   2095                     only_floats_so_far = false;
   2096                     if (pos < 4) {
   2097                         printf("%s", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos));
   2098                     } else {
   2099                         printf("MEM_%c32(sp + %d)", *p == 'i' ? 'S' : 'U', pos * 4);
   2100                     }
   2101                     ++pos;
   2102                     break;
   2103 
   2104                 case 'f':
   2105                     if (only_floats_so_far && pos_float < 4) {
   2106                         printf("%s", fr((int)rabbitizer::Registers::Cpu::Cop1O32::COP1_O32_fa0 + pos_float));
   2107                         pos_float += 2;
   2108                     } else if (pos < 4) {
   2109                         printf("BITCAST_U32_TO_F32(%s)", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos));
   2110                     } else {
   2111                         printf("BITCAST_U32_TO_F32(MEM_U32(sp + %d))", pos * 4);
   2112                     }
   2113                     ++pos;
   2114                     break;
   2115 
   2116                 case 'd':
   2117                     if (pos % 1 != 0) {
   2118                         ++pos;
   2119                     }
   2120                     if (only_floats_so_far && pos_float < 4) {
   2121                         printf("double_from_FloatReg(%s)",
   2122                                dr((int)rabbitizer::Registers::Cpu::Cop1O32::COP1_O32_fa0 + pos_float));
   2123                         pos_float += 2;
   2124                     } else if (pos < 4) {
   2125                         printf("BITCAST_U64_TO_F64(((uint64_t)%s << 32) | (uint64_t)%s)",
   2126                                r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos),
   2127                                r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos + 1));
   2128                     } else {
   2129                         printf("BITCAST_U64_TO_F64(((uint64_t)MEM_U32(sp + %d) << 32) | "
   2130                                "(uint64_t)MEM_U32(sp + "
   2131                                "%d))",
   2132                                pos * 4, (pos + 1) * 4);
   2133                     }
   2134                     pos += 2;
   2135                     break;
   2136 
   2137                 case 'l':
   2138                 case 'j':
   2139                     if (pos % 1 != 0) {
   2140                         ++pos;
   2141                     }
   2142                     only_floats_so_far = false;
   2143                     if (*p == 'l') {
   2144                         printf("(int64_t)");
   2145                     }
   2146                     if (pos < 4) {
   2147                         printf("(((uint64_t)%s << 32) | (uint64_t)%s)",
   2148                                r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos),
   2149                                r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + pos + 1));
   2150                     } else {
   2151                         printf("(((uint64_t)MEM_U32(sp + %d) << 32) | (uint64_t)MEM_U32(sp + %d))", pos * 4,
   2152                                (pos + 1) * 4);
   2153                     }
   2154                     pos += 2;
   2155                     break;
   2156             }
   2157         }
   2158 
   2159         if ((found_fn->flags & FLAG_VARARG) || needs_sp) {
   2160             printf("%s%s", first ? "" : ", ", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_sp));
   2161         }
   2162 
   2163         printf(");\n");
   2164 
   2165         if (ret_type == 'l' || ret_type == 'j') {
   2166             printf("%s = (uint32_t)(temp64 >> 32);\n", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0));
   2167             printf("%s = (uint32_t)temp64;\n", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1));
   2168         } else if (ret_type == 'd') {
   2169             printf("%s = FloatReg_from_double(tempf64);\n", dr((int)rabbitizer::Registers::Cpu::Cop1O32::COP1_O32_fv0));
   2170         }
   2171     } else {
   2172         Function& f = functions.find(imm)->second;
   2173 
   2174         if (f.nret == 1) {
   2175             printf("v0 = ");
   2176         } else if (f.nret == 2) {
   2177             printf("temp64 = ");
   2178         }
   2179 
   2180         if (!name.empty()) {
   2181             printf("f_%s", string(name).c_str());
   2182         } else {
   2183             printf("func_%x", imm);
   2184         }
   2185 
   2186         printf("(mem, sp");
   2187 
   2188         if (f.v0_in) {
   2189             printf(", %s", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0));
   2190         }
   2191 
   2192         for (uint32_t i = 0; i < f.nargs; i++) {
   2193             printf(", %s", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + i));
   2194         }
   2195 
   2196         printf(");\n");
   2197 
   2198         if (f.nret == 2) {
   2199             printf("%s = (uint32_t)(temp64 >> 32);\n", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0));
   2200             printf("%s = (uint32_t)temp64;\n", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1));
   2201         }
   2202     }
   2203 
   2204     printf("goto L%x;\n", text_vaddr + (i + 2) * 4);
   2205     label_addresses.insert(text_vaddr + (i + 2) * 4);
   2206 }
   2207 
   2208 void dump_instr(int i) {
   2209     Insn& insn = insns[i];
   2210 
   2211     const char* symbol_name = NULL;
   2212     if (symbol_names.count(text_vaddr + i * sizeof(uint32_t)) != 0) {
   2213         symbol_name = symbol_names[text_vaddr + i * sizeof(uint32_t)].c_str();
   2214         printf("//%s:\n", symbol_name);
   2215     }
   2216 
   2217     if (TRACE) {
   2218         printf("++cnt; printf(\"pc=0x%08x%s%s\\n\"); ", text_vaddr + i * 4, symbol_name ? " " : "",
   2219                symbol_name ? symbol_name : "");
   2220     }
   2221 
   2222     uint64_t src_regs_map;
   2223     if (!insn.instruction.isJump() && !insn.instruction.isBranch() && !conservative) {
   2224         switch (insn_to_type(insn)) {
   2225             case TYPE_S:
   2226                 src_regs_map = get_all_source_reg_mask(insn.instruction);
   2227                 if (!((insn.f_livein & src_regs_map) == src_regs_map)) {
   2228                     printf("// fdead %llx ", (unsigned long long)insn.f_livein);
   2229                 }
   2230                 break;
   2231 
   2232             case TYPE_D_S: {
   2233                 uint64_t reg_mask = get_all_source_reg_mask(insn.instruction);
   2234 
   2235                 if ((insn.f_livein & reg_mask) != reg_mask) {
   2236                     printf("// fdead %llx ", (unsigned long long)insn.f_livein);
   2237                     break;
   2238                 }
   2239             }
   2240                 // fallthrough
   2241             case TYPE_D:
   2242                 if (!(insn.b_liveout & get_dest_reg_mask(insn))) {
   2243 #if 0
   2244                     printf("// %i bdead %llx %llx ", i, (unsigned long long)insn.b_liveout,
   2245                            (unsigned long long)get_dest_reg_mask(insn));
   2246 #else
   2247                     printf("// bdead %llx ", (unsigned long long)insn.b_liveout);
   2248 #endif
   2249                 }
   2250                 break;
   2251 
   2252             case TYPE_NOP:
   2253                 break;
   2254         }
   2255     }
   2256 
   2257     int32_t imm;
   2258     char buf[0x100];
   2259     switch (insn.instruction.getUniqueId()) {
   2260         case rabbitizer::InstrId::UniqueId::cpu_add:
   2261         case rabbitizer::InstrId::UniqueId::cpu_addu:
   2262             if (insn.instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero) {
   2263                 printf("%s = %s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rt()));
   2264             } else if (insn.instruction.GetO32_rt() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero) {
   2265                 printf("%s = %s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()));
   2266             } else {
   2267                 printf("%s = %s + %s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()),
   2268                        r((int)insn.instruction.GetO32_rt()));
   2269             }
   2270             break;
   2271 
   2272         case rabbitizer::InstrId::UniqueId::cpu_add_s:
   2273             printf("%s = %s + %s;\n", fr((int)insn.instruction.GetO32_fd()), fr((int)insn.instruction.GetO32_fs()),
   2274                    fr((int)insn.instruction.GetO32_ft()));
   2275             break;
   2276 
   2277         case rabbitizer::InstrId::UniqueId::cpu_add_d:
   2278             printf("%s = FloatReg_from_double(double_from_FloatReg(%s) + double_from_FloatReg(%s));\n",
   2279                    dr((int)insn.instruction.GetO32_fd()), dr((int)insn.instruction.GetO32_fs()),
   2280                    dr((int)insn.instruction.GetO32_ft()));
   2281             break;
   2282 
   2283         case rabbitizer::InstrId::UniqueId::cpu_addi:
   2284         case rabbitizer::InstrId::UniqueId::cpu_addiu:
   2285             imm = insn.getImmediate();
   2286             if (insn.instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero) {
   2287                 printf("%s = 0x%x;\n", r((int)insn.instruction.GetO32_rt()), imm);
   2288             } else {
   2289                 printf("%s = %s + 0x%x;\n", r((int)insn.instruction.GetO32_rt()), r((int)insn.instruction.GetO32_rs()),
   2290                        imm);
   2291             }
   2292             break;
   2293 
   2294         case rabbitizer::InstrId::UniqueId::cpu_and:
   2295             printf("%s = %s & %s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()),
   2296                    r((int)insn.instruction.GetO32_rt()));
   2297             break;
   2298 
   2299         case rabbitizer::InstrId::UniqueId::cpu_andi:
   2300             imm = insn.getImmediate();
   2301             printf("%s = %s & 0x%x;\n", r((int)insn.instruction.GetO32_rt()), r((int)insn.instruction.GetO32_rs()),
   2302                    imm);
   2303             break;
   2304 
   2305         case rabbitizer::InstrId::UniqueId::cpu_beq:
   2306             dump_cond_branch(i, r((int)insn.instruction.GetO32_rs()), "==", r((int)insn.instruction.GetO32_rt()));
   2307             break;
   2308 
   2309         case rabbitizer::InstrId::UniqueId::cpu_beql:
   2310             dump_cond_branch_likely(i, r((int)insn.instruction.GetO32_rs()),
   2311                                     "==", r((int)insn.instruction.GetO32_rt()));
   2312             break;
   2313 
   2314         case rabbitizer::InstrId::UniqueId::cpu_bgez:
   2315             dump_cond_branch(i, r((int)insn.instruction.GetO32_rs()), ">=", "0");
   2316             break;
   2317 
   2318         case rabbitizer::InstrId::UniqueId::cpu_bgezl:
   2319             dump_cond_branch_likely(i, r((int)insn.instruction.GetO32_rs()), ">=", "0");
   2320             break;
   2321 
   2322         case rabbitizer::InstrId::UniqueId::cpu_bgtz:
   2323             dump_cond_branch(i, r((int)insn.instruction.GetO32_rs()), ">", "0");
   2324             break;
   2325 
   2326         case rabbitizer::InstrId::UniqueId::cpu_bgtzl:
   2327             dump_cond_branch_likely(i, r((int)insn.instruction.GetO32_rs()), ">", "0");
   2328             break;
   2329 
   2330         case rabbitizer::InstrId::UniqueId::cpu_blez:
   2331             dump_cond_branch(i, r((int)insn.instruction.GetO32_rs()), "<=", "0");
   2332             break;
   2333 
   2334         case rabbitizer::InstrId::UniqueId::cpu_blezl:
   2335             dump_cond_branch_likely(i, r((int)insn.instruction.GetO32_rs()), "<=", "0");
   2336             break;
   2337 
   2338         case rabbitizer::InstrId::UniqueId::cpu_bltz:
   2339             dump_cond_branch(i, r((int)insn.instruction.GetO32_rs()), "<", "0");
   2340             break;
   2341 
   2342         case rabbitizer::InstrId::UniqueId::cpu_bltzl:
   2343             dump_cond_branch_likely(i, r((int)insn.instruction.GetO32_rs()), "<", "0");
   2344             break;
   2345 
   2346         case rabbitizer::InstrId::UniqueId::cpu_bne:
   2347             dump_cond_branch(i, r((int)insn.instruction.GetO32_rs()), "!=", r((int)insn.instruction.GetO32_rt()));
   2348             break;
   2349 
   2350         case rabbitizer::InstrId::UniqueId::cpu_bnel:
   2351             dump_cond_branch_likely(i, r((int)insn.instruction.GetO32_rs()),
   2352                                     "!=", r((int)insn.instruction.GetO32_rt()));
   2353             break;
   2354 
   2355         case rabbitizer::InstrId::UniqueId::cpu_break:
   2356             printf("abort();\n");
   2357             break;
   2358 
   2359         case rabbitizer::InstrId::UniqueId::cpu_beqz:
   2360             dump_cond_branch(i, r((int)insn.instruction.GetO32_rs()), "==", "0");
   2361             break;
   2362 
   2363         case rabbitizer::InstrId::UniqueId::cpu_b:
   2364             dump_instr(i + 1);
   2365             imm = insn.getAddress();
   2366             printf("goto L%x;\n", imm);
   2367             break;
   2368 
   2369         case rabbitizer::InstrId::UniqueId::cpu_bc1f:
   2370             printf("if (!cf) {\n");
   2371             dump_instr(i + 1);
   2372             imm = insn.getAddress();
   2373             printf("goto L%x;}\n", imm);
   2374             break;
   2375 
   2376         case rabbitizer::InstrId::UniqueId::cpu_bc1t:
   2377             printf("if (cf) {\n");
   2378             dump_instr(i + 1);
   2379             imm = insn.getAddress();
   2380             printf("goto L%x;}\n", imm);
   2381             break;
   2382 
   2383         case rabbitizer::InstrId::UniqueId::cpu_bc1fl: {
   2384             uint32_t target = text_vaddr + (i + 2) * sizeof(uint32_t);
   2385             printf("if (!cf) {\n");
   2386             dump_instr(i + 1);
   2387             imm = insn.getAddress();
   2388             printf("goto L%x;}\n", imm);
   2389             if (!TRACE) {
   2390                 printf("else goto L%x;\n", target);
   2391             } else {
   2392                 printf("else {printf(\"pc=0x%08x (ignored)\\n\"); goto L%x;}\n", text_vaddr + (i + 1) * 4, target);
   2393             }
   2394             label_addresses.insert(target);
   2395         } break;
   2396 
   2397         case rabbitizer::InstrId::UniqueId::cpu_bc1tl: {
   2398             uint32_t target = text_vaddr + (i + 2) * sizeof(uint32_t);
   2399             printf("if (cf) {\n");
   2400             dump_instr(i + 1);
   2401             imm = insn.getAddress();
   2402             printf("goto L%x;}\n", imm);
   2403             if (!TRACE) {
   2404                 printf("else goto L%x;\n", target);
   2405             } else {
   2406                 printf("else {printf(\"pc=0x%08x (ignored)\\n\"); goto L%x;}\n", text_vaddr + (i + 1) * 4, target);
   2407             }
   2408             label_addresses.insert(target);
   2409         } break;
   2410 
   2411         case rabbitizer::InstrId::UniqueId::cpu_bnez:
   2412             dump_cond_branch(i, r((int)insn.instruction.GetO32_rs()), "!=", "0");
   2413             break;
   2414 
   2415         case rabbitizer::InstrId::UniqueId::cpu_c_lt_s:
   2416             printf("cf = %s < %s;\n", fr((int)insn.instruction.GetO32_fs()), fr((int)insn.instruction.GetO32_ft()));
   2417             break;
   2418 
   2419         case rabbitizer::InstrId::UniqueId::cpu_c_le_s:
   2420             printf("cf = %s <= %s;\n", fr((int)insn.instruction.GetO32_fs()), fr((int)insn.instruction.GetO32_ft()));
   2421             break;
   2422 
   2423         case rabbitizer::InstrId::UniqueId::cpu_c_eq_s:
   2424             printf("cf = %s == %s;\n", fr((int)insn.instruction.GetO32_fs()), fr((int)insn.instruction.GetO32_ft()));
   2425             break;
   2426 
   2427         case rabbitizer::InstrId::UniqueId::cpu_c_lt_d:
   2428             printf("cf = double_from_FloatReg(%s) < double_from_FloatReg(%s);\n", dr((int)insn.instruction.GetO32_fs()),
   2429                    dr((int)insn.instruction.GetO32_ft()));
   2430             break;
   2431 
   2432         case rabbitizer::InstrId::UniqueId::cpu_c_le_d:
   2433             printf("cf = double_from_FloatReg(%s) <= double_from_FloatReg(%s);\n",
   2434                    dr((int)insn.instruction.GetO32_fs()), dr((int)insn.instruction.GetO32_ft()));
   2435             break;
   2436 
   2437         case rabbitizer::InstrId::UniqueId::cpu_c_eq_d:
   2438             printf("cf = double_from_FloatReg(%s) == double_from_FloatReg(%s);\n",
   2439                    dr((int)insn.instruction.GetO32_fs()), dr((int)insn.instruction.GetO32_ft()));
   2440             break;
   2441 
   2442         case rabbitizer::InstrId::UniqueId::cpu_cvt_s_w:
   2443             printf("%s = (int)%s;\n", fr((int)insn.instruction.GetO32_fd()), wr((int)insn.instruction.GetO32_fs()));
   2444             break;
   2445 
   2446         case rabbitizer::InstrId::UniqueId::cpu_cvt_d_w:
   2447             printf("%s = FloatReg_from_double((int)%s);\n", dr((int)insn.instruction.GetO32_fd()),
   2448                    wr((int)insn.instruction.GetO32_fs()));
   2449             break;
   2450 
   2451         case rabbitizer::InstrId::UniqueId::cpu_cvt_d_s:
   2452             printf("%s = FloatReg_from_double(%s);\n", dr((int)insn.instruction.GetO32_fd()),
   2453                    fr((int)insn.instruction.GetO32_fs()));
   2454             break;
   2455 
   2456         case rabbitizer::InstrId::UniqueId::cpu_cvt_s_d:
   2457             printf("%s = double_from_FloatReg(%s);\n", fr((int)insn.instruction.GetO32_fd()),
   2458                    dr((int)insn.instruction.GetO32_fs()));
   2459             break;
   2460 
   2461         case rabbitizer::InstrId::UniqueId::cpu_cvt_w_d:
   2462             printf("%s = cvt_w_d(double_from_FloatReg(%s));\n", wr((int)insn.instruction.GetO32_fd()),
   2463                    dr((int)insn.instruction.GetO32_fs()));
   2464             break;
   2465 
   2466         case rabbitizer::InstrId::UniqueId::cpu_cvt_w_s:
   2467             printf("%s = cvt_w_s(%s);\n", wr((int)insn.instruction.GetO32_fd()), fr((int)insn.instruction.GetO32_fs()));
   2468             break;
   2469 
   2470         case rabbitizer::InstrId::UniqueId::cpu_cvt_l_d:
   2471         case rabbitizer::InstrId::UniqueId::cpu_cvt_l_s:
   2472         case rabbitizer::InstrId::UniqueId::cpu_cvt_s_l:
   2473         case rabbitizer::InstrId::UniqueId::cpu_cvt_d_l:
   2474             goto unimplemented;
   2475 
   2476         case rabbitizer::InstrId::UniqueId::cpu_cfc1:
   2477             assert(insn.instruction.Get_cop1cs() == rabbitizer::Registers::Cpu::Cop1Control::COP1_CONTROL_FpcCsr);
   2478             printf("%s = fcsr;\n", r((int)insn.instruction.GetO32_rt()));
   2479             break;
   2480 
   2481         case rabbitizer::InstrId::UniqueId::cpu_ctc1:
   2482             assert(insn.instruction.Get_cop1cs() == rabbitizer::Registers::Cpu::Cop1Control::COP1_CONTROL_FpcCsr);
   2483             printf("fcsr = %s;\n", r((int)insn.instruction.GetO32_rt()));
   2484             break;
   2485 
   2486         case rabbitizer::InstrId::UniqueId::cpu_div:
   2487             printf("lo = (int)%s / (int)%s; ", r((int)insn.instruction.GetO32_rs()),
   2488                    r((int)insn.instruction.GetO32_rt()));
   2489             printf("hi = (int)%s %% (int)%s;\n", r((int)insn.instruction.GetO32_rs()),
   2490                    r((int)insn.instruction.GetO32_rt()));
   2491             break;
   2492 
   2493         case rabbitizer::InstrId::UniqueId::cpu_divu:
   2494             printf("lo = %s / %s; ", r((int)insn.instruction.GetO32_rs()), r((int)insn.instruction.GetO32_rt()));
   2495             printf("hi = %s %% %s;\n", r((int)insn.instruction.GetO32_rs()), r((int)insn.instruction.GetO32_rt()));
   2496             break;
   2497 
   2498         case rabbitizer::InstrId::UniqueId::cpu_div_s:
   2499             printf("%s = %s / %s;\n", fr((int)insn.instruction.GetO32_fd()), fr((int)insn.instruction.GetO32_fs()),
   2500                    fr((int)insn.instruction.GetO32_ft()));
   2501             break;
   2502 
   2503         case rabbitizer::InstrId::UniqueId::cpu_div_d:
   2504             printf("%s = FloatReg_from_double(double_from_FloatReg(%s) / double_from_FloatReg(%s));\n",
   2505                    dr((int)insn.instruction.GetO32_fd()), dr((int)insn.instruction.GetO32_fs()),
   2506                    dr((int)insn.instruction.GetO32_ft()));
   2507             break;
   2508 
   2509         case rabbitizer::InstrId::UniqueId::cpu_mov_s:
   2510             printf("%s = %s;\n", fr((int)insn.instruction.GetO32_fd()), fr((int)insn.instruction.GetO32_fs()));
   2511             break;
   2512 
   2513         case rabbitizer::InstrId::UniqueId::cpu_mov_d:
   2514             printf("%s = %s;\n", dr((int)insn.instruction.GetO32_fd()), dr((int)insn.instruction.GetO32_fs()));
   2515             break;
   2516 
   2517         case rabbitizer::InstrId::UniqueId::cpu_mul_s:
   2518             printf("%s = %s * %s;\n", fr((int)insn.instruction.GetO32_fd()), fr((int)insn.instruction.GetO32_fs()),
   2519                    fr((int)insn.instruction.GetO32_ft()));
   2520             break;
   2521 
   2522         case rabbitizer::InstrId::UniqueId::cpu_mul_d:
   2523             printf("%s = FloatReg_from_double(double_from_FloatReg(%s) * double_from_FloatReg(%s));\n",
   2524                    dr((int)insn.instruction.GetO32_fd()), dr((int)insn.instruction.GetO32_fs()),
   2525                    dr((int)insn.instruction.GetO32_ft()));
   2526             break;
   2527 
   2528         case rabbitizer::InstrId::UniqueId::cpu_negu:
   2529             printf("%s = -%s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rt()));
   2530             break;
   2531 
   2532         case rabbitizer::InstrId::UniqueId::cpu_neg_s:
   2533             printf("%s = -%s;\n", fr((int)insn.instruction.GetO32_fd()), fr((int)insn.instruction.GetO32_fs()));
   2534             break;
   2535 
   2536         case rabbitizer::InstrId::UniqueId::cpu_neg_d:
   2537             printf("%s = FloatReg_from_double(-double_from_FloatReg(%s));\n", dr((int)insn.instruction.GetO32_fd()),
   2538                    dr((int)insn.instruction.GetO32_fs()));
   2539             break;
   2540 
   2541         case rabbitizer::InstrId::UniqueId::cpu_sub:
   2542             if (insn.instruction.GetO32_rs() == rabbitizer::Registers::Cpu::GprO32::GPR_O32_zero) {
   2543                 printf("%s = -%s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rt()));
   2544                 break;
   2545             } else {
   2546                 goto unimplemented;
   2547             }
   2548 
   2549         case rabbitizer::InstrId::UniqueId::cpu_sub_s:
   2550             printf("%s = %s - %s;\n", fr((int)insn.instruction.GetO32_fd()), fr((int)insn.instruction.GetO32_fs()),
   2551                    fr((int)insn.instruction.GetO32_ft()));
   2552             break;
   2553 
   2554         case rabbitizer::InstrId::UniqueId::cpu_sub_d:
   2555             printf("%s = FloatReg_from_double(double_from_FloatReg(%s) - double_from_FloatReg(%s));\n",
   2556                    dr((int)insn.instruction.GetO32_fd()), dr((int)insn.instruction.GetO32_fs()),
   2557                    dr((int)insn.instruction.GetO32_ft()));
   2558             break;
   2559 
   2560             // Jumps
   2561 
   2562         case rabbitizer::InstrId::UniqueId::cpu_j:
   2563             dump_instr(i + 1);
   2564             imm = insn.getAddress();
   2565             printf("goto L%x;\n", imm);
   2566             break;
   2567 
   2568         case rabbitizer::InstrId::UniqueId::cpu_jal:
   2569             imm = insn.getAddress();
   2570             dump_jal(i, imm);
   2571             break;
   2572 
   2573         case rabbitizer::InstrId::UniqueId::cpu_jalr:
   2574             printf("fp_dest = %s;\n", r((int)insn.instruction.GetO32_rs()));
   2575             dump_instr(i + 1);
   2576             printf("temp64 = trampoline(mem, sp, %s, %s, %s, %s, fp_dest);\n",
   2577                    r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0),
   2578                    r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a1),
   2579                    r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a2),
   2580                    r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a3));
   2581             printf("%s = (uint32_t)(temp64 >> 32);\n", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0));
   2582             printf("%s = (uint32_t)temp64;\n", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_v1));
   2583             printf("goto L%x;\n", text_vaddr + (i + 2) * 4);
   2584             label_addresses.insert(text_vaddr + (i + 2) * 4);
   2585             break;
   2586 
   2587         case rabbitizer::InstrId::UniqueId::cpu_jr:
   2588             // TODO: understand why the switch version fails, and why only it needs the nop
   2589             if (insn.jtbl_addr != 0) {
   2590                 uint32_t jtbl_pos = insn.jtbl_addr - rodata_vaddr;
   2591 
   2592                 assert(jtbl_pos < rodata_section_len &&
   2593                        jtbl_pos + insn.num_cases * sizeof(uint32_t) <= rodata_section_len);
   2594 #if 1
   2595                 printf(";static void *const Lswitch%x[] = {\n", insn.jtbl_addr);
   2596 
   2597                 for (uint32_t i = 0; i < insn.num_cases; i++) {
   2598                     uint32_t dest_addr = read_u32_be(rodata_section + jtbl_pos + i * sizeof(uint32_t)) + gp_value;
   2599                     printf("&&L%x,\n", dest_addr);
   2600                     label_addresses.insert(dest_addr);
   2601                 }
   2602 
   2603                 printf("};\n");
   2604                 printf("dest = Lswitch%x[%s];\n", insn.jtbl_addr, r((int)insn.index_reg));
   2605                 dump_instr(i + 1);
   2606                 printf("goto *dest;\n");
   2607 #else
   2608                 // This block produces a switch instead of an array of labels.
   2609                 // It is not being used because currently it is a bit bugged.
   2610                 // It has been keep as a reference and with the main intention to fix it
   2611 
   2612                 assert(insns[i + 1].id == MIPS_INS_NOP);
   2613                 printf("switch (%s) {\n", r(insn.index_reg));
   2614 
   2615                 for (uint32_t i = 0; i < insn.num_cases; i++) {
   2616                     uint32_t dest_addr = read_u32_be(rodata_section + jtbl_pos + i * sizeof(uint32_t)) + gp_value;
   2617                     printf("case %u: goto L%x;\n", i, dest_addr);
   2618                     label_addresses.insert(dest_addr);
   2619                 }
   2620 
   2621                 printf("}\n");
   2622 #endif
   2623             } else {
   2624                 if (insn.instruction.GetO32_rs() != rabbitizer::Registers::Cpu::GprO32::GPR_O32_ra) {
   2625                     printf("UNSUPPORTED JR %s    (no jumptable available)\n", r((int)insn.instruction.GetO32_rs()));
   2626                 } else {
   2627                     dump_instr(i + 1);
   2628                     switch (find_function(text_vaddr + i * sizeof(uint32_t))->second.nret) {
   2629                         case 0:
   2630                             printf("return;\n");
   2631                             break;
   2632 
   2633                         case 1:
   2634                             printf("return v0;\n");
   2635                             break;
   2636 
   2637                         case 2:
   2638                             printf("return ((uint64_t)v0 << 32) | v1;\n");
   2639                             break;
   2640                     }
   2641                 }
   2642             }
   2643             break;
   2644 
   2645         case rabbitizer::InstrId::UniqueId::cpu_lb:
   2646             imm = insn.getImmediate();
   2647             printf("%s = MEM_S8(%s + %d);\n", r((int)insn.instruction.GetO32_rt()),
   2648                    r((int)insn.instruction.GetO32_rs()), imm);
   2649             break;
   2650 
   2651         case rabbitizer::InstrId::UniqueId::cpu_lbu:
   2652             imm = insn.getImmediate();
   2653             printf("%s = MEM_U8(%s + %d);\n", r((int)insn.instruction.GetO32_rt()),
   2654                    r((int)insn.instruction.GetO32_rs()), imm);
   2655             break;
   2656 
   2657         case rabbitizer::InstrId::UniqueId::cpu_lh:
   2658             imm = insn.getImmediate();
   2659             printf("%s = MEM_S16(%s + %d);\n", r((int)insn.instruction.GetO32_rt()),
   2660                    r((int)insn.instruction.GetO32_rs()), imm);
   2661             break;
   2662 
   2663         case rabbitizer::InstrId::UniqueId::cpu_lhu:
   2664             imm = insn.getImmediate();
   2665             printf("%s = MEM_U16(%s + %d);\n", r((int)insn.instruction.GetO32_rt()),
   2666                    r((int)insn.instruction.GetO32_rs()), imm);
   2667             break;
   2668 
   2669         case rabbitizer::InstrId::UniqueId::cpu_lui:
   2670             imm = insn.getImmediate();
   2671             printf("%s = 0x%x;\n", r((int)insn.instruction.GetO32_rt()), imm << 16);
   2672             break;
   2673 
   2674         case rabbitizer::InstrId::UniqueId::cpu_lw:
   2675             imm = insn.getImmediate();
   2676             printf("%s = MEM_U32(%s + %d);\n", r((int)insn.instruction.GetO32_rt()),
   2677                    r((int)insn.instruction.GetO32_rs()), imm);
   2678             break;
   2679 
   2680         case rabbitizer::InstrId::UniqueId::cpu_lwc1:
   2681             imm = insn.getImmediate();
   2682             printf("%s = MEM_U32(%s + %d);\n", wr((int)insn.instruction.GetO32_ft()),
   2683                    r((int)insn.instruction.GetO32_rs()), imm);
   2684             break;
   2685 
   2686         case rabbitizer::InstrId::UniqueId::cpu_ldc1:
   2687             imm = insn.getImmediate();
   2688             assert(((int)insn.instruction.GetO32_ft() - (int)rabbitizer::Registers::Cpu::Cop1O32::COP1_O32_fv0) % 2 ==
   2689                    0);
   2690             printf("%s = MEM_U32(%s + %d);\n", wr((int)insn.instruction.GetO32_ft() + 1),
   2691                    r((int)insn.instruction.GetO32_rs()), imm);
   2692             printf("%s = MEM_U32(%s + %d + 4);\n", wr((int)insn.instruction.GetO32_ft()),
   2693                    r((int)insn.instruction.GetO32_rs()), imm);
   2694             break;
   2695 
   2696         case rabbitizer::InstrId::UniqueId::cpu_lwl: {
   2697             const char* reg = r((int)insn.instruction.GetO32_rt());
   2698 
   2699             imm = insn.getImmediate();
   2700 
   2701             printf("%s = %s + %d; ", reg, r((int)insn.instruction.GetO32_rs()), imm);
   2702             printf("%s = ((uint32_t)MEM_U8(%s) << 24) | (MEM_U8(%s + 1) << 16) | (MEM_U8(%s + 2) << 8) | MEM_U8(%s + 3);\n", reg,
   2703                    reg, reg, reg, reg);
   2704         } break;
   2705 
   2706         case rabbitizer::InstrId::UniqueId::cpu_lwr:
   2707             printf("//%s\n", insn.disassemble().c_str());
   2708             break;
   2709 
   2710         case UniqueId_cpu_la: {
   2711             uint32_t addr = insn.getAddress();
   2712 
   2713             printf("%s = 0x%x;", r((int)insn.lila_dst_reg), addr);
   2714             if ((text_vaddr <= addr) && (addr < text_vaddr + text_section_len)) {
   2715                 printf(" // function pointer");
   2716                 label_addresses.insert(addr);
   2717             }
   2718             printf("\n");
   2719         } break;
   2720 
   2721         case UniqueId_cpu_li:
   2722             imm = insn.getImmediate();
   2723 
   2724             printf("%s = 0x%x;\n", r((int)insn.lila_dst_reg), imm);
   2725             break;
   2726 
   2727         case rabbitizer::InstrId::UniqueId::cpu_mfc1:
   2728             printf("%s = %s;\n", r((int)insn.instruction.GetO32_rt()), wr((int)insn.instruction.GetO32_fs()));
   2729             break;
   2730 
   2731         case rabbitizer::InstrId::UniqueId::cpu_mfhi:
   2732             printf("%s = hi;\n", r((int)insn.instruction.GetO32_rd()));
   2733             break;
   2734 
   2735         case rabbitizer::InstrId::UniqueId::cpu_mflo:
   2736             printf("%s = lo;\n", r((int)insn.instruction.GetO32_rd()));
   2737             break;
   2738 
   2739         case rabbitizer::InstrId::UniqueId::cpu_move:
   2740             printf("%s = %s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()));
   2741             break;
   2742 
   2743         case rabbitizer::InstrId::UniqueId::cpu_mtc1:
   2744             printf("%s = %s;\n", wr((int)insn.instruction.GetO32_fs()), r((int)insn.instruction.GetO32_rt()));
   2745             break;
   2746 
   2747         case rabbitizer::InstrId::UniqueId::cpu_mult:
   2748             printf("lo = %s * %s;\n", r((int)insn.instruction.GetO32_rs()), r((int)insn.instruction.GetO32_rt()));
   2749             printf("hi = (uint32_t)((int64_t)(int)%s * (int64_t)(int)%s >> 32);\n",
   2750                    r((int)insn.instruction.GetO32_rs()), r((int)insn.instruction.GetO32_rt()));
   2751             break;
   2752 
   2753         case rabbitizer::InstrId::UniqueId::cpu_multu:
   2754             printf("lo = %s * %s;\n", r((int)insn.instruction.GetO32_rs()), r((int)insn.instruction.GetO32_rt()));
   2755             printf("hi = (uint32_t)((uint64_t)%s * (uint64_t)%s >> 32);\n", r((int)insn.instruction.GetO32_rs()),
   2756                    r((int)insn.instruction.GetO32_rt()));
   2757             break;
   2758 
   2759         case rabbitizer::InstrId::UniqueId::cpu_sqrt_s:
   2760             printf("%s = sqrtf(%s);\n", fr((int)insn.instruction.GetO32_fd()), fr((int)insn.instruction.GetO32_fs()));
   2761             break;
   2762 
   2763         case rabbitizer::InstrId::UniqueId::cpu_nor:
   2764             printf("%s = ~(%s | %s);\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()),
   2765                    r((int)insn.instruction.GetO32_rt()));
   2766             break;
   2767 
   2768         case rabbitizer::InstrId::UniqueId::cpu_not:
   2769             printf("%s = ~%s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()));
   2770             break;
   2771 
   2772         case rabbitizer::InstrId::UniqueId::cpu_or:
   2773             printf("%s = %s | %s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()),
   2774                    r((int)insn.instruction.GetO32_rt()));
   2775             break;
   2776 
   2777         case rabbitizer::InstrId::UniqueId::cpu_ori:
   2778             imm = insn.getImmediate();
   2779             printf("%s = %s | 0x%x;\n", r((int)insn.instruction.GetO32_rt()), r((int)insn.instruction.GetO32_rs()),
   2780                    imm);
   2781             break;
   2782 
   2783         case rabbitizer::InstrId::UniqueId::cpu_sb:
   2784             imm = insn.getImmediate();
   2785             printf("MEM_U8(%s + %d) = (uint8_t)%s;\n", r((int)insn.instruction.GetO32_rs()), imm,
   2786                    r((int)insn.instruction.GetO32_rt()));
   2787             break;
   2788 
   2789         case rabbitizer::InstrId::UniqueId::cpu_sh:
   2790             imm = insn.getImmediate();
   2791             printf("MEM_U16(%s + %d) = (uint16_t)%s;\n", r((int)insn.instruction.GetO32_rs()), imm,
   2792                    r((int)insn.instruction.GetO32_rt()));
   2793             break;
   2794 
   2795         case rabbitizer::InstrId::UniqueId::cpu_sll:
   2796             printf("%s = %s << %d;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rt()),
   2797                    insn.instruction.Get_sa());
   2798             break;
   2799 
   2800         case rabbitizer::InstrId::UniqueId::cpu_sllv:
   2801             printf("%s = %s << (%s & 0x1f);\n", r((int)insn.instruction.GetO32_rd()),
   2802                    r((int)insn.instruction.GetO32_rt()), r((int)insn.instruction.GetO32_rs()));
   2803             break;
   2804 
   2805         case rabbitizer::InstrId::UniqueId::cpu_slt:
   2806             printf("%s = (int)%s < (int)%s;\n", r((int)insn.instruction.GetO32_rd()),
   2807                    r((int)insn.instruction.GetO32_rs()), r((int)insn.instruction.GetO32_rt()));
   2808             break;
   2809 
   2810         case rabbitizer::InstrId::UniqueId::cpu_slti:
   2811             imm = insn.getImmediate();
   2812             printf("%s = (int)%s < (int)0x%x;\n", r((int)insn.instruction.GetO32_rt()),
   2813                    r((int)insn.instruction.GetO32_rs()), imm);
   2814             break;
   2815 
   2816         case rabbitizer::InstrId::UniqueId::cpu_sltiu:
   2817             imm = insn.getImmediate();
   2818             printf("%s = %s < 0x%x;\n", r((int)insn.instruction.GetO32_rt()), r((int)insn.instruction.GetO32_rs()),
   2819                    imm);
   2820             break;
   2821 
   2822         case rabbitizer::InstrId::UniqueId::cpu_sltu:
   2823             printf("%s = %s < %s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()),
   2824                    r((int)insn.instruction.GetO32_rt()));
   2825             break;
   2826 
   2827         case rabbitizer::InstrId::UniqueId::cpu_sra:
   2828             printf("%s = (int)%s >> %d;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rt()),
   2829                    insn.instruction.Get_sa());
   2830             break;
   2831 
   2832         case rabbitizer::InstrId::UniqueId::cpu_srav:
   2833             printf("%s = (int)%s >> (%s & 0x1f);\n", r((int)insn.instruction.GetO32_rd()),
   2834                    r((int)insn.instruction.GetO32_rt()), r((int)insn.instruction.GetO32_rs()));
   2835             break;
   2836 
   2837         case rabbitizer::InstrId::UniqueId::cpu_srl:
   2838             printf("%s = %s >> %d;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rt()),
   2839                    insn.instruction.Get_sa());
   2840             break;
   2841 
   2842         case rabbitizer::InstrId::UniqueId::cpu_srlv:
   2843             printf("%s = %s >> (%s & 0x1f);\n", r((int)insn.instruction.GetO32_rd()),
   2844                    r((int)insn.instruction.GetO32_rt()), r((int)insn.instruction.GetO32_rs()));
   2845             break;
   2846 
   2847         case rabbitizer::InstrId::UniqueId::cpu_subu:
   2848             printf("%s = %s - %s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()),
   2849                    r((int)insn.instruction.GetO32_rt()));
   2850             break;
   2851 
   2852         case rabbitizer::InstrId::UniqueId::cpu_sw:
   2853             imm = insn.getImmediate();
   2854             printf("MEM_U32(%s + %d) = %s;\n", r((int)insn.instruction.GetO32_rs()), imm,
   2855                    r((int)insn.instruction.GetO32_rt()));
   2856             break;
   2857 
   2858         case rabbitizer::InstrId::UniqueId::cpu_swc1:
   2859             imm = insn.getImmediate();
   2860             printf("MEM_U32(%s + %d) = %s;\n", r((int)insn.instruction.GetO32_rs()), imm,
   2861                    wr((int)insn.instruction.GetO32_ft()));
   2862             break;
   2863 
   2864         case rabbitizer::InstrId::UniqueId::cpu_sdc1:
   2865             assert(((int)insn.instruction.GetO32_ft() - (int)rabbitizer::Registers::Cpu::Cop1O32::COP1_O32_fv0) % 2 ==
   2866                    0);
   2867             imm = insn.getImmediate();
   2868             printf("MEM_U32(%s + %d) = %s;\n", r((int)insn.instruction.GetO32_rs()), imm,
   2869                    wr((int)insn.instruction.GetO32_ft() + 1));
   2870             printf("MEM_U32(%s + %d + 4) = %s;\n", r((int)insn.instruction.GetO32_rs()), imm,
   2871                    wr((int)insn.instruction.GetO32_ft()));
   2872             break;
   2873 
   2874         case rabbitizer::InstrId::UniqueId::cpu_swl:
   2875             imm = insn.getImmediate();
   2876             for (int i = 0; i < 4; i++) {
   2877                 printf("MEM_U8(%s + %d + %d) = (uint8_t)(%s >> %d);\n", r((int)insn.instruction.GetO32_rs()), imm, i,
   2878                        r((int)insn.instruction.GetO32_rt()), (3 - i) * 8);
   2879             }
   2880             break;
   2881 
   2882         case rabbitizer::InstrId::UniqueId::cpu_swr:
   2883             printf("//%s\n", insn.disassemble().c_str());
   2884             break;
   2885 
   2886         case rabbitizer::InstrId::UniqueId::cpu_trunc_w_s:
   2887             printf("%s = (int)%s;\n", wr((int)insn.instruction.GetO32_fd()), fr((int)insn.instruction.GetO32_fs()));
   2888             break;
   2889 
   2890         case rabbitizer::InstrId::UniqueId::cpu_trunc_w_d:
   2891             printf("%s = (int)double_from_FloatReg(%s);\n", wr((int)insn.instruction.GetO32_fd()),
   2892                    dr((int)insn.instruction.GetO32_fs()));
   2893             break;
   2894 
   2895         case rabbitizer::InstrId::UniqueId::cpu_trunc_l_d:
   2896         case rabbitizer::InstrId::UniqueId::cpu_trunc_l_s:
   2897             goto unimplemented;
   2898 
   2899         case rabbitizer::InstrId::UniqueId::cpu_xor:
   2900             printf("%s = %s ^ %s;\n", r((int)insn.instruction.GetO32_rd()), r((int)insn.instruction.GetO32_rs()),
   2901                    r((int)insn.instruction.GetO32_rt()));
   2902             break;
   2903 
   2904         case rabbitizer::InstrId::UniqueId::cpu_xori:
   2905             imm = insn.getImmediate();
   2906             printf("%s = %s ^ 0x%x;\n", r((int)insn.instruction.GetO32_rt()), r((int)insn.instruction.GetO32_rs()),
   2907                    imm);
   2908             break;
   2909 
   2910         case rabbitizer::InstrId::UniqueId::cpu_tne:
   2911             imm = insn.instruction.Get_code_lower();
   2912             printf("assert(%s == %s && \"tne %d\");\n", r((int)insn.instruction.GetO32_rs()),
   2913                    r((int)insn.instruction.GetO32_rt()), imm);
   2914             break;
   2915 
   2916         case rabbitizer::InstrId::UniqueId::cpu_teq:
   2917             imm = insn.instruction.Get_code_lower();
   2918             printf("assert(%s != %s && \"teq %d\");\n", r((int)insn.instruction.GetO32_rs()),
   2919                    r((int)insn.instruction.GetO32_rt()), imm);
   2920             break;
   2921 
   2922         case rabbitizer::InstrId::UniqueId::cpu_tge:
   2923             imm = insn.instruction.Get_code_lower();
   2924             printf("assert((int)%s < (int)%s && \"tge %d\");\n", r((int)insn.instruction.GetO32_rs()),
   2925                    r((int)insn.instruction.GetO32_rt()), imm);
   2926             break;
   2927 
   2928         case rabbitizer::InstrId::UniqueId::cpu_tgeu:
   2929             imm = insn.instruction.Get_code_lower();
   2930             printf("assert(%s < %s && \"tgeu %d\");\n", r((int)insn.instruction.GetO32_rs()),
   2931                    r((int)insn.instruction.GetO32_rt()), imm);
   2932             break;
   2933 
   2934         case rabbitizer::InstrId::UniqueId::cpu_tlt:
   2935             imm = insn.instruction.Get_code_lower();
   2936             printf("assert((int)%s >= (int)%s && \"tlt %d\");\n", r((int)insn.instruction.GetO32_rs()),
   2937                    r((int)insn.instruction.GetO32_rt()), imm);
   2938             break;
   2939 
   2940         case rabbitizer::InstrId::UniqueId::cpu_nop:
   2941             printf("//nop;\n");
   2942             break;
   2943 
   2944         default:
   2945         unimplemented:
   2946             printf("UNIMPLEMENTED 0x%X : %s\n", insn.instruction.getRaw(), insn.disassemble().c_str());
   2947             break;
   2948     }
   2949 }
   2950 
   2951 void inspect_data_function_pointers(vector<pair<uint32_t, uint32_t>>& ret, const uint8_t* section,
   2952                                     uint32_t section_vaddr, uint32_t len) {
   2953     for (uint32_t i = 0; i < len; i += 4) {
   2954         uint32_t addr = read_u32_be(section + i);
   2955 
   2956         if (addr == 0x430b00 || addr == 0x433b00) {
   2957             // in as1, not function pointers (normal integers)
   2958             continue;
   2959         }
   2960 
   2961         if (addr == 0x4a0000) {
   2962             // in copt
   2963             continue;
   2964         }
   2965 
   2966         if (section_vaddr + i >= procedure_table_start &&
   2967             section_vaddr + i < procedure_table_start + procedure_table_len) {
   2968             // some linking table with a "all" functions, in as1 5.3
   2969             continue;
   2970         }
   2971 
   2972         if ((addr >= text_vaddr) && (addr < text_vaddr + text_section_len) && ((addr % 4) == 0)) {
   2973 #if INSPECT_FUNCTION_POINTERS
   2974             fprintf(stderr, "assuming function pointer 0x%x at 0x%x\n", addr, section_vaddr + i);
   2975 #endif
   2976             ret.push_back(make_pair(section_vaddr + i, addr));
   2977             label_addresses.insert(addr);
   2978             functions[addr].referenced_by_function_pointer = true;
   2979         }
   2980     }
   2981 }
   2982 
   2983 void dump_function_signature(Function& f, uint32_t vaddr) {
   2984     printf("static ");
   2985     switch (f.nret) {
   2986         case 0:
   2987             printf("void ");
   2988             break;
   2989 
   2990         case 1:
   2991             printf("uint32_t ");
   2992             break;
   2993 
   2994         case 2:
   2995             printf("uint64_t ");
   2996             break;
   2997     }
   2998 
   2999     auto name_it = symbol_names.find(vaddr);
   3000 
   3001     if (name_it != symbol_names.end()) {
   3002         printf("f_%s", name_it->second.c_str());
   3003     } else {
   3004         printf("func_%x", vaddr);
   3005     }
   3006 
   3007     printf("(uint8_t *mem, uint32_t sp");
   3008 
   3009     if (f.v0_in) {
   3010         printf(", uint32_t %s", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_v0));
   3011     }
   3012 
   3013     for (uint32_t i = 0; i < f.nargs; i++) {
   3014         printf(", uint32_t %s", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + i));
   3015     }
   3016 
   3017     printf(")");
   3018 }
   3019 
   3020 void dump_c(void) {
   3021     map<string, uint32_t> symbol_names_inv;
   3022 
   3023     for (auto& it : symbol_names) {
   3024         symbol_names_inv[it.second] = it.first;
   3025     }
   3026 
   3027     uint32_t min_addr = UINT32_MAX;
   3028     uint32_t max_addr = 0;
   3029 
   3030     if (data_section_len > 0) {
   3031         min_addr = std::min(min_addr, data_vaddr);
   3032         max_addr = std::max(max_addr, data_vaddr + data_section_len);
   3033     }
   3034     if (rodata_section_len > 0) {
   3035         min_addr = std::min(min_addr, rodata_vaddr);
   3036         max_addr = std::max(max_addr, rodata_vaddr + rodata_section_len);
   3037     }
   3038     if (bss_section_len) {
   3039         min_addr = std::min(min_addr, bss_vaddr);
   3040         max_addr = std::max(max_addr, bss_vaddr + bss_section_len);
   3041     }
   3042 
   3043     // 64 kB. Supposedly the worst-case smallest permitted page size, increase if necessary.
   3044     // Ensures the hardcoded min_addr and max_addr are sufficiently aligned for the machine running the
   3045     // recompiled binaries (and not just the one doing the original recomp build).
   3046     uint32_t page_size = 0x10000;
   3047 
   3048     min_addr = min_addr & ~(page_size - 1);
   3049     max_addr = (max_addr + (page_size - 1)) & ~(page_size - 1);
   3050 
   3051     uint32_t stack_bottom = min_addr;
   3052     min_addr -= 0x100000; // 1 MB stack
   3053     stack_bottom -= 0x10; // for main's stack frame
   3054 
   3055     printf("#include \"header.h\"\n");
   3056 
   3057     if (conservative) {
   3058         printf("static uint32_t s0, s1, s2, s3, s4, s5, s6, s7, fp;\n");
   3059     }
   3060 
   3061     printf("static const uint32_t rodata[] = {\n");
   3062 
   3063     for (size_t i = 0; i < rodata_section_len; i += 4) {
   3064         printf("0x%x,%s", read_u32_be(rodata_section + i), i % 32 == 28 ? "\n" : "");
   3065     }
   3066 
   3067     printf("};\n");
   3068     printf("static const uint32_t data[] = {\n");
   3069 
   3070     for (size_t i = 0; i < data_section_len; i += 4) {
   3071         printf("0x%x,%s", read_u32_be(data_section + i), i % 32 == 28 ? "\n" : "");
   3072     }
   3073 
   3074     printf("};\n");
   3075 
   3076     /* if (!data_function_pointers.empty()) {
   3077         printf("static const struct { uint32_t orig_addr; void *recompiled_addr; } data_function_pointers[] = {\n");
   3078         for (auto item : data_function_pointers) {
   3079             printf("{0x%x, &&L%x},\n", item.first, item.second);
   3080         }
   3081         printf("};\n");
   3082     } */
   3083 
   3084     if (TRACE) {
   3085         printf("static unsigned long long int cnt = 0;\n");
   3086     }
   3087 
   3088     for (auto& f_it : functions) {
   3089         uint32_t addr = f_it.first;
   3090         auto& ins = insns.at(addr_to_i(addr));
   3091 
   3092         if (ins.f_livein != 0) {
   3093             // Function is used
   3094             dump_function_signature(f_it.second, addr);
   3095             printf(";\n");
   3096         }
   3097     }
   3098 
   3099     if (!data_function_pointers.empty() || !la_function_pointers.empty()) {
   3100         printf("uint64_t trampoline(uint8_t *mem, uint32_t sp, uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3, "
   3101                "uint32_t fp_dest) {\n");
   3102         printf("switch (fp_dest) {\n");
   3103 
   3104         for (auto& it : functions) {
   3105             Function& f = it.second;
   3106 
   3107             if (f.referenced_by_function_pointer) {
   3108                 printf("case 0x%x: ", it.first);
   3109 
   3110                 if (f.nret == 1) {
   3111                     printf("return (uint64_t)");
   3112                 } else if (f.nret == 2) {
   3113                     printf("return ");
   3114                 }
   3115 
   3116                 auto name_it = symbol_names.find(it.first);
   3117 
   3118                 if (name_it != symbol_names.end()) {
   3119                     printf("f_%s", name_it->second.c_str());
   3120                 } else {
   3121                     printf("func_%x", it.first);
   3122                 }
   3123 
   3124                 printf("(mem, sp");
   3125 
   3126                 for (unsigned int i = 0; i < f.nargs; i++) {
   3127                     printf(", a%d", i);
   3128                 }
   3129 
   3130                 printf(")");
   3131 
   3132                 if (f.nret == 1) {
   3133                     printf(" << 32");
   3134                 }
   3135 
   3136                 printf(";");
   3137 
   3138                 if (f.nret == 0) {
   3139                     printf(" return 0;");
   3140                 }
   3141 
   3142                 printf("\n");
   3143             }
   3144         }
   3145 
   3146         printf("default: abort();");
   3147         printf("}\n");
   3148         printf("}\n");
   3149     }
   3150 
   3151     printf("int run(uint8_t *mem, int argc, char *argv[]) {\n");
   3152     printf("mmap_initial_data_range(mem, 0x%x, 0x%x);\n", min_addr, max_addr);
   3153 
   3154     printf("memcpy(mem + 0x%x, rodata, 0x%x);\n", rodata_vaddr, rodata_section_len);
   3155     printf("memcpy(mem + 0x%x, data, 0x%x);\n", data_vaddr, data_section_len);
   3156 
   3157     /* if (!data_function_pointers.empty()) {
   3158         if (!LABELS_64_BIT) {
   3159             printf("for (int i = 0; i < %d; i++) MEM_U32(data_function_pointers[i].orig_addr) =
   3160     (uint32_t)(uintptr_t)data_function_pointers[i].recompiled_addr;\n", (int)data_function_pointers.size()); } else {
   3161             printf("for (int i = 0; i < %d; i++) MEM_U32(data_function_pointers[i].orig_addr) =
   3162     (uint32_t)((uintptr_t)data_function_pointers[i].recompiled_addr - (uintptr_t)&&Loffset);\n",
   3163     (int)data_function_pointers.size());
   3164         }
   3165     } */
   3166 
   3167     printf("MEM_S32(0x%x) = argc;\n", symbol_names_inv.at("__Argc"));
   3168     printf("MEM_S32(0x%x) = argc;\n", stack_bottom);
   3169     printf("uint32_t al = argc * 4; for (int i = 0; i < argc; i++) al += strlen(argv[i]) + 1;\n");
   3170     printf("uint32_t arg_addr = wrapper_malloc(mem, al);\n");
   3171     printf("MEM_U32(0x%x) = arg_addr;\n", symbol_names_inv.at("__Argv"));
   3172     printf("MEM_U32(0x%x) = arg_addr;\n", stack_bottom + 4);
   3173     printf("uint32_t arg_strpos = arg_addr + argc * 4;\n");
   3174     printf("for (int i = 0; i < argc; i++) {MEM_U32(arg_addr + i * 4) = arg_strpos; uint32_t p = 0; do { "
   3175            "MEM_S8(arg_strpos) = argv[i][p]; ++arg_strpos; } while (argv[i][p++] != '\\0');}\n");
   3176 
   3177     printf("setup_libc_data(mem);\n");
   3178 
   3179     // printf("gp = 0x%x;\n", gp_value); // only to recreate the outcome when ugen reads uninitialized stack memory
   3180 
   3181     printf("int ret = f_main(mem, 0x%x", stack_bottom);
   3182 
   3183     Function& main_func = functions[main_addr];
   3184 
   3185     if (main_func.nargs >= 1) {
   3186         printf(", argc");
   3187     }
   3188 
   3189     if (main_func.nargs >= 2) {
   3190         printf(", arg_addr");
   3191     }
   3192 
   3193     printf(");\n");
   3194 
   3195     if (TRACE) {
   3196         printf("end: fprintf(stderr, \"cnt: %%llu\\n\", cnt);\n");
   3197     }
   3198 
   3199     printf("return ret;\n");
   3200     printf("}\n");
   3201 
   3202     for (auto& f_it : functions) {
   3203         Function& f = f_it.second;
   3204         uint32_t start_addr = f_it.first;
   3205         uint32_t end_addr = f.end_addr;
   3206 
   3207         if (insns[addr_to_i(start_addr)].f_livein == 0) {
   3208             // Non-used function, skip
   3209             continue;
   3210         }
   3211 
   3212         printf("\n");
   3213         dump_function_signature(f, start_addr);
   3214         printf(" {\n");
   3215         printf("const uint32_t zero = 0;\n");
   3216 
   3217         if (!conservative) {
   3218             printf("uint32_t at = 0, v1 = 0, t0 = 0, t1 = 0, t2 = 0,\n");
   3219             printf("t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, s0 = 0, s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0,\n");
   3220             printf("s6 = 0, s7 = 0, t8 = 0, t9 = 0, gp = 0, fp = 0, s8 = 0, ra = 0;\n");
   3221         } else {
   3222             printf("uint32_t at = 0, v1 = 0, t0 = 0, t1 = 0, t2 = 0,\n");
   3223             printf("t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0, t9 = 0, gp = 0x10000, ra = 0x10000;\n");
   3224         }
   3225 
   3226         printf("uint32_t lo = 0, hi = 0;\n");
   3227         printf("int cf = 0;\n");
   3228         printf("uint64_t temp64;\n");
   3229         printf("double tempf64;\n");
   3230         printf("uint32_t fp_dest;\n");
   3231         printf("void *dest;\n");
   3232 
   3233         if (!f.v0_in) {
   3234             printf("uint32_t v0 = 0;\n");
   3235         }
   3236 
   3237         for (uint32_t j = f.nargs; j < 4; j++) {
   3238             printf("uint32_t %s = 0;\n", r((int)rabbitizer::Registers::Cpu::GprO32::GPR_O32_a0 + j));
   3239         }
   3240 
   3241         for (size_t i = addr_to_i(start_addr), end_i = addr_to_i(end_addr); i < end_i; i++) {
   3242             Insn& insn = insns[i];
   3243             uint32_t vaddr = text_vaddr + i * 4;
   3244             if (label_addresses.count(vaddr)) {
   3245                 printf("L%x:\n", vaddr);
   3246             }
   3247 #if DUMP_INSTRUCTIONS
   3248             printf("// %s:\n", insn.disassemble().c_str());
   3249 #endif
   3250             dump_instr(i);
   3251         }
   3252 
   3253         printf("}\n");
   3254     }
   3255     /* for (size_t i = 0; i < insns.size(); i++) {
   3256         Insn& insn = insns[i];
   3257         uint32_t vaddr = text_vaddr + i * 4;
   3258         auto fn_it = functions.find(vaddr);
   3259 
   3260         if (fn_it != functions.end()) {
   3261             Function& f = fn_it->second;
   3262 
   3263             printf("}\n\n");
   3264 
   3265             switch (f.nret) {
   3266                 case 0:
   3267                     printf("void ");
   3268                     break;
   3269 
   3270                 case 1:
   3271                     printf("uint32_t ");
   3272                     break;
   3273 
   3274                 case 2:
   3275                     printf("uint64_t ");
   3276                     break;
   3277             }
   3278 
   3279             auto name_it = symbol_names.find(vaddr);
   3280 
   3281             if (name_it != symbol_names.end()) {
   3282                 printf("%s", name_it->second.c_str());
   3283             } else {
   3284                 printf("func_%x", vaddr);
   3285             }
   3286 
   3287             printf("(uint8_t *mem, uint32_t sp");
   3288 
   3289             if (f.v0_in) {
   3290                 printf(", uint32_t %s", r(MIPS_REG_V0));
   3291             }
   3292 
   3293             for (uint32_t i = 0; i < f.nargs; i++) {
   3294                 printf(", uint32_t %s", r(MIPS_REG_A0 + i));
   3295             }
   3296 
   3297             printf(") {\n");
   3298             printf("const uint32_t zero = 0;\n");
   3299             printf("uint32_t at = 0, v1 = 0, t0 = 0, t1 = 0, t2 = 0,\n");
   3300             printf("t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, s0 = 0, s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0,\n");
   3301             printf("s6 = 0, s7 = 0, t8 = 0, t9 = 0, gp = 0, fp = 0, s8 = 0, ra = 0;\n");
   3302             printf("uint32_t lo = 0, hi = 0;\n");
   3303             printf("int cf = 0;\n");
   3304 
   3305             if (!f.v0_in) {
   3306                 printf("uint32_t v0 = 0;\n");
   3307             }
   3308 
   3309             for (uint32_t j = f.nargs; j < 4; j++) {
   3310                 printf("uint32_t %s = 0;\n", r(MIPS_REG_A0 + j));
   3311             }
   3312         }
   3313 
   3314         if (label_addresses.count(vaddr)) {
   3315             printf("L%x:\n", vaddr);
   3316         }
   3317 
   3318         dump_instr(i);
   3319     } */
   3320 }
   3321 
   3322 void parse_elf(const uint8_t* data, size_t file_len) {
   3323     Elf32_Ehdr* ehdr;
   3324     Elf32_Shdr *shdr, *str_shdr, *sym_shdr = NULL, *dynsym_shdr, *dynamic_shdr, *reginfo_shdr, *got_shdr,
   3325                                  *sym_strtab = NULL, *sym_dynstr;
   3326     int text_section_index = -1;
   3327     int symtab_section_index = -1;
   3328     int dynsym_section_index = -1;
   3329     int reginfo_section_index = -1;
   3330     int dynamic_section_index = -1;
   3331     int got_section_index = -1;
   3332     int rodata_section_index = -1;
   3333     int data_section_index = -1;
   3334     int bss_section_index = -1;
   3335     uint32_t text_offset = 0;
   3336     uint32_t vaddr_adj = 0;
   3337 
   3338     if (file_len < 4 || data[0] != 0x7f || data[1] != 'E' || data[2] != 'L' || data[3] != 'F') {
   3339         fprintf(stderr, "Not an ELF file.\n");
   3340         exit(EXIT_FAILURE);
   3341     }
   3342 
   3343     ehdr = (Elf32_Ehdr*)data;
   3344     if (ehdr->e_ident[EI_DATA] != 2 || u16be(ehdr->e_machine) != 8) {
   3345         fprintf(stderr, "Not big-endian MIPS.\n");
   3346         exit(EXIT_FAILURE);
   3347     }
   3348 
   3349     if (u16be(ehdr->e_shstrndx) == 0) {
   3350         // (We could look at program headers instead in this case.)
   3351         fprintf(stderr, "Missing section headers; stripped binaries are not yet supported.\n");
   3352         exit(EXIT_FAILURE);
   3353     }
   3354 
   3355 #define SECTION(index) (Elf32_Shdr*)(data + u32be(ehdr->e_shoff) + (index)*u16be(ehdr->e_shentsize))
   3356 #define STR(strtab, offset) (const char*)(data + u32be(strtab->sh_offset) + offset)
   3357 
   3358     str_shdr = SECTION(u16be(ehdr->e_shstrndx));
   3359     for (int i = 0; i < u16be(ehdr->e_shnum); i++) {
   3360         shdr = SECTION(i);
   3361 
   3362         const char* name = STR(str_shdr, u32be(shdr->sh_name));
   3363 
   3364         if (strcmp(name, ".text") == 0) {
   3365             text_offset = u32be(shdr->sh_offset);
   3366             text_vaddr = u32be(shdr->sh_addr);
   3367             vaddr_adj = text_vaddr - u32be(shdr->sh_addr);
   3368             text_section_len = u32be(shdr->sh_size);
   3369             text_section = data + text_offset;
   3370             text_section_index = i;
   3371         }
   3372 
   3373         if (u32be(shdr->sh_type) == SHT_SYMTAB) {
   3374             symtab_section_index = i;
   3375         }
   3376 
   3377         if (u32be(shdr->sh_type) == SHT_DYNSYM) {
   3378             dynsym_section_index = i;
   3379         }
   3380 
   3381         if (u32be(shdr->sh_type) == SHT_MIPS_REGINFO) {
   3382             reginfo_section_index = i;
   3383         }
   3384 
   3385         if (u32be(shdr->sh_type) == SHT_DYNAMIC) {
   3386             dynamic_section_index = i;
   3387         }
   3388 
   3389         if (strcmp(name, ".got") == 0) {
   3390             got_section_index = i;
   3391         }
   3392 
   3393         if (strcmp(name, ".rodata") == 0) {
   3394             rodata_section_index = i;
   3395         }
   3396 
   3397         if (strcmp(name, ".data") == 0) {
   3398             data_section_index = i;
   3399         }
   3400 
   3401         if (strcmp(name, ".bss") == 0) {
   3402             bss_section_index = i;
   3403         }
   3404     }
   3405 
   3406     if (text_section_index == -1) {
   3407         fprintf(stderr, "Missing .text section.\n");
   3408         exit(EXIT_FAILURE);
   3409     }
   3410 
   3411     if (symtab_section_index == -1 && dynsym_section_index == -1) {
   3412         fprintf(stderr, "Missing .symtab or .dynsym section.\n");
   3413         exit(EXIT_FAILURE);
   3414     }
   3415 
   3416     if (dynsym_section_index != -1) {
   3417         if (reginfo_section_index == -1) {
   3418             fprintf(stderr, "Missing .reginfo section.\n");
   3419             exit(EXIT_FAILURE);
   3420         }
   3421 
   3422         if (dynamic_section_index == -1) {
   3423             fprintf(stderr, "Missing .dynamic section.\n");
   3424             exit(EXIT_FAILURE);
   3425         }
   3426 
   3427         if (got_section_index == -1) {
   3428             fprintf(stderr, "Missing .got section.\n");
   3429             exit(EXIT_FAILURE);
   3430         }
   3431     }
   3432 
   3433     if (rodata_section_index != -1) {
   3434         shdr = SECTION(rodata_section_index);
   3435         uint32_t size = u32be(shdr->sh_size);
   3436         rodata_section = data + u32be(shdr->sh_offset);
   3437         rodata_section_len = size;
   3438         rodata_vaddr = u32be(shdr->sh_addr);
   3439     }
   3440 
   3441     if (data_section_index != -1) {
   3442         shdr = SECTION(data_section_index);
   3443         uint32_t size = u32be(shdr->sh_size);
   3444         data_section = data + u32be(shdr->sh_offset);
   3445         data_section_len = size;
   3446         data_vaddr = u32be(shdr->sh_addr);
   3447     }
   3448 
   3449     if (bss_section_index != -1) {
   3450         shdr = SECTION(bss_section_index);
   3451         uint32_t size = u32be(shdr->sh_size);
   3452         bss_section_len = size;
   3453         bss_vaddr = u32be(shdr->sh_addr);
   3454     }
   3455 
   3456     // add symbols
   3457     if (symtab_section_index != -1) {
   3458         sym_shdr = SECTION(symtab_section_index);
   3459         sym_strtab = SECTION(u32be(sym_shdr->sh_link));
   3460         assert(0 && ".symtab not supported - use a program with .dynsym instead");
   3461 
   3462         assert(u32be(sym_shdr->sh_entsize) == sizeof(Elf32_Sym));
   3463         for (uint32_t i = 0; i < u32be(sym_shdr->sh_size); i += sizeof(Elf32_Sym)) {
   3464             Elf32_Sym* sym = (Elf32_Sym*)(data + u32be(sym_shdr->sh_offset) + i);
   3465             const char* name = STR(sym_strtab, u32be(sym->st_name));
   3466             uint32_t addr = u32be(sym->st_value);
   3467 
   3468             if (u16be(sym->st_shndx) != text_section_index || name[0] == '.') {
   3469                 continue;
   3470             }
   3471 
   3472             addr += vaddr_adj;
   3473             // disasm_label_add(state, name, addr, u32be(sym->st_size), true);
   3474         }
   3475     }
   3476 
   3477     if (dynsym_section_index != -1) {
   3478         dynsym_shdr = SECTION(dynsym_section_index);
   3479         sym_dynstr = SECTION(u32be(dynsym_shdr->sh_link));
   3480         reginfo_shdr = SECTION(reginfo_section_index);
   3481         dynamic_shdr = SECTION(dynamic_section_index);
   3482         got_shdr = SECTION(got_section_index);
   3483 
   3484         Elf32_RegInfo* reg_info = (Elf32_RegInfo*)(data + u32be(reginfo_shdr->sh_offset));
   3485         uint32_t gp_base = u32be(reg_info->ri_gp_value); // gp should have this value through the program run
   3486         uint32_t got_start = 0;
   3487         uint32_t local_got_no = 0;
   3488         uint32_t first_got_sym = 0;
   3489         uint32_t dynsym_no = 0; // section size can't be used due to alignment 16 padding
   3490 
   3491         assert(u32be(dynamic_shdr->sh_entsize) == sizeof(Elf32_Dyn));
   3492         for (uint32_t i = 0; i < u32be(dynamic_shdr->sh_size); i += sizeof(Elf32_Dyn)) {
   3493             Elf32_Dyn* dyn = (Elf32_Dyn*)(data + u32be(dynamic_shdr->sh_offset) + i);
   3494 
   3495             if (u32be(dyn->d_tag) == DT_PLTGOT) {
   3496                 got_start = u32be(dyn->d_un.d_ptr);
   3497             }
   3498 
   3499             if (u32be(dyn->d_tag) == DT_MIPS_LOCAL_GOTNO) {
   3500                 local_got_no = u32be(dyn->d_un.d_val);
   3501             }
   3502 
   3503             if (u32be(dyn->d_tag) == DT_MIPS_GOTSYM) {
   3504                 first_got_sym = u32be(dyn->d_un.d_val);
   3505             }
   3506 
   3507             if (u32be(dyn->d_tag) == DT_MIPS_SYMTABNO) {
   3508                 dynsym_no = u32be(dyn->d_un.d_val);
   3509             }
   3510         }
   3511 
   3512         assert(got_start != 0);
   3513 
   3514         // value to add to asm gp offset, for example 32752, if -32752(gp) refers to the first entry in got.
   3515         uint32_t gp_adj = gp_base - got_start;
   3516 
   3517         assert(gp_adj < 0x10000);
   3518 
   3519         assert(u32be(dynsym_shdr->sh_entsize) == sizeof(Elf32_Sym));
   3520 
   3521         uint32_t global_got_no = dynsym_no - first_got_sym;
   3522         // global_got_entry *global_entries = (global_got_entry *)calloc(global_got_no, sizeof(global_got_entry));
   3523 
   3524         got_globals.resize(global_got_no);
   3525 
   3526         uint32_t common_start = ~0U;
   3527         vector<string> common_order;
   3528 
   3529         for (uint32_t i = 0; i < dynsym_no; i++) {
   3530             Elf32_Sym* sym = (Elf32_Sym*)(data + u32be(dynsym_shdr->sh_offset) + i * sizeof(Elf32_Sym));
   3531             const char* name = STR(sym_dynstr, u32be(sym->st_name));
   3532             uint32_t addr = u32be(sym->st_value);
   3533 
   3534             addr += vaddr_adj;
   3535 
   3536             uint8_t type = ELF32_ST_TYPE(sym->st_info);
   3537 
   3538             if (!strcmp(name, "_procedure_table")) {
   3539                 procedure_table_start = addr;
   3540             } else if (!strcmp(name, "_procedure_table_size")) {
   3541                 procedure_table_len = 40 * u32be(sym->st_value);
   3542             }
   3543 
   3544             if ((u16be(sym->st_shndx) == SHN_MIPS_TEXT && type == STT_FUNC) ||
   3545                 (type == STT_OBJECT &&
   3546                  (u16be(sym->st_shndx) == SHN_MIPS_ACOMMON || u16be(sym->st_shndx) == SHN_MIPS_DATA))) {
   3547                 // disasm_label_add(state, name, addr, u32be(sym->st_size), true);
   3548                 if (type == STT_OBJECT) {}
   3549 
   3550                 if (u16be(sym->st_shndx) == SHN_MIPS_ACOMMON) {
   3551                     if (addr < common_start) {
   3552                         common_start = addr;
   3553                     }
   3554 
   3555                     common_order.push_back(name);
   3556                 }
   3557 
   3558                 if (type == STT_FUNC) {
   3559                     add_function(addr);
   3560 
   3561                     if (strcmp(name, "main") == 0) {
   3562                         main_addr = addr;
   3563                     }
   3564 
   3565                     if (strcmp(name, "_mcount") == 0) {
   3566                         mcount_addr = addr;
   3567                     }
   3568 
   3569                     symbol_names[addr] = name;
   3570                 }
   3571             }
   3572 
   3573             if (i >= first_got_sym) {
   3574                 uint32_t got_value = u32be(*(uint32_t*)(data + u32be(got_shdr->sh_offset) +
   3575                                                         (local_got_no + (i - first_got_sym)) * sizeof(uint32_t)));
   3576 
   3577                 if (u16be(sym->st_shndx) == SHN_MIPS_TEXT && type == STT_FUNC) {
   3578                     // got_globals[i - first_got_sym] = got_value;
   3579                     // label_addresses.insert(got_value);
   3580                     got_globals[i - first_got_sym] = addr; // to include the 3 instr gp header thing
   3581                     label_addresses.insert(addr);
   3582                 } else if (type == STT_OBJECT &&
   3583                            (u16be(sym->st_shndx) == SHN_UNDEF || u16be(sym->st_shndx) == SHN_COMMON)) {
   3584                     // symbol defined externally (for example in libc)
   3585                     got_globals[i - first_got_sym] = got_value;
   3586                 } else {
   3587                     got_globals[i - first_got_sym] = addr;
   3588                 }
   3589 
   3590                 symbol_names[got_globals[i - first_got_sym]] = name;
   3591             }
   3592         }
   3593 
   3594         uint32_t* local_entries = (uint32_t*)calloc(local_got_no, sizeof(uint32_t));
   3595         got_locals.resize(local_got_no);
   3596         for (uint32_t i = 0; i < local_got_no; i++) {
   3597             uint32_t* entry = (uint32_t*)(data + u32be(got_shdr->sh_offset) + i * sizeof(uint32_t));
   3598             got_locals[i] = u32be(*entry);
   3599         }
   3600 
   3601         gp_value = gp_base;
   3602         gp_value_adj = gp_adj;
   3603 
   3604         free(local_entries);
   3605     }
   3606 
   3607     // add relocations
   3608     for (int i = 0; i < u16be(ehdr->e_shnum); i++) {
   3609         Elf32_Rel* prevHi = NULL;
   3610 
   3611         shdr = SECTION(i);
   3612         if (u32be(shdr->sh_type) != SHT_REL || u32be(shdr->sh_info) != (uint32_t)text_section_index)
   3613             continue;
   3614 
   3615         if (sym_shdr == NULL) {
   3616             fprintf(stderr, "Relocations without .symtab section\n");
   3617             exit(EXIT_FAILURE);
   3618         }
   3619 
   3620         assert(u32be(shdr->sh_link) == (uint32_t)symtab_section_index);
   3621         assert(u32be(shdr->sh_entsize) == sizeof(Elf32_Rel));
   3622 
   3623         for (uint32_t i = 0; i < u32be(shdr->sh_size); i += sizeof(Elf32_Rel)) {
   3624             Elf32_Rel* rel = (Elf32_Rel*)(data + u32be(shdr->sh_offset) + i);
   3625             uint32_t offset = text_offset + u32be(rel->r_offset);
   3626             uint32_t symIndex = ELF32_R_SYM(u32be(rel->r_info));
   3627             uint32_t rtype = ELF32_R_TYPE(u32be(rel->r_info));
   3628             const char* symName = "0";
   3629 
   3630             if (symIndex != STN_UNDEF) {
   3631                 Elf32_Sym* sym = (Elf32_Sym*)(data + u32be(sym_shdr->sh_offset) + symIndex * sizeof(Elf32_Sym));
   3632 
   3633                 symName = STR(sym_strtab, u32be(sym->st_name));
   3634             }
   3635 
   3636             if (rtype == R_MIPS_HI16) {
   3637                 if (prevHi != NULL) {
   3638                     fprintf(stderr, "Consecutive R_MIPS_HI16.\n");
   3639                     exit(EXIT_FAILURE);
   3640                 }
   3641 
   3642                 prevHi = rel;
   3643                 continue;
   3644             }
   3645 
   3646             if (rtype == R_MIPS_LO16) {
   3647                 int32_t addend = (int16_t)((data[offset + 2] << 8) + data[offset + 3]);
   3648 
   3649                 if (prevHi != NULL) {
   3650                     uint32_t offset2 = text_offset + u32be(prevHi->r_offset);
   3651 
   3652                     addend += (uint32_t)((data[offset2 + 2] << 8) + data[offset2 + 3]) << 16;
   3653                     // add_reloc(state, offset2, symName, addend, out_range.vaddr);
   3654                 }
   3655                 prevHi = NULL;
   3656                 // add_reloc(state, offset, symName, addend, out_range.vaddr);
   3657             } else if (rtype == R_MIPS_26) {
   3658                 int32_t addend = (u32be(*(uint32_t*)(data + offset)) & ((1 << 26) - 1)) << 2;
   3659 
   3660                 if (addend >= (1 << 27)) {
   3661                     addend -= 1 << 28;
   3662                 }
   3663                 // add_reloc(state, offset, symName, addend, out_range.vaddr);
   3664             }
   3665 
   3666             else {
   3667                 fprintf(stderr, "Bad relocation type %d.\n", rtype);
   3668                 exit(EXIT_FAILURE);
   3669             }
   3670         }
   3671 
   3672         if (prevHi != NULL) {
   3673             fprintf(stderr, "R_MIPS_HI16 without matching R_MIPS_LO16.\n");
   3674             exit(EXIT_FAILURE);
   3675         }
   3676     }
   3677 }
   3678 #undef SECTION
   3679 #undef STR
   3680 
   3681 size_t read_file(const char* file_name, uint8_t** data) {
   3682     FILE* in;
   3683     uint8_t* in_buf = NULL;
   3684     long file_size;
   3685     long bytes_read;
   3686 
   3687     in = fopen(file_name, "rb");
   3688     assert(in != nullptr);
   3689 
   3690     // allocate buffer to read from offset to end of file
   3691     fseek(in, 0, SEEK_END);
   3692     file_size = ftell(in);
   3693     assert(file_size != -1L);
   3694 
   3695     in_buf = (uint8_t*)malloc(file_size);
   3696     fseek(in, 0, SEEK_SET);
   3697 
   3698     // read bytes
   3699     bytes_read = fread(in_buf, 1, file_size, in);
   3700     assert(bytes_read == file_size);
   3701 
   3702     fclose(in);
   3703     *data = in_buf;
   3704     return bytes_read;
   3705 }
   3706 
   3707 #ifdef UNIX_PLATFORM
   3708 void crashHandler(int sig) {
   3709     void* array[4096];
   3710     const size_t nMaxFrames = std::size(array);
   3711     size_t size = backtrace(array, nMaxFrames);
   3712     char** symbols = backtrace_symbols(array, nMaxFrames);
   3713 
   3714     fprintf(stderr, "\n recomp crashed. (Signal: %i)\n", sig);
   3715 
   3716     // Feel free to add more crash messages.
   3717     const char* crashEasterEgg[] = {
   3718         "\tIT'S A SECRET TO EVERYBODY. \n\tBut it shouldn't be, you'd better ask about it!",
   3719         "\tI AM ERROR.",
   3720         "\tGRUMBLE,GRUMBLE...",
   3721         "\tDODONGO DISLIKES SMOKE \n\tAnd recomp dislikes whatever you fed it.",
   3722         "\tMay the way of the Hero lead \n\tto the debugger.",
   3723         "\tTHE WIND FISH SLUMBERS LONG... \n\tTHE HERO'S LIFE GONE... ",
   3724         "\tSEA BEARS FOAM, SLEEP BEARS DREAMS. \n\tBOTH END IN THE SAME WAY CRASSSH!",
   3725         "\tYou've met with a terrible fate, haven't you?",
   3726         "\tMaster, I calculate a 100% probability that recomp has crashed. \n\tAdditionally, the "
   3727         "batteries in your Wii Remote are nearly depleted.",
   3728         "\t    CONGRATURATIONS!    \n"
   3729         "\tAll Pages are displayed.\n"
   3730         "\t       THANK YOU!       \n"
   3731         "\t You are great debugger!",
   3732         "\tRCP is HUNG UP!!\n"
   3733         "\tOh! MY GOD!!",
   3734     };
   3735 
   3736     srand(time(nullptr));
   3737     auto easterIndex = rand() % std::size(crashEasterEgg);
   3738 
   3739     fprintf(stderr, "\n%s\n\n", crashEasterEgg[easterIndex]);
   3740 
   3741     fprintf(stderr, "Traceback:\n");
   3742     for (size_t i = 1; i < size; i++) {
   3743         Dl_info info;
   3744         uint32_t gotAddress = dladdr(array[i], &info);
   3745         std::string functionName(symbols[i]);
   3746 
   3747         if (gotAddress != 0 && info.dli_sname != nullptr) {
   3748             int32_t status;
   3749             char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
   3750             const char* nameFound = info.dli_sname;
   3751 
   3752             if (status == 0) {
   3753                 nameFound = demangled;
   3754             }
   3755 
   3756             {
   3757                 char auxBuffer[0x8000];
   3758 
   3759                 snprintf(auxBuffer, std::size(auxBuffer), "%s (+0x%lX)", nameFound,
   3760                          (char*)array[i] - (char*)info.dli_saddr);
   3761                 functionName = auxBuffer;
   3762             }
   3763             free(demangled);
   3764 
   3765 #if FULL_TRACEBACK == 0
   3766             fprintf(stderr, "%-3zd %s\n", i, functionName.c_str());
   3767 #endif
   3768         }
   3769 
   3770 #if FULL_TRACEBACK != 0
   3771         fprintf(stderr, "%-3zd %s\n", i, functionName.c_str());
   3772 #endif
   3773     }
   3774 
   3775     fprintf(stderr, "\n");
   3776 
   3777     free(symbols);
   3778     exit(1);
   3779 }
   3780 #endif
   3781 
   3782 int main(int argc, char* argv[]) {
   3783     const char* filename = argv[1];
   3784 
   3785     if (strcmp(filename, "--conservative") == 0) {
   3786         conservative = true;
   3787         filename = argv[2];
   3788     }
   3789 
   3790 #ifdef UNIX_PLATFORM
   3791     signal(SIGSEGV, crashHandler);
   3792     signal(SIGABRT, crashHandler);
   3793 #endif
   3794 
   3795     uint8_t* data;
   3796     size_t len = read_file(filename, &data);
   3797 
   3798     parse_elf(data, len);
   3799     disassemble();
   3800     inspect_data_function_pointers(data_function_pointers, rodata_section, rodata_vaddr, rodata_section_len);
   3801     inspect_data_function_pointers(data_function_pointers, data_section, data_vaddr, data_section_len);
   3802     pass1();
   3803     pass2();
   3804     pass3();
   3805     pass4();
   3806     pass5();
   3807     pass6();
   3808     // dump();
   3809     dump_c();
   3810     free(data);
   3811 
   3812     return 0;
   3813 }