tp/tools/libelf/object.py

213 lines
8.3 KiB
Python

import struct
import os
import io
import sys
from dataclasses import dataclass, field
from typing import IO, Dict, Optional, List, Set
from functools import reduce
from . import elf
from .section import *
from .relocation import *
from .symbol import *
@dataclass
class Object:
sections: Dict[str, Section] = field(default_factory=dict)
sym_sections: Dict[int, SymbolTableSection] = field(default_factory=dict)
str_sections: Dict[int, StringTableSection] = field(default_factory=dict)
rel_sections: Dict[int, Section] = field(default_factory=dict)
rela_sections: Dict[int, RelocationASection] = field(default_factory=dict)
symbols: List[Symbol] = field(default_factory=list)
symbol_map: Dict[str, Symbol] = field(default_factory=dict)
relocations: List[Relocation] = field(default_factory=list)
name: str = None
path: Path = None
def load_object_from_file(path, name, file) -> Object:
obj = Object()
obj.path = path
obj.name = name
header = elf.Header()
header.read(file)
if header.e_ident[elf.EI_MAG] != 2135247942:
fail("invalid elf file: 0x%08X == 0x7F454C46 '%s'" % (header.e_ident[elf.EI_MAG], name))
if header.e_ident[elf.EI_CLASS] != 1:
fail("only support elf 32-bit")
if header.e_ident[elf.EI_DATA] != 2:
fail("only support big-endianess")
if header.e_ident[elf.EI_VERSION] != 1:
fail("invalid elf version")
if header.e_type != 1 and header.e_type != 2:
fail("invalid object file type")
if header.e_machine != 20:
fail("invalid target")
if header.e_version != 1:
fail("invalid elf version")
if header.e_phnum > 0 and header.e_phentsize != 0x20:
fail("invalid program header size")
if header.e_shnum > 0 and header.e_shentsize != 0x28:
fail("invalid section header size")
program_headers = []
section_headers = []
# Read program headers
file.seek(header.e_phoff, os.SEEK_SET)
for _ in range(header.e_phnum):
program_header = elf.ProgramHeader()
program_header.read(file)
# Read sections headers
file.seek(header.e_shoff, os.SEEK_SET)
for i in range(header.e_shnum):
section_header = elf.SectionHeader()
section_header.read(file)
if not (section_header.sh_type == elf.SHT_NULL
or section_header.sh_type == elf.SHT_PROGBITS
or section_header.sh_type == elf.SHT_SYMTAB
or section_header.sh_type == elf.SHT_STRTAB
or section_header.sh_type == elf.SHT_RELA
or section_header.sh_type == elf.SHT_NOBITS
or section_header.sh_type == elf.SHT_MW_CATS):
fail("unsupport section type: 0x%08X = %s (%s)" % (
section_header.sh_type,
elf.SH_TYPES[section_header.sh_type] if section_header.sh_type in elf.SH_TYPES else "????",
path))
section_headers.append(section_header)
# Create sections
idx_sections = dict()
for i, sheader in enumerate(section_headers):
section = None
if sheader.sh_type == elf.SHT_PROGBITS:
section = ProgBitsSection(sheader, file)
elif sheader.sh_type == elf.SHT_NOBITS:
section = NoBitsSection(sheader)
elif sheader.sh_type == elf.SHT_STRTAB:
section = StringTableSection(sheader, file)
obj.str_sections[i] = section
elif sheader.sh_type == elf.SHT_SYMTAB:
section = SymbolTableSection(sheader, file)
obj.sym_sections[i] = section
elif sheader.sh_type == elf.SHT_RELA:
section = RelocationASection(sheader, file)
obj.rela_sections[i] = section
elif sheader.sh_type == elf.SHT_NULL:
section = Section(sheader)
elif sheader.sh_type == elf.SHT_MW_CATS:
section = Section(sheader)
assert section
idx_sections[i] = section
# Find .shstrtab containing section names
if not header.e_shstrndx in idx_sections:
fail("header.e_shstrndx out-of-bounds")
shstrtab_section = idx_sections[header.e_shstrndx]
if not isinstance(shstrtab_section, StringTableSection):
fail("header.e_shstrndx is not a string table")
# Get section names
for i, section in idx_sections.items():
if section.header.sh_type == elf.SHT_NULL:
continue
section.name = shstrtab_section.readString(section.header.sh_name)
if section.name:
obj.sections[section.name] = section
# Find all symbols
for symtab in obj.sym_sections.values():
if not symtab.header.sh_link in obj.str_sections:
fail("symbol table '%s' is not referenceing a valid string table section (sh_link: %i)" % (
symtab.name, symtab.header.sh_link))
symtab.object_offset = len(obj.symbols)
strtab = obj.str_sections[symtab.header.sh_link]
for i,sym in enumerate(symtab.symbols):
if i == 0:
symbol = NullSymbol(sym)
symbol.object = obj
obj.symbols.append(symbol)
continue
name = None
if sym.st_name:
name = strtab.readString(sym.st_name)
symbol = None
if sym.st_shndx == elf.SHN_UNDEF:
symbol = UndefSymbol(sym, name)
elif sym.st_shndx == elf.SHN_ABS:
symbol = AbsoluteSymbol(sym, name, sym.st_value)
else:
if not sym.st_shndx in idx_sections:
fail("symbol '%s' has invalid section-id (st_shndx: %i)" % (name, sym.st_shndx))
s = idx_sections[sym.st_shndx]
symbol = OffsetSymbol(sym, name, idx_sections[sym.st_shndx], sym.st_value)
assert symbol
symbol.object = obj
if symbol.name:
if symbol.name in obj.symbol_map:
fail("multiple definition of '%s'" % symbol.name)
obj.symbol_map[symbol] = symbol
obj.symbols.append(symbol)
# Find all relocations
for rela_section in obj.rela_sections.values():
if not rela_section.header.sh_link in obj.sym_sections:
fail("relocation section '%s' is not referenceing a valid symbol table section (sh_link: %i)" % (
symtab.name, rela_section.header.sh_link))
if not rela_section.header.sh_info in idx_sections:
fail("relocation section '%s' is not referenceing a valid section (sh_info: %i)" % (
symtab.name, rela_section.header.sh_info))
symtab = obj.sym_sections[rela_section.header.sh_link]
modify = idx_sections[rela_section.header.sh_info]
for rela in rela_section.relocations:
type = elf.R_TYPE(rela.r_info)
sym_id = elf.R_SYM(rela.r_info)
if not type in RELOCATION_NAMES:
fail("unsupported relocation type: 0x%02X (in '%s')" % (type, path))
if sym_id < 0 or sym_id >= len(symtab.symbols):
fail("invalid symbol index: %i (%i symbols)" % (sym_id, len(symtab.symbols)))
symbol = obj.symbols[symtab.object_offset + sym_id]
relocation = None
if type == 1:
relocation = R_PPC_ADDR32(symbol, modify, rela.r_offset, rela.r_addend)
elif type == 3:
relocation = R_PPC_ADDR16(symbol, modify, rela.r_offset, rela.r_addend)
elif type == 4:
relocation = R_PPC_ADDR16_LO(symbol, modify, rela.r_offset, rela.r_addend)
elif type == 5:
relocation = R_PPC_ADDR16_HI(symbol, modify, rela.r_offset, rela.r_addend)
elif type == 6:
relocation = R_PPC_ADDR16_HA(symbol, modify, rela.r_offset, rela.r_addend)
elif type == 10:
relocation = R_PPC_REL24(symbol, modify, rela.r_offset, rela.r_addend)
elif type == 11:
relocation = R_PPC_REL14(symbol, modify, rela.r_offset, rela.r_addend)
elif type == 109:
relocation = R_PPC_EMB_SDA21(symbol, modify, rela.r_offset, rela.r_addend)
assert relocation
obj.relocations.append(relocation)
return obj
def load_object_from_path(path) -> Object:
with open(path, 'rb') as file:
return load_f(path, path.parts[-1], file)