project public/privat structure updated

This commit is contained in:
Nicolas 2025-07-28 16:05:05 +02:00
parent d0b0901ed7
commit 97e44b8dbf
4 changed files with 1229 additions and 568 deletions

View File

@ -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]

586
src/pelfy/_fields_data.py Normal file
View File

@ -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, "")
}
}

572
src/pelfy/_main.py Normal file
View File

@ -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]

View File

@ -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