def _process_rela_section(self, elfpath: str, elf: ELFFile, relocs: RelocationSection, section: Section, symTab: SymbolTableSection): if relocs.header["sh_entsize"] != 12: raise InvalidDataException( "Invalid relocs format (sh_entsize != 12)") if not isinstance(symTab, SymbolTableSection): raise InvalidDataException( "Symbol table does not have type SHT_SYMTAB") for relocation in relocs.iter_relocations(): reloc = relocation["r_info"] & 0xFF symIndex = relocation["r_info"] >> 8 _symkey = self.__get_section_key(section) if symIndex == 0: raise InvalidDataException("Linking to undefined symbol") elif _symkey not in self._sectionBases: continue symName = self._symbolTableContents[self.__get_section_key( symTab)][symIndex] source = KWord( self._sectionBases[_symkey].value + relocation["r_offset"], KWord.Types.ABSOLUTE) dest = KWord( self._resolve_symbol(elfpath, symName).address.value + relocation["r_addend"], KWord.Types.ABSOLUTE) if not self._kamek_use_reloc(reloc, source, dest): self._fixups.append(Linker.RelocFixup(reloc, source, dest))
def get_any_pointer_arg(word: KWord, mapper: AddressMapper) -> KWord: if word.type == KWord.Types.VALUE: return KWord(mapper.remap(word.value), KWord.Types.ABSOLUTE) elif word.type in (KWord.Types.ABSOLUTE, KWord.Types.RELATIVE): return word else: raise NotImplementedError()
def _import_sections(self, prefix: str, alignEnd: int = 4, padding: int = 0): imported = False baseAddress = self._location.value for elf in self._modules.values(): for section in elf.iter_sections(): if not section.name.startswith(prefix): continue self._sectionBases[self.__get_section_key(section)] = KWord( self._location, KWord.Types.ABSOLUTE) sectionPadding = b"\x00" * (4 - (self._location.value % 4)) self._location += (section.data_size + 3) & -4 self._binaries.append(BytesIO(section.data() + sectionPadding)) imported = True if imported: self._externSymbols[f"_f_{prefix[1:]}"] = baseAddress self._externSymbols[ f"_e_{prefix[1:]}"] = self._location.value - padding self._location += padding if alignEnd > 0 and self._location.value % alignEnd != 0: padlen = alignEnd - (self._location.value % alignEnd) + padding self._location = (self._location + (alignEnd - 1)) & -alignEnd self._binaries.append(BytesIO(b"\x00" * padlen))
def __init__(self, address: KWord, size: int = 0, isWeak: bool = False): self.address = KWord(address, address.type) self.size = size self.isWeak = isWeak
def _resolve_symbol(self, elfpath: str, name: str) -> Linker.Symbol: _locals = self._localSymbols[elfpath] if name in _locals: return _locals[name] elif name in self._globalSymbols: return self._globalSymbols[name] elif name in self._externSymbols: return Linker.Symbol( KWord(self._externSymbols[name], KWord.Types.ABSOLUTE)) raise InvalidDataException(f"Undefined symbol \"{name}\"")
def __init__(self, f: str = None, baseAddr: int = 0x80000000): if f is not None: self.read_ppc_code(f) else: self.rawCode = BytesIO() self.baseAddr = baseAddr self.bssSize = KWord(0, KWord.Types.VALUE) self.commands = {} self.hooks = [] self.symbolSizes = {} self.mapper = None
def _process_hooks(self): for _elfkey in self._modules: for _symbolkey in self._localSymbols[_elfkey]: if _symbolkey.startswith("_kHook"): cmdAddr = self._localSymbols[_elfkey][_symbolkey].address self._memory.seek(cmdAddr.value - self.baseAddress.value) argCount = read_uint32(self._memory) _type = read_uint32(self._memory) args = [] for i in range(argCount): argAddr = cmdAddr + (8 + (i << 2)) if argAddr in self._kamekRelocs: args.append(self._kamekRelocs[argAddr]) else: self._memory.seek(argAddr.value - self.baseAddress.value) args.append( KWord(read_uint32(self._memory), KWord.Types.VALUE)) self._kamekHooks.append(HookData(_type, args))
def __init__(self, base: AddressMapper): super().__init__(base) self.baseAddress = KWord(0x80000000, KWord.Types.ABSOLUTE) self.outputStart, self.outputEnd = KWord(0, KWord.Types.ABSOLUTE), KWord( 0, KWord.Types.ABSOLUTE) self.bssStart, self.bssEnd = KWord(0, KWord.Types.ABSOLUTE), KWord( 0, KWord.Types.ABSOLUTE) self.kamekStart, self.kamekEnd = KWord(0, KWord.Types.ABSOLUTE), KWord( 0, KWord.Types.ABSOLUTE) # SECTIONS self._linked = False self._modules = {} self._binaries = [] self._sectionBases = {} self._location = 0 self._memory = BytesIO() # SYMBOLS self._globalSymbols = {} self._localSymbols = {} self._symbolTableContents = {} self._externSymbols = {} self._symbolSizes = {} # RELOCATIONS self._fixups = [] # KAMEK HOOKS self._kamekRelocs = {} self._kamekHooks = [] # OTHER self._shndx_sections = {}
def _collect_sections(self): self._location = KWord(self.baseAddress, KWord.Types.ABSOLUTE) self.outputStart.value = self._location.value self._import_sections(".init") self._import_sections(".fini") self._import_sections(".text") self._import_sections(".ctors", alignEnd=32, padding=4) self._import_sections(".dtors", alignEnd=32, padding=4) self._import_sections(".rodata", alignEnd=32) self._import_sections(".data", alignEnd=32) self.outputEnd.value = self._location.value self.bssStart.value = self.outputEnd.value self._import_sections(".bss", alignEnd=32) self.bssEnd.value = self._location.value self.kamekStart.value = self._location.value self._import_sections(".kamek") self.kamekEnd.value = self._location.value for binary in self._binaries: self._memory.write(binary.getvalue())
def get_absolute_arg(word: KWord, mapper: AddressMapper) -> KWord: if word.type != KWord.Types.ABSOLUTE: word.assert_value() return KWord(mapper.remap(word.value), KWord.Types.ABSOLUTE) return word
def get_value_arg(word: KWord) -> KWord: word.assert_value() return word
def __init__(self, reloctype: ELFFlags.Reloc, source: KWord, dest: KWord): self.type = reloctype self.source = KWord(source, source.type) self.dest = KWord(dest, dest.type)
def link_dynamic(self, symbolData: dict): self.baseAddress = KWord(0, KWord.Types.RELATIVE) self._do_link(symbolData)
def _parse_symbol_table(self, elfpath: str, elf: ELFFile, symTab: SymbolTableSection, strTab: StringTableSection, _locals: dict) -> list: if symTab.header["sh_entsize"] != 16: raise InvalidDataException( "Invalid symbol table format (sh_entsize != 16)") if not isinstance(strTab, StringTableSection): raise InvalidDataException( "String table does not have type SHT_STRTAB") _symbolNames = [] for symbol in symTab.iter_symbols(): name = symbol.name st_value = symbol["st_value"] st_size = symbol["st_size"] st_info = symbol["st_info"] st_shndx = symbol["st_shndx"] _symbolNames.append(name) if len(name) == 0 or st_shndx == "SHN_UNDEF": continue # What location is this referencing? if isinstance(st_shndx, int): # Reference refSection = elf.get_section(st_shndx) _refkey = self.__get_section_key(refSection) if _refkey not in self._sectionBases: continue # Skip past unwanted symbols addr = KWord(self._sectionBases[_refkey] + st_value, self._sectionBases[_refkey].type) elif st_shndx == "SHN_ABS": # Absolute symbol refSection = None addr = KWord(st_value, KWord.Types.ABSOLUTE) else: raise InvalidDataException( "Unknown section index found in symbol table") if st_info["bind"] == "STB_LOCAL": if name in _locals: raise InvalidDataException( f"Redefinition of local symbol {name}") _locals[name] = Linker.Symbol(addr, st_size) self._symbolSizes[addr] = st_size elif st_info["bind"] == "STB_GLOBAL": if name in self._globalSymbols and not self._globalSymbols[ name].isWeak: raise InvalidDataException( f"Redefinition of global symbol {name}") self._globalSymbols[name] = Linker.Symbol(addr, st_size) self._symbolSizes[addr] = st_size elif st_info["bind"] == "STB_WEAK": if name not in self._globalSymbols: self._globalSymbols[name] = Linker.Symbol(addr, st_size, isWeak=True) self._symbolSizes[addr] = st_size self._localSymbols[elfpath] = _locals return _symbolNames
class PatchExitCommand(Command): def __init__(self, source: KWord, target: KWord): super().__init__(Command.KCmdID.Branch, source) self.target = target self.endAddress = KWord(0, KWord.Types.ABSOLUTE) def __repr__(self) -> str: return f"repr={vars(self)}" def __str__(self) -> str: return f"Exit Patch Command; {self.__repr__()}" def write_arguments(self, io: BytesIO): self.endAddress.assert_not_ambiguous() self.target.assert_not_ambiguous() write_uint32(io, self.endAddress.value) write_uint32(io, self.target.value) def is_equal_reloc_types(self) -> bool: return self.address.type == self.target.type == self.endAddress.type def is_equal_reloc_absolute(self) -> bool: return self.is_equal_reloc_types() and self.target.is_absolute_addr() def is_equal_reloc_relative(self) -> bool: return self.is_equal_reloc_types() and self.target.is_relative_addr() def apply(self, f: "KamekBinary") -> bool: funcSize = f.get_symbol_size(self.address) funcEnd = self.address + (funcSize - 4) if funcSize < 4: raise InvalidOperationException("Queried function is too small") if f.read_u32(funcEnd) != 0x4E800020: raise InvalidOperationException("Function does not end in blr") instrLoc = self.address while instrLoc < funcEnd: insn = f.read_u32(instrLoc) if (insn & 0xFC00FFFF == 0x4C000020): raise InvalidOperationException( "Function contains a return partway through") instrLoc += 4 self.endAddress = funcEnd if self.is_equal_reloc_absolute() and f.contains(self.address): f.write_u32(self.endAddress.value, self._generate_instruction()) return True else: return False def pack_riivo(self) -> str: raise NotImplementedError() def pack_gecko_codes(self) -> list: raise NotImplementedError() def apply_to_dol(self, dol: DolFile, linker: "Linker"): funcSize = linker._symbolSizes[self.address] funcEnd = self.address + (funcSize - 4) if funcSize < 4: raise InvalidOperationException("Queried function is too small") dol.seek(funcEnd) if read_uint32(dol) != 0x4E800020: raise InvalidOperationException("Function does not end in blr") instrLoc = self.address dol.seek(instrLoc) while instrLoc < funcEnd: insn = read_uint32(dol) if (insn & 0xFC00FFFF == 0x4C000020): raise InvalidOperationException( "Function contains a return partway through") instrLoc += 4 self.endAddress = funcEnd try: dol.resolve_address(self.address) dol.seek(self.endAddress.value) write_uint32(dol, self._generate_instruction()) return True except UnmappedAddressError: return False def _generate_instruction(self) -> int: delta = self.target - self.address insn = 0x48000001 if self.id == Command.KCmdID.BranchLink else 0x48000000 return insn | (delta.value & 0x3FFFFFC)
def __init__(self, source: KWord, target: KWord): super().__init__(Command.KCmdID.Branch, source) self.target = target self.endAddress = KWord(0, KWord.Types.ABSOLUTE)
def link_static(self, symbolData: dict, baseAddr: int = None): if baseAddr: self.baseAddress = KWord(self.remap(baseAddr), KWord.Types.ABSOLUTE) self._do_link(symbolData)