Exemple #1
0
    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
Exemple #2
0
    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()
Exemple #3
0
 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
Exemple #4
0
    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}")
Exemple #5
0
    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
Exemple #6
0
    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)
Exemple #7
0
    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}")
Exemple #8
0
    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))
Exemple #9
0
    def get_c_preamble(self):
        ret = []

        preamble = options.get("generated_c_preamble", "#include \"common.h\"")
        ret.append(preamble)
        ret.append("")

        return ret
Exemple #10
0
    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)
Exemple #11
0
    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}")
Exemple #12
0
    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)
Exemple #13
0
    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)
Exemple #14
0
    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}")
Exemple #15
0
    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}")
Exemple #16
0
    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}")
Exemple #17
0
    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)
Exemple #18
0
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")
Exemple #19
0
    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)
Exemple #20
0
    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)
Exemple #21
0
def get_symbol_addrs_path(repo_path):
    return os.path.join(repo_path,
                        options.get("symbol_addrs_path", "symbol_addrs.txt"))
Exemple #22
0
 def get_ld_files(self):
     return [(options.get("assets_dir", "bin"), f"{self.name}.Yay0",
              ".data", self.rom_start)]
Exemple #23
0
def get_undefined_syms_path(repo_path):
    return os.path.join(
        repo_path, options.get("undefined_syms_path", "undefined_syms.txt"))
Exemple #24
0
 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))
Exemple #25
0
def parse_segment_subalign(segment):
    default = options.get("subalign", default_subalign)
    if type(segment) is dict:
        return segment.get("subalign", default)
    return default
Exemple #26
0
 def log(self, msg):
     if options.get("verbose", False):
         log.write(f"{self.type} {self.name}: {msg}")
Exemple #27
0
    def get_ld_files(self):
        ext = f".{self.type}.png"

        return [(options.get("assets_dir", "img"), f"{self.name}{ext}",
                 ".data", self.rom_start)]
Exemple #28
0
    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
Exemple #29
0
    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
Exemple #30
0
def get_undefined_funcs_auto_path(repo_path):
    return os.path.join(
        repo_path,
        options.get("undefined_funcs_auto_path", "undefined_funcs_auto.txt"))