Exemplo n.º 1
0
    def add_new_rel(self, rel_path, new_rel, section_index_of_actor_profile,
                    offset_of_actor_profile):
        if not rel_path.startswith("files/rels/"):
            raise Exception(
                "Cannot add a new REL to a folder besides files/rels/: " +
                rel_path)
        if rel_path.lower() in self.gcm.files_by_path_lowercase:
            raise Exception(
                "Cannot add a new REL that has the same name as an existing one: "
                + rel_path)

        # Read the actor ID out of the actor profile.
        section_data_actor_profile = new_rel.sections[
            section_index_of_actor_profile].data
        new_actor_id = read_u16(section_data_actor_profile,
                                offset_of_actor_profile + 8)

        if new_actor_id in self.used_actor_ids:
            raise Exception(
                "Cannot add a new REL with an actor ID that is already used:\nActor ID: %03X\nNew REL path: %s"
                % (new_actor_id, rel_path))

        # We need to add the new REL to the profile list.
        profile_list = self.get_rel("files/rels/f_pc_profile_lst.rel")

        rel_relocation = RELRelocation()
        rel_relocation.relocation_type = RELRelocationType.R_PPC_ADDR32

        rel_relocation.curr_section_num = 4  # List section
        rel_relocation.relocation_offset = new_actor_id * 4  # Offset in the list

        # Write a null placeholder for the pointer to the profile that will be relocated.
        list_data = profile_list.sections[rel_relocation.curr_section_num].data
        write_u32(list_data, new_actor_id * 4, 0)
        # For some reason, there's an extra four 0x00 bytes after the last entry in the list, so we put that there just to be safe.
        write_u32(list_data, new_actor_id * 4 + 4, 0)

        rel_relocation.section_num_to_relocate_against = section_index_of_actor_profile
        rel_relocation.symbol_address = offset_of_actor_profile

        if new_rel.id in profile_list.relocation_entries_for_module:
            raise Exception(
                "Cannot add a new REL with a unique ID that is already present in the profile list:\nREL ID: %03X\nNew REL path: %s"
                % (new_rel.id, rel_path))

        profile_list.relocation_entries_for_module[new_rel.id] = [
            rel_relocation
        ]

        # Then add the REL to the game's filesystem.
        self.gcm.add_new_file(rel_path)
        self.rels_by_path[rel_path] = new_rel

        # Don't allow this actor ID to be used again by any more custom RELs we add.
        self.used_actor_ids.append(new_actor_id)
Exemplo n.º 2
0
def add_relocations_to_rel(self, file_path, rel_section_index,
                           offset_into_section, relocations):
    # Create the new REL relocations.

    rel = self.get_rel(file_path)

    main_symbols = self.get_symbol_map("files/maps/framework.map")

    free_space_start = self.free_space_start_offsets[file_path]

    for relocation_dict in relocations:
        symbol_name = relocation_dict["SymbolName"]
        relocation_offset = relocation_dict["Offset"]
        relocation_type = relocation_dict["Type"]

        relocation_offset += offset_into_section

        rel_relocation = RELRelocation()
        rel_relocation.relocation_type = RELRelocationType[relocation_type]

        branch_label_match = re.search(r"^branch_label_([0-9A-F]+)$",
                                       symbol_name, re.IGNORECASE)
        if symbol_name in self.main_custom_symbols:
            # Custom symbol located in main.dol.
            module_num = 0

            rel_relocation.symbol_address = self.main_custom_symbols[
                symbol_name]

            # I don't think this value is used for dol relocations.
            # In vanilla, this was written as 4 for some reason?
            rel_relocation.section_num_to_relocate_against = 0
        elif symbol_name in self.custom_symbols.get(file_path, {}):
            # Custom symbol located in the current REL.
            custom_symbol_offset = self.custom_symbols[file_path][symbol_name]

            module_num = rel.id

            if custom_symbol_offset >= free_space_start:
                # In our custom free space section.
                other_rel_section_index = 7
                relative_offset = custom_symbol_offset - free_space_start
            else:
                other_rel_section_index, relative_offset = rel.convert_rel_offset_to_section_index_and_relative_offset(
                    custom_symbol_offset)

            rel_relocation.section_num_to_relocate_against = other_rel_section_index
            rel_relocation.symbol_address = relative_offset
        elif ":" in symbol_name:
            # Vanilla symbol located in a REL.
            # We use a colon to signify rel_name:symbol_name.
            # (This is because we don't necessarily know for sure that the REL is calling a function inside of itself, it's possible to call a function in another REL.)
            rel_name, symbol_name = symbol_name.split(":", 1)
            other_rel = self.get_rel("files/rels/%s.rel" % rel_name)
            other_rel_symbols = self.get_symbol_map("files/maps/%s.map" %
                                                    rel_name)

            if symbol_name not in other_rel_symbols:
                raise Exception(
                    "Symbol \"%s\" could not be found in the symbol map for REL %s.rel"
                    % (symbol_name, rel_name))

            module_num = other_rel.id

            other_rel_section_index, relative_offset = other_rel.convert_rel_offset_to_section_index_and_relative_offset(
                other_rel_symbols[symbol_name])
            rel_relocation.section_num_to_relocate_against = other_rel_section_index
            rel_relocation.symbol_address = relative_offset
        elif branch_label_match:
            # Relative branch. The destination must be in the current REL.
            branch_dest_offset = int(branch_label_match.group(1), 16)

            module_num = rel.id

            other_rel_section_index, relative_offset = rel.convert_rel_offset_to_section_index_and_relative_offset(
                branch_dest_offset)
            rel_relocation.section_num_to_relocate_against = other_rel_section_index
            rel_relocation.symbol_address = relative_offset
        elif symbol_name in main_symbols:
            # Vanilla symbol located in main.dol.
            module_num = 0

            rel_relocation.symbol_address = main_symbols[symbol_name]

            # I don't think this value is used for dol relocations.
            # In vanilla, it was written as some kind of section index in the DOL (e.g. 4 for .text, 7 for .rodata), but this doesn't seem necessary.
            rel_relocation.section_num_to_relocate_against = 0
        else:
            raise Exception("Could not find symbol name: %s" % symbol_name)

        rel_relocation.relocation_offset = relocation_offset
        rel_relocation.curr_section_num = rel_section_index

        if module_num not in rel.relocation_entries_for_module:
            rel.relocation_entries_for_module[module_num] = []
        rel.relocation_entries_for_module[module_num].append(rel_relocation)
Exemplo n.º 3
0
def convert_elf_to_rel(in_elf_path,
                       out_rel_path,
                       rel_id,
                       actor_profile_name,
                       rels_arc_path=None):
    elf = ELF()
    elf.read_from_file(in_elf_path)

    rel = REL()

    rel.id = rel_id

    # First convert the sections we want to include in the REL from ELF sections to REL sections.
    section_name_to_rel_section = {}
    elf_section_index_to_rel_section = {}
    for elf_section_index, elf_section in enumerate(elf.sections):
        if elf_section.name in ALLOWED_SECTIONS or elf_section.type == ELFSectionType.SHT_NULL:
            # Sections we will add to the REL.
            rel_section = RELSection()
            rel_section.data = make_copy_data(elf_section.data)
            rel.sections.append(rel_section)

            if elf_section.flags & ELFSectionFlags.SHF_EXECINSTR.value:
                rel_section.is_executable = True

            section_name_to_rel_section[elf_section.name] = rel_section
            elf_section_index_to_rel_section[elf_section_index] = rel_section

            if elf_section.type == ELFSectionType.SHT_NULL:
                rel_section.is_uninitialized = True
                rel_section.is_bss = False
            else:
                rel_section.is_uninitialized = False
                rel_section.is_bss = False
            # TODO: bss support

    # Then generate the relocations.
    for elf_section in elf.sections:
        if elf_section.type == ELFSectionType.SHT_RELA:
            assert elf_section.name.startswith(".rela")
            relocated_section_name = elf_section.name[len(".rela"):]

            if relocated_section_name not in section_name_to_rel_section:
                # Ignored section
                continue

            rel_section = section_name_to_rel_section[relocated_section_name]
            section_index = rel.sections.index(rel_section)
            for elf_relocation in elf.relocations[elf_section.name]:
                rel_relocation = RELRelocation()

                elf_symbol = elf.symbols[".symtab"][
                    elf_relocation.symbol_index]
                rel_relocation.relocation_type = RELRelocationType(
                    elf_relocation.type.value)
                #print("%X" % elf_symbol.section_index)

                if elf_symbol.section_index == 0:
                    raise Exception(
                        "Unresolved external symbol in main.dol: %s" %
                        elf_symbol.name)

                if elf_symbol.section_index == ELFSymbolSpecialSection.SHN_ABS.value:
                    # Symbol is located in main.dol.
                    module_num = 0

                    # I don't think this value is used for dol relocations.
                    # In vanilla, this was written as 4 for some reason?
                    rel_relocation.section_num_to_relocate_against = 0
                elif elf_symbol.section_index >= 0xFF00:
                    raise Exception(
                        "Special section number not implemented: %04X" %
                        elf_symbol.section_index)
                else:
                    # Symbol is located in the current REL.
                    # TODO: support for relocating against other rels besides the current one
                    module_num = rel.id

                    section_name_to_relocate_against = elf.sections[
                        elf_symbol.section_index].name
                    if section_name_to_relocate_against not in section_name_to_rel_section:
                        raise Exception(
                            "Section name \"%s\" could not be found for symbol \"%s\""
                            % (section_name_to_relocate_against,
                               elf_symbol.name))
                    rel_section_to_relocate_against = section_name_to_rel_section[
                        section_name_to_relocate_against]
                    rel_section_index_to_relocate_against = rel.sections.index(
                        rel_section_to_relocate_against)
                    rel_relocation.section_num_to_relocate_against = rel_section_index_to_relocate_against

                rel_relocation.symbol_address = elf_symbol.address + elf_relocation.addend
                rel_relocation.relocation_offset = elf_relocation.relocation_offset
                rel_relocation.curr_section_num = section_index

                if module_num not in rel.relocation_entries_for_module:
                    rel.relocation_entries_for_module[module_num] = []
                rel.relocation_entries_for_module[module_num].append(
                    rel_relocation)

    symbol = elf.symbols_by_name[".symtab"]["_prolog"]
    rel_section = elf_section_index_to_rel_section[symbol.section_index]
    rel_section_index = rel.sections.index(rel_section)
    rel.prolog_section = rel_section_index
    rel.prolog_offset = symbol.address

    symbol = elf.symbols_by_name[".symtab"]["_epilog"]
    rel_section = elf_section_index_to_rel_section[symbol.section_index]
    rel_section_index = rel.sections.index(rel_section)
    rel.epilog_section = rel_section_index
    rel.epilog_offset = symbol.address

    symbol = elf.symbols_by_name[".symtab"]["_unresolved"]
    rel_section = elf_section_index_to_rel_section[symbol.section_index]
    rel_section_index = rel.sections.index(rel_section)
    rel.unresolved_section = rel_section_index
    rel.unresolved_offset = symbol.address

    rel.save_to_file(out_rel_path)

    # TODO: instead of replacing a REL, add a new REL!
    if rels_arc_path is not None and os.path.isfile(rels_arc_path):
        with open(rels_arc_path, "rb") as f:
            data = BytesIO(f.read())
        rels_arc = RARC()
        rels_arc.read(data)

        # Update the profile list to properly reference the profile in the custom REL.
        # Then insert the custom REL and the updated profile list into RELS.arc and save it for quick testing.
        profile_list_data = rels_arc.get_file_entry(
            "f_pc_profile_lst.rel").data
        profile_list = REL()
        profile_list.read(profile_list_data)

        if rel.id not in profile_list.relocation_entries_for_module:
            raise Exception(
                "REL ID %02X could not be found in the profile list." % rel.id)
            # TODO: add new REL instead of replacing!

        if actor_profile_name not in elf.symbols_by_name[".symtab"]:
            raise Exception(
                "Could not find the actor profile. The specified symbol name for it was \"%s\", but that symbol could not be found in the ELF."
                % actor_profile_name)
        profile_symbol = elf.symbols_by_name[".symtab"][actor_profile_name]
        profile_list.relocation_entries_for_module[
            rel.id][0].symbol_address = profile_symbol.address

        section_with_profile = section_name_to_rel_section[".rodata"]
        new_section_index_with_profile = rel.sections.index(
            section_with_profile)
        profile_list.relocation_entries_for_module[rel.id][
            0].section_num_to_relocate_against = new_section_index_with_profile

        profile_list.save_changes(preserve_section_data_offsets=True)

        # Insert the RELs.
        rels_arc.get_file_entry(
            "f_pc_profile_lst.rel").data = profile_list.data
        found_rel = False
        for file_entry in rels_arc.file_entries:
            if file_entry.is_dir:
                continue
            file_entry.decompress_data_if_necessary()
            if read_u32(file_entry.data, 0) == rel.id:
                file_entry.data = rel.data
                found_rel = True
                break
        if not found_rel:
            raise Exception("Failed to find REL to replace with ID 0x%03X" %
                            rel.id)

        rels_arc.save_changes()
        with open(rels_arc_path, "wb") as f:
            f.write(read_all_bytes(rels_arc.data))