Files
tp/tools/libelf/object.py
T

234 lines
9.8 KiB
Python
Raw Normal View History

2021-03-28 22:49:05 +02:00
import struct
import os
import io
import sys
from dataclasses import dataclass, field
2021-04-06 18:00:35 +02:00
from typing import IO, Dict, Optional, List, Set, Tuple
2021-03-28 22:49:05 +02:00
from functools import reduce
2021-04-06 18:00:35 +02:00
from collections import defaultdict
2021-03-28 22:49:05 +02:00
from . import elf
from .section import *
from .relocation import *
from .symbol import *
2021-04-06 18:00:35 +02:00
class ElfException(Exception):
...
2021-03-28 22:49:05 +02:00
@dataclass
class Object:
2021-04-06 18:00:35 +02:00
header: elf.Header = None
2021-03-28 22:49:05 +02:00
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)
2021-04-06 18:00:35 +02:00
symbol_map: Dict[str, Symbol] = field(default_factory=lambda: defaultdict(list))
section_relocations: List[Tuple[str, List[Relocation]]] = field(default_factory=list)
2021-03-28 22:49:05 +02:00
relocations: List[Relocation] = field(default_factory=list)
name: str = None
path: Path = None
2021-04-06 18:00:35 +02:00
executable: bool = False
2021-03-28 22:49:05 +02:00
2021-12-02 23:38:37 +01:00
def load_object_from_file(path, name, file, skip_symbols = False, skip_relocations = False) -> Object:
2021-03-28 22:49:05 +02:00
obj = Object()
obj.path = path
obj.name = name
header = elf.Header()
header.read(file)
2021-04-06 18:00:35 +02:00
obj.header = header
2021-03-28 22:49:05 +02:00
if header.e_ident[elf.EI_MAG] != 2135247942:
2021-04-06 18:00:35 +02:00
raise ElfException("invalid elf file: 0x%08X == 0x7F454C46 '%s'" % (header.e_ident[elf.EI_MAG], name))
2021-03-28 22:49:05 +02:00
if header.e_ident[elf.EI_CLASS] != 1:
2021-04-06 18:00:35 +02:00
raise ElfException("only support elf 32-bit")
2021-03-28 22:49:05 +02:00
if header.e_ident[elf.EI_DATA] != 2:
2021-04-06 18:00:35 +02:00
raise ElfException("only support big-endianess")
2021-03-28 22:49:05 +02:00
if header.e_ident[elf.EI_VERSION] != 1:
2021-04-06 18:00:35 +02:00
raise ElfException("invalid elf version")
2021-03-28 22:49:05 +02:00
if header.e_type != 1 and header.e_type != 2:
2021-04-06 18:00:35 +02:00
raise ElfException("invalid object file type")
2021-03-28 22:49:05 +02:00
if header.e_machine != 20:
2021-04-06 18:00:35 +02:00
raise ElfException("invalid target")
2021-03-28 22:49:05 +02:00
if header.e_version != 1:
2021-04-06 18:00:35 +02:00
raise ElfException("invalid elf version")
2021-03-28 22:49:05 +02:00
if header.e_phnum > 0 and header.e_phentsize != 0x20:
2021-04-06 18:00:35 +02:00
raise ElfException("invalid program header size")
2021-03-28 22:49:05 +02:00
if header.e_shnum > 0 and header.e_shentsize != 0x28:
2021-04-06 18:00:35 +02:00
raise ElfException("invalid section header size")
if header.e_type == elf.ET_EXEC:
obj.executable = True
2021-03-28 22:49:05 +02:00
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()
2021-04-06 18:00:35 +02:00
section_header.read(i, file)
2021-03-28 22:49:05 +02:00
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):
2021-04-06 18:00:35 +02:00
raise ElfException("unsupport section type: 0x%08X = %s (%s)" % (
2021-03-28 22:49:05 +02:00
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:
2021-04-06 18:00:35 +02:00
raise ElfException("header.e_shstrndx out-of-bounds")
2021-03-28 22:49:05 +02:00
shstrtab_section = idx_sections[header.e_shstrndx]
if not isinstance(shstrtab_section, StringTableSection):
2021-04-06 18:00:35 +02:00
raise ElfException("header.e_shstrndx is not a string table")
2021-03-28 22:49:05 +02:00
# 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
2021-12-02 23:38:37 +01:00
if not skip_symbols:
# Find all symbols
for symtab in obj.sym_sections.values():
if not symtab.header.sh_link in obj.str_sections:
raise ElfException("symbol table '%s' is not referenceing a valid string table section (sh_link: %i)" % (
symtab.name, symtab.header.sh_link))
2021-03-28 22:49:05 +02:00
2021-12-02 23:38:37 +01:00
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:
raise ElfException("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
2021-03-28 22:49:05 +02:00
symbol.object = obj
2021-12-02 23:38:37 +01:00
if symbol.name:
obj.symbol_map[symbol.name].append(symbol)
2021-03-28 22:49:05 +02:00
obj.symbols.append(symbol)
2021-12-02 23:38:37 +01:00
if not skip_relocations:
# Find all relocations
for rela_section in obj.rela_sections.values():
if not rela_section.header.sh_link in obj.sym_sections:
raise ElfException("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:
raise ElfException("relocation section '%s' is not referenceing a valid section (sh_info: %i)" % (
symtab.name, rela_section.header.sh_info))
2021-03-28 22:49:05 +02:00
2021-12-02 23:38:37 +01:00
symtab = obj.sym_sections[rela_section.header.sh_link]
modify = idx_sections[rela_section.header.sh_info]
2021-03-28 22:49:05 +02:00
2021-12-02 23:38:37 +01:00
section_relocations = []
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:
raise ElfException("unsupported relocation type: 0x%02X (in '%s')" % (type, path))
2021-03-28 22:49:05 +02:00
2021-12-02 23:38:37 +01:00
if sym_id < 0 or sym_id >= len(symtab.symbols):
# report warning?
# main.elf will generate relocation sections with invalid symbol indices
# raise ElfException("invalid symbol index: %i (%i symbols)" % (sym_id, len(symtab.symbols)))
continue
symbol = obj.symbols[symtab.object_offset + sym_id]
2021-03-28 22:49:05 +02:00
2021-12-02 23:38:37 +01:00
relocation = None
if type == 1:
relocation = R_PPC_ADDR32(type, symbol, modify, rela.r_offset, rela.r_addend)
elif type == 3:
relocation = R_PPC_ADDR16(type, symbol, modify, rela.r_offset, rela.r_addend)
elif type == 4:
relocation = R_PPC_ADDR16_LO(type, symbol, modify, rela.r_offset, rela.r_addend)
elif type == 5:
relocation = R_PPC_ADDR16_HI(type, symbol, modify, rela.r_offset, rela.r_addend)
elif type == 6:
relocation = R_PPC_ADDR16_HA(type, symbol, modify, rela.r_offset, rela.r_addend)
elif type == 10:
relocation = R_PPC_REL24(type, symbol, modify, rela.r_offset, rela.r_addend)
elif type == 11:
relocation = R_PPC_REL14(type, symbol, modify, rela.r_offset, rela.r_addend)
elif type == 109:
relocation = R_PPC_EMB_SDA21(type, symbol, modify, rela.r_offset, rela.r_addend)
else:
print("unsupported relocation type: 0x%02X \"%s\" (in '%s')" % (type, RELOCATION_NAMES[type], path), file = sys.stderr)
continue
assert relocation
section_relocations.append(relocation)
obj.relocations.append(relocation)
2021-03-28 22:49:05 +02:00
2021-12-02 23:38:37 +01:00
obj.section_relocations.append((rela_section.name, section_relocations))
2021-04-06 18:00:35 +02:00
2021-03-28 22:49:05 +02:00
return obj
2021-12-02 23:38:37 +01:00
def load_object_from_path(path, skip_symbols = False, skip_relocations = False) -> Object:
2021-03-28 22:49:05 +02:00
with open(path, 'rb') as file:
2021-12-02 23:38:37 +01:00
return load_object_from_file(path, path.parts[-1], file, skip_symbols, skip_relocations)