Пример #1
0
 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
Пример #2
0
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)
Пример #3
0
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)
Пример #4
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))
Пример #5
0
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)