Compare commits

..

No commits in common. "7d2028c2bbf3cda1a68fcef2c9ad3990dbfc1ca0" and "8267f6794a6c9c5cfa34f847138ddf6124f6ec0e" have entirely different histories.

2 changed files with 90 additions and 70 deletions

View File

@ -1,11 +1,3 @@
"""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
@ -29,27 +21,34 @@ def open_elf_file(file_path: str) -> 'elf_file':
class elf_symbol():
"""A class for representing data of an ELF symbol
Args:
file: ELF file object
fields: symbol header fields
index: Absolut index in the symbol table
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
@ -65,11 +64,13 @@ class elf_symbol():
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:
def read_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
Returns:
Symbol data
"""
assert self.section, 'This symbol is not associated to a data section'
if self.section.type == 'SHT_NOBITS':
@ -78,18 +79,17 @@ class elf_symbol():
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)
def read_data_hex(self) -> str:
return ' '.join(f'{d:02X}' for d in self.read_data())
@property
def relocations(self) -> 'relocation_list':
"""Relocations that are pointing to this symbol.
def get_relocations(self) -> 'relocation_list':
"""List all 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
this function lists typically all relocations that will be
applied to the function represented by the symbol.
Returns:
List of relocations
"""
ret: list[elf_relocation] = list()
assert self.section and self.section.type == 'SHT_PROGBITS'
@ -118,23 +118,29 @@ class elf_symbol():
class elf_section():
"""A class for representing data of an ELF section
Args:
file: ELF file object
fields: Section header fields
name: Name of the section
index: Absolut index in the symbol table
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
@ -150,27 +156,29 @@ class elf_section():
self.description = ''
self.type = str(hex(fields['sh_type']))
@property
def data(self) -> bytes:
def read_data(self) -> bytes:
"""Returns the binary data from the section.
The offset in the ELF file is given by: section.sh_offset
Returns:
Data of the section
"""
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
def get_symbols(self) -> 'symbol_list':
"""Lists all ELF symbols associated with this section
Returns:
Symbol list
"""
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 get_data_hex(self) -> str:
data = self.file.read_bytes(self['sh_offset'], self['sh_size'])
return ' '.join(f'{d:02X}' for d in data)
def __getitem__(self, key: str | int) -> int:
if isinstance(key, str):
@ -189,28 +197,38 @@ class elf_section():
class elf_relocation():
"""A class for representing data of a relocation
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
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
@ -347,29 +365,37 @@ class relocation_list(elf_list[elf_relocation]):
class elf_file:
"""A class for representing data of an ELF file in a structured form
Args:
data: binary ELF data
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: 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 = data
@ -498,6 +524,7 @@ class 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:
@ -512,7 +539,9 @@ class elf_file:
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:
@ -529,6 +558,7 @@ class 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:

View File

@ -19,22 +19,12 @@ def test_simple_c() -> None:
print(elf.code_relocations)
print('\n')
section_count = 0
assert elf.sections
for section in elf.sections:
assert known_name(section.description), f"Section type {section.type} for {elf.architecture} in {path} is unknown."
for sym in section.symbols:
assert known_name(sym.info), f"Symbol info {sym.info} for {elf.architecture} in {path} is unknown."
section_count += 1
assert section_count > 2
assert elf.symbols
for sym in elf.symbols:
assert known_name(sym.info), f"Symbol info {sym.info} for {elf.architecture} in {path} is unknown."
assert elf.get_relocations()
for reloc in elf.get_relocations():
assert known_name(reloc.type), f"Relocation type {reloc.type} for {elf.architecture} in {path} is unknown."