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)
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)
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))