def get_ld_section(self): replace_ext = options.get("ld_o_replace_extension", True) sect_name = self.ld_name_override if self.ld_name_override else self.get_ld_section_name() vram_or_rom = self.rom_start if self.vram_start == 0 else self.vram_start subalign_str = f"SUBALIGN({self.subalign})" s = ( f"SPLAT_BEGIN_SEG({sect_name}, 0x{self.rom_start:X}, 0x{vram_or_rom:X}, {subalign_str})\n" ) i = 0 do_next = False for subdir, path, obj_type, start in self.get_ld_files(): # Manual linker segment creation if obj_type == "linker": s += ( "}\n" f"SPLAT_BEGIN_SEG({path}, 0x{start:X}, 0x{self.rom_to_ram(start):X}, {subalign_str})\n" ) # Create new sections for non-0x10 alignment (hack) if start % 0x10 != 0 and i != 0 or do_next: tmp_sect_name = path.replace(".", "_") tmp_sect_name = tmp_sect_name.replace("/", "_") s += ( "}\n" f"SPLAT_BEGIN_SEG({tmp_sect_name}, 0x{start:X}, 0x{self.rom_to_ram(start):X}, {subalign_str})\n" ) do_next = False if start % 0x10 != 0 and i != 0: do_next = True path_cname = re.sub(r"[^0-9a-zA-Z_]", "_", path) s += f" {path_cname} = .;\n" if subdir == options.get("assets_dir"): path = PurePath(path) else: path = PurePath(subdir) / PurePath(path) # Remove leading ..s while path.parts[0] == "..": path = path.relative_to("..") path = path.with_suffix(".o" if replace_ext else path.suffix + ".o") if obj_type != "linker": s += f" BUILD_DIR/{path}({obj_type});\n" i += 1 s += ( f"SPLAT_END_SEG({sect_name}, 0x{self.rom_end:X})\n" ) return s
def __init__(self): self.shiftable: bool = options.get("shiftable", False) self.linker_discard_section: bool = options.get( "linker_discard_section", True) self.entries: List[LinkerEntry] = [] self.buffer: List[str] = [] self.symbols: List[str] = [] self._indent_level = 0 self._writeln("SECTIONS") self._begin_block()
def get_out_subdir(self): if self.type.startswith("."): if self.parent: return self.parent.get_out_subdir() else: return options.get("src_path", "src") elif self.type in ["c"]: return options.get("src_path", "src") elif self.type in ["asm", "hasm", "header"]: return "asm" elif self.type == "bin": return options.get("assets_dir", "bin") elif self.type in ["i4", "i8", "ia4", "ia8", "ia16", "rgba16", "rgba32", "ci4", "ci8", "palette"]: return options.get("assets_dir", "img") return self.type
def create_c_asm_file(self, funcs_text, func, out_dir, sub, func_name): if options.get("compiler", "IDO") == "GCC": out_lines = self.get_gcc_inc_header() else: out_lines = [] if func in self.rodata_syms: func_rodata = list({s for s in self.rodata_syms[func] if s.disasm_str}) func_rodata.sort(key=lambda s:s.vram_start) if len(func_rodata) > 0: rsub = self.get_subsection_for_ram(func_rodata[0].vram_start) if rsub and rsub.type != "rodata": out_lines.append(".section .rodata") for sym in func_rodata: if sym.disasm_str: out_lines.extend(sym.disasm_str.replace("\n\n", "\n").split("\n")) out_lines.append("") out_lines.append(".section .text") out_lines.append("") out_lines.extend(funcs_text[func][0]) out_lines.append("") outpath = Path(os.path.join(out_dir, sub.name, func_name + ".s")) outpath.parent.mkdir(parents=True, exist_ok=True) with open(outpath, "w", newline="\n") as f: f.write("\n".join(out_lines)) self.log(f"Disassembled {func_name} to {outpath}")
def parse_header(self, rom_bytes): encoding = options.get("header_encoding", "ASCII") header_lines = [] header_lines.append(f".section .data\n") header_lines.append(self.get_line("word", rom_bytes[0x00:0x04], "PI BSB Domain 1 register")) header_lines.append(self.get_line("word", rom_bytes[0x04:0x08], "Clockrate setting")) header_lines.append(self.get_line("word", rom_bytes[0x08:0x0C], "Entrypoint address")) header_lines.append(self.get_line("word", rom_bytes[0x0C:0x10], "Revision")) header_lines.append(self.get_line("word", rom_bytes[0x10:0x14], "Checksum 1")) header_lines.append(self.get_line("word", rom_bytes[0x14:0x18], "Checksum 2")) header_lines.append(self.get_line("word", rom_bytes[0x18:0x1C], "Unknown 1")) header_lines.append(self.get_line("word", rom_bytes[0x1C:0x20], "Unknown 2")) if encoding != "word": header_lines.append(".ascii \"" + rom_bytes[0x20:0x34].decode(encoding).strip().ljust(20) + "\" /* Internal name */") else: for i in range(0x20, 0x34, 4): header_lines.append(self.get_line("word", rom_bytes[i:i+4], "Internal name")) header_lines.append(self.get_line("word", rom_bytes[0x34:0x38], "Unknown 3")) header_lines.append(self.get_line("word", rom_bytes[0x38:0x3C], "Cartridge")) header_lines.append(self.get_line("ascii", rom_bytes[0x3C:0x3E], "Cartridge ID")) header_lines.append(self.get_line("ascii", rom_bytes[0x3E:0x3F], "Country code")) header_lines.append(self.get_line("byte", rom_bytes[0x3F:0x40], "Version")) header_lines.append("") return header_lines
def disassemble_data(self, rom_bytes): vertex_data = rom_bytes[self.rom_start:self.rom_end] segment_length = len(vertex_data) if (segment_length) % 16 != 0: print( f'Error: Vtx segment {self.name} length ({segment_length}) is not a multiple of 16!' ) sys.exit(1) lines = [] if self.standalone: preamble = options.get('generated_c_preamble', '#include "common.h"') lines.append(preamble) vertex_count = segment_length // 16 if self.standalone: lines.append(f'Vtx {self.label}[{vertex_count}] = {{') for vtx in struct.iter_unpack('>hhhHhhBBBB', vertex_data): x, y, z, flg, t, c, r, g, b, a = vtx indent = ' ' if self.standalone else '' vtx_string = f'{indent}{{{{{{ {x:5}, {y:5}, {z:5} }}, {flg}, {{ {t:5}, {c:5} }}, {{ {r:3}, {g:3}, {b:3}, {a:3} }}}}}},' if flg != 0: self.warn(f'Non-zero flag found in vertex data {self.name}!') lines.append(vtx_string) if self.standalone: lines.append('};') # enforce newline at end of file lines.append('') return '\n'.join(lines)
def split(self, rom_bytes): encoding = options.get("header_encoding", "ASCII") header_lines = [] header_lines.append(f".section .data\n") header_lines.append(self.get_line("word", rom_bytes[0x00:0x04], "PI BSB Domain 1 register")) header_lines.append(self.get_line("word", rom_bytes[0x04:0x08], "Clockrate setting")) header_lines.append(self.get_line("word", rom_bytes[0x08:0x0C], "Entrypoint address")) header_lines.append(self.get_line("word", rom_bytes[0x0C:0x10], "Revision")) header_lines.append(self.get_line("word", rom_bytes[0x10:0x14], "Checksum 1")) header_lines.append(self.get_line("word", rom_bytes[0x14:0x18], "Checksum 2")) header_lines.append(self.get_line("word", rom_bytes[0x18:0x1C], "Unknown 1")) header_lines.append(self.get_line("word", rom_bytes[0x1C:0x20], "Unknown 2")) if encoding != "word": header_lines.append(".ascii \"" + rom_bytes[0x20:0x34].decode(encoding).strip().ljust(20) + "\" /* Internal name */") else: for i in range(0x20, 0x34, 4): header_lines.append(self.get_line("word", rom_bytes[i:i+4], "Internal name")) header_lines.append(self.get_line("word", rom_bytes[0x34:0x38], "Unknown 3")) header_lines.append(self.get_line("word", rom_bytes[0x38:0x3C], "Cartridge")) header_lines.append(self.get_line("ascii", rom_bytes[0x3C:0x3E], "Cartridge ID")) header_lines.append(self.get_line("ascii", rom_bytes[0x3E:0x3F], "Country code")) header_lines.append(self.get_line("byte", rom_bytes[0x3F:0x40], "Version")) header_lines.append("") src_path = self.out_path() src_path.parent.mkdir(parents=True, exist_ok=True) with open(src_path, "w", newline="\n") as f: f.write("\n".join(header_lines)) self.log(f"Wrote {self.name} to {src_path}")
def split_inner(self, segment, rom_bytes, base_path, generic_out_path): if not self.rom_start == self.rom_end: if self.type == "c": asm_out_dir = Segment.create_split_dir(base_path, os.path.join("asm", "nonmatchings")) for func in self.funcs_text: func_name = segment.get_symbol(func, type="func", local_only=True).name if func_name not in self.defined_funcs: segment.create_c_asm_file(self.funcs_text, func, asm_out_dir, self, func_name) if not os.path.exists(generic_out_path) and options.get("create_new_c_files", True): segment.create_c_file(self.funcs_text, self, asm_out_dir, base_path, generic_out_path) else: asm_out_dir = Segment.create_split_dir(base_path, "asm") out_lines = self.get_standalone_asm_header() for func in self.funcs_text: out_lines.extend(self.funcs_text[func][0]) out_lines.append("") outpath = Path(os.path.join(asm_out_dir, self.name + ".s")) outpath.parent.mkdir(parents=True, exist_ok=True) if self.type == "asm" or not os.path.exists(outpath): with open(outpath, "w", newline="\n") as f: f.write("\n".join(out_lines))
def get_c_preamble(self): ret = [] preamble = options.get("generated_c_preamble", "#include \"common.h\"") ret.append(preamble) ret.append("") return ret
def split(self, rom_bytes, base_path): out_dir = self.create_parent_dir( base_path + "/" + options.get("assets_dir", "img"), self.name) self.path = os.path.join(out_dir, os.path.basename(self.name) + ".png") data = rom_bytes[self.rom_start:self.rom_end] self.palette = N64SegPalette.parse_palette(data)
def split(self, rom_bytes, base_path): out_dir = Segment.create_split_dir(base_path, options.get("assets_dir", "bin")) bin_path = os.path.join(out_dir, self.name + ".bin") Path(bin_path).parent.mkdir(parents=True, exist_ok=True) with open(bin_path, "wb") as f: f.write(rom_bytes[self.rom_start:self.rom_end]) self.log(f"Wrote {self.name} to {bin_path}")
def split(self, rom_bytes, base_path): out_dir = self.create_parent_dir( base_path + "/" + options.get("assets_dir", "img"), self.name) self.path = os.path.join(out_dir, os.path.basename(self.name) + ".png") data = rom_bytes[self.rom_start:self.rom_end] self.image = self.__class__.parse_image(data, self.width, self.height, self.flip_horizontal, self.flip_vertical)
def scan(self, rom_bytes: bytes): if self.rom_start is not None and self.rom_end is not None and self.rom_start != self.rom_end: path = self.out_path() if path: if options.get("do_c_func_detection", True) and os.path.exists(path): # TODO run cpp? self.defined_funcs = self.get_funcs_defined_in_c(path) self.mark_c_funcs_as_defined(self.defined_funcs) self.global_asm_funcs = self.get_global_asm_funcs(path) self.funcs_text = self.disassemble_code(rom_bytes)
def split(self, rom_bytes, base_path): out_dir = self.create_parent_dir( os.path.join(base_path, options.get("assets_dir", "bin")), self.name) path = os.path.join(out_dir, os.path.basename(self.name) + ".bin") with open(path, "wb") as f: self.log(f"Decompressing {self.name}...") compressed_bytes = rom_bytes[self.rom_start:self.rom_end] decompressed_bytes = Yay0decompress.decompress_yay0( compressed_bytes) f.write(decompressed_bytes) self.log(f"Wrote {self.name} to {path}")
def split(self, rom_bytes, base_path): out_dir = self.create_parent_dir( base_path + "/" + options.get("assets_dir", "img"), self.name) path = os.path.join(out_dir, os.path.basename(self.name) + ".png") data = rom_bytes[self.rom_start:self.rom_end] w = self.__class__.get_writer(self.width, self.height) with open(path, "wb") as f: w.write_array( f, self.parse_image(data, self.width, self.height, self.flip_horizontal, self.flip_vertical)) self.log(f"Wrote {self.name} to {path}")
def create_c_file(self, funcs_text, sub, asm_out_dir, base_path, c_path): c_lines = self.get_c_preamble() for func in funcs_text: func_name = self.get_symbol(func, type="func", local_only=True).name if options.get("compiler", "IDO") == "GCC": c_lines.append("INCLUDE_ASM(s32, \"{}\", {});".format(sub.name, func_name)) else: asm_outpath = Path(os.path.join(asm_out_dir, sub.name, func_name + ".s")) rel_asm_outpath = os.path.relpath(asm_outpath, base_path) c_lines.append(f"#pragma GLOBAL_ASM(\"{rel_asm_outpath}\")") c_lines.append("") Path(c_path).parent.mkdir(parents=True, exist_ok=True) with open(c_path, "w") as f: f.write("\n".join(c_lines)) print(f"Wrote {sub.name} to {c_path}")
def split(self, rom_bytes: bytes): if not self.rom_start == self.rom_end: asm_out_dir = options.get_asm_path() / "nonmatchings" / self.dir asm_out_dir.mkdir(parents=True, exist_ok=True) for func in self.funcs_text: func_name = self.parent.get_symbol(func, type="func", local_only=True).name if func_name not in self.defined_funcs: self.create_c_asm_file(self.funcs_text, func, asm_out_dir, func_name) c_path = self.out_path() if c_path: if not os.path.exists(c_path) and options.get( "create_new_c_files", True): self.create_c_file(self.funcs_text, asm_out_dir, c_path)
def write_ldscript(rom_name, repo_path, sections): with open(os.path.join(repo_path, rom_name + ".ld"), "w", newline="\n") as f: f.write("#ifndef SPLAT_BEGIN_SEG\n" "#ifndef SHIFT\n" "#define SPLAT_BEGIN_SEG(name, start, vram, subalign) \\\n" " . = start;\\\n" " name##_ROM_START = .;\\\n" " name##_VRAM = ADDR(.name);\\\n" " .name vram : AT(name##_ROM_START) subalign {\n" "#else\n" "#define SPLAT_BEGIN_SEG(name, start, vram, subalign) \\\n" " name##_ROM_START = .;\\\n" " name##_VRAM = ADDR(.name);\\\n" " .name vram : AT(name##_ROM_START) subalign {\n" "#endif\n" "#endif\n" "\n" "#ifndef SPLAT_END_SEG\n" "#ifndef SHIFT\n" "#define SPLAT_END_SEG(name, end) \\\n" " } \\\n" " . = end;\\\n" " name##_ROM_END = .;\n" "#else\n" "#define SPLAT_END_SEG(name, end) \\\n" " } \\\n" " name##_ROM_END = .;\n" "#endif\n" "#endif\n" "\n") if options.get("ld_bare", False): f.write("\n".join(sections)) else: f.write("SECTIONS\n" "{\n" " ") f.write("\n ".join(s.replace("\n", "\n ") for s in sections)[:-4]) f.write(" /DISCARD/ :\n" " {\n" " *(*);\n" " }\n" "}\n")
def scan_inner(self, segment, rom_bytes, base_path, generic_out_path): if not self.rom_start == self.rom_end: if self.type == "c": if options.get("do_c_func_detection", True) and os.path.exists(generic_out_path): # TODO run cpp? self.defined_funcs = CodeSubsegment.get_funcs_defined_in_c(generic_out_path) segment.mark_c_funcs_as_defined(self.defined_funcs) insns = [insn for insn in CodeSubsegment.md.disasm(rom_bytes[self.rom_start : self.rom_end], self.vram_start)] funcs = segment.process_insns(insns, self.rom_start) # TODO: someday make func a subclass of symbol and store this disasm info there too for func in funcs: segment.get_symbol(func, type="func", create=True, define=True, local_only=True) funcs = segment.determine_symbols(funcs) segment.gather_jumptable_labels(rom_bytes) self.funcs_text = segment.add_labels(funcs)
def add(self, segment: Segment): entries = segment.get_linker_entries() self.entries.extend(entries) self._begin_segment(segment) seg_name = get_segment_cname(segment) self._write_symbol(f"{seg_name}_TEXT_START", ".") force_new_section = False text_ended = False data_started = False data_ended = False bss_started = False cur_section = None for i, entry in enumerate(entries): cur_section = entry.section if cur_section == "linker": # TODO: isinstance is preferable self._end_block() self._begin_segment(entry.segment) continue elif cur_section == "linker_offset": self._write_symbol( f"{get_segment_cname(entry.segment)}_OFFSET", f". - {get_segment_cname(segment)}_ROM_START") continue # text/data/bss START/END labels if not data_started and ("data" in cur_section or "rodata" in cur_section): if not text_ended: text_ended = True self._write_symbol(f"{seg_name}_TEXT_END", ".") data_started = True self._write_symbol(f"{seg_name}_DATA_START", ".") elif data_started and not data_ended and "data" not in cur_section and "rodata" not in cur_section: data_ended = True self._write_symbol(f"{seg_name}_DATA_END", ".") if not bss_started and i < ( len(entries) - 1) and "bss" in entries[i + 1].section: bss_started = True self._write_symbol(f"{seg_name}_BSS_START", ".") elif not bss_started and "bss" in cur_section: bss_started = True self._write_symbol(f"{seg_name}_BSS_START", ".") if options.get("enable_ld_alignment_hack", False): start = entry.segment.rom_start if isinstance(start, int): # Create new sections for non-subalign alignment (hack) if start % 0x10 != 0 and i != 0 or force_new_section: self._end_block() self._begin_segment(entry.segment, mid_segment=True) force_new_section = False if start % 0x10 != 0 and i != 0: force_new_section = True if entry.object_path and cur_section == ".data": path_cname = re.sub( r"[^0-9a-zA-Z_]", "_", str(entry.segment.dir / entry.segment.name) + ".".join(entry.object_path.suffixes[:-1])) self._write_symbol(path_cname, ".") self._writeln(f"{entry.object_path}({cur_section});") if not text_ended: self._write_symbol(f"{seg_name}_TEXT_END", ".") if not data_started: self._write_symbol(f"{seg_name}_DATA_START", ".") if not data_ended: self._write_symbol(f"{seg_name}_DATA_END", ".") if not bss_started: self._write_symbol(f"{seg_name}_BSS_START", ".") self._write_symbol(f"{seg_name}_BSS_END", ".") self._end_segment(segment)
def get_symbol_addrs_path(repo_path): return os.path.join(repo_path, options.get("symbol_addrs_path", "symbol_addrs.txt"))
def get_ld_files(self): return [(options.get("assets_dir", "bin"), f"{self.name}.Yay0", ".data", self.rom_start)]
def get_undefined_syms_path(repo_path): return os.path.join( repo_path, options.get("undefined_syms_path", "undefined_syms.txt"))
def scan(self, rom_bytes: bytes): if self.rom_start != "auto" and self.rom_end != "auto" and self.rom_start != self.rom_end: self.funcs_text = self.disassemble_code(rom_bytes, options.get("asm_endlabels", False))
def parse_segment_subalign(segment): default = options.get("subalign", default_subalign) if type(segment) is dict: return segment.get("subalign", default) return default
def log(self, msg): if options.get("verbose", False): log.write(f"{self.type} {self.name}: {msg}")
def get_ld_files(self): ext = f".{self.type}.png" return [(options.get("assets_dir", "img"), f"{self.name}{ext}", ".data", self.rom_start)]
def determine_symbols(self, funcs): hi_lo_max_distance = options.get("hi_lo_max_distance", 6) ret = {} for func_addr in funcs: func = funcs[func_addr] func_end_addr = func[-1][0].address + 4 possible_jtbl_jumps = [(k, v) for k, v in self.parent.jtbl_jumps.items() if k >= func_addr and k < func_end_addr] possible_jtbl_jumps.sort(key=lambda x: x[0]) for i in range(len(func)): insn = func[i][0] # Ensure the first item in the list is always ahead of where we're looking while len(possible_jtbl_jumps ) > 0 and possible_jtbl_jumps[0][0] < insn.address: del possible_jtbl_jumps[0] if insn.mnemonic == "lui": op_split = insn.op_str.split(", ") reg = op_split[0] if not op_split[1].startswith("0x"): continue lui_val = int(op_split[1], 0) if lui_val >= 0x8000: for j in range(i + 1, min(i + hi_lo_max_distance, len(func))): s_insn = func[j][0] s_op_split = s_insn.op_str.split(", ") if s_insn.mnemonic == "lui" and reg == s_op_split[ 0]: break if s_insn.mnemonic in ["addiu", "ori"]: s_reg = s_op_split[-2] else: s_reg = s_op_split[-1][s_op_split[-1]. rfind("(") + 1:-1] if reg == s_reg: if s_insn.mnemonic not in [ "addiu", "lw", "sw", "lh", "sh", "lhu", "lb", "sb", "lbu", "lwc1", "swc1", "ldc1", "sdc1" ]: break # Match! reg_ext = "" junk_search = re.search( r"[\(]", s_op_split[-1]) if junk_search is not None: if junk_search.start() == 0: break s_str = s_op_split[-1][:junk_search.start( )] reg_ext = s_op_split[-1][junk_search.start( ):] else: s_str = s_op_split[-1] symbol_addr = (lui_val * 0x10000) + int( s_str, 0) sym = None offset_str = "" if symbol_addr > func_addr and symbol_addr < self.parent.vram_end and len( possible_jtbl_jumps ) > 0 and func_end_addr - s_insn.address >= 0x30: for jump in possible_jtbl_jumps: if jump[1] == s_op_split[0]: dist_to_jump = possible_jtbl_jumps[ 0][0] - s_insn.address if dist_to_jump <= 16: sym = self.parent.get_symbol( symbol_addr, create=True, reference=True, type="jtbl", local_only=True) self.parent.jumptables[ symbol_addr] = ( func_addr, func_end_addr) break if not sym: sym = self.parent.get_symbol( symbol_addr, create=True, offsets=True, reference=True) offset = symbol_addr - sym.vram_start if offset != 0: offset_str = f"+0x{offset:X}" if self.parent: self.parent.check_rodata_sym( func_addr, sym) self.update_access_mnemonic( sym, s_insn.mnemonic) sym_label = sym.name + offset_str func[i] += ("%hi({})".format(sym_label), ) func[j] += ("%lo({}){}".format( sym_label, reg_ext), ) break ret[func_addr] = func return ret
def add_labels(self, funcs, addsuffix): ret = {} for func in funcs: func_text = [] # Add function glabel rom_addr = funcs[func][0][3] sym = self.parent.get_symbol(func, type="func", create=True, define=True, local_only=True) func_text.append(f"glabel {sym.name}") indent_next = False mnemonic_ljust = options.get("mnemonic_ljust", 11) rom_addr_padding = options.get("rom_address_padding", None) for insn in funcs[func]: insn_addr = insn[0].address # Add a label if we need one if insn_addr in self.parent.jtbl_glabels_to_add: func_text.append(f"glabel L{insn_addr:X}_{insn[3]:X}") elif insn_addr in self.parent.labels_to_add: self.parent.labels_to_add.remove(insn_addr) func_text.append(".L{:X}:".format(insn_addr)) if rom_addr_padding: rom_str = "{0:0{1}X}".format(insn[3], rom_addr_padding) else: rom_str = "{:X}".format(insn[3]) asm_comment = "/* {} {:X} {} */".format( rom_str, insn_addr, insn[0].bytes.hex().upper()) if len(insn) > 4: op_str = ", ".join(insn[2].split(", ")[:-1] + [insn[4]]) else: op_str = insn[2] if self.is_branch_insn(insn[0].mnemonic): branch_addr = int(insn[0].op_str.split(",")[-1].strip(), 0) if branch_addr in self.parent.jtbl_glabels_to_add: label_str = f"L{branch_addr:X}_{self.ram_to_rom(branch_addr):X}" op_str = ", ".join(insn[2].split(", ")[:-1] + [label_str]) insn_text = insn[1] if indent_next: indent_next = False insn_text = " " + insn_text asm_insn_text = " {}{}".format( insn_text.ljust(mnemonic_ljust), op_str).rstrip() func_text.append(asm_comment + asm_insn_text) if insn[0].mnemonic != "branch" and insn[0].mnemonic.startswith( "b") or insn[0].mnemonic.startswith("j"): indent_next = True if addsuffix: func_text.append(f"endlabel {sym.name}") ret[func] = (func_text, rom_addr) if options.get("find_file_boundaries"): # If this is not the last function in the file if func != list(funcs.keys())[-1]: # Find where the function returns jr_pos: Optional[int] = None for i, insn in enumerate(reversed(funcs[func])): if insn[0].mnemonic == "jr" and insn[0].op_str == "$ra": jr_pos = i break # If there is more than 1 nop after the return if jr_pos is not None and jr_pos > 1 and self.is_nops( [i[0] for i in funcs[func][-jr_pos + 1:]]): new_file_addr = funcs[func][-1][3] + 4 if (new_file_addr % 16) == 0: if not self.parent.reported_file_split: self.parent.reported_file_split = True print( f"Segment {self.name}, function at vram {func:X} ends with extra nops, indicating a likely file split." ) print( "File split suggestions for this segment will follow in config yaml format:" ) print(f" - [0x{new_file_addr:X}, asm]") return ret
def get_undefined_funcs_auto_path(repo_path): return os.path.join( repo_path, options.get("undefined_funcs_auto_path", "undefined_funcs_auto.txt"))