Compare commits

...

6 Commits

Author SHA1 Message Date
Nicolas Kruse 7d2028c2bb blank line removed 2025-04-02 21:46:48 +02:00
Nicolas Kruse 27e48f8d5b .data_hex properties fixed 2025-04-02 21:46:18 +02:00
Nicolas Kruse da74692664 test extended for iterating section.symbols 2025-04-02 21:42:44 +02:00
Nicolas Kruse e2a6c1bcd4 changed functions without argument to property: symbol.relocations, .data, .data_hex 2025-04-02 21:42:06 +02:00
Nicolas Kruse 6b797baf0e test checking for empty results 2025-04-02 21:20:36 +02:00
Nicolas Kruse 85d911c5cb Docstring format updated, module description added 2025-04-02 21:20:04 +02:00
2 changed files with 70 additions and 90 deletions

View File

@ -1,3 +1,11 @@
"""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
@ -21,34 +29,27 @@ 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
@ -64,13 +65,11 @@ 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]
def read_data(self) -> bytes:
@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
Returns:
Symbol data
"""
assert self.section, 'This symbol is not associated to a data section'
if self.section.type == 'SHT_NOBITS':
@ -79,17 +78,18 @@ class elf_symbol():
offset = self.section['sh_offset'] + self['st_value']
return self.file.read_bytes(offset, self['st_size'])
def read_data_hex(self) -> str:
return ' '.join(f'{d:02X}' for d in self.read_data())
@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 get_relocations(self) -> 'relocation_list':
"""List all relocations that are pointing to this symbol.
@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 function lists typically all relocations that will be
this property returns 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,29 +118,23 @@ 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
@ -156,29 +150,27 @@ class elf_section():
self.description = ''
self.type = str(hex(fields['sh_type']))
def read_data(self) -> bytes:
@property
def 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'])
def get_symbols(self) -> 'symbol_list':
"""Lists all ELF symbols associated with this section
Returns:
Symbol list
@property
def symbols(self) -> 'symbol_list':
"""All ELF symbols associated with this section
"""
return symbol_list(self.file._list_symbols(self.index))
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)
@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: str | int) -> int:
if isinstance(key, str):
@ -197,38 +189,28 @@ 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
@ -365,37 +347,29 @@ 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
@ -524,7 +498,6 @@ 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:
@ -539,9 +512,7 @@ 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:
@ -558,7 +529,6 @@ 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,12 +19,22 @@ 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."