#!/usr/bin/env python3 # # MIPS ELF library # import struct from mdebug import EcoffHDRR, EcoffFdr, EcoffPdr, EcoffLiner, EcoffSymr # ===================================================================================================== # Utility # ===================================================================================================== def align_as(value, align): if align == 0: return value while (value % align != 0): value += 1 return value # ===================================================================================================== # ELF Identity # ===================================================================================================== # Offsets into e_ident EI_MAG0 = 0x00 # Magic char 0 , 0x7F EI_MAG1 = 0x01 # Magic char 1 , 0x45 EI_MAG2 = 0x02 # Magic char 2 , 0x4C EI_MAG3 = 0x03 # Magic char 3 , 0x46 EI_CLASS = 0x04 # EI_DATA = 0x05 # EI_VERSION = 0x06 # EI_OSABI = 0x07 # EI_ABIVERSION = 0x08 # EI_PAD = 0x09 # EI_NIDENT = 0x10 # # Values for e_ident[EI_DATA] EI_DATA_LE = 1 # little endian EI_DATA_BE = 2 # big endian EI_DATA_V = { EI_DATA_LE : 'Little Endian', EI_DATA_BE : 'Big Endian' } # Values for e_ident[EI_CLASS] EI_CLASS_32 = 1 # 32-bit EI_CLASS_64 = 2 # 64-bit EI_CLASS_V = { EI_CLASS_32 : 'ELF32', EI_CLASS_64 : 'ELF64' } # Values for e_ident[EI_OSABI] EI_OSABI_V = { 0x00 : 'UNIX / System V', 0x01 : 'HP-UX', 0x02 : 'NetBSD', 0x03 : 'Linux', 0x04 : 'GNU Hurd', 0x06 : 'Solaris', 0x07 : 'AIX', 0x08 : 'IRIX', 0x09 : 'FreeBSD', 0x0A : 'Tru64', 0x0B : 'Novell Modesto', 0x0C : 'OpenBSD', 0x0D : 'OpenVMS', 0x0E : 'NonStop Kernel', 0x0F : 'AROS', 0x10 : 'Fenix OS', 0x11 : 'CloudABI', 0x12 : 'Stratus Technologies OpenVOS' } # ===================================================================================================== # ELF Types # ===================================================================================================== ET_NONE = 0x0000 # ET_REL = 0x0001 # relocatable ET_EXEC = 0x0002 # ET_DYN = 0x0003 # ET_CORE = 0x0004 # ET_LOOS = 0xFE00 # ET_HIOS = 0xFEFF # ET_LOPROC = 0xFF00 # ET_HIPROC = 0xFFFF # E_TYPE = { ET_NONE : 'ET_NONE', ET_REL : 'ET_REL', ET_EXEC : 'ET_EXEC', ET_DYN : 'ET_DYN', ET_CORE : 'ET_CORE', ET_LOOS : 'ET_LOOS', ET_HIOS : 'ET_HIOS', ET_LOPROC : 'ET_LOPROC', ET_HIPROC : 'ET_HIPROC' } # ===================================================================================================== # ELF Machines # ===================================================================================================== EM_UNSPECIFIED = 0x00 EM_ATNT_WE_32100 = 0x01 EM_SPARC = 0x02 EM_X86 = 0x03 EM_MOTOROLA_68000 = 0x04 EM_MOTOROLA_88000 = 0x05 EM_INTEL_MCU = 0x06 EM_INTEL_80860 = 0x07 EM_MIPS = 0x08 EM_IBM_SYSTEM_370 = 0x09 EM_MIPS_RS3000_LE = 0x0A EM_RESERVED_xB = 0x0B EM_RESERVED_xC = 0x0C EM_RESERVED_xD = 0x0D EM_HEWLETT_PACKARD = 0x0E EM_RESERVED_xF = 0x0F EM_INTEL_80960 = 0x13 EM_POWERPC = 0x14 EM_POWERPC_64 = 0x15 EM_5390 = 0x16 EM_ARM = 0x28 EM_SUPERH = 0x2A EM_IA64 = 0x32 EM_AMD64 = 0x3E EM_TMS320C6000 = 0x8C EM_ARM64 = 0xB7 EM_RISC_V = 0xF3 E_MACHINE = { EM_UNSPECIFIED : 'Unspecified', EM_ATNT_WE_32100 : 'AT&T WE 32100', EM_SPARC : 'SPARC', EM_X86 : 'x86', EM_MOTOROLA_68000 : 'Motorola 68000 (M68K)', EM_MOTOROLA_88000 : 'Motorola 88000 (M88K)', EM_INTEL_MCU : 'Intel MCU', EM_INTEL_80860 : 'Intel 80860', EM_MIPS : 'MIPS', EM_IBM_SYSTEM_370 : 'IBM_System/370', EM_MIPS_RS3000_LE : 'MIPS RS3000 Little-endian', EM_RESERVED_xB : 'Reserved', EM_RESERVED_xC : 'Reserved', EM_RESERVED_xD : 'Reserved', EM_HEWLETT_PACKARD : 'Hewlett-Packard PA-RISC', EM_RESERVED_xF : 'Reserved', EM_INTEL_80960 : 'Intel 80960', EM_POWERPC : 'PowerPC', EM_POWERPC_64 : 'PowerPC (64-bit)', EM_5390 : 'S390, including S390x', EM_ARM : 'ARM (up to ARMv7/Aarch32)', EM_IA64 : 'SuperH', EM_IA64 : 'IA-64', EM_AMD64 : 'amd64', EM_TMS320C6000 : 'TMS320C6000 Family', EM_ARM64 : 'ARM 64-bits (ARMv8/Aarch64)', EM_RISC_V : 'RISC-V' } # ===================================================================================================== # Program Types # ===================================================================================================== PT_NULL = 0x00000000 # Program header table entry unused PT_LOAD = 0x00000001 # Loadable segment PT_DYNAMIC = 0x00000002 # Dynamic linking information PT_INTERP = 0x00000003 # Interpreter information PT_NOTE = 0x00000004 # Auxiliary information PT_SHLIB = 0x00000005 # Reserved PT_PHDR = 0x00000006 # Segment containing the program header table itself PT_TLS = 0x00000007 # Thread-Local storage template PT_LOOS = 0x60000000 # Inclusive reserved range for processor-specific semantics PT_HIOS = 0x6FFFFFFF # ^ PT_LOPROC = 0x70000000 # Inclusive reserved range for processor-specific semantics PT_HIPROC = 0x7FFFFFFF # ^ P_TYPE = { PT_NULL : 'PT_NULL' , PT_LOAD : 'PT_LOAD' , PT_DYNAMIC : 'PT_DYNAMIC', PT_INTERP : 'PT_INTERP' , PT_NOTE : 'PT_NOTE' , PT_SHLIB : 'PT_SHLIB' , PT_PHDR : 'PT_PHDR' , PT_TLS : 'PT_TLS' , PT_LOOS : 'PT_LOOS' , PT_HIOS : 'PT_HIOS' , PT_LOPROC : 'PT_LOPROC' , PT_HIPROC : 'PT_HIPROC' } # ===================================================================================================== # Program Flags ( May be platform specific ! ) # ===================================================================================================== PF_E = 1 << 0 # Execute PF_W = 1 << 1 # Write PF_R = 1 << 2 # Read # ===================================================================================================== # Symbol Info # ===================================================================================================== # Symbol Types ST_NOTYPE = 0 ST_OBJECT = 1 ST_FUNC = 2 ST_SECTION = 3 ST_FILE = 4 SYM_TYPE = { ST_NOTYPE : 'NOTYPE', ST_OBJECT : 'OBJECT', ST_FUNC : 'FUNC', ST_SECTION : 'SECTION', ST_FILE : 'FILE' } # Symbol Bind SB_LOCAL = 0 SB_GLOBAL = 1 SB_WEAK = 2 SYM_BIND = { SB_LOCAL : 'LOCAL', SB_GLOBAL : 'GLOBAL', SB_WEAK : 'WEAK' } # Symbol Visibility SV_DEFAULT = 0 SYM_VIS = { SV_DEFAULT : 'DEFAULT' } # NDX SHN_UND = 0x0000 SHN_ABS = 0xFFF1 SHN_COMMON = 0xFFF2 SHN_XINDEX = 0xFFFF SHN_LORESERVE = 0xFF00 SYM_NDX = { SHN_UND : 'UND', SHN_ABS : 'ABS', SHN_COMMON : 'COMMON', SHN_XINDEX : 'XINDEX', SHN_LORESERVE : 'LORESERVE' } # ===================================================================================================== # Relocation Type # ===================================================================================================== # EM_MIPS R_MIPS_32 = 2 # Write the 32 bit address of the symbol R_MIPS_26 = 4 # Write the 26 bit address of the symbol divided by four (for relocating branch instructions). Fail if address won't fit R_MIPS_HI16 = 5 # Write the high 16 bits of the address of the symbol R_MIPS_LO16 = 6 # Write the low 16 bits of the address of the symbol # EM_POWERPC R_PPC_NONE = 0 # Do nothing. Skip this entry R_PPC_ADDR32 = 1 # Write the 32 bit address of the symbol R_PPC_ADDR24 = 2 # Write the 24 bit address of the symbol divided by four shifted up 2 bits to the 32 bit value (for relocating branch instructions). Fail if address won't fit R_PPC_ADDR16 = 3 # Write the 16 bit address of the symbol. Fail if address more than 16 bits R_PPC_ADDR16_LO = 4 # Write the low 16 bits of the address of the symbol R_PPC_ADDR16_HI = 5 # Write the high 16 bits of the address of the symbol R_PPC_ADDR16_HA = 6 # Write the high 16 bits of the address of the symbol plus 0x8000 R_PPC_ADDR14 = 7 # Write the 14 bits of the address of the symbol divided by four shifted up 2 bits to the 32 bit value (for relocating conditional branch instructions). Fail if address won't fit R_PPC_REL24 = 10 # Write the 24 bit address of the symbol minus the address of the relocation divided by four shifted up 2 bits to the 32 bit value (for relocating relative branch instructions). Fail if address won't fit R_PPC_REL14 = 11 # Write the 14 bit address of the symbol minus the address of the relocation divided by four shifted up 2 bits to the 32 bit value (for relocating conditional relative branch instructions). Fail if address won't fit R_PPC_EMB_SDA21 = 109 # Small Data Area # Gamecube/Wii custom relocations R_DOLPHIN_NOP = 201 # Do nothing. Skip this entry. Carry the address of the symbol to the next entry R_DOLPHIN_SECTION = 202 # Change which section relocations are being applied to. Set the offset into the section to 0 R_DOLPHIN_END = 203 # Stop parsing the relocation table R_DOLPHIN_MRKREF = 204 # Unknown RELOC_TYPE = { EM_MIPS : { R_MIPS_32 : 'R_MIPS_32', R_MIPS_26 : 'R_MIPS_26', R_MIPS_HI16 : 'R_MIPS_HI16', R_MIPS_LO16 : 'R_MIPS_LO16' }, EM_POWERPC : { R_PPC_NONE : 'R_PPC_NONE', R_PPC_ADDR32 : 'R_PPC_ADDR32', R_PPC_ADDR24 : 'R_PPC_ADDR24', R_PPC_ADDR16 : 'R_PPC_ADDR16', R_PPC_ADDR16_LO : 'R_PPC_ADDR16_LO', R_PPC_ADDR16_HI : 'R_PPC_ADDR16_HI', R_PPC_ADDR16_HA : 'R_PPC_ADDR16_HA', R_PPC_ADDR14 : 'R_PPC_ADDR14', 8 : 'R_PPC_ADDR14', 9 : 'R_PPC_ADDR14', R_PPC_REL24 : 'R_PPC_REL24', R_PPC_REL14 : 'R_PPC_REL14', 12 : 'R_PPC_REL14', 13 : 'R_PPC_REL14', R_PPC_EMB_SDA21 : 'R_PPC_EMB_SDA21', R_DOLPHIN_NOP : 'R_DOLPHIN_NOP', R_DOLPHIN_SECTION : 'R_DOLPHIN_SECTION', R_DOLPHIN_END : 'R_DOLPHIN_END', R_DOLPHIN_MRKREF : 'R_DOLPHIN_MRKREF' } } # ===================================================================================================== # Section Types # ===================================================================================================== SHT_NULL = 0x00000000 # Section header table entry unused SHT_PROGBITS = 0x00000001 # Program data SHT_SYMTAB = 0x00000002 # Symbol table SHT_STRTAB = 0x00000003 # String table SHT_RELA = 0x00000004 # Relocation entries with addends SHT_HASH = 0x00000005 # Symbol hash table SHT_DYNAMIC = 0x00000006 # Dynamic linking information SHT_NOTE = 0x00000007 # Notes SHT_NOBITS = 0x00000008 # Program space with no data (bss) SHT_REL = 0x00000009 # Relocation entries, no addends SHT_SHLIB = 0x0000000A # Reserved SHT_DYNSYM = 0x0000000B # Dynamic linker symbol table SHT_INIT_ARRAY = 0x0000000E # Array of constructors SHT_FINI_ARRAY = 0x0000000F # Array of destructors SHT_PREINIT_ARRAY = 0x00000010 # Array of pre-constructors SHT_GROUP = 0x00000011 # Section group SHT_SYMTAB_SHNDX = 0x00000012 # Extended section indices SHT_NUM = 0x00000013 # Number of defined types. SHT_LOOS = 0x60000000 # Start OS-specific. # MIPS specific SHT_MIPS_DEBUG = 0x70000005 # .mdebug SHT_MIPS_REGINFO = 0x70000006 # .reginfo SHT_MIPS_OPTIONS = 0x7000000D # .options SH_TYPE = { SHT_NULL : 'SHT_NULL', SHT_PROGBITS : 'SHT_PROGBITS', SHT_SYMTAB : 'SHT_SYMTAB', SHT_STRTAB : 'SHT_STRTAB', SHT_RELA : 'SHT_RELA', SHT_HASH : 'SHT_HASH', SHT_DYNAMIC : 'SHT_DYNAMIC', SHT_NOTE : 'SHT_NOTE', SHT_NOBITS : 'SHT_NOBITS', SHT_REL : 'SHT_REL', SHT_SHLIB : 'SHT_SHLIB', SHT_DYNSYM : 'SHT_DYNSYM', SHT_INIT_ARRAY : 'SHT_INIT_ARRAY', SHT_FINI_ARRAY : 'SHT_FINI_ARRAY', SHT_PREINIT_ARRAY : 'SHT_PREINIT_ARRAY', SHT_GROUP : 'SHT_GROUP', SHT_SYMTAB_SHNDX : 'SHT_SYMTAB_SHNDX', SHT_NUM : 'SHT_NUM', SHT_LOOS : 'SHT_LOOS', SHT_MIPS_DEBUG : 'SHT_MIPS_DEBUG', SHT_MIPS_REGINFO : 'SHT_MIPS_REGINFO', SHT_MIPS_OPTIONS : 'SHT_MIPS_OPTIONS', } # ===================================================================================================== # Section Flags # ===================================================================================================== SHF_WRITE = 1 << 0 # Writable SHF_ALLOC = 1 << 1 # Occupies memory during execution SHF_EXECINSTR = 1 << 2 # Executable SHF_MERGE = 1 << 4 # Might be merged SHF_STRINGS = 1 << 5 # Contains null-terminated strings SHF_INFO_LINK = 1 << 6 # 'sh_info' contains SHT index SHF_LINK_ORDER = 1 << 7 # Preserve order after combining SHF_OS_NONCONFORMING = 1 << 8 # Non-standard OS specific handling required SHF_GROUP = 1 << 9 # Section is member of a group SHF_TLS = 1 << 10 # Section hold thread-local data SHF_MASKOS = 0x0ff00000 # OS-specific SHF_MASKPROC = 0xf0000000 # Processor-specific SHF_ORDERED = 0x04000000 # Special ordering requirement (Solaris) SHF_EXCLUDE = 0x08000000 # Section is excluded unless referenced or allocated (Solaris) SH_FLAG = { SHF_WRITE : 'SHF_WRITE', SHF_ALLOC : 'SHF_ALLOC', SHF_EXECINSTR : 'SHF_EXECINSTR', SHF_MERGE : 'SHF_MERGE', SHF_STRINGS : 'SHF_STRINGS', SHF_INFO_LINK : 'SHF_INFO_LINK', SHF_LINK_ORDER : 'SHF_LINK_ORDER', SHF_OS_NONCONFORMING : 'SHF_OS_NONCONFORMING', SHF_GROUP : 'SHF_GROUP', SHF_TLS : 'SHF_TLS', SHF_MASKOS : 'SHF_MASKOS', SHF_MASKPROC : 'SHF_MASKPROC', SHF_ORDERED : 'SHF_ORDERED', SHF_EXCLUDE : 'SHF_EXCLUDE' } # ===================================================================================================== # ELF Header # ===================================================================================================== ELF32_BIG_STRUCT = '>HHIIIIIHHHHHH' ELF32_LITTLE_STRUCT = 'IIIBBH', data) assert self.st_shndx != SHN_XINDEX, "too many sections (SHN_XINDEX not supported)" self.bind = st_info >> 4 self.type = st_info & 0xF self.visibility = self.st_other & 3 self.parent_section = elf_file.sections[self.st_shndx] if self.st_shndx < SHN_LORESERVE else None self.name = name if self.type == ST_SECTION: self.name = elf_file.shstrtab.lookup_str(self.parent_section.sh_name) if self.name == None: self.name = elf_file.symtab.strtab.lookup_str(self.st_name) @staticmethod def from_parts(st_name, st_value, st_size, st_info, st_other, st_shndx, strtab, name): header = struct.pack('>IIIBBH', st_name, st_value, st_size, st_info, st_other, st_shndx) return Symbol(header, strtab, name) def to_bin(self): st_info = (self.bind << 4) | self.type return struct.pack('>IIIBBH', self.st_name, self.st_value, self.st_size, st_info, self.st_other, self.st_shndx) def section_offset(self): return self.st_value - self.parent_section.sh_addr def padded_size_n(self, n): return align_as(self.st_size, n) def padded_size(self): return self.padded_size_n(self.parent_section.sh_addralign) def __str__(self): # Num: Value Size Type Bind Vis Ndx Name ndx = ' ' + (SYM_NDX[self.st_shndx] if self.st_shndx in SYM_NDX.keys() else self.parent_section.name) out = f"{self.st_value:08X} {self.st_size:06X} {SYM_TYPE[self.type]:7} {SYM_BIND[self.bind]:6} {SYM_VIS[self.visibility]:7} {ndx:12} {self.name}" return out # ===================================================================================================== # Relocation # ===================================================================================================== class Relocation: """ typedef struct { Elf32_Word r_offset; struct { Elf32_Word sym_index : 24; Elf32_Word rel_type : 8; } r_info; Elf32_Word r_addend; } Elf32_Reloc; """ def __init__(self, data, elf_file, target_section, sh_type): self.sh_type = sh_type if sh_type == SHT_REL: self.r_offset, self.r_info = struct.unpack('>II', data) self.r_addend = 0 else: self.r_offset, self.r_info, self.r_addend = struct.unpack('>III', data) self.sym_index = self.r_info >> 8 self.rel_type = self.r_info & 0xff self.relocated_symbol = elf_file.symtab.symbol_entries[self.sym_index] self.elf_machine = elf_file.elf_header.e_machine self.target_section = target_section # TODO obtain the addend if the target section is executable def to_bin(self): self.r_info = (self.sym_index << 8) | self.rel_type if self.sh_type == SHT_REL: return struct.pack('>II', self.r_offset, self.r_info) else: return struct.pack('>III', self.r_offset, self.r_info, self.r_addend) def relocated_symbol_in_section(self, symtab, section): return symtab.lookup_symbol_in_section(symtab.symbol_entries[self.sym_index].st_value, section) def __str__(self): relocated_symbol = self.relocated_symbol # Offset Info Type Sym.Value Sym. Name + Addend # 80135560 00091201 R_MY_RELOC_TYPE 800f39e0 ...data.0 + 0 out = f"{self.r_offset:08X} {self.r_info:08X} {RELOC_TYPE[self.elf_machine][self.rel_type]:17} {relocated_symbol.st_value:08X} {relocated_symbol.name} + 0x{self.r_addend:X}" return out # ===================================================================================================== # Section # ===================================================================================================== class Section: """ Describes a section typedef struct { Elf32_Word sh_name; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info; Elf32_Word sh_addralign; Elf32_Word sh_entsize; } Elf32_Shdr; x00 x00 4 4 sh_name Offset to string in the .shstrtab section representing this section's name x04 x04 4 4 sh_type See SH_TYPE x08 x08 4 8 sh_flags See SH_FLAG x0C x10 4 8 sh_addr Virtual address of the section in memory (for loaded sections) x10 x18 4 8 sh_offset Offset of the section in the file image x14 x20 4 8 sh_size Size in bytes of the section in the file image x18 x28 4 4 sh_link Contains the section index of an associated section. This field is used for several purposes depending on section type x1C x2C 4 4 sh_info Contains extra information about the section. This field is used for several purposes, depending on section type x20 x30 4 8 sh_addralign Contains the required alignment of the section. This field must be a power of two x24 x38 4 8 sh_entsize Contains the size, in bytes, of each entry, for sections that contain fixed-size entries. Zero otherwise x28 x40 END """ def __init__(self, header, elf_file, index): self.late_init_done = False self.sh_name, self.sh_type, \ self.sh_flags, self.sh_addr, \ self.sh_offset, self.sh_size, \ self.sh_link, self.sh_info, \ self.sh_addralign, self.sh_entsize = struct.unpack('>IIIIIIIIII', header) assert not self.sh_flags & SHF_LINK_ORDER if self.sh_entsize != 0: assert self.sh_size % self.sh_entsize == 0 if self.sh_type in [SHT_NULL, SHT_NOBITS]: self.data = None else: self.data = elf_file.data[self.sh_offset:][:self.sh_size] self.index = index self.relocated_by = [] self.elf_file = elf_file def late_init(self): self.late_init_done = True @staticmethod def from_parts(sh_name, sh_type, sh_flags, sh_link, sh_info, sh_addralign, sh_entsize, sh_addr, sh_offset, data, index): header = struct.pack('>IIIIIIIIII', sh_name, sh_type, sh_flags, sh_addr, sh_offset, \ len(data), sh_link, sh_info, sh_addralign, sh_entsize) return Section(header, data, index) def to_bin(self): if self.sh_type not in [SHT_NULL, SHT_NOBITS]: self.sh_size = len(self.data) header = struct.pack('>IIIIIIIIII', self.sh_name, self.sh_type, self.sh_flags, self.sh_addr, self.sh_offset, self.sh_size, self.sh_link, self.sh_info, self.sh_addralign, self.sh_entsize) return header, self.data def get_rel(self, addr): relocs = [] for reloc_section in self.relocated_by: reloc = reloc_section.find_reloc(addr) if reloc is not None: relocs.append(reloc) return relocs def get_sym(self, addr): symtab = self.elf_file.symtab symbols = [] for symbol in symtab.symbol_entries: if symbol.st_value == addr and symbol.st_shndx == self.index and symbol.type != ST_SECTION: symbols.append(symbol) return symbols def is_rel(self): return self.sh_type == SHT_REL or self.sh_type == SHT_RELA def is_allocable(self): return (self.sh_flags & SHF_ALLOC) == SHF_ALLOC def is_executable(self): return (self.sh_flags & SHF_EXECINSTR) == SHF_EXECINSTR def is_writable(self): return (self.sh_flags & SHF_WRITE) == SHF_WRITE def padded_size(self): return align_as(self.sh_size, self.sh_addralign) def padded_size_4(self): return align_as(self.sh_size, 4) def flags_str(self): flags = "" flags += "W" if self.is_writable() else " " flags += "A" if self.is_allocable() else " " flags += "X" if self.is_executable() else " " return flags def __str__(self): s_type = SH_TYPE[self.sh_type].replace('SHT_','') if self.sh_type in SH_TYPE.keys() else str(self.sh_type) out = f"{self.name:12}{s_type:12} " out += f"{self.sh_addr:08X} " out += f"{self.sh_offset:06X} " out += f"{self.sh_size:06X} " out += f"{self.sh_entsize:2X} " out += f"{self.flags_str():3} " out += f"{self.sh_link:2} " out += f"{self.sh_info:3} " out += f"{self.sh_addralign:5X} " return out class SymtabSection(Section): """ Symbol Table Section """ def __init__(self, header, elf_file, index): super().__init__(header, elf_file, index) assert self.sh_entsize == 16 def late_init(self): if self.late_init_done: return super().late_init() self.strtab = self.elf_file.sections[self.sh_link] self.symbol_entries = [] for i in range(0, self.sh_size, self.sh_entsize): self.symbol_entries.append(Symbol(self.data[i:i+self.sh_entsize], self.elf_file)) def to_bin(self): header, _ = super().to_bin() data = bytearray() for sym in self.symbol_entries: data.extend(sym.to_bin()) return header, data def find_symbol(self, name): for s in self.symbol_entries: if s.name == name: return (s.st_shndx, s.st_value) return None def lookup_symbol(self, vaddr): found = [] for s in self.symbol_entries: if s.st_value == vaddr and s.type != ST_SECTION: found.append(s) if len(found) != 0: found.sort(reverse=True, key=(lambda s : s.type)) return found[0] return None def lookup_symbol_for_section(self, vaddr, shndx): found = [] for s in self.symbol_entries: if s.st_value == vaddr and s.type != ST_SECTION and s.st_shndx == shndx: found.append(s) if len(found) != 0: found.sort(reverse=True, key=(lambda s : s.type)) return found[0] return None def lookup_symbol_in_section(self, vaddr, section): found = [] for s in self.symbol_entries: if s.st_value == vaddr and s.type != ST_SECTION and s.st_shndx == section.index: found.append(s) if len(found) != 0: found.sort(reverse=True, key=(lambda s : s.type)) return found[0] return None def find_symbol_in_section(self, name, section): pos = self.find_symbol(name) assert pos is not None assert pos[0] == section.index return pos[1] def local_symbols(self): return self.symbol_entries[:self.sh_info] def global_symbols(self): return self.symbol_entries[self.sh_info:] class StrtabSection(Section): """ String Table Section """ def __init__(self, header, data, index): super().__init__(header, data, index) def lookup_str(self, index): to = self.data.find(b'\0', index) assert to != -1 return self.data[index:to].decode('latin1') class RelocationSection(Section): """ Relocation Section """ def __init__(self, header, data, index): super().__init__(header, data, index) def late_init(self): if self.late_init_done: return super().late_init() self.rel_target = self.elf_file.sections[self.sh_info] self.rel_target.relocated_by.append(self) self.relocations = [] for i in range(0, self.sh_size, self.sh_entsize): self.relocations.append(Relocation(self.data[i:][:self.sh_entsize], self.elf_file, self.rel_target, self.sh_type)) def to_bin(self): header, _ = super().to_bin() data = bytearray() for rel in self.relocations: data.extend(rel.to_bin()) return header, data def lookup_reloc(self, vaddr): for r in self.relocations: if r.r_offset - r.r_offset % 4 == vaddr: return r return None def find_reloc(self, vaddr): for r in self.relocations: if r.r_offset == vaddr: return r return None def lookup_jtbl_reloc(self, vaddr, symtab): for r in self.relocations: r_sym = r.relocated_symbol(symtab) if r_sym.st_value + r.r_addend == vaddr: return r return None class MdebugSection(Section): """ MIPS Debugging Section """ def __init__(self, header, elf_file, index): super().__init__(header, elf_file, index) self.parent = self.elf_file self.hdrr = EcoffHDRR(self.data) self.fdrs = [] for i in range(self.hdrr.ifdMax): fdr = EcoffFdr.from_binary(self, i) self.fdrs.append(fdr) def late_init(self): if self.late_init_done: return super().late_init() for fdr in self.fdrs: fdr.late_init() def fdr_forname(self, filename): for fdr in self.fdrs: # remove path and file ext normalized_name = ".".join(fdr.name.split("/")[-1].split(".")[:-1]) if normalized_name == filename: return fdr return None def fdr_foraddr(self, addr, extensions=('.c')): for fdr in self.fdrs: if fdr.adr == addr and any((fdr.name.endswith(ext) for ext in extensions)): return fdr return None def read_string(self, index): to = self.elf_file.data.find(b'\0', self.hdrr.cbSsOffset + index) assert to != -1 return self.elf_file.data[self.hdrr.cbSsOffset + index:to].decode("ASCII") def read_ext_string(self, index): to = self.elf_file.data.find(b'\0', self.hdrr.cbSsExtOffset + index) assert to != -1 return self.elf_file.data[self.hdrr.cbSsExtOffset + index:to].decode("ASCII") class ReginfoSection(Section): """ MIPS Register Information Section """ def __init__(self, header, elf_file, index): super().__init__(header, elf_file, index) # ===================================================================================================== # Elf File # ===================================================================================================== class ElfFile: def __init__(self, data): def init_section(i): offset = self.elf_header.e_shoff + i * self.elf_header.e_shentsize section_type = struct.unpack(">I", data[offset + 4:][:4])[0] header_data = data[offset:][:self.elf_header.e_shentsize] if section_type == SHT_REL or section_type == SHT_RELA: return RelocationSection(header_data, self, i) elif section_type == SHT_SYMTAB: return SymtabSection(header_data, self, i) elif section_type == SHT_STRTAB: return StrtabSection(header_data, self, i) elif section_type == SHT_MIPS_DEBUG: return MdebugSection(header_data, self, i) elif section_type == SHT_MIPS_REGINFO: return ReginfoSection(header_data, self, i) else: return Section(header_data, self, i) self.data = data self.elf_header = ElfHeader(data[0:52]) num_progheaders = self.elf_header.e_phnum num_sections = self.elf_header.e_shnum # Init program headers self.progheaders = [] for i in range(num_progheaders): offset = self.elf_header.e_phoff + i * self.elf_header.e_phentsize self.progheaders.append(ProgramHeader(data[offset:][:self.elf_header.e_phentsize], self.elf_header.e_ident[EI_CLASS])) # Init sections self.sections = [] for i in range(num_sections): self.sections.append(init_section(i)) # Init shstrtab and name sections self.shstrtab = self.sections[self.elf_header.e_shstrndx] assert isinstance(self.shstrtab, StrtabSection) for s in self.sections: s.name = self.shstrtab.lookup_str(s.sh_name) # Init symtab symtab = None for s in self.sections: if s.sh_type == SHT_SYMTAB: assert not symtab , "Found more than one symtab section?" symtab = s self.symtab = symtab if self.symtab is not None: self.symtab.late_init() # Late Init sections for s in self.sections: s.late_init() def find_section_by_name(self, name): for s in self.sections: if s.name == name: return s return None def find_section_by_type(self, type): for s in self.sections: if s.sh_type == type: return s return None ### Tests if __name__ == "__main__": import sys elf_file = None with open(sys.argv[1], "rb") as elf: elf_file = ElfFile(bytearray(elf.read())) # Header Info Test print(elf_file.elf_header) # Program Headers Info Test print("") print("Program Headers:") print(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align") for phdr in elf_file.progheaders: print(phdr) # Section Headers Info Test print("") print("Section Headers:") print(" [Nr] Name Type Addr Off Size ES Flg Lk Inf Al") for i,s in enumerate(elf_file.sections,0): print(f" [{i:2}] {s}") # Symbols Info Test if elf_file.symtab is not None: print("") print(f"Symbol table '{elf_file.symtab.name}' contains {len(elf_file.symtab.symbol_entries)} entries") print(" Num: Value Size Type Bind Vis Ndx Name") for i,sym in enumerate(elf_file.symtab.symbol_entries,0): print(f"{i:6}: {sym}") # Relocations Info Test print("") for s in elf_file.sections: if s.is_rel(): print(f"\nRelocation section '{s.name}' at offset 0x{s.sh_offset:06X} contains {len(s.relocations)} entries:") print(" Offset Info Type Sym.Value Sym.Name + Addend") for reloc in s.relocations: print(f"{reloc}") # mdebug Info Test print("") mdebug_section = elf_file.find_section_by_type(SHT_MIPS_DEBUG) if mdebug_section is not None: """ for fdr in mdebug_section.fdrs: print(fdr) for symr in fdr.symrs: print(symr) for pdr in fdr.pdrs: print(pdr) for symr in pdr.symrs: print(symr) for liner in pdr.lines: print(liner) """ for fdr in mdebug_section.fdrs: print(fdr.c_str())