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 }