def get_rel(self, rel_path): rel_path = rel_path.replace("\\", "/") if rel_path in self.rels_by_path: return self.rels_by_path[rel_path] else: if not rel_path.startswith("files/rels/"): raise Exception("Invalid REL path: %s" % rel_path) rel_name = os.path.basename(rel_path) rels_arc = self.get_arc("files/RELS.arc") rel_file_entry = rels_arc.get_file_entry(rel_name) if rel_file_entry: rel_file_entry.decompress_data_if_necessary() data = rel_file_entry.data else: data = self.gcm.read_file_data(rel_path) rel = REL() rel.read(data) self.rels_by_path[rel_path] = rel return rel
def add_relocations_and_symbols_to_rel(asm_path, rel_path, main_symbols, rel_map_data): rel = REL(rel_path) replacements = {} replacement_offsets = {} for module_num, relocation_entries in rel.relocation_entries_for_module.items(): for relocation_data_entry in relocation_entries: #print("Type: %X" % relocation_data_entry.relocation_type) curr_section = rel.sections[relocation_data_entry.curr_section_num] curr_section_offset = curr_section.offset replacement_location = curr_section_offset+relocation_data_entry.relocation_offset rounded_down_location = replacement_location & (~3) # round down to nearest 4 #print("location of replacement: %04X" % replacement_location) if module_num == 0: #print("symbol address: %X %s" % (relocation_data_entry.symbol_address, main_symbols.get(relocation_data_entry.symbol_address, ""))) symbol_name = main_symbols.get(relocation_data_entry.symbol_address, "") replacements[rounded_down_location] = "%X %s" % (relocation_data_entry.symbol_address, symbol_name) else: section_to_relocate_against = rel.sections[relocation_data_entry.section_num_to_relocate_against] section_offset_to_relocate_against = section_to_relocate_against.offset #print("address: %04X (%X + %X)" % (section_offset_to_relocate_against + relocation_data_entry.symbol_address, section_offset_to_relocate_against, relocation_data_entry.symbol_address)) #print("section #%X; section offset %X" % (relocation_data_entry.section_num_to_relocate_against, section_offset_to_relocate_against)) #replacements[rounded_down_location] = section_offset_to_relocate_against + relocation_data_entry.symbol_address replacements[rounded_down_location] = "%X (%X + %X)" % ( section_offset_to_relocate_against + relocation_data_entry.symbol_address, section_offset_to_relocate_against, relocation_data_entry.symbol_address, ) replacement_offsets[rounded_down_location] = (section_offset_to_relocate_against + relocation_data_entry.symbol_address) #print() rel_map_lines = rel_map_data.splitlines() found_memory_map = False next_section_index = 0 section_name_to_section_index = {} for line in rel_map_lines: if line.strip() == "Memory map:": found_memory_map = True if found_memory_map: section_match = re.search(r"^ +\.(text|ctors|dtors|rodata|data|bss) [0-9a-f]{8} ([0-9a-f]{8}) [0-9a-f]{8}$", line) if section_match: section_name = section_match.group(1) section_size = int(section_match.group(2), 16) if section_size > 0: section_name_to_section_index[section_name] = next_section_index next_section_index += 1 if not found_memory_map: raise Exception("Failed to find memory map") rel_symbol_names = {} all_valid_sections = [] for section in rel.sections: if section.length != 0: all_valid_sections.append(section) current_section_name = None current_section_index = None current_section = None for line in rel_map_lines: section_header_match = re.search(r"^\.(text|ctors|dtors|rodata|data|bss) section layout$", line) if section_header_match: current_section_name = section_header_match.group(1) if current_section_name in section_name_to_section_index: current_section_index = section_name_to_section_index[current_section_name] #print(current_section_name, current_section_index, all_valid_sections) current_section = all_valid_sections[current_section_index] else: current_section_index = None current_section = None symbol_entry_match = re.search(r"^ [0-9a-f]{8} [0-9a-f]{6} ([0-9a-f]{8})(?: \d)? (.+?) \t", line, re.IGNORECASE) if current_section is not None and symbol_entry_match: current_section_offset = current_section.offset if current_section_offset == 0: raise Exception("Found symbol in section with offset 0") symbol_offset = symbol_entry_match.group(1) symbol_offset = int(symbol_offset, 16) symbol_offset += current_section_offset symbol_name = symbol_entry_match.group(2) rel_symbol_names[symbol_offset] = symbol_name #print("%08X %s" % (symbol_offset, symbol_name)) #print(rel_symbol_names) with open(asm_path) as f: asm = f.read() out_str = "" for line in asm.splitlines(): match = re.search(r"^ +([0-9a-f]+):\s.+", line) if match: word_offset = int(match.group(1), 16) for offset in range(word_offset, word_offset+4): if offset in rel_symbol_names: symbol_name = rel_symbol_names[offset] out_str += "; SYMBOL: %X %s" % (offset, symbol_name) if rel.bss_section_index and offset >= rel.fix_size: out_str += " [BSS symbol, value initialized at runtime]" out_str += "\n" out_str += line if match: offset = int(match.group(1), 16) if offset in replacements: out_str += " ; " replacement = replacements[offset] out_str += replacement if offset in replacement_offsets: relocated_offset = replacement_offsets[offset] if relocated_offset in rel_symbol_names: symbol_name = rel_symbol_names[relocated_offset] out_str += " " + symbol_name if rel.bss_section_index and relocated_offset >= rel.fix_size: out_str += " [BSS]" else: branch_match = re.search(r"\s(bl|b|beq|bne|blt|bgt|ble|bge)\s+0x([0-9a-f]+)", line, re.IGNORECASE) if branch_match: branch_offset = int(branch_match.group(2), 16) if branch_offset in rel_symbol_names: symbol_name = rel_symbol_names[branch_offset] out_str += " ; " + symbol_name if rel.bss_section_index and branch_offset >= rel.fix_size: out_str += " [BSS]" out_str += "\n" if line.endswith("blr"): out_str += "\n" # Separate functions with open(asm_path, "w") as f: f.write(out_str)
def disassemble_all_code(self): if not os.path.isfile(r"C:\devkitPro\devkitPPC\bin\powerpc-eabi-objdump.exe"): raise Exception(r"Failed to disassemble code: Could not find devkitPPC. devkitPPC should be installed to: C:\devkitPro\devkitPPC") rels_arc = self.get_arc("files/RELS.arc") out_dir = os.path.join(self.randomized_output_folder, "disassemble") if not os.path.isdir(out_dir): os.mkdir(out_dir) demangled_map_path = os.path.join(ASM_PATH, "maps-out", "framework.map.out") if os.path.isfile(demangled_map_path): with open(demangled_map_path, "rb") as f: framework_map_contents = BytesIO(f.read()) else: framework_map_contents = self.gcm.read_file_data("files/maps/framework.map") framework_map_contents = read_all_bytes(framework_map_contents).decode("ascii") main_symbols = get_main_symbols(framework_map_contents) all_rel_paths = get_list_of_all_rels(self) files_to_disassemble = all_rel_paths.copy() files_to_disassemble.append("sys/main.dol") for file_path_in_gcm in files_to_disassemble: basename_with_ext = os.path.basename(file_path_in_gcm) rel_file_entry = rels_arc.get_file_entry(basename_with_ext) if rel_file_entry: rel_file_entry.decompress_data_if_necessary() data = rel_file_entry.data else: data = self.gcm.read_file_data(file_path_in_gcm) if try_read_str(data, 0, 4) == "Yaz0": data = Yaz0.decompress(data) basename, file_ext = os.path.splitext(basename_with_ext) bin_path = os.path.join(out_dir, basename_with_ext) with open(bin_path, "wb") as f: data.seek(0) f.write(data.read()) all_rels_by_path = OrderedDict() all_rel_symbols_by_path = OrderedDict() for file_path_in_gcm in all_rel_paths: basename_with_ext = os.path.basename(file_path_in_gcm) basename, file_ext = os.path.splitext(basename_with_ext) bin_path = os.path.join(out_dir, basename_with_ext) rel = REL() rel.read_from_file(bin_path) all_rels_by_path[file_path_in_gcm] = rel demangled_map_path = os.path.join(ASM_PATH, "maps-out", basename + ".map.out") if os.path.isfile(demangled_map_path): with open(demangled_map_path, "rb") as f: rel_map_data = BytesIO(f.read()) else: rel_map_data = self.gcm.read_file_data("files/maps/" + basename + ".map") rel_map_data.seek(0) rel_map_data = rel_map_data.read() # Copy the map file to the output directory rel_map_path = os.path.join(out_dir, basename + ".map") with open(rel_map_path, "wb") as f: f.write(rel_map_data) rel_map_data = rel_map_data.decode("ascii") all_rel_symbols_by_path[file_path_in_gcm] = get_rel_symbols(rel, rel_map_data) for file_path_in_gcm in files_to_disassemble: basename_with_ext = os.path.basename(file_path_in_gcm) print(basename_with_ext) basename, file_ext = os.path.splitext(basename_with_ext) bin_path = os.path.join(out_dir, basename_with_ext) asm_path = os.path.join(out_dir, basename + ".asm") disassemble_file(bin_path, asm_path) is_rel = (file_ext == ".rel") if is_rel: add_relocations_and_symbols_to_rel(asm_path, bin_path, file_path_in_gcm, main_symbols, all_rel_symbols_by_path, all_rels_by_path) else: add_symbols_to_main(asm_path, main_symbols)
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))
def add_relocations_and_symbols_to_rel(asm_path, rel_path, main_symbols, rel_map_data): rel = REL(rel_path) replacements = {} replacement_offsets = {} for module_num, relocation_entries in rel.relocation_entries_for_module.items( ): for relocation_data_entry in relocation_entries: #print("Type: %X" % relocation_data_entry.relocation_type) curr_section = rel.sections[relocation_data_entry.curr_section_num] curr_section_offset = curr_section.offset #print(rel_name) replacement_location = curr_section_offset + relocation_data_entry.relocation_offset rounded_down_location = replacement_location & ( ~3) # round down to nearest 4 #print("location of replacement: %04X" % replacement_location) if module_num == 0: #print("symbol address: %X %s" % (relocation_data_entry.symbol_address, main_symbols.get(relocation_data_entry.symbol_address, ""))) symbol_name = main_symbols.get( relocation_data_entry.symbol_address, "") replacements[rounded_down_location] = "%X %s" % ( relocation_data_entry.symbol_address, symbol_name) else: section_to_relocate_against = rel.sections[ relocation_data_entry.section_num_to_relocate_against] section_offset_to_relocate_against = section_to_relocate_against.offset #print("address: %04X" % (section_offset_to_relocate_against + relocation_data_entry.symbol_address)) #replacements[rounded_down_location] = section_offset_to_relocate_against + relocation_data_entry.symbol_address replacements[rounded_down_location] = "%X (%X + %X)" % ( section_offset_to_relocate_against + relocation_data_entry.symbol_address, section_offset_to_relocate_against, relocation_data_entry.symbol_address, ) replacement_offsets[rounded_down_location] = ( section_offset_to_relocate_against + relocation_data_entry.symbol_address) #print() rel_symbol_names = {} all_valid_sections = [] for section in rel.sections: if section.length != 0: all_valid_sections.append(section) current_section_index = 0 current_section_offset = None found_any_symbols_in_this_section = False for line in rel_map_data.splitlines(): section_header_match = re.search( r"^.(text|ctors|dtors|rodata|data|bss) section layout$", line) if section_header_match: section_type = section_header_match.group(1) #print("Found section header of type: %s" % section_type) found_any_symbols_in_this_section = False if current_section_index >= len(all_valid_sections): if section_type == "bss": # .bss is uninitialized break else: raise Exception("Not enough sections in rel %s" % rel_path) else: current_section_offset = all_valid_sections[ current_section_index].offset symbol_entry_match = re.search( r"^ [0-9a-f]{8} [0-9a-f]{6} ([0-9a-f]{8}) \d (\S+)", line, re.IGNORECASE) if current_section_offset is not None and symbol_entry_match: if not found_any_symbols_in_this_section: found_any_symbols_in_this_section = True current_section_index += 1 symbol_offset = symbol_entry_match.group(1) symbol_offset = int(symbol_offset, 16) symbol_offset += current_section_offset symbol_name = symbol_entry_match.group(2) rel_symbol_names[symbol_offset] = symbol_name #print("%08X %s" % (symbol_offset, symbol_name)) #print(rel_symbol_names) with open(asm_path) as f: asm = f.read() out_str = "" for line in asm.splitlines(): match = re.search(r"^ +([0-9a-f]+):\s.+", line) if match: word_offset = int(match.group(1), 16) for offset in range(word_offset, word_offset + 4): if offset in rel_symbol_names: symbol_name = rel_symbol_names[offset] out_str += "; SYMBOL: %X %s\n" % (offset, symbol_name) out_str += line if match: offset = int(match.group(1), 16) if offset in replacements: out_str += " ; " replacement = replacements[offset] out_str += replacement if offset in replacement_offsets: relocated_offset = replacement_offsets[offset] if relocated_offset in rel_symbol_names: symbol_name = rel_symbol_names[relocated_offset] out_str += " " + symbol_name else: branch_match = re.search( r"\s(bl|b|beq|bne|blt|bgt|ble|bge)\s+0x([0-9a-f]+)", line, re.IGNORECASE) if branch_match: branch_offset = int(branch_match.group(2), 16) if branch_offset in rel_symbol_names: symbol_name = rel_symbol_names[branch_offset] out_str += " ; " + symbol_name out_str += "\n" if line.endswith("blr"): out_str += "\n" # Separate functions with open(asm_path, "w") as f: f.write(out_str)