mirror of https://github.com/Nonannet/pelfy.git
project public/privat structure updated
This commit is contained in:
parent
d0b0901ed7
commit
97e44b8dbf
|
@ -1,572 +1,15 @@
|
||||||
"""Pelfy is an ELF parser for parsing header fields, sections, symbols and relocations.
|
"""
|
||||||
|
pelfy is an ELF parser written in python.
|
||||||
|
|
||||||
Typical usage example:
|
Example usage:
|
||||||
|
>>> import pelfy
|
||||||
elf = pelfy.open_elf_file('obj/test-c-riscv64-linux-gnu-gcc-12-O3.o')
|
>>> elf = pelfy.open_elf_file('tests/obj/test-c-riscv64-linux-gnu-gcc-12-O3.o')
|
||||||
print(elf.sections)
|
>>> elf.sections
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import fields_data as fdat
|
from ._main import open_elf_file, elf_symbol, elf_section, \
|
||||||
from . import output_formatter
|
elf_relocation, elf_list, section_list, symbol_list, relocation_list, elf_file
|
||||||
from typing import TypeVar, Literal, Iterable, Generic, Iterator, Generator, Optional, Union
|
|
||||||
|
|
||||||
_T = TypeVar('_T')
|
__all__ = [
|
||||||
|
'open_elf_file', 'elf_symbol', 'elf_section', 'elf_relocation',
|
||||||
|
'elf_list', 'section_list', 'symbol_list', 'relocation_list', 'elf_file']
|
||||||
def open_elf_file(file_path: str) -> 'elf_file':
|
|
||||||
"""Reads ELF data from file
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file_path: path of the ELF file
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
elf_file object
|
|
||||||
"""
|
|
||||||
with open(file_path, mode='rb') as f:
|
|
||||||
return elf_file(f.read())
|
|
||||||
|
|
||||||
|
|
||||||
class elf_symbol():
|
|
||||||
"""A class for representing data of an ELF symbol
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
file: Points to the parent ELF file object.
|
|
||||||
name: Name of the symbol
|
|
||||||
section: section where the symbol data is placed
|
|
||||||
index: Absolut index in the symbol table
|
|
||||||
info: Type of the symbol
|
|
||||||
description: Description of the symbol type
|
|
||||||
stb: visibility of the symbol (local, global, etc.)
|
|
||||||
stb_description: Description of the symbol visibility
|
|
||||||
fields: All symbol header fields as dict
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, file: 'elf_file', fields: dict[str, int], index: int):
|
|
||||||
"""
|
|
||||||
Initializes ELF symbol instance
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file: ELF file object
|
|
||||||
fields: symbol header fields
|
|
||||||
index: Absolut index in the symbol table
|
|
||||||
"""
|
|
||||||
self.fields = fields
|
|
||||||
self.file = file
|
|
||||||
|
|
||||||
if file.string_table_section:
|
|
||||||
self.name = file.read_string(file.string_table_section['sh_offset'] + fields['st_name'])
|
|
||||||
else:
|
|
||||||
self.name = ''
|
|
||||||
|
|
||||||
self.section: Optional[elf_section] = self.file.sections[self['st_shndx']] if self['st_shndx'] < len(self.file.sections) else None
|
|
||||||
|
|
||||||
self.index = index
|
|
||||||
|
|
||||||
self.info, self.description = fdat.st_info_values[fields['st_info'] & 0x0F]
|
|
||||||
self.stb, self.stb_description = fdat.stb_values[fields['st_info'] >> 4]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data(self) -> bytes:
|
|
||||||
"""Returns the binary data the symbol is pointing to.
|
|
||||||
The offset in the ELF file is calculated by:
|
|
||||||
sections[symbol.st_shndx].sh_offset + symbol.st_value
|
|
||||||
"""
|
|
||||||
assert self.section, 'This symbol is not associated to a data section'
|
|
||||||
if self.section.type == 'SHT_NOBITS':
|
|
||||||
return b'\x00' * self['st_size']
|
|
||||||
else:
|
|
||||||
offset = self.section['sh_offset'] + self['st_value']
|
|
||||||
return self.file.read_bytes(offset, self['st_size'])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data_hex(self) -> str:
|
|
||||||
"""Returns the binary data the symbol is pointing to as hex string.
|
|
||||||
"""
|
|
||||||
return ' '.join(f'{d:02X}' for d in self.data)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def relocations(self) -> 'relocation_list':
|
|
||||||
"""Relocations that are pointing to this symbol.
|
|
||||||
The symbol section must be of type SHT_PROGBITS (program code). Therefore
|
|
||||||
this property returns typically all relocations that will be
|
|
||||||
applied to the function represented by the symbol.
|
|
||||||
"""
|
|
||||||
ret: list[elf_relocation] = list()
|
|
||||||
assert self.section and self.section.type == 'SHT_PROGBITS'
|
|
||||||
for reloc in self.file.get_relocations():
|
|
||||||
if reloc.target_section == self.section:
|
|
||||||
offset = reloc['r_offset'] - self['st_value']
|
|
||||||
if 0 <= offset < self['st_size']:
|
|
||||||
ret.append(reloc)
|
|
||||||
return relocation_list(ret)
|
|
||||||
|
|
||||||
def __getitem__(self, key: Union[str, int]) -> int:
|
|
||||||
if isinstance(key, str):
|
|
||||||
assert key in self.fields, f'Unknown field name: {key}'
|
|
||||||
return self.fields[key]
|
|
||||||
else:
|
|
||||||
return list(self.fields.values())[key]
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'index {self.index}\n' +\
|
|
||||||
f'name {self.name}\n' +\
|
|
||||||
f'stb {self.stb} ({self.stb_description})\n' +\
|
|
||||||
f'info {self.info} ({self.description})\n' +\
|
|
||||||
'\n'.join(f"{k:18} {v:4} {fdat.symbol_fields[k]}" for k, v in self.fields.items()) + '\n'
|
|
||||||
|
|
||||||
|
|
||||||
class elf_section():
|
|
||||||
"""A class for representing data of an ELF section
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
file: Points to the parent ELF file object.
|
|
||||||
name: Name of the section
|
|
||||||
index: Absolut index of the section
|
|
||||||
type: Type of the section
|
|
||||||
description: Description of the section type
|
|
||||||
fields: All symbol header fields as dict
|
|
||||||
"""
|
|
||||||
def __init__(self, file: 'elf_file', fields: dict[str, int], name: str, index: int):
|
|
||||||
"""Initializes an ELF section instance
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file: ELF file object
|
|
||||||
fields: Section header fields
|
|
||||||
name: Name of the section
|
|
||||||
index: Absolut index in the symbol table
|
|
||||||
"""
|
|
||||||
self.fields = fields
|
|
||||||
self.file = file
|
|
||||||
self.index = index
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
if fields['sh_type'] > 0x60000000:
|
|
||||||
# Range for OS, compiler and application specific types
|
|
||||||
self.description = [v for k, v in fdat.section_header_types_ex.items() if k >= fields['sh_type']][0]
|
|
||||||
self.type = str(hex(fields['sh_type']))
|
|
||||||
elif fields['sh_type'] in fdat.section_header_types:
|
|
||||||
self.type, self.description = fdat.section_header_types[fields['sh_type']]
|
|
||||||
else:
|
|
||||||
self.description = ''
|
|
||||||
self.type = str(hex(fields['sh_type']))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data(self) -> bytes:
|
|
||||||
"""Returns the binary data from the section.
|
|
||||||
The offset in the ELF file is given by: section.sh_offset
|
|
||||||
"""
|
|
||||||
if self.type == 'SHT_NOBITS':
|
|
||||||
return b'\x00' * self['sh_size']
|
|
||||||
else:
|
|
||||||
return self.file.read_bytes(self['sh_offset'], self['sh_size'])
|
|
||||||
|
|
||||||
@property
|
|
||||||
def symbols(self) -> 'symbol_list':
|
|
||||||
"""All ELF symbols associated with this section
|
|
||||||
"""
|
|
||||||
return symbol_list(self.file.list_symbols(self.index))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data_hex(self) -> str:
|
|
||||||
"""Returns the binary data from the section as hex string.
|
|
||||||
"""
|
|
||||||
return ' '.join(f'{d:02X}' for d in self.data)
|
|
||||||
|
|
||||||
def __getitem__(self, key: Union[str, int]) -> int:
|
|
||||||
if isinstance(key, str):
|
|
||||||
assert key in self.fields, f'Unknown field name: {key}'
|
|
||||||
return self.fields[key]
|
|
||||||
else:
|
|
||||||
return list(self.fields.values())[key]
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'index {self.index}\n' +\
|
|
||||||
f'name {self.name}\n' +\
|
|
||||||
f'type {self.type} ({self.description})\n' +\
|
|
||||||
'\n'.join(f"{k:18} {v:4} {fdat.section_header[k]['description']}" for k, v in self.fields.items()) + '\n'
|
|
||||||
|
|
||||||
|
|
||||||
class elf_relocation():
|
|
||||||
"""A class for representing data of a relocation
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
file: Points to the parent ELF file object.
|
|
||||||
index: Absolut index of the relocation in the associated relocation section
|
|
||||||
symbol: Symbol to relocate
|
|
||||||
type: Type of the relocation
|
|
||||||
calculation: Description of the relocation calculation
|
|
||||||
bits: number ob bits to patch by the relocation
|
|
||||||
target_section: Pointing to the section where this relocation applies to
|
|
||||||
fields: All relocation header fields as dict
|
|
||||||
"""
|
|
||||||
def __init__(self, file: 'elf_file', fields: dict[str, int], symbol_index: int,
|
|
||||||
relocation_type: int, sh_info: int, index: int):
|
|
||||||
"""Initializes a ELF relocation instance
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file: ELF file object
|
|
||||||
fields: Relocation header fields
|
|
||||||
symbol_index: Index of the symbol to relocate in the symbol table
|
|
||||||
relocation_type: Type of the relocation (numeric)
|
|
||||||
sh_info: Index of the section this relocation applies to
|
|
||||||
index: Absolut index of the relocation in the associated relocation section
|
|
||||||
"""
|
|
||||||
self.fields = fields
|
|
||||||
self.file = file
|
|
||||||
self.index = index
|
|
||||||
self.symbol = file.symbols[symbol_index]
|
|
||||||
reloc_types = fdat.relocation_table_types.get(file.architecture)
|
|
||||||
if reloc_types and relocation_type in reloc_types:
|
|
||||||
self.type = reloc_types[relocation_type][0]
|
|
||||||
self.bits = reloc_types[relocation_type][1]
|
|
||||||
self.calculation = reloc_types[relocation_type][2]
|
|
||||||
else:
|
|
||||||
self.type = str(relocation_type)
|
|
||||||
self.bits = 0
|
|
||||||
self.calculation = ''
|
|
||||||
self.target_section: elf_section = file.sections[sh_info]
|
|
||||||
|
|
||||||
def __getitem__(self, key: Union[str, int]) -> int:
|
|
||||||
if isinstance(key, str):
|
|
||||||
assert key in self.fields, f'Unknown field name: {key}'
|
|
||||||
return self.fields[key]
|
|
||||||
else:
|
|
||||||
return list(self.fields.values())[key]
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'index {self.symbol.index}\n' +\
|
|
||||||
f'symbol {self.symbol.name}\n' +\
|
|
||||||
f'relocation type {self.type}\n' +\
|
|
||||||
f'calculation {self.calculation}\n' +\
|
|
||||||
f'bits {self.bits}\n' +\
|
|
||||||
'\n'.join(f'{k:18} {v:4}' for k, v in self.fields.items()) + '\n'
|
|
||||||
|
|
||||||
|
|
||||||
class elf_list(Generic[_T]):
|
|
||||||
"""A generic class for representing a list of ELF data items
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data: Iterable of ELF data items
|
|
||||||
"""
|
|
||||||
def __init__(self, data: Iterable[_T]):
|
|
||||||
self._data = list(data)
|
|
||||||
|
|
||||||
def __getitem__(self, key: Union[str, int]) -> _T:
|
|
||||||
if isinstance(key, str):
|
|
||||||
elements = [el for el in self._data if getattr(el, 'name', '') == key]
|
|
||||||
assert elements, f'Unknown name: {key}'
|
|
||||||
return elements[0]
|
|
||||||
else:
|
|
||||||
return self._data.__getitem__(key)
|
|
||||||
|
|
||||||
def __len__(self) -> int:
|
|
||||||
return len(self._data)
|
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[_T]:
|
|
||||||
return iter(self._data)
|
|
||||||
|
|
||||||
def _compact_table(self) -> tuple[list[str], list[list[Union[str, int]]], list[str]]:
|
|
||||||
return [], [[]], []
|
|
||||||
|
|
||||||
def _repr_table(self, format: output_formatter.table_format, raw_data: bool = False) -> str:
|
|
||||||
if raw_data and len(self):
|
|
||||||
table_dict: list[dict[str, int]] = [el.__dict__.get('fields', {' ': 0}) for el in self]
|
|
||||||
columns = list(table_dict[0].keys())
|
|
||||||
data: list[list[Union[str, int]]] = [list(el.values()) for el in table_dict]
|
|
||||||
radj = columns
|
|
||||||
else:
|
|
||||||
columns, data, radj = self._compact_table()
|
|
||||||
return output_formatter.generate_table(data, columns, right_adj_col=radj, format=format)
|
|
||||||
|
|
||||||
def to_dict_list(self) -> list[dict[str, Union[str, int]]]:
|
|
||||||
"""Exporting the ELF item data table to a list of dicts. It can be used with pandas:
|
|
||||||
df = pandas.DataFrame(elements.to_dict_list())
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Table data
|
|
||||||
"""
|
|
||||||
columns, data, _ = self._compact_table()
|
|
||||||
return [{k: v for k, v in zip(columns, row)} for row in data]
|
|
||||||
|
|
||||||
def to_html(self) -> str:
|
|
||||||
"""Exporting the ELF item data table to HTML.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
HTML table
|
|
||||||
"""
|
|
||||||
return self._repr_table('html')
|
|
||||||
|
|
||||||
def to_markdown(self) -> str:
|
|
||||||
"""Exporting the ELF item data table to markdown.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Markdown table
|
|
||||||
"""
|
|
||||||
return self._repr_table('markdown')
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return self._repr_table('text')
|
|
||||||
|
|
||||||
def _repr_html_(self) -> str:
|
|
||||||
return self._repr_table('html')
|
|
||||||
|
|
||||||
def _repr_markdown_(self) -> str:
|
|
||||||
return self._repr_table('markdown')
|
|
||||||
|
|
||||||
|
|
||||||
class section_list(elf_list[elf_section]):
|
|
||||||
"""A class for representing a list of ELF section
|
|
||||||
"""
|
|
||||||
def _compact_table(self) -> tuple[list[str], list[list[Union[str, int]]], list[str]]:
|
|
||||||
columns = ['index', 'name', 'type', 'description']
|
|
||||||
data: list[list[Union[str, int]]] = [[item.index, item.name, item.type,
|
|
||||||
item.description] for item in self]
|
|
||||||
return columns, data, ['index']
|
|
||||||
|
|
||||||
|
|
||||||
class symbol_list(elf_list[elf_symbol]):
|
|
||||||
"""A class for representing a list of ELF symbols
|
|
||||||
"""
|
|
||||||
def _compact_table(self) -> tuple[list[str], list[list[Union[str, int]]], list[str]]:
|
|
||||||
columns = ['index', 'name', 'info', 'size', 'stb', 'section', 'description']
|
|
||||||
data: list[list[Union[str, int]]] = [[item.index, item.name, item.info, item.fields['st_size'],
|
|
||||||
item.stb, item.section.name if item.section else '', item.description] for item in self]
|
|
||||||
return columns, data, ['index', 'size']
|
|
||||||
|
|
||||||
|
|
||||||
class relocation_list(elf_list[elf_relocation]):
|
|
||||||
"""A class for representing a list of ELF relocations
|
|
||||||
"""
|
|
||||||
def _compact_table(self) -> tuple[list[str], list[list[Union[str, int]]], list[str]]:
|
|
||||||
columns = ['index', 'symbol name', 'type', 'calculation', 'bits']
|
|
||||||
data: list[list[Union[str, int]]] = [[item.index, item.symbol.name, item.type,
|
|
||||||
item.calculation, item.bits] for item in self]
|
|
||||||
return columns, data, ['index', 'bits']
|
|
||||||
|
|
||||||
|
|
||||||
class elf_file:
|
|
||||||
"""A class for representing data of an ELF file in a structured form
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
byteorder: Byte order of the architecture 'little' or 'big'
|
|
||||||
(based on e_ident[EI_DATA])
|
|
||||||
bit_width: Bit with of the architecture: 32 or 64 (based on
|
|
||||||
e_ident[EI_CLASS])
|
|
||||||
architecture: Name of the architecture (based on e_machine)
|
|
||||||
fields: All ELF header fields as dict
|
|
||||||
sections: A list of all ELF sections
|
|
||||||
symbols: A list of all ELF symbols
|
|
||||||
functions: A list of all function symbols (STT_FUNC)
|
|
||||||
objects: A list of all variable/object symbols (STT_OBJECT)
|
|
||||||
code_relocations: A list of all code relocations (.rela.text and .rel.text)
|
|
||||||
symbol_table_section: The symbol table section (first section with
|
|
||||||
the type SHT_SYMTAB)
|
|
||||||
string_table_section: The string table section (first section with
|
|
||||||
the name .strtab)
|
|
||||||
"""
|
|
||||||
def __init__(self, data: Union[bytes, bytearray]):
|
|
||||||
"""Initializes an ELF file instance
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data: binary ELF data
|
|
||||||
"""
|
|
||||||
assert isinstance(data, (bytes, bytearray)), 'Binary ELF data must be provided as bytes or bytearray.'
|
|
||||||
self._data = bytes(data)
|
|
||||||
|
|
||||||
# Defaults required for function _read_int_from_elf_field
|
|
||||||
self.bit_width = 32
|
|
||||||
self.byteorder: Literal['little', 'big'] = 'little'
|
|
||||||
|
|
||||||
assert self._read_bytes_from_elf_field('e_ident[EI_MAG]') == b'\x7fELF', 'Not an ELF file'
|
|
||||||
|
|
||||||
self.bit_width = {1: 32, 2: 64}[self._read_int_from_elf_field('e_ident[EI_CLASS]')]
|
|
||||||
|
|
||||||
byte_order = self._read_int_from_elf_field('e_ident[EI_DATA]')
|
|
||||||
assert byte_order in [1, 2], 'Invalid byte order value e_ident[EI_DATA]'
|
|
||||||
self.byteorder = 'little' if byte_order == 1 else 'big'
|
|
||||||
|
|
||||||
self.fields = {fn: self._read_int_from_elf_field(fn) for fn in fdat.elf_header_field.keys()}
|
|
||||||
|
|
||||||
arch_entr = fdat.e_machine_dict.get(self.fields['e_machine'])
|
|
||||||
self.architecture = arch_entr[0] if arch_entr else str(self.fields['e_machine'])
|
|
||||||
|
|
||||||
section_data = list(self._list_sections())
|
|
||||||
name_addr: dict[str, int] = section_data[self.fields['e_shstrndx']] if section_data else dict()
|
|
||||||
section_names = (self.read_string(name_addr['sh_offset'] + f['sh_name']) for f in section_data)
|
|
||||||
|
|
||||||
self.sections = section_list(elf_section(self, sd, sn, i)
|
|
||||||
for i, (sd, sn) in enumerate(zip(section_data, section_names)))
|
|
||||||
|
|
||||||
ret_sections = [sh for sh in self.sections if sh.type == 'SHT_SYMTAB']
|
|
||||||
self.symbol_table_section = ret_sections[0] if ret_sections else None
|
|
||||||
|
|
||||||
ret_sections = [sh for sh in self.sections if sh.name == '.strtab']
|
|
||||||
self.string_table_section = ret_sections[0] if ret_sections else None
|
|
||||||
|
|
||||||
self.symbols = symbol_list(self.list_symbols())
|
|
||||||
|
|
||||||
self.functions = symbol_list(s for s in self.symbols if s.info == 'STT_FUNC')
|
|
||||||
self.objects = symbol_list(s for s in self.symbols if s.info == 'STT_OBJECT')
|
|
||||||
|
|
||||||
self.code_relocations = self.get_relocations(['.rela.text', '.rel.text'])
|
|
||||||
|
|
||||||
def _list_sections(self) -> Generator[dict[str, int], None, None]:
|
|
||||||
for i in range(self.fields['e_shnum']):
|
|
||||||
offs = self.fields['e_shoff'] + i * self.fields['e_shentsize']
|
|
||||||
yield {fn: self._read_from_sh_field(offs, fn) for fn in fdat.section_header.keys()}
|
|
||||||
|
|
||||||
def list_symbols(self, section_index: Optional[int] = None) -> Generator[elf_symbol, None, None]:
|
|
||||||
"""List ELF symbols.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
section_index: If provided, only symbols from the specified section are returned.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of ELF symbols
|
|
||||||
"""
|
|
||||||
if self.symbol_table_section:
|
|
||||||
offs = self.symbol_table_section['sh_offset']
|
|
||||||
|
|
||||||
for j, i in enumerate(range(offs, self.symbol_table_section['sh_size'] + offs, self.symbol_table_section['sh_entsize'])):
|
|
||||||
ret = {'st_name': self.read_int(i, 4)}
|
|
||||||
|
|
||||||
if self.bit_width == 32:
|
|
||||||
ret['st_value'] = self.read_int(i + 4, 4)
|
|
||||||
ret['st_size'] = self.read_int(i + 8, 4)
|
|
||||||
ret['st_info'] = self.read_int(i + 12, 1)
|
|
||||||
ret['st_other'] = self.read_int(i + 13, 1)
|
|
||||||
ret['st_shndx'] = self.read_int(i + 14, 2)
|
|
||||||
elif self.bit_width == 64:
|
|
||||||
ret['st_info'] = self.read_int(i + 4, 1)
|
|
||||||
ret['st_other'] = self.read_int(i + 5, 1)
|
|
||||||
ret['st_shndx'] = self.read_int(i + 6, 2)
|
|
||||||
ret['st_value'] = self.read_int(i + 8, 8)
|
|
||||||
ret['st_size'] = self.read_int(i + 16, 8)
|
|
||||||
|
|
||||||
if section_index is None or section_index == ret['st_shndx']:
|
|
||||||
yield elf_symbol(self, ret, j)
|
|
||||||
|
|
||||||
def get_relocations(self, reloc_section: Optional[Union[elf_section, str, list[str]]] = None) -> relocation_list:
|
|
||||||
"""List relocations.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
reloc_section: Specifies the relocation section from which the
|
|
||||||
relocations should be listed. It can be provided as
|
|
||||||
elf_section object or by its name. If not provided
|
|
||||||
(reloc_section=None) relocations from all relocation
|
|
||||||
sections are returned.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of relocations
|
|
||||||
"""
|
|
||||||
if isinstance(reloc_section, elf_section):
|
|
||||||
assert reloc_section.type in ('SHT_REL', 'SHT_RELA'), f'{reloc_section.name} is not a relocation section'
|
|
||||||
return relocation_list(self._list_relocations(reloc_section))
|
|
||||||
else:
|
|
||||||
relocations: list[elf_relocation] = list()
|
|
||||||
for sh in self.sections:
|
|
||||||
if sh.type in ('SHT_REL', 'SHT_RELA'):
|
|
||||||
if reloc_section is None or \
|
|
||||||
(isinstance(reloc_section, str) and sh.name == reloc_section) or \
|
|
||||||
(isinstance(reloc_section, list) and sh.name in reloc_section):
|
|
||||||
relocations += relocation_list(self._list_relocations(sh))
|
|
||||||
|
|
||||||
return relocation_list(relocations)
|
|
||||||
|
|
||||||
def _list_relocations(self, sh: elf_section) -> Generator[elf_relocation, None, None]:
|
|
||||||
"""List relocations for a elf_section.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
elf_section: Specifies the relocation section from which the
|
|
||||||
relocations should be listed.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Relocations from specified elf_section
|
|
||||||
"""
|
|
||||||
offs = sh['sh_offset']
|
|
||||||
for i, el_off in enumerate(range(offs, sh['sh_size'] + offs, sh['sh_entsize'])):
|
|
||||||
ret: dict[str, int] = dict()
|
|
||||||
|
|
||||||
if self.bit_width == 32:
|
|
||||||
ret['r_offset'] = self.read_int(el_off, 4)
|
|
||||||
r_info = self.read_int(el_off + 4, 4)
|
|
||||||
ret['r_info'] = r_info
|
|
||||||
ret['r_addend'] = self.read_int(el_off + 8, 4, True) if sh.type == 'SHT_RELA' else 0
|
|
||||||
yield elf_relocation(self, ret, r_info >> 8, r_info & 0xFF, sh['sh_info'], i)
|
|
||||||
elif self.bit_width == 64:
|
|
||||||
ret['r_offset'] = self.read_int(el_off, 8)
|
|
||||||
r_info = self.read_int(el_off + 8, 8)
|
|
||||||
ret['r_info'] = r_info
|
|
||||||
ret['r_addend'] = self.read_int(el_off + 16, 8, True) if sh.type == 'SHT_RELA' else 0
|
|
||||||
yield elf_relocation(self, ret, r_info >> 32, r_info & 0xFFFFFFFF, sh['sh_info'], i)
|
|
||||||
|
|
||||||
def read_bytes(self, offset: int, num_bytes: int) -> bytes:
|
|
||||||
"""Read bytes from ELF file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
offset: Specify first byte relative to the start of
|
|
||||||
the ELF file.
|
|
||||||
num_bytes: Specify the number of bytes to read.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Binary data as bytes
|
|
||||||
"""
|
|
||||||
return self._data[offset:offset + num_bytes]
|
|
||||||
|
|
||||||
def read_int(self, offset: int, num_bytes: int, signed: bool = False) -> int:
|
|
||||||
"""Read an integer from the ELF file. Byte order is
|
|
||||||
selected according to the architecture (e_ident[EI_DATA]).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
offset: Specify first byte of the integer relative to
|
|
||||||
the start of the ELF file.
|
|
||||||
num_bytes: Specify the size of the integer in bytes.
|
|
||||||
signed: Select if the integer is a signed integer.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Integer value
|
|
||||||
"""
|
|
||||||
return int.from_bytes(self._data[offset:offset + num_bytes], self.byteorder, signed=signed)
|
|
||||||
|
|
||||||
# def int_to_bytes(self, value: int, num_bytes: int = 4, signed: bool = False) -> int:
|
|
||||||
# return value.to_bytes(length=num_bytes, byteorder=self.byteorder, signed=signed)
|
|
||||||
|
|
||||||
def read_string(self, offset: int, encoding: str = 'utf-8') -> str:
|
|
||||||
"""Read a zero-terminated text string from the ELF file.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
offset: Specify first byte of the string relative to
|
|
||||||
the start of the ELF file.
|
|
||||||
encoding: Encoding used for text decoding.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Text string
|
|
||||||
"""
|
|
||||||
str_end = self._data.find(b'\x00', offset)
|
|
||||||
return self._data[offset:str_end].decode(encoding)
|
|
||||||
|
|
||||||
def _read_int_from_elf_field(self, field_name: str) -> int:
|
|
||||||
field = fdat.elf_header_field[field_name]
|
|
||||||
offs = int(field[str(self.bit_width)], base=16)
|
|
||||||
byte_len = int(field['size' + str(self.bit_width)])
|
|
||||||
return self.read_int(offs, byte_len)
|
|
||||||
|
|
||||||
def _read_bytes_from_elf_field(self, field_name: str) -> bytes:
|
|
||||||
field = fdat.elf_header_field[field_name]
|
|
||||||
offs = int(field[str(self.bit_width)], base=16)
|
|
||||||
byte_len = int(field['size' + str(self.bit_width)])
|
|
||||||
return self.read_bytes(offs, byte_len)
|
|
||||||
|
|
||||||
def _read_from_sh_field(self, offset: int, field_name: str) -> int:
|
|
||||||
field = fdat.section_header[field_name]
|
|
||||||
offs = int(field[str(self.bit_width)], base=16) + offset
|
|
||||||
byte_len = int(field['size' + str(self.bit_width)])
|
|
||||||
return self.read_int(offs, byte_len)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
hf_list = ((hf, self.fields[hf['field_name']]) for hf in fdat.elf_header_field.values())
|
|
||||||
return '\n'.join(f"{hf['field_name']:24} {v:4} {hf['description']}" for hf, v in hf_list) + '\n'
|
|
||||||
|
|
||||||
def __getitem__(self, key: str) -> int:
|
|
||||||
assert key in self.fields, f'Unknown field name: {key}'
|
|
||||||
return self.fields[key]
|
|
||||||
|
|
|
@ -0,0 +1,586 @@
|
||||||
|
elf_header_field = {
|
||||||
|
"e_ident[EI_MAG]": {
|
||||||
|
"32": "0x00", "64": "0x00", "size32": "4", "size64": "4", "field_name": "e_ident[EI_MAG]",
|
||||||
|
"description": "0x7F followed by ELF(45 4c 46) in ASCII; these four bytes constitute the magic number"
|
||||||
|
},
|
||||||
|
"e_ident[EI_CLASS]": {
|
||||||
|
"32": "0x04", "64": "0x04", "size32": "1", "size64": "1", "field_name": "e_ident[EI_CLASS]",
|
||||||
|
"description": "This byte is set to either 1 or 2 to signify 32- or 64-bit format, respectively"
|
||||||
|
},
|
||||||
|
"e_ident[EI_DATA]": {
|
||||||
|
"32": "0x05", "64": "0x05", "size32": "1", "size64": "1", "field_name": "e_ident[EI_DATA]",
|
||||||
|
"description": "This byte is set to either 1 or 2 to signify little or big endianness, respectively This affects interpretation of multi-byte fields starting with offset 0x10"
|
||||||
|
},
|
||||||
|
"e_ident[EI_VERSION]": {
|
||||||
|
"32": "0x06", "64": "0x06", "size32": "1", "size64": "1", "field_name": "e_ident[EI_VERSION]",
|
||||||
|
"description": "Set to 1 for the original and current version of ELF"
|
||||||
|
},
|
||||||
|
"e_ident[EI_OSABI]": {
|
||||||
|
"32": "0x07", "64": "0x07", "size32": "1", "size64": "1", "field_name": "e_ident[EI_OSABI]",
|
||||||
|
"description": "Identifies the target operating system ABI"
|
||||||
|
},
|
||||||
|
"e_ident[EI_ABIVERSION]": {
|
||||||
|
"32": "0x08", "64": "0x08", "size32": "1", "size64": "1", "field_name": "e_ident[EI_ABIVERSION]",
|
||||||
|
"description": "Further specifies the ABI version"
|
||||||
|
},
|
||||||
|
"e_ident[EI_PAD]": {
|
||||||
|
"32": "0x09", "64": "0x09", "size32": "7", "size64": "7", "field_name": "e_ident[EI_PAD]",
|
||||||
|
"description": "Reserved padding bytes Currently unused Should be filled with zeros and ignored when read"
|
||||||
|
},
|
||||||
|
"e_type": {
|
||||||
|
"32": "0x10", "64": "0x10", "size32": "2", "size64": "2", "field_name": "e_type",
|
||||||
|
"description": "Identifies object file type"
|
||||||
|
},
|
||||||
|
"e_machine": {
|
||||||
|
"32": "0x12", "64": "0x12", "size32": "2", "size64": "2", "field_name": "e_machine",
|
||||||
|
"description": "Specifies target instruction set architecture"
|
||||||
|
},
|
||||||
|
"e_version": {
|
||||||
|
"32": "0x14", "64": "0x14", "size32": "4", "size64": "4", "field_name": "e_version",
|
||||||
|
"description": "Set to 1 for the original version of ELF"
|
||||||
|
},
|
||||||
|
"e_entry": {
|
||||||
|
"32": "0x18", "64": "0x18", "size32": "4", "size64": "8", "field_name": "e_entry",
|
||||||
|
"description": "This is the memory address of the entry point from where the process starts executing This field is either 32 or 64 bits long, depending on the format defined earlier (byte 0x04) If the file doesn't have an associated entry point, then this holds zero"
|
||||||
|
},
|
||||||
|
"e_phoff": {
|
||||||
|
"32": "0x1C", "64": "0x20", "size32": "4", "size64": "8", "field_name": "e_phoff",
|
||||||
|
"description": "Points to the start of the program header table It usually follows the file header immediately following this one, making the offset 0x34 or 0x40 for 32- and 64-bit ELF executables, respectively"
|
||||||
|
},
|
||||||
|
"e_shoff": {
|
||||||
|
"32": "0x20", "64": "0x28", "size32": "4", "size64": "8", "field_name": "e_shoff",
|
||||||
|
"description": "Points to the start of the section header table"
|
||||||
|
},
|
||||||
|
"e_flags": {
|
||||||
|
"32": "0x24", "64": "0x30", "size32": "4", "size64": "4", "field_name": "e_flags",
|
||||||
|
"description": "Interpretation of this field depends on the target architecture"
|
||||||
|
},
|
||||||
|
"e_ehsize": {
|
||||||
|
"32": "0x28", "64": "0x34", "size32": "2", "size64": "2", "field_name": "e_ehsize",
|
||||||
|
"description": "Contains the size of this header, normally 64 Bytes for 64-bit and 52 Bytes for 32-bit format"
|
||||||
|
},
|
||||||
|
"e_phentsize": {
|
||||||
|
"32": "0x2A", "64": "0x36", "size32": "2", "size64": "2", "field_name": "e_phentsize",
|
||||||
|
"description": "Contains the size of a program header table entry As explained below, this will typically be 0x20 (32 bit) or 0x38 (64 bit)"
|
||||||
|
},
|
||||||
|
"e_phnum": {
|
||||||
|
"32": "0x2C", "64": "0x38", "size32": "2", "size64": "2", "field_name": "e_phnum",
|
||||||
|
"description": "Contains the number of entries in the program header table"
|
||||||
|
},
|
||||||
|
"e_shentsize": {
|
||||||
|
"32": "0x2E", "64": "0x3A", "size32": "2", "size64": "2", "field_name": "e_shentsize",
|
||||||
|
"description": "Contains the size of a section header table entry As explained below, this will typically be 0x28 (32 bit) or 0x40 (64 bit)"
|
||||||
|
},
|
||||||
|
"e_shnum": {
|
||||||
|
"32": "0x30", "64": "0x3C", "size32": "2", "size64": "2", "field_name": "e_shnum",
|
||||||
|
"description": "Contains the number of entries in the section header table"
|
||||||
|
},
|
||||||
|
"e_shstrndx": {
|
||||||
|
"32": "0x32", "64": "0x3E", "size32": "2", "size64": "2", "field_name": "e_shstrndx",
|
||||||
|
"description": "Contains index of the section header table entry that contains the section names"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e_machine_dict = {
|
||||||
|
0: ("EM_NONE", "No machine"),
|
||||||
|
1: ("EM_M32", "AT&T WE 32100"),
|
||||||
|
2: ("EM_SPARC", "SUN SPARC"),
|
||||||
|
3: ("EM_386", "Intel 80386"),
|
||||||
|
4: ("EM_68K", "Motorola m68k family"),
|
||||||
|
5: ("EM_88K", "Motorola m88k family"),
|
||||||
|
6: ("EM_IAMCU", "Intel MCU"),
|
||||||
|
7: ("EM_860", "Intel 80860"),
|
||||||
|
8: ("EM_MIPS", "MIPS R3000 big-endian"),
|
||||||
|
9: ("EM_S370", "IBM System/370"),
|
||||||
|
10: ("EM_MIPS_RS3_LE", "MIPS R3000 little-endian"),
|
||||||
|
15: ("EM_PARISC", "HPPA"),
|
||||||
|
17: ("EM_VPP500", "Fujitsu VPP500"),
|
||||||
|
18: ("EM_SPARC32PLUS", "Sun's v8plus"),
|
||||||
|
19: ("EM_960", "Intel 80960"),
|
||||||
|
20: ("EM_PPC", "PowerPC"),
|
||||||
|
21: ("EM_PPC64", "PowerPC 64-bit"),
|
||||||
|
22: ("EM_S390", "IBM S390"),
|
||||||
|
23: ("EM_SPU", "IBM SPU/SPC"),
|
||||||
|
36: ("EM_V800", "NEC V800 series"),
|
||||||
|
37: ("EM_FR20", "Fujitsu FR20"),
|
||||||
|
38: ("EM_RH32", "TRW RH-32"),
|
||||||
|
39: ("EM_RCE", "Motorola RCE"),
|
||||||
|
40: ("EM_ARM", "ARM"),
|
||||||
|
41: ("EM_FAKE_ALPHA", "Digital Alpha"),
|
||||||
|
42: ("EM_SH", "Hitachi SH"),
|
||||||
|
43: ("EM_SPARCV9", "SPARC v9 64-bit"),
|
||||||
|
44: ("EM_TRICORE", "Siemens Tricore"),
|
||||||
|
45: ("EM_ARC", "Argonaut RISC Core"),
|
||||||
|
46: ("EM_H8_300", "Hitachi H8/300"),
|
||||||
|
47: ("EM_H8_300H", "Hitachi H8/300H"),
|
||||||
|
48: ("EM_H8S", "Hitachi H8S"),
|
||||||
|
49: ("EM_H8_500", "Hitachi H8/500"),
|
||||||
|
50: ("EM_IA_64", "Intel Merced"),
|
||||||
|
51: ("EM_MIPS_X", "Stanford MIPS-X"),
|
||||||
|
52: ("EM_COLDFIRE", "Motorola Coldfire"),
|
||||||
|
53: ("EM_68HC12", "Motorola M68HC12"),
|
||||||
|
54: ("EM_MMA", "Fujitsu MMA Multimedia Accelerator"),
|
||||||
|
55: ("EM_PCP", "Siemens PCP"),
|
||||||
|
56: ("EM_NCPU", "Sony nCPU embedded RISC"),
|
||||||
|
57: ("EM_NDR1", "Denso NDR1 microprocessor"),
|
||||||
|
58: ("EM_STARCORE", "Motorola Start*Core processor"),
|
||||||
|
59: ("EM_ME16", "Toyota ME16 processor"),
|
||||||
|
60: ("EM_ST100", "STMicroelectronic ST100 processor"),
|
||||||
|
61: ("EM_TINYJ", "Advanced Logic Corp. Tinyj emb.fam"),
|
||||||
|
62: ("EM_X86_64", "AMD x86-64 architecture"),
|
||||||
|
63: ("EM_PDSP", "Sony DSP Processor"),
|
||||||
|
64: ("EM_PDP10", "Digital PDP-10"),
|
||||||
|
65: ("EM_PDP11", "Digital PDP-11"),
|
||||||
|
66: ("EM_FX66", "Siemens FX66 microcontroller"),
|
||||||
|
67: ("EM_ST9PLUS", "STMicroelectronics ST9+ 8/16 mc"),
|
||||||
|
68: ("EM_ST7", "STMicroelectronics ST7 8-bit mc"),
|
||||||
|
75: ("EM_VAX", "Digital VAX"),
|
||||||
|
76: ("EM_CRIS", "Axis Communications 32-bit emb.proc"),
|
||||||
|
80: ("EM_MMIX", "Donald Knuth's educational 64-bit proc"),
|
||||||
|
83: ("EM_AVR", "Atmel AVR 8-bit microcontroller"),
|
||||||
|
87: ("EM_V850", "NEC v850"),
|
||||||
|
88: ("EM_M32R", "Mitsubishi M32R"),
|
||||||
|
89: ("EM_MN10300", "Matsushita MN10300"),
|
||||||
|
90: ("EM_MN10200", "Matsushita MN10200"),
|
||||||
|
91: ("EM_PJ", "picoJava"),
|
||||||
|
92: ("EM_OPENRISC", "OpenRISC 32-bit embedded processor"),
|
||||||
|
94: ("EM_XTENSA", "Tensilica Xtensa Architecture"),
|
||||||
|
95: ("EM_VIDEOCORE", "Alphamosaic VideoCore"),
|
||||||
|
96: ("EM_TMM_GPP", "Thompson Multimedia General Purpose Proc"),
|
||||||
|
97: ("EM_NS32K", "National Semi. 32000"),
|
||||||
|
98: ("EM_TPC", "Tenor Network TPC"),
|
||||||
|
99: ("EM_SNP1K", "Trebia SNP 1000"),
|
||||||
|
100: ("EM_ST200", "STMicroelectronics ST200"),
|
||||||
|
101: ("EM_IP2K", "Ubicom IP2xxx"),
|
||||||
|
102: ("EM_MAX", "MAX processor"),
|
||||||
|
103: ("EM_CR", "National Semi. CompactRISC"),
|
||||||
|
104: ("EM_F2MC16", "Fujitsu F2MC16"),
|
||||||
|
105: ("EM_MSP430", "Texas Instruments msp430"),
|
||||||
|
106: ("EM_BLACKFIN", "Analog Devices Blackfin DSP"),
|
||||||
|
107: ("EM_SE_C33", "Seiko Epson S1C33 family"),
|
||||||
|
108: ("EM_SEP", "Sharp embedded microprocessor"),
|
||||||
|
109: ("EM_ARCA", "Arca RISC"),
|
||||||
|
110: ("EM_UNICORE", "PKU-Unity & MPRC Peking Uni. mc series"),
|
||||||
|
111: ("EM_EXCESS", "eXcess configurable cpu"),
|
||||||
|
112: ("EM_DXP", "Icera Semi. Deep Execution Processor"),
|
||||||
|
113: ("EM_ALTERA_NIOS2", "Altera Nios II"),
|
||||||
|
114: ("EM_CRX", "National Semi. CompactRISC CRX"),
|
||||||
|
115: ("EM_XGATE", "Motorola XGATE"),
|
||||||
|
116: ("EM_C166", "Infineon C16x/XC16x"),
|
||||||
|
117: ("EM_M16C", "Renesas M16C"),
|
||||||
|
118: ("EM_DSPIC30F", "Microchip Technology dsPIC30F"),
|
||||||
|
119: ("EM_CE", "Freescale Communication Engine RISC"),
|
||||||
|
120: ("EM_M32C", "Renesas M32C"),
|
||||||
|
131: ("EM_TSK3000", "Altium TSK3000"),
|
||||||
|
132: ("EM_RS08", "Freescale RS08"),
|
||||||
|
133: ("EM_SHARC", "Analog Devices SHARC family"),
|
||||||
|
134: ("EM_ECOG2", "Cyan Technology eCOG2"),
|
||||||
|
135: ("EM_SCORE7", "Sunplus S+core7 RISC"),
|
||||||
|
136: ("EM_DSP24", "New Japan Radio (NJR) 24-bit DSP"),
|
||||||
|
137: ("EM_VIDEOCORE3", "Broadcom VideoCore III"),
|
||||||
|
138: ("EM_LATTICEMICO32", "RISC for Lattice FPGA"),
|
||||||
|
139: ("EM_SE_C17", "Seiko Epson C17"),
|
||||||
|
140: ("EM_TI_C6000", "Texas Instruments TMS320C6000 DSP"),
|
||||||
|
141: ("EM_TI_C2000", "Texas Instruments TMS320C2000 DSP"),
|
||||||
|
142: ("EM_TI_C5500", "Texas Instruments TMS320C55x DSP"),
|
||||||
|
143: ("EM_TI_ARP32", "Texas Instruments App. Specific RISC"),
|
||||||
|
144: ("EM_TI_PRU", "Texas Instruments Prog. Realtime Unit"),
|
||||||
|
160: ("EM_MMDSP_PLUS", "STMicroelectronics 64bit VLIW DSP"),
|
||||||
|
161: ("EM_CYPRESS_M8C", "Cypress M8C"),
|
||||||
|
162: ("EM_R32C", "Renesas R32C"),
|
||||||
|
163: ("EM_TRIMEDIA", "NXP Semi. TriMedia"),
|
||||||
|
164: ("EM_QDSP6", "QUALCOMM DSP6"),
|
||||||
|
165: ("EM_8051", "Intel 8051 and variants"),
|
||||||
|
166: ("EM_STXP7X", "STMicroelectronics STxP7x"),
|
||||||
|
167: ("EM_NDS32", "Andes Tech. compact code emb. RISC"),
|
||||||
|
168: ("EM_ECOG1X", "Cyan Technology eCOG1X"),
|
||||||
|
169: ("EM_MAXQ30", "Dallas Semi. MAXQ30 mc"),
|
||||||
|
170: ("EM_XIMO16", "New Japan Radio (NJR) 16-bit DSP"),
|
||||||
|
171: ("EM_MANIK", "M2000 Reconfigurable RISC"),
|
||||||
|
172: ("EM_CRAYNV2", "Cray NV2 vector architecture"),
|
||||||
|
173: ("EM_RX", "Renesas RX"),
|
||||||
|
174: ("EM_METAG", "Imagination Tech. META"),
|
||||||
|
175: ("EM_MCST_ELBRUS", "MCST Elbrus"),
|
||||||
|
176: ("EM_ECOG16", "Cyan Technology eCOG16"),
|
||||||
|
177: ("EM_CR16", "National Semi. CompactRISC CR16"),
|
||||||
|
178: ("EM_ETPU", "Freescale Extended Time Processing Unit"),
|
||||||
|
179: ("EM_SLE9X", "Infineon Tech. SLE9X"),
|
||||||
|
180: ("EM_L10M", "Intel L10M"),
|
||||||
|
181: ("EM_K10M", "Intel K10M"),
|
||||||
|
183: ("EM_AARCH64", "ARM AARCH64"),
|
||||||
|
185: ("EM_AVR32", "Amtel 32-bit microprocessor"),
|
||||||
|
186: ("EM_STM8", "STMicroelectronics STM8"),
|
||||||
|
187: ("EM_TILE64", "Tileta TILE64"),
|
||||||
|
188: ("EM_TILEPRO", "Tilera TILEPro"),
|
||||||
|
189: ("EM_MICROBLAZE", "Xilinx MicroBlaze"),
|
||||||
|
190: ("EM_CUDA", "NVIDIA CUDA"),
|
||||||
|
191: ("EM_TILEGX", "Tilera TILE-Gx"),
|
||||||
|
192: ("EM_CLOUDSHIELD", "CloudShield"),
|
||||||
|
193: ("EM_COREA_1ST", "KIPO-KAIST Core-A 1st gen."),
|
||||||
|
194: ("EM_COREA_2ND", "KIPO-KAIST Core-A 2nd gen."),
|
||||||
|
195: ("EM_ARC_COMPACT2", "Synopsys ARCompact V2"),
|
||||||
|
196: ("EM_OPEN8", "Open8 RISC"),
|
||||||
|
197: ("EM_RL78", "Renesas RL78"),
|
||||||
|
198: ("EM_VIDEOCORE5", "Broadcom VideoCore V"),
|
||||||
|
199: ("EM_78KOR", "Renesas 78KOR"),
|
||||||
|
200: ("EM_56800EX", "Freescale 56800EX DSC"),
|
||||||
|
201: ("EM_BA1", "Beyond BA1"),
|
||||||
|
202: ("EM_BA2", "Beyond BA2"),
|
||||||
|
203: ("EM_XCORE", "XMOS xCORE"),
|
||||||
|
204: ("EM_MCHP_PIC", "Microchip 8-bit PIC(r)"),
|
||||||
|
210: ("EM_KM32", "KM211 KM32"),
|
||||||
|
211: ("EM_KMX32", "KM211 KMX32"),
|
||||||
|
212: ("EM_EMX16", "KM211 KMX16"),
|
||||||
|
213: ("EM_EMX8", "KM211 KMX8"),
|
||||||
|
214: ("EM_KVARC", "KM211 KVARC"),
|
||||||
|
215: ("EM_CDP", "Paneve CDP"),
|
||||||
|
216: ("EM_COGE", "Cognitive Smart Memory Processor"),
|
||||||
|
217: ("EM_COOL", "Bluechip CoolEngine"),
|
||||||
|
218: ("EM_NORC", "Nanoradio Optimized RISC"),
|
||||||
|
219: ("EM_CSR_KALIMBA", "CSR Kalimba"),
|
||||||
|
220: ("EM_Z80", "Zilog Z80"),
|
||||||
|
221: ("EM_VISIUM", "Controls and Data Services VISIUMcore"),
|
||||||
|
222: ("EM_FT32", "FTDI Chip FT32"),
|
||||||
|
223: ("EM_MOXIE", "Moxie processor"),
|
||||||
|
224: ("EM_AMDGPU", "AMD GPU"),
|
||||||
|
243: ("EM_RISCV", "RISC-V"),
|
||||||
|
247: ("EM_BPF", "Linux BPF -- in-kernel virtual machine"),
|
||||||
|
252: ("EM_CSKY", "C-SKY")
|
||||||
|
}
|
||||||
|
|
||||||
|
section_header = {
|
||||||
|
"sh_name": {
|
||||||
|
"32": "0x00", "64": "0x00", "size32": "4", "size64": "4", "field_name": "sh_name",
|
||||||
|
"description": "An offset to a string in the .shstrtab section that represents the name of this section."
|
||||||
|
},
|
||||||
|
"sh_type": {
|
||||||
|
"32": "0x04", "64": "0x04", "size32": "4", "size64": "4", "field_name": "sh_type",
|
||||||
|
"description": "Identifies the type of this header."
|
||||||
|
},
|
||||||
|
"sh_flags": {
|
||||||
|
"32": "0x08", "64": "0x08", "size32": "4", "size64": "8", "field_name": "sh_flags",
|
||||||
|
"description": "Identifies the attributes of the section."
|
||||||
|
},
|
||||||
|
"sh_addr": {
|
||||||
|
"32": "0x0C", "64": "0x10", "size32": "4", "size64": "8", "field_name": "sh_addr",
|
||||||
|
"description": "Virtual address of the section in memory, for sections that are loaded."
|
||||||
|
},
|
||||||
|
"sh_offset": {
|
||||||
|
"32": "0x10", "64": "0x18", "size32": "4", "size64": "8", "field_name": "sh_offset",
|
||||||
|
"description": "Offset of the section in the file image."
|
||||||
|
},
|
||||||
|
"sh_size": {
|
||||||
|
"32": "0x14", "64": "0x20", "size32": "4", "size64": "8", "field_name": "sh_size",
|
||||||
|
"description": "Size in bytes of the section in memory or 0. May be equal to size in file."
|
||||||
|
},
|
||||||
|
"sh_link": {
|
||||||
|
"32": "0x18", "64": "0x28", "size32": "4", "size64": "4", "field_name": "sh_link",
|
||||||
|
"description": "Contains the section index of an associated section. This field is used for several purposes, depending on the type of section."
|
||||||
|
},
|
||||||
|
"sh_info": {
|
||||||
|
"32": "0x1C", "64": "0x2C", "size32": "4", "size64": "4", "field_name": "sh_info",
|
||||||
|
"description": "Contains extra information about the section. This field is used for several purposes, depending on the type of section."
|
||||||
|
},
|
||||||
|
"sh_addralign": {
|
||||||
|
"32": "0x20", "64": "0x30", "size32": "4", "size64": "8", "field_name": "sh_addralign",
|
||||||
|
"description": "Contains the required alignment of the section. This field must be a power of two."
|
||||||
|
},
|
||||||
|
"sh_entsize": {
|
||||||
|
"32": "0x24", "64": "0x38", "size32": "4", "size64": "8", "field_name": "sh_entsize",
|
||||||
|
"description": "Contains the size, in bytes, of each entry, for sections that contain fixed-size entries. Otherwise, this field contains zero."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section_header_types = {
|
||||||
|
0: ("SHT_NULL", "Section header table entry unused"),
|
||||||
|
1: ("SHT_PROGBITS", "Program data"),
|
||||||
|
2: ("SHT_SYMTAB", "Symbol table"),
|
||||||
|
3: ("SHT_STRTAB", "String table"),
|
||||||
|
4: ("SHT_RELA", "Relocation entries with addends"),
|
||||||
|
5: ("SHT_HASH", "Symbol hash table"),
|
||||||
|
6: ("SHT_DYNAMIC", "Dynamic linking information"),
|
||||||
|
7: ("SHT_NOTE", "Notes"),
|
||||||
|
8: ("SHT_NOBITS", "Program space with no data (bss)"),
|
||||||
|
9: ("SHT_REL", "Relocation entries, no addends"),
|
||||||
|
10: ("SHT_SHLIB", "Reserved"),
|
||||||
|
11: ("SHT_DYNSYM", "Dynamic linker symbol table"),
|
||||||
|
14: ("SHT_INIT_ARRAY", "Array of constructors"),
|
||||||
|
15: ("SHT_FINI_ARRAY", "Array of destructors"),
|
||||||
|
16: ("SHT_PREINIT_ARRAY", "Array of pre-constructors"),
|
||||||
|
17: ("SHT_GROUP", "Section group"),
|
||||||
|
18: ("SHT_SYMTAB_SHNDX", "Extended section indices"),
|
||||||
|
19: ("SHT_NUM", "Number of defined types.")
|
||||||
|
}
|
||||||
|
|
||||||
|
section_header_types_ex = {0x60000000: 'OS-specific',
|
||||||
|
0x70000000: 'Processor-specific',
|
||||||
|
0x80000000: 'Application-specific'}
|
||||||
|
|
||||||
|
symbol_fields = {
|
||||||
|
"st_name": "Index into the string table for the symbol's name",
|
||||||
|
"st_info": "Encodes the symbol's type and its binding attribute",
|
||||||
|
"st_other": "Defines symbol visibility within and outside the object file",
|
||||||
|
"st_shndx": "Specifies which section this symbol is associated with",
|
||||||
|
"st_value": "Holds the symbol's address or offset, depending on context",
|
||||||
|
"st_size": "Specifies the size of the symbol (e.g., function length or variable size)"
|
||||||
|
}
|
||||||
|
|
||||||
|
st_info_values = {
|
||||||
|
0: ("STT_NOTYPE", "Type is unspecified"),
|
||||||
|
1: ("STT_OBJECT", "Data object"),
|
||||||
|
2: ("STT_FUNC", "Code object"),
|
||||||
|
3: ("STT_SECTION", "Section associated"),
|
||||||
|
4: ("STT_FILE", "File name"),
|
||||||
|
5: ("STT_COMMON", "Common data object"),
|
||||||
|
6: ("STT_TLS", "Thread-local data object"),
|
||||||
|
7: ("STT_NUM", "Number of defined types"),
|
||||||
|
10: ("STT_GNU_IFUNC", "Indirect code object")
|
||||||
|
}
|
||||||
|
|
||||||
|
stb_values = {
|
||||||
|
0: ("STB_LOCAL", "Local, not visible outside the object file"),
|
||||||
|
1: ("STB_GLOBAL", "Global, visible to all object files"),
|
||||||
|
2: ("STB_WEAK", "Weak, like global but with lower precedence"),
|
||||||
|
10: ("STB_GNU_UNIQUE", "Unique in the entire process (GNU extension)"),
|
||||||
|
12: ("STB_HIPROC", "Processor-specific binding type")
|
||||||
|
}
|
||||||
|
|
||||||
|
relocation_table_types = {
|
||||||
|
"EM_386": {
|
||||||
|
0: ("R_386_NONE", 0, ""),
|
||||||
|
1: ("R_386_32", 32, "S + A"),
|
||||||
|
2: ("R_386_PC32", 32, "S + A - P"),
|
||||||
|
3: ("R_386_GOT32", 32, "G + A"),
|
||||||
|
4: ("R_386_PLT32", 32, "L + A - P"),
|
||||||
|
5: ("R_386_COPY", 0, ""),
|
||||||
|
6: ("R_386_GLOB_DAT", 32, "S"),
|
||||||
|
7: ("R_386_JMP_SLOT", 32, "S"),
|
||||||
|
8: ("R_386_RELATIVE", 32, "B + A"),
|
||||||
|
9: ("R_386_GOTOFF", 32, "S + A - GOT"),
|
||||||
|
10: ("R_386_GOTPC", 32, "GOT + A - P"),
|
||||||
|
11: ("R_386_32PLT", 32, "L + A"),
|
||||||
|
20: ("R_386_16", 16, "S + A"),
|
||||||
|
21: ("R_386_PC16", 16, "S + A - P"),
|
||||||
|
22: ("R_386_8", 8, "S + A"),
|
||||||
|
23: ("R_386_PC8", 8, "S + A - P"),
|
||||||
|
38: ("R_386_SIZE32", 32, "Z + A")
|
||||||
|
},
|
||||||
|
"EM_X86_64": {
|
||||||
|
0: ("R_AMD64_NONE", 0, ""),
|
||||||
|
1: ("R_AMD64_64", 64, "S + A"),
|
||||||
|
2: ("R_AMD64_PC32", 32, "S + A - P"),
|
||||||
|
3: ("R_AMD64_GOT32", 32, "G + A"),
|
||||||
|
4: ("R_AMD64_PLT32", 32, "L + A - P"),
|
||||||
|
5: ("R_AMD64_COPY", 0, ""),
|
||||||
|
6: ("R_AMD64_GLOB_DAT", 64, "S"),
|
||||||
|
7: ("R_AMD64_JUMP_SLOT", 64, "S"),
|
||||||
|
8: ("R_AMD64_RELATIVE", 64, "B + A"),
|
||||||
|
9: ("R_AMD64_GOTPCREL", 32, "G + GOT + A - P"),
|
||||||
|
10: ("R_AMD64_32", 32, "S + A"),
|
||||||
|
11: ("R_AMD64_32S", 32, "S + A"),
|
||||||
|
12: ("R_AMD64_16", 16, "S + A"),
|
||||||
|
13: ("R_AMD64_PC16", 16, "S + A - P"),
|
||||||
|
14: ("R_AMD64_8", 8, "S + A"),
|
||||||
|
15: ("R_AMD64_PC8", 8, "S + A - P"),
|
||||||
|
24: ("R_AMD64_PC64", 64, "S + A - P"),
|
||||||
|
25: ("R_AMD64_GOTOFF64", 64, "S + A - GOT"),
|
||||||
|
26: ("R_AMD64_GOTPC32", 32, "GOT + A + P"),
|
||||||
|
32: ("R_AMD64_SIZE32", 32, "Z + A"),
|
||||||
|
33: ("R_AMD64_SIZE64", 64, "Z + A"),
|
||||||
|
},
|
||||||
|
"EM_ARM": {
|
||||||
|
0: ("R_ARM_NONE", 0, ""),
|
||||||
|
1: ("R_ARM_PC24", 24, "S - P + A"),
|
||||||
|
2: ("R_ARM_ABS32", 32, "S + A"),
|
||||||
|
3: ("R_ARM_REL32", 32, "S - P + A"),
|
||||||
|
4: ("R_ARM_PC13", 13, "S - P + A"),
|
||||||
|
5: ("R_ARM_ABS16", 16, "S + A"),
|
||||||
|
6: ("R_ARM_ABS12", 12, "S + A"),
|
||||||
|
7: ("R_ARM_THM_ABS5", 5, "S + A"),
|
||||||
|
8: ("R_ARM_ABS8", 8, "S + A"),
|
||||||
|
9: ("R_ARM_SBREL32", 32, "S - B + A"),
|
||||||
|
10: ("R_ARM_THM_PC22", 22, "S - P + A"),
|
||||||
|
11: ("R_ARM_THM_PC8", 8, "S - P + A"),
|
||||||
|
12: ("Reserved", 0, ""),
|
||||||
|
13: ("R_ARM_SWI24", 24, "S + A"),
|
||||||
|
14: ("R_ARM_THM_SWI8", 8, "S + A"),
|
||||||
|
15: ("R_ARM_XPC25", 25, ""),
|
||||||
|
16: ("R_ARM_THM_XPC22", 22, ""),
|
||||||
|
30: ("R_ARM_TLS_DESC", 0, ""),
|
||||||
|
32: ("R_ARM_ALU_PCREL_7_0", 7, "(S - P + A) & 0x000000FF"),
|
||||||
|
33: ("R_ARM_ALU_PCREL_15_8", 15, "(S - P + A) & 0x0000FF00"),
|
||||||
|
34: ("R_ARM_ALU_PCREL_23_15", 23, "(S - P + A) & 0x00FF0000"),
|
||||||
|
35: ("R_ARM_LDR_SBREL_11_0", 11, "(S - B + A) & 0x00000FFF"),
|
||||||
|
36: ("R_ARM_ALU_SBREL_19_12", 19, "(S - B + A) & 0x000FF000"),
|
||||||
|
37: ("R_ARM_ALU_SBREL_27_20", 27, "(S - B + A) & 0x0FF00000"),
|
||||||
|
38: ("R_ARM_RELABS32", 32, "S + A or S - P + A"),
|
||||||
|
39: ("R_ARM_ROSEGREL32", 32, "S - E + A"),
|
||||||
|
40: ("R_ARM_V4BX", 0, ""),
|
||||||
|
41: ("R_ARM_STKCHK", 0, ""),
|
||||||
|
42: ("R_ARM_THM_STKCHK", 0, ""),
|
||||||
|
},
|
||||||
|
"EM_AARCH64": {
|
||||||
|
0: ("R_AARCH64_NONE", 0, ""),
|
||||||
|
257: ("R_AARCH64_ABS64", 64, "S + A"),
|
||||||
|
258: ("R_AARCH64_ABS32", 32, "S + A"),
|
||||||
|
259: ("R_AARCH64_ABS16", 16, "S + A"),
|
||||||
|
260: ("R_AARCH64_PREL64", 64, "S + A - P"),
|
||||||
|
261: ("R_AARCH64_PREL32", 32, "S + A - P"),
|
||||||
|
262: ("R_AARCH64_PREL16", 16, "S + A - P"),
|
||||||
|
263: ("R_AARCH64_MOVW_UABS_G0", 16, "S + A"),
|
||||||
|
264: ("R_AARCH64_MOVW_UABS_G0_NC", 16, "S + A"),
|
||||||
|
265: ("R_AARCH64_MOVW_UABS_G1", 32, "S + A"),
|
||||||
|
266: ("R_AARCH64_MOVW_UABS_G1_NC", 32, "S + A"),
|
||||||
|
267: ("R_AARCH64_MOVW_UABS_G2", 48, "S + A"),
|
||||||
|
268: ("R_AARCH64_MOVW_UABS_G2_NC", 48, "S + A"),
|
||||||
|
269: ("R_AARCH64_MOVW_UABS_G3", 64, "S + A"),
|
||||||
|
270: ("R_AARCH64_MOVW_SABS_G0", 16, "S + A"),
|
||||||
|
271: ("R_AARCH64_MOVW_SABS_G1", 32, "S + A"),
|
||||||
|
272: ("R_AARCH64_MOVW_SABS_G2", 48, "S + A"),
|
||||||
|
273: ("R_AARCH64_LD_PREL_LO19", 19, "S + A - P"),
|
||||||
|
274: ("R_AARCH64_ADR_PREL_LO21", 21, "S + A - P"),
|
||||||
|
275: ("R_AARCH64_ADR_PREL_PG_HI21", 21, "Page(S+A) - Page(P)"),
|
||||||
|
276: ("R_AARCH64_ADR_PREL_PG_HI21_NC", 21, "Page(S+A) - Page(P)"),
|
||||||
|
277: ("R_AARCH64_ADD_ABS_LO12_NC", 12, "S + A"),
|
||||||
|
278: ("R_AARCH64_LDST8_ABS_LO12_NC", 12, "S + A"),
|
||||||
|
279: ("R_AARCH64_TSTBR14", 14, "S + A - P"),
|
||||||
|
280: ("R_AARCH64_CONDBR19", 19, "S + A - P"),
|
||||||
|
282: ("R_AARCH64_JUMP26", 26, "S + A - P"),
|
||||||
|
283: ("R_AARCH64_CALL26", 26, "S + A - P"),
|
||||||
|
284: ("R_AARCH64_LDST16_ABS_LO12_NC", 16, "S + A"),
|
||||||
|
285: ("R_AARCH64_LDST32_ABS_LO12_NC", 32, "S + A"),
|
||||||
|
286: ("R_AARCH64_LDST64_ABS_LO12_NC", 64, "S + A"),
|
||||||
|
287: ("R_AARCH64_MOVW_PREL_G0", 16, "S + A - P"),
|
||||||
|
288: ("R_AARCH64_MOVW_PREL_G0_NC", 16, "S + A - P"),
|
||||||
|
289: ("R_AARCH64_MOVW_PREL_G1", 32, "S + A - P"),
|
||||||
|
290: ("R_AARCH64_MOVW_PREL_G1_NC", 32, "S + A - P"),
|
||||||
|
291: ("R_AARCH64_MOVW_PREL_G2", 48, "S + A - P"),
|
||||||
|
292: ("R_AARCH64_MOVW_PREL_G2_NC", 48, "S + A - P"),
|
||||||
|
293: ("R_AARCH64_MOVW_PREL_G3", 64, "S + A - P"),
|
||||||
|
299: ("R_AARCH64_LDST128_ABS_LO12_NC", 128, "S + A"),
|
||||||
|
300: ("R_AARCH64_MOVW_GOTOFF_G0", 16, "G(GDAT(S+A)) - GOT"),
|
||||||
|
301: ("R_AARCH64_MOVW_GOTOFF_G0_NC", 16, "G(GDAT(S+A)) - GOT"),
|
||||||
|
302: ("R_AARCH64_MOVW_GOTOFF_G1", 32, "G(GDAT(S+A)) - GOT"),
|
||||||
|
303: ("R_AARCH64_MOVW_GOTOFF_G1_NC", 32, "G(GDAT(S+A)) - GOT"),
|
||||||
|
304: ("R_AARCH64_MOVW_GOTOFF_G2", 48, "G(GDAT(S+A)) - GOT"),
|
||||||
|
305: ("R_AARCH64_MOVW_GOTOFF_G2_NC", 48, "G(GDAT(S+A)) - GOT"),
|
||||||
|
306: ("R_AARCH64_MOVW_GOTOFF_G3", 64, "G(GDAT(S+A)) - GOT"),
|
||||||
|
307: ("R_AARCH64_GOTREL64", 64, "S + A - GOT"),
|
||||||
|
308: ("R_AARCH64_GOTREL32", 32, "S + A - GOT"),
|
||||||
|
309: ("R_AARCH64_GOT_LD_PREL19", 19, "G(GDAT(S+A)) - P"),
|
||||||
|
310: ("R_AARCH64_LD64_GOTOFF_LO15", 15, "G(GDAT(S+A)) - GOT"),
|
||||||
|
311: ("R_AARCH64_ADR_GOT_PAGE", 21, "Page(G(GDAT(S+A))) - Page(P)"),
|
||||||
|
312: ("R_AARCH64_LD64_GOT_LO12_NC", 12, "G(GDAT(S+A))"),
|
||||||
|
313: ("R_AARCH64_LD64_GOTPAGE_LO15", 15, "G(GDAT(S+A)) - Page(GOT)"),
|
||||||
|
},
|
||||||
|
"EM_MIPS": {
|
||||||
|
0: ("R_MIPS_NONE", 0, ""),
|
||||||
|
1: ("R_MIPS_16", 16, "S + A"),
|
||||||
|
2: ("R_MIPS_32", 32, "S + A"),
|
||||||
|
3: ("R_MIPS_REL32", 32, "A + S - P"),
|
||||||
|
4: ("R_MIPS_26", 26, "((A << 2) | (P & 0xF0000000)) + S"),
|
||||||
|
5: ("R_MIPS_HI16", 16, "((A + S) >> 16) & 0xFFFF"),
|
||||||
|
6: ("R_MIPS_LO16", 16, "(A + S) & 0xFFFF"),
|
||||||
|
7: ("R_MIPS_GPREL16", 16, "S + A - GP"),
|
||||||
|
8: ("R_MIPS_LITERAL", 16, ""),
|
||||||
|
9: ("R_MIPS_GOT16", 16, "G + A"),
|
||||||
|
10: ("R_MIPS_PC16", 16, "S + A - P"),
|
||||||
|
11: ("R_MIPS_CALL16", 16, "G + A"),
|
||||||
|
12: ("R_MIPS_GPREL32", 32, "S + A - GP"),
|
||||||
|
16: ("R_MIPS_SHIFT5", 5, ""),
|
||||||
|
17: ("R_MIPS_SHIFT6", 6, ""),
|
||||||
|
18: ("R_MIPS_64", 64, "S + A"),
|
||||||
|
19: ("R_MIPS_GOT_DISP", 16, "G + A - GP"),
|
||||||
|
20: ("R_MIPS_GOT_PAGE", 16, "(G + A - GP) >> 16"),
|
||||||
|
21: ("R_MIPS_GOT_OFST", 16, "(G + A - GP) & 0xFFFF"),
|
||||||
|
22: ("R_MIPS_GOT_HI16", 16, "((G + A) >> 16) & 0xFFFF"),
|
||||||
|
23: ("R_MIPS_GOT_LO16", 16, "(G + A) & 0xFFFF"),
|
||||||
|
24: ("R_MIPS_SUB", 64, "S - A"),
|
||||||
|
25: ("R_MIPS_INSERT_A", 0, ""),
|
||||||
|
26: ("R_MIPS_INSERT_B", 0, ""),
|
||||||
|
27: ("R_MIPS_DELETE", 0, ""),
|
||||||
|
28: ("R_MIPS_HIGHER", 16, "(A + S) >> 32"),
|
||||||
|
29: ("R_MIPS_HIGHEST", 16, "(A + S) >> 48"),
|
||||||
|
30: ("R_MIPS_SCN_DISP", 16, ""),
|
||||||
|
31: ("R_MIPS_REL16", 16, ""),
|
||||||
|
32: ("R_MIPS_ADD_IMMEDIATE", 16, ""),
|
||||||
|
33: ("R_MIPS_PJUMP", 26, ""),
|
||||||
|
34: ("R_MIPS_RELGOT", 32, ""),
|
||||||
|
35: ("R_MIPS_JALR", 0, ""),
|
||||||
|
36: ("R_MIPS_TLS_DTPMOD32", 32, "TLSMODULE"),
|
||||||
|
37: ("R_MIPS_TLS_DTPREL32", 32, "S + A - TLS_DTV_OFFSET"),
|
||||||
|
38: ("R_MIPS_TLS_DTPMOD64", 64, "TLSMODULE"),
|
||||||
|
39: ("R_MIPS_TLS_DTPREL64", 64, "S + A - TLS_DTV_OFFSET"),
|
||||||
|
40: ("R_MIPS_TLS_GD", 16, "G"),
|
||||||
|
41: ("R_MIPS_TLS_LDM", 16, "G"),
|
||||||
|
42: ("R_MIPS_TLS_DTPREL_HI16", 16, "((S + A - TLS_DTV_OFFSET) >> 16) & 0xFFFF"),
|
||||||
|
43: ("R_MIPS_TLS_DTPREL_LO16", 16, "(S + A - TLS_DTV_OFFSET) & 0xFFFF"),
|
||||||
|
44: ("R_MIPS_TLS_GOTTPREL", 16, "G"),
|
||||||
|
45: ("R_MIPS_TLS_TPREL32", 32, "S + A + TLSOFFSET"),
|
||||||
|
46: ("R_MIPS_TLS_TPREL64", 64, "S + A + TLSOFFSET"),
|
||||||
|
47: ("R_MIPS_TLS_TPREL_HI16", 16, "((S + A + TLSOFFSET) >> 16) & 0xFFFF"),
|
||||||
|
48: ("R_MIPS_TLS_TPREL_LO16", 16, "(S + A + TLSOFFSET) & 0xFFFF")
|
||||||
|
},
|
||||||
|
"EM_RISCV": {
|
||||||
|
0: ("R_RISCV_NONE", 0, ""),
|
||||||
|
1: ("R_RISCV_32", 32, "S + A"),
|
||||||
|
2: ("R_RISCV_64", 64, "S + A"),
|
||||||
|
3: ("R_RISCV_RELATIVE", 64, "B + A"),
|
||||||
|
4: ("R_RISCV_COPY", 0, ""),
|
||||||
|
5: ("R_RISCV_JUMP_SLOT", 0, "S"),
|
||||||
|
6: ("R_RISCV_TLS_DTPMOD32", 32, "TLSMODULE"),
|
||||||
|
7: ("R_RISCV_TLS_DTPMOD64", 64, "TLSMODULE"),
|
||||||
|
8: ("R_RISCV_TLS_DTPREL32", 32, "S + A - TLS_DTV_OFFSET"),
|
||||||
|
9: ("R_RISCV_TLS_DTPREL64", 64, "S + A - TLS_DTV_OFFSET"),
|
||||||
|
10: ("R_RISCV_TLS_TPREL32", 32, "S + A + TLSOFFSET"),
|
||||||
|
11: ("R_RISCV_TLS_TPREL64", 64, "S + A + TLSOFFSET"),
|
||||||
|
12: ("R_RISCV_TLSDESC", 0, "TLSDESC(S+A)"),
|
||||||
|
16: ("R_RISCV_BRANCH", 0, "S + A - P"),
|
||||||
|
17: ("R_RISCV_JAL", 0, "S + A - P"),
|
||||||
|
18: ("R_RISCV_CALL", 0, "S + A - P"),
|
||||||
|
19: ("R_RISCV_CALL_PLT", 0, "S + A - P"),
|
||||||
|
20: ("R_RISCV_GOT_HI20", 0, "G + GOT + A - P"),
|
||||||
|
21: ("R_RISCV_TLS_GOT_HI20", 0, ""),
|
||||||
|
22: ("R_RISCV_TLS_GD_HI20", 0, ""),
|
||||||
|
23: ("R_RISCV_PCREL_HI20", 0, "S + A - P"),
|
||||||
|
24: ("R_RISCV_PCREL_LO12_I", 0, "S - P"),
|
||||||
|
25: ("R_RISCV_PCREL_LO12_S", 0, "S - P"),
|
||||||
|
26: ("R_RISCV_HI20", 0, "S + A"),
|
||||||
|
27: ("R_RISCV_LO12_I", 0, "S + A"),
|
||||||
|
28: ("R_RISCV_LO12_S", 0, "S + A"),
|
||||||
|
29: ("R_RISCV_TPREL_HI20", 0, ""),
|
||||||
|
30: ("R_RISCV_TPREL_LO12_I", 0, ""),
|
||||||
|
31: ("R_RISCV_TPREL_LO12_S", 0, ""),
|
||||||
|
32: ("R_RISCV_TPREL_ADD", 0, ""),
|
||||||
|
33: ("R_RISCV_ADD8", 8, "V + S + A"),
|
||||||
|
34: ("R_RISCV_ADD16", 16, "V + S + A"),
|
||||||
|
35: ("R_RISCV_ADD32", 32, "V + S + A"),
|
||||||
|
36: ("R_RISCV_ADD64", 64, "V + S + A"),
|
||||||
|
37: ("R_RISCV_SUB8", 8, "V - S - A"),
|
||||||
|
38: ("R_RISCV_SUB16", 16, "V - S - A"),
|
||||||
|
39: ("R_RISCV_SUB32", 32, "V - S - A"),
|
||||||
|
40: ("R_RISCV_SUB64", 64, "V - S - A"),
|
||||||
|
41: ("R_RISCV_GOT32_PCREL", 32, "G + GOT + A - P"),
|
||||||
|
42: ("R_RISCV_Reserved", 0, ""),
|
||||||
|
43: ("R_RISCV_ALIGN", 0, ""),
|
||||||
|
44: ("R_RISCV_RVC_BRANCH", 0, "S + A - P"),
|
||||||
|
45: ("R_RISCV_RVC_JUMP", 0, "S + A - P"),
|
||||||
|
46: ("R_RISCV_Reserved", 0, ""),
|
||||||
|
51: ("R_RISCV_RELAX", 0, ""),
|
||||||
|
52: ("R_RISCV_SUB6", 6, "V - S - A"),
|
||||||
|
53: ("R_RISCV_SET6", 6, "S + A"),
|
||||||
|
54: ("R_RISCV_SET8", 8, "S + A"),
|
||||||
|
55: ("R_RISCV_SET16", 16, "S + A"),
|
||||||
|
56: ("R_RISCV_SET32", 32, "S + A"),
|
||||||
|
57: ("R_RISCV_32_PCREL", 32, "S + A - P"),
|
||||||
|
58: ("R_RISCV_IRELATIVE", 0, "ifunc_resolver(B + A)"),
|
||||||
|
59: ("R_RISCV_PLT32", 32, "S + A - P"),
|
||||||
|
60: ("R_RISCV_SET_ULEB128", 0, "S + A"),
|
||||||
|
61: ("R_RISCV_SUB_ULEB128", 0, "V - S - A"),
|
||||||
|
62: ("R_RISCV_TLSDESC_HI20", 0, "S + A - P"),
|
||||||
|
63: ("R_RISCV_TLSDESC_LOAD_LO12", 0, "S - P"),
|
||||||
|
64: ("R_RISCV_TLSDESC_ADD_LO12", 0, "S - P"),
|
||||||
|
65: ("R_RISCV_TLSDESC_CALL", 0, ""),
|
||||||
|
191: ("R_RISCV_VENDOR", 0, "")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,572 @@
|
||||||
|
"""Pelfy is an ELF parser for parsing header fields, sections, symbols and relocations.
|
||||||
|
|
||||||
|
Typical usage example:
|
||||||
|
|
||||||
|
elf = pelfy.open_elf_file('obj/test-c-riscv64-linux-gnu-gcc-12-O3.o')
|
||||||
|
print(elf.sections)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import _fields_data as fdat
|
||||||
|
from . import _output_formatter
|
||||||
|
from typing import TypeVar, Literal, Iterable, Generic, Iterator, Generator, Optional, Union
|
||||||
|
|
||||||
|
_T = TypeVar('_T')
|
||||||
|
|
||||||
|
|
||||||
|
def open_elf_file(file_path: str) -> 'elf_file':
|
||||||
|
"""Reads ELF data from file
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: path of the ELF file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
elf_file object
|
||||||
|
"""
|
||||||
|
with open(file_path, mode='rb') as f:
|
||||||
|
return elf_file(f.read())
|
||||||
|
|
||||||
|
|
||||||
|
class elf_symbol():
|
||||||
|
"""A class for representing data of an ELF symbol
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
file: Points to the parent ELF file object.
|
||||||
|
name: Name of the symbol
|
||||||
|
section: section where the symbol data is placed
|
||||||
|
index: Absolut index in the symbol table
|
||||||
|
info: Type of the symbol
|
||||||
|
description: Description of the symbol type
|
||||||
|
stb: visibility of the symbol (local, global, etc.)
|
||||||
|
stb_description: Description of the symbol visibility
|
||||||
|
fields: All symbol header fields as dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, file: 'elf_file', fields: dict[str, int], index: int):
|
||||||
|
"""
|
||||||
|
Initializes ELF symbol instance
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file: ELF file object
|
||||||
|
fields: symbol header fields
|
||||||
|
index: Absolut index in the symbol table
|
||||||
|
"""
|
||||||
|
self.fields = fields
|
||||||
|
self.file = file
|
||||||
|
|
||||||
|
if file.string_table_section:
|
||||||
|
self.name = file.read_string(file.string_table_section['sh_offset'] + fields['st_name'])
|
||||||
|
else:
|
||||||
|
self.name = ''
|
||||||
|
|
||||||
|
self.section: Optional[elf_section] = self.file.sections[self['st_shndx']] if self['st_shndx'] < len(self.file.sections) else None
|
||||||
|
|
||||||
|
self.index = index
|
||||||
|
|
||||||
|
self.info, self.description = fdat.st_info_values[fields['st_info'] & 0x0F]
|
||||||
|
self.stb, self.stb_description = fdat.stb_values[fields['st_info'] >> 4]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self) -> bytes:
|
||||||
|
"""Returns the binary data the symbol is pointing to.
|
||||||
|
The offset in the ELF file is calculated by:
|
||||||
|
sections[symbol.st_shndx].sh_offset + symbol.st_value
|
||||||
|
"""
|
||||||
|
assert self.section, 'This symbol is not associated to a data section'
|
||||||
|
if self.section.type == 'SHT_NOBITS':
|
||||||
|
return b'\x00' * self['st_size']
|
||||||
|
else:
|
||||||
|
offset = self.section['sh_offset'] + self['st_value']
|
||||||
|
return self.file.read_bytes(offset, self['st_size'])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_hex(self) -> str:
|
||||||
|
"""Returns the binary data the symbol is pointing to as hex string.
|
||||||
|
"""
|
||||||
|
return ' '.join(f'{d:02X}' for d in self.data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def relocations(self) -> 'relocation_list':
|
||||||
|
"""Relocations that are pointing to this symbol.
|
||||||
|
The symbol section must be of type SHT_PROGBITS (program code). Therefore
|
||||||
|
this property returns typically all relocations that will be
|
||||||
|
applied to the function represented by the symbol.
|
||||||
|
"""
|
||||||
|
ret: list[elf_relocation] = list()
|
||||||
|
assert self.section and self.section.type == 'SHT_PROGBITS'
|
||||||
|
for reloc in self.file.get_relocations():
|
||||||
|
if reloc.target_section == self.section:
|
||||||
|
offset = reloc['r_offset'] - self['st_value']
|
||||||
|
if 0 <= offset < self['st_size']:
|
||||||
|
ret.append(reloc)
|
||||||
|
return relocation_list(ret)
|
||||||
|
|
||||||
|
def __getitem__(self, key: Union[str, int]) -> int:
|
||||||
|
if isinstance(key, str):
|
||||||
|
assert key in self.fields, f'Unknown field name: {key}'
|
||||||
|
return self.fields[key]
|
||||||
|
else:
|
||||||
|
return list(self.fields.values())[key]
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'index {self.index}\n' +\
|
||||||
|
f'name {self.name}\n' +\
|
||||||
|
f'stb {self.stb} ({self.stb_description})\n' +\
|
||||||
|
f'info {self.info} ({self.description})\n' +\
|
||||||
|
'\n'.join(f"{k:18} {v:4} {fdat.symbol_fields[k]}" for k, v in self.fields.items()) + '\n'
|
||||||
|
|
||||||
|
|
||||||
|
class elf_section():
|
||||||
|
"""A class for representing data of an ELF section
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
file: Points to the parent ELF file object.
|
||||||
|
name: Name of the section
|
||||||
|
index: Absolut index of the section
|
||||||
|
type: Type of the section
|
||||||
|
description: Description of the section type
|
||||||
|
fields: All symbol header fields as dict
|
||||||
|
"""
|
||||||
|
def __init__(self, file: 'elf_file', fields: dict[str, int], name: str, index: int):
|
||||||
|
"""Initializes an ELF section instance
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file: ELF file object
|
||||||
|
fields: Section header fields
|
||||||
|
name: Name of the section
|
||||||
|
index: Absolut index in the symbol table
|
||||||
|
"""
|
||||||
|
self.fields = fields
|
||||||
|
self.file = file
|
||||||
|
self.index = index
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
if fields['sh_type'] > 0x60000000:
|
||||||
|
# Range for OS, compiler and application specific types
|
||||||
|
self.description = [v for k, v in fdat.section_header_types_ex.items() if k >= fields['sh_type']][0]
|
||||||
|
self.type = str(hex(fields['sh_type']))
|
||||||
|
elif fields['sh_type'] in fdat.section_header_types:
|
||||||
|
self.type, self.description = fdat.section_header_types[fields['sh_type']]
|
||||||
|
else:
|
||||||
|
self.description = ''
|
||||||
|
self.type = str(hex(fields['sh_type']))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data(self) -> bytes:
|
||||||
|
"""Returns the binary data from the section.
|
||||||
|
The offset in the ELF file is given by: section.sh_offset
|
||||||
|
"""
|
||||||
|
if self.type == 'SHT_NOBITS':
|
||||||
|
return b'\x00' * self['sh_size']
|
||||||
|
else:
|
||||||
|
return self.file.read_bytes(self['sh_offset'], self['sh_size'])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def symbols(self) -> 'symbol_list':
|
||||||
|
"""All ELF symbols associated with this section
|
||||||
|
"""
|
||||||
|
return symbol_list(self.file.list_symbols(self.index))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_hex(self) -> str:
|
||||||
|
"""Returns the binary data from the section as hex string.
|
||||||
|
"""
|
||||||
|
return ' '.join(f'{d:02X}' for d in self.data)
|
||||||
|
|
||||||
|
def __getitem__(self, key: Union[str, int]) -> int:
|
||||||
|
if isinstance(key, str):
|
||||||
|
assert key in self.fields, f'Unknown field name: {key}'
|
||||||
|
return self.fields[key]
|
||||||
|
else:
|
||||||
|
return list(self.fields.values())[key]
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'index {self.index}\n' +\
|
||||||
|
f'name {self.name}\n' +\
|
||||||
|
f'type {self.type} ({self.description})\n' +\
|
||||||
|
'\n'.join(f"{k:18} {v:4} {fdat.section_header[k]['description']}" for k, v in self.fields.items()) + '\n'
|
||||||
|
|
||||||
|
|
||||||
|
class elf_relocation():
|
||||||
|
"""A class for representing data of a relocation
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
file: Points to the parent ELF file object.
|
||||||
|
index: Absolut index of the relocation in the associated relocation section
|
||||||
|
symbol: Symbol to relocate
|
||||||
|
type: Type of the relocation
|
||||||
|
calculation: Description of the relocation calculation
|
||||||
|
bits: number ob bits to patch by the relocation
|
||||||
|
target_section: Pointing to the section where this relocation applies to
|
||||||
|
fields: All relocation header fields as dict
|
||||||
|
"""
|
||||||
|
def __init__(self, file: 'elf_file', fields: dict[str, int], symbol_index: int,
|
||||||
|
relocation_type: int, sh_info: int, index: int):
|
||||||
|
"""Initializes a ELF relocation instance
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file: ELF file object
|
||||||
|
fields: Relocation header fields
|
||||||
|
symbol_index: Index of the symbol to relocate in the symbol table
|
||||||
|
relocation_type: Type of the relocation (numeric)
|
||||||
|
sh_info: Index of the section this relocation applies to
|
||||||
|
index: Absolut index of the relocation in the associated relocation section
|
||||||
|
"""
|
||||||
|
self.fields = fields
|
||||||
|
self.file = file
|
||||||
|
self.index = index
|
||||||
|
self.symbol = file.symbols[symbol_index]
|
||||||
|
reloc_types = fdat.relocation_table_types.get(file.architecture)
|
||||||
|
if reloc_types and relocation_type in reloc_types:
|
||||||
|
self.type = reloc_types[relocation_type][0]
|
||||||
|
self.bits = reloc_types[relocation_type][1]
|
||||||
|
self.calculation = reloc_types[relocation_type][2]
|
||||||
|
else:
|
||||||
|
self.type = str(relocation_type)
|
||||||
|
self.bits = 0
|
||||||
|
self.calculation = ''
|
||||||
|
self.target_section: elf_section = file.sections[sh_info]
|
||||||
|
|
||||||
|
def __getitem__(self, key: Union[str, int]) -> int:
|
||||||
|
if isinstance(key, str):
|
||||||
|
assert key in self.fields, f'Unknown field name: {key}'
|
||||||
|
return self.fields[key]
|
||||||
|
else:
|
||||||
|
return list(self.fields.values())[key]
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'index {self.symbol.index}\n' +\
|
||||||
|
f'symbol {self.symbol.name}\n' +\
|
||||||
|
f'relocation type {self.type}\n' +\
|
||||||
|
f'calculation {self.calculation}\n' +\
|
||||||
|
f'bits {self.bits}\n' +\
|
||||||
|
'\n'.join(f'{k:18} {v:4}' for k, v in self.fields.items()) + '\n'
|
||||||
|
|
||||||
|
|
||||||
|
class elf_list(Generic[_T]):
|
||||||
|
"""A generic class for representing a list of ELF data items
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: Iterable of ELF data items
|
||||||
|
"""
|
||||||
|
def __init__(self, data: Iterable[_T]):
|
||||||
|
self._data = list(data)
|
||||||
|
|
||||||
|
def __getitem__(self, key: Union[str, int]) -> _T:
|
||||||
|
if isinstance(key, str):
|
||||||
|
elements = [el for el in self._data if getattr(el, 'name', '') == key]
|
||||||
|
assert elements, f'Unknown name: {key}'
|
||||||
|
return elements[0]
|
||||||
|
else:
|
||||||
|
return self._data.__getitem__(key)
|
||||||
|
|
||||||
|
def __len__(self) -> int:
|
||||||
|
return len(self._data)
|
||||||
|
|
||||||
|
def __iter__(self) -> Iterator[_T]:
|
||||||
|
return iter(self._data)
|
||||||
|
|
||||||
|
def _compact_table(self) -> tuple[list[str], list[list[Union[str, int]]], list[str]]:
|
||||||
|
return [], [[]], []
|
||||||
|
|
||||||
|
def _repr_table(self, format: _output_formatter.table_format, raw_data: bool = False) -> str:
|
||||||
|
if raw_data and len(self):
|
||||||
|
table_dict: list[dict[str, int]] = [el.__dict__.get('fields', {' ': 0}) for el in self]
|
||||||
|
columns = list(table_dict[0].keys())
|
||||||
|
data: list[list[Union[str, int]]] = [list(el.values()) for el in table_dict]
|
||||||
|
radj = columns
|
||||||
|
else:
|
||||||
|
columns, data, radj = self._compact_table()
|
||||||
|
return _output_formatter.generate_table(data, columns, right_adj_col=radj, format=format)
|
||||||
|
|
||||||
|
def to_dict_list(self) -> list[dict[str, Union[str, int]]]:
|
||||||
|
"""Exporting the ELF item data table to a list of dicts. It can be used with pandas:
|
||||||
|
df = pandas.DataFrame(elements.to_dict_list())
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Table data
|
||||||
|
"""
|
||||||
|
columns, data, _ = self._compact_table()
|
||||||
|
return [{k: v for k, v in zip(columns, row)} for row in data]
|
||||||
|
|
||||||
|
def to_html(self) -> str:
|
||||||
|
"""Exporting the ELF item data table to HTML.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
HTML table
|
||||||
|
"""
|
||||||
|
return self._repr_table('html')
|
||||||
|
|
||||||
|
def to_markdown(self) -> str:
|
||||||
|
"""Exporting the ELF item data table to markdown.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Markdown table
|
||||||
|
"""
|
||||||
|
return self._repr_table('markdown')
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return self._repr_table('text')
|
||||||
|
|
||||||
|
def _repr_html_(self) -> str:
|
||||||
|
return self._repr_table('html')
|
||||||
|
|
||||||
|
def _repr_markdown_(self) -> str:
|
||||||
|
return self._repr_table('markdown')
|
||||||
|
|
||||||
|
|
||||||
|
class section_list(elf_list[elf_section]):
|
||||||
|
"""A class for representing a list of ELF section
|
||||||
|
"""
|
||||||
|
def _compact_table(self) -> tuple[list[str], list[list[Union[str, int]]], list[str]]:
|
||||||
|
columns = ['index', 'name', 'type', 'description']
|
||||||
|
data: list[list[Union[str, int]]] = [[item.index, item.name, item.type,
|
||||||
|
item.description] for item in self]
|
||||||
|
return columns, data, ['index']
|
||||||
|
|
||||||
|
|
||||||
|
class symbol_list(elf_list[elf_symbol]):
|
||||||
|
"""A class for representing a list of ELF symbols
|
||||||
|
"""
|
||||||
|
def _compact_table(self) -> tuple[list[str], list[list[Union[str, int]]], list[str]]:
|
||||||
|
columns = ['index', 'name', 'info', 'size', 'stb', 'section', 'description']
|
||||||
|
data: list[list[Union[str, int]]] = [[item.index, item.name, item.info, item.fields['st_size'],
|
||||||
|
item.stb, item.section.name if item.section else '', item.description] for item in self]
|
||||||
|
return columns, data, ['index', 'size']
|
||||||
|
|
||||||
|
|
||||||
|
class relocation_list(elf_list[elf_relocation]):
|
||||||
|
"""A class for representing a list of ELF relocations
|
||||||
|
"""
|
||||||
|
def _compact_table(self) -> tuple[list[str], list[list[Union[str, int]]], list[str]]:
|
||||||
|
columns = ['index', 'symbol name', 'type', 'calculation', 'bits']
|
||||||
|
data: list[list[Union[str, int]]] = [[item.index, item.symbol.name, item.type,
|
||||||
|
item.calculation, item.bits] for item in self]
|
||||||
|
return columns, data, ['index', 'bits']
|
||||||
|
|
||||||
|
|
||||||
|
class elf_file:
|
||||||
|
"""A class for representing data of an ELF file in a structured form
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
byteorder: Byte order of the architecture 'little' or 'big'
|
||||||
|
(based on e_ident[EI_DATA])
|
||||||
|
bit_width: Bit with of the architecture: 32 or 64 (based on
|
||||||
|
e_ident[EI_CLASS])
|
||||||
|
architecture: Name of the architecture (based on e_machine)
|
||||||
|
fields: All ELF header fields as dict
|
||||||
|
sections: A list of all ELF sections
|
||||||
|
symbols: A list of all ELF symbols
|
||||||
|
functions: A list of all function symbols (STT_FUNC)
|
||||||
|
objects: A list of all variable/object symbols (STT_OBJECT)
|
||||||
|
code_relocations: A list of all code relocations (.rela.text and .rel.text)
|
||||||
|
symbol_table_section: The symbol table section (first section with
|
||||||
|
the type SHT_SYMTAB)
|
||||||
|
string_table_section: The string table section (first section with
|
||||||
|
the name .strtab)
|
||||||
|
"""
|
||||||
|
def __init__(self, data: Union[bytes, bytearray]):
|
||||||
|
"""Initializes an ELF file instance
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: binary ELF data
|
||||||
|
"""
|
||||||
|
assert isinstance(data, (bytes, bytearray)), 'Binary ELF data must be provided as bytes or bytearray.'
|
||||||
|
self._data = bytes(data)
|
||||||
|
|
||||||
|
# Defaults required for function _read_int_from_elf_field
|
||||||
|
self.bit_width = 32
|
||||||
|
self.byteorder: Literal['little', 'big'] = 'little'
|
||||||
|
|
||||||
|
assert self._read_bytes_from_elf_field('e_ident[EI_MAG]') == b'\x7fELF', 'Not an ELF file'
|
||||||
|
|
||||||
|
self.bit_width = {1: 32, 2: 64}[self._read_int_from_elf_field('e_ident[EI_CLASS]')]
|
||||||
|
|
||||||
|
byte_order = self._read_int_from_elf_field('e_ident[EI_DATA]')
|
||||||
|
assert byte_order in [1, 2], 'Invalid byte order value e_ident[EI_DATA]'
|
||||||
|
self.byteorder = 'little' if byte_order == 1 else 'big'
|
||||||
|
|
||||||
|
self.fields = {fn: self._read_int_from_elf_field(fn) for fn in fdat.elf_header_field.keys()}
|
||||||
|
|
||||||
|
arch_entr = fdat.e_machine_dict.get(self.fields['e_machine'])
|
||||||
|
self.architecture = arch_entr[0] if arch_entr else str(self.fields['e_machine'])
|
||||||
|
|
||||||
|
section_data = list(self._list_sections())
|
||||||
|
name_addr: dict[str, int] = section_data[self.fields['e_shstrndx']] if section_data else dict()
|
||||||
|
section_names = (self.read_string(name_addr['sh_offset'] + f['sh_name']) for f in section_data)
|
||||||
|
|
||||||
|
self.sections = section_list(elf_section(self, sd, sn, i)
|
||||||
|
for i, (sd, sn) in enumerate(zip(section_data, section_names)))
|
||||||
|
|
||||||
|
ret_sections = [sh for sh in self.sections if sh.type == 'SHT_SYMTAB']
|
||||||
|
self.symbol_table_section = ret_sections[0] if ret_sections else None
|
||||||
|
|
||||||
|
ret_sections = [sh for sh in self.sections if sh.name == '.strtab']
|
||||||
|
self.string_table_section = ret_sections[0] if ret_sections else None
|
||||||
|
|
||||||
|
self.symbols = symbol_list(self.list_symbols())
|
||||||
|
|
||||||
|
self.functions = symbol_list(s for s in self.symbols if s.info == 'STT_FUNC')
|
||||||
|
self.objects = symbol_list(s for s in self.symbols if s.info == 'STT_OBJECT')
|
||||||
|
|
||||||
|
self.code_relocations = self.get_relocations(['.rela.text', '.rel.text'])
|
||||||
|
|
||||||
|
def _list_sections(self) -> Generator[dict[str, int], None, None]:
|
||||||
|
for i in range(self.fields['e_shnum']):
|
||||||
|
offs = self.fields['e_shoff'] + i * self.fields['e_shentsize']
|
||||||
|
yield {fn: self._read_from_sh_field(offs, fn) for fn in fdat.section_header.keys()}
|
||||||
|
|
||||||
|
def list_symbols(self, section_index: Optional[int] = None) -> Generator[elf_symbol, None, None]:
|
||||||
|
"""List ELF symbols.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
section_index: If provided, only symbols from the specified section are returned.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of ELF symbols
|
||||||
|
"""
|
||||||
|
if self.symbol_table_section:
|
||||||
|
offs = self.symbol_table_section['sh_offset']
|
||||||
|
|
||||||
|
for j, i in enumerate(range(offs, self.symbol_table_section['sh_size'] + offs, self.symbol_table_section['sh_entsize'])):
|
||||||
|
ret = {'st_name': self.read_int(i, 4)}
|
||||||
|
|
||||||
|
if self.bit_width == 32:
|
||||||
|
ret['st_value'] = self.read_int(i + 4, 4)
|
||||||
|
ret['st_size'] = self.read_int(i + 8, 4)
|
||||||
|
ret['st_info'] = self.read_int(i + 12, 1)
|
||||||
|
ret['st_other'] = self.read_int(i + 13, 1)
|
||||||
|
ret['st_shndx'] = self.read_int(i + 14, 2)
|
||||||
|
elif self.bit_width == 64:
|
||||||
|
ret['st_info'] = self.read_int(i + 4, 1)
|
||||||
|
ret['st_other'] = self.read_int(i + 5, 1)
|
||||||
|
ret['st_shndx'] = self.read_int(i + 6, 2)
|
||||||
|
ret['st_value'] = self.read_int(i + 8, 8)
|
||||||
|
ret['st_size'] = self.read_int(i + 16, 8)
|
||||||
|
|
||||||
|
if section_index is None or section_index == ret['st_shndx']:
|
||||||
|
yield elf_symbol(self, ret, j)
|
||||||
|
|
||||||
|
def get_relocations(self, reloc_section: Optional[Union[elf_section, str, list[str]]] = None) -> relocation_list:
|
||||||
|
"""List relocations.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
reloc_section: Specifies the relocation section from which the
|
||||||
|
relocations should be listed. It can be provided as
|
||||||
|
elf_section object or by its name. If not provided
|
||||||
|
(reloc_section=None) relocations from all relocation
|
||||||
|
sections are returned.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of relocations
|
||||||
|
"""
|
||||||
|
if isinstance(reloc_section, elf_section):
|
||||||
|
assert reloc_section.type in ('SHT_REL', 'SHT_RELA'), f'{reloc_section.name} is not a relocation section'
|
||||||
|
return relocation_list(self._list_relocations(reloc_section))
|
||||||
|
else:
|
||||||
|
relocations: list[elf_relocation] = list()
|
||||||
|
for sh in self.sections:
|
||||||
|
if sh.type in ('SHT_REL', 'SHT_RELA'):
|
||||||
|
if reloc_section is None or \
|
||||||
|
(isinstance(reloc_section, str) and sh.name == reloc_section) or \
|
||||||
|
(isinstance(reloc_section, list) and sh.name in reloc_section):
|
||||||
|
relocations += relocation_list(self._list_relocations(sh))
|
||||||
|
|
||||||
|
return relocation_list(relocations)
|
||||||
|
|
||||||
|
def _list_relocations(self, sh: elf_section) -> Generator[elf_relocation, None, None]:
|
||||||
|
"""List relocations for a elf_section.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
elf_section: Specifies the relocation section from which the
|
||||||
|
relocations should be listed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Relocations from specified elf_section
|
||||||
|
"""
|
||||||
|
offs = sh['sh_offset']
|
||||||
|
for i, el_off in enumerate(range(offs, sh['sh_size'] + offs, sh['sh_entsize'])):
|
||||||
|
ret: dict[str, int] = dict()
|
||||||
|
|
||||||
|
if self.bit_width == 32:
|
||||||
|
ret['r_offset'] = self.read_int(el_off, 4)
|
||||||
|
r_info = self.read_int(el_off + 4, 4)
|
||||||
|
ret['r_info'] = r_info
|
||||||
|
ret['r_addend'] = self.read_int(el_off + 8, 4, True) if sh.type == 'SHT_RELA' else 0
|
||||||
|
yield elf_relocation(self, ret, r_info >> 8, r_info & 0xFF, sh['sh_info'], i)
|
||||||
|
elif self.bit_width == 64:
|
||||||
|
ret['r_offset'] = self.read_int(el_off, 8)
|
||||||
|
r_info = self.read_int(el_off + 8, 8)
|
||||||
|
ret['r_info'] = r_info
|
||||||
|
ret['r_addend'] = self.read_int(el_off + 16, 8, True) if sh.type == 'SHT_RELA' else 0
|
||||||
|
yield elf_relocation(self, ret, r_info >> 32, r_info & 0xFFFFFFFF, sh['sh_info'], i)
|
||||||
|
|
||||||
|
def read_bytes(self, offset: int, num_bytes: int) -> bytes:
|
||||||
|
"""Read bytes from ELF file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
offset: Specify first byte relative to the start of
|
||||||
|
the ELF file.
|
||||||
|
num_bytes: Specify the number of bytes to read.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Binary data as bytes
|
||||||
|
"""
|
||||||
|
return self._data[offset:offset + num_bytes]
|
||||||
|
|
||||||
|
def read_int(self, offset: int, num_bytes: int, signed: bool = False) -> int:
|
||||||
|
"""Read an integer from the ELF file. Byte order is
|
||||||
|
selected according to the architecture (e_ident[EI_DATA]).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
offset: Specify first byte of the integer relative to
|
||||||
|
the start of the ELF file.
|
||||||
|
num_bytes: Specify the size of the integer in bytes.
|
||||||
|
signed: Select if the integer is a signed integer.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Integer value
|
||||||
|
"""
|
||||||
|
return int.from_bytes(self._data[offset:offset + num_bytes], self.byteorder, signed=signed)
|
||||||
|
|
||||||
|
# def int_to_bytes(self, value: int, num_bytes: int = 4, signed: bool = False) -> int:
|
||||||
|
# return value.to_bytes(length=num_bytes, byteorder=self.byteorder, signed=signed)
|
||||||
|
|
||||||
|
def read_string(self, offset: int, encoding: str = 'utf-8') -> str:
|
||||||
|
"""Read a zero-terminated text string from the ELF file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
offset: Specify first byte of the string relative to
|
||||||
|
the start of the ELF file.
|
||||||
|
encoding: Encoding used for text decoding.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Text string
|
||||||
|
"""
|
||||||
|
str_end = self._data.find(b'\x00', offset)
|
||||||
|
return self._data[offset:str_end].decode(encoding)
|
||||||
|
|
||||||
|
def _read_int_from_elf_field(self, field_name: str) -> int:
|
||||||
|
field = fdat.elf_header_field[field_name]
|
||||||
|
offs = int(field[str(self.bit_width)], base=16)
|
||||||
|
byte_len = int(field['size' + str(self.bit_width)])
|
||||||
|
return self.read_int(offs, byte_len)
|
||||||
|
|
||||||
|
def _read_bytes_from_elf_field(self, field_name: str) -> bytes:
|
||||||
|
field = fdat.elf_header_field[field_name]
|
||||||
|
offs = int(field[str(self.bit_width)], base=16)
|
||||||
|
byte_len = int(field['size' + str(self.bit_width)])
|
||||||
|
return self.read_bytes(offs, byte_len)
|
||||||
|
|
||||||
|
def _read_from_sh_field(self, offset: int, field_name: str) -> int:
|
||||||
|
field = fdat.section_header[field_name]
|
||||||
|
offs = int(field[str(self.bit_width)], base=16) + offset
|
||||||
|
byte_len = int(field['size' + str(self.bit_width)])
|
||||||
|
return self.read_int(offs, byte_len)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
hf_list = ((hf, self.fields[hf['field_name']]) for hf in fdat.elf_header_field.values())
|
||||||
|
return '\n'.join(f"{hf['field_name']:24} {v:4} {hf['description']}" for hf, v in hf_list) + '\n'
|
||||||
|
|
||||||
|
def __getitem__(self, key: str) -> int:
|
||||||
|
assert key in self.fields, f'Unknown field name: {key}'
|
||||||
|
return self.fields[key]
|
|
@ -0,0 +1,60 @@
|
||||||
|
from typing import Any, Literal
|
||||||
|
|
||||||
|
table_format = Literal['text', 'markdown', 'html']
|
||||||
|
|
||||||
|
|
||||||
|
def generate_table(data: list[list[Any]], columns: list[str],
|
||||||
|
right_adj_col: list[str] = [],
|
||||||
|
format: table_format = 'markdown') -> str:
|
||||||
|
if format == 'html':
|
||||||
|
return generate_html_table(data, columns, right_adj_col)
|
||||||
|
elif format == 'markdown':
|
||||||
|
return generate_md_table(data, columns, right_adj_col)
|
||||||
|
else:
|
||||||
|
return generate_md_table(data, columns, right_adj_col).replace('|', '').replace(':', '-')
|
||||||
|
|
||||||
|
|
||||||
|
def generate_md_table(data: list[list[Any]], columns: list[str], right_adj_col: list[str] = []) -> str:
|
||||||
|
column_widths = [max(len(str(item)) for item in column) for column in zip(*([columns] + data))]
|
||||||
|
|
||||||
|
table = ''
|
||||||
|
|
||||||
|
formatted_row = ' | '.join(f"{str(item):<{column_widths[i]}}" for i, item in enumerate(columns))
|
||||||
|
table += '| ' + formatted_row + ' |\n'
|
||||||
|
|
||||||
|
table += '|' + '|'.join('-' * width + '-:' if c in right_adj_col else ':-' + '-' * width for width, c in zip(column_widths, columns)) + '|\n'
|
||||||
|
|
||||||
|
for row in data:
|
||||||
|
formatted_row = ''
|
||||||
|
for i, item in enumerate(row):
|
||||||
|
if columns[i] in right_adj_col:
|
||||||
|
formatted_row += f"| {str(item):>{column_widths[i]}} "
|
||||||
|
else:
|
||||||
|
formatted_row += f"| {str(item):<{column_widths[i]}} "
|
||||||
|
table += formatted_row + "|\n"
|
||||||
|
|
||||||
|
return table
|
||||||
|
|
||||||
|
|
||||||
|
def generate_html_table(data: list[list[Any]], columns: list[str], right_adj_col: list[str] = []) -> str:
|
||||||
|
table_html = "<table border='1'>\n"
|
||||||
|
|
||||||
|
table_html += "<thead>\n<tr>\n"
|
||||||
|
for column in columns:
|
||||||
|
table_html += f" <th style='text-align:left'>{column}</th>\n"
|
||||||
|
table_html += "</tr>\n</thead>\n"
|
||||||
|
|
||||||
|
table_html += "<tbody>\n"
|
||||||
|
for row in data:
|
||||||
|
table_html += "<tr>\n"
|
||||||
|
for i, item in enumerate(row):
|
||||||
|
if columns[i] in right_adj_col:
|
||||||
|
table_html += f" <td style='text-align:right'>{item}</td>\n"
|
||||||
|
else:
|
||||||
|
table_html += f" <td style='text-align:left'>{item}</td>\n"
|
||||||
|
table_html += "</tr>\n"
|
||||||
|
table_html += "</tbody>\n"
|
||||||
|
|
||||||
|
table_html += "</table>\n"
|
||||||
|
|
||||||
|
return table_html
|
Loading…
Reference in New Issue