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 fields_data as fdat
from . import output_formatter from . import output_formatter
from typing import TypeVar, Literal, Iterable, Generic, Iterator, Generator 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(): class elf_symbol():
"""A class for representing data of an 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: Attributes:
file: Points to the parent ELF file object. file: Points to the parent ELF file object.
name: Name of the symbol name: Name of the symbol
section: section where the symbol data is placed section: section where the symbol data is placed
index: Absolut index in the symbol table index: Absolut index in the symbol table
info: Type of the symbol info: Type of the symbol
description: Description of the symbol type description: Description of the symbol type
stb: visibility of the symbol (local, global, etc.) stb: visibility of the symbol (local, global, etc.)
stb_description: Description of the symbol visibility stb_description: Description of the symbol visibility
fields: All symbol header fields as dict fields: All symbol header fields as dict
""" """
def __init__(self, file: 'elf_file', fields: dict[str, int], index: int): 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.fields = fields
self.file = file self.file = file
@ -65,11 +64,13 @@ class elf_symbol():
self.info, self.description = fdat.st_info_values[fields['st_info'] & 0x0F] self.info, self.description = fdat.st_info_values[fields['st_info'] & 0x0F]
self.stb, self.stb_description = fdat.stb_values[fields['st_info'] >> 4] self.stb, self.stb_description = fdat.stb_values[fields['st_info'] >> 4]
@property def read_data(self) -> bytes:
def data(self) -> bytes:
"""Returns the binary data the symbol is pointing to. """Returns the binary data the symbol is pointing to.
The offset in the ELF file is calculated by: The offset in the ELF file is calculated by:
sections[symbol.st_shndx].sh_offset + symbol.st_value sections[symbol.st_shndx].sh_offset + symbol.st_value
Returns:
Symbol data
""" """
assert self.section, 'This symbol is not associated to a data section' assert self.section, 'This symbol is not associated to a data section'
if self.section.type == 'SHT_NOBITS': if self.section.type == 'SHT_NOBITS':
@ -78,18 +79,17 @@ class elf_symbol():
offset = self.section['sh_offset'] + self['st_value'] offset = self.section['sh_offset'] + self['st_value']
return self.file.read_bytes(offset, self['st_size']) return self.file.read_bytes(offset, self['st_size'])
@property def read_data_hex(self) -> str:
def data_hex(self) -> str: return ' '.join(f'{d:02X}' for d in self.read_data())
"""Returns the binary data the symbol is pointing to as hex string.
"""
return ' '.join(f'{d:02X}' for d in self.data)
@property def get_relocations(self) -> 'relocation_list':
def relocations(self) -> 'relocation_list': """List all relocations that are pointing to this symbol.
"""Relocations that are pointing to this symbol.
The symbol section must be of type SHT_PROGBITS (program code). Therefore 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. applied to the function represented by the symbol.
Returns:
List of relocations
""" """
ret: list[elf_relocation] = list() ret: list[elf_relocation] = list()
assert self.section and self.section.type == 'SHT_PROGBITS' assert self.section and self.section.type == 'SHT_PROGBITS'
@ -118,23 +118,29 @@ class elf_symbol():
class elf_section(): class elf_section():
"""A class for representing data of an 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: Attributes:
file: Points to the parent ELF file object. file: Points to the parent ELF file object.
name: Name of the section name: Name of the section
index: Absolut index of the section index: Absolut index of the section
type: Type of the section type: Type of the section
description: Description of the section type description: Description of the section type
fields: All symbol header fields as dict fields: All symbol header fields as dict
""" """
def __init__(self, file: 'elf_file', fields: dict[str, int], name: str, index: int): 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.fields = fields
self.file = file self.file = file
self.index = index self.index = index
@ -150,27 +156,29 @@ class elf_section():
self.description = '' self.description = ''
self.type = str(hex(fields['sh_type'])) self.type = str(hex(fields['sh_type']))
@property def read_data(self) -> bytes:
def data(self) -> bytes:
"""Returns the binary data from the section. """Returns the binary data from the section.
The offset in the ELF file is given by: section.sh_offset The offset in the ELF file is given by: section.sh_offset
Returns:
Data of the section
""" """
if self.type == 'SHT_NOBITS': if self.type == 'SHT_NOBITS':
return b'\x00' * self['sh_size'] return b'\x00' * self['sh_size']
else: else:
return self.file.read_bytes(self['sh_offset'], self['sh_size']) return self.file.read_bytes(self['sh_offset'], self['sh_size'])
@property def get_symbols(self) -> 'symbol_list':
def symbols(self) -> 'symbol_list': """Lists all ELF symbols associated with this section
"""All ELF symbols associated with this section
Returns:
Symbol list
""" """
return symbol_list(self.file._list_symbols(self.index)) return symbol_list(self.file._list_symbols(self.index))
@property def get_data_hex(self) -> str:
def data_hex(self) -> str: data = self.file.read_bytes(self['sh_offset'], self['sh_size'])
"""Returns the binary data from the section as hex string. return ' '.join(f'{d:02X}' for d in data)
"""
return ' '.join(f'{d:02X}' for d in self.data)
def __getitem__(self, key: str | int) -> int: def __getitem__(self, key: str | int) -> int:
if isinstance(key, str): if isinstance(key, str):
@ -189,28 +197,38 @@ class elf_section():
class elf_relocation(): class elf_relocation():
"""A class for representing data of a 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: Attributes:
file: Points to the parent ELF file object. file: Points to the parent ELF file object.
index: Absolut index of the relocation in the associated relocation section index: Absolut index of the relocation in the associated relocation section
symbol: Symbol to relocate symbol: Symbol to relocate
type: Type of the relocation type: Type of the relocation
calculation: Description of the relocation calculation calculation: Description of the relocation calculation
bits: number ob bits to patch by the relocation bits: number ob bits to patch by the relocation
target_section: Pointing to the section where this relocation applies to target_section: Pointing to the section where this relocation applies to
fields: All relocation header fields as dict fields: All relocation header fields as dict
""" """
def __init__(self, file: 'elf_file', fields: dict[str, int], symbol_index: int, def __init__(self, file: 'elf_file', fields: dict[str, int], symbol_index: int,
relocation_type: int, sh_info: int, 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.fields = fields
self.file = file self.file = file
self.index = index self.index = index
@ -347,29 +365,37 @@ class relocation_list(elf_list[elf_relocation]):
class elf_file: class elf_file:
"""A class for representing data of an ELF file in a structured form """A class for representing data of an ELF file in a structured form
Args:
data: binary ELF data
Attributes: Attributes:
byteorder: Byte order of the architecture 'little' or 'big' byteorder: Byte order of the architecture 'little' or 'big'
(based on e_ident[EI_DATA]) (based on e_ident[EI_DATA])
bit_width: Bit with of the architecture: 32 or 64 (based on bit_width: Bit with of the architecture: 32 or 64 (based on
e_ident[EI_CLASS]) e_ident[EI_CLASS])
architecture: Name of the architecture (based on e_machine) architecture: Name of the architecture (based on e_machine)
fields: All ELF header fields as dict fields: All ELF header fields as dict
sections: A list of all ELF sections sections: A list of all ELF sections
symbols: A list of all ELF symbols symbols: A list of all ELF symbols
functions: A list of all function symbols (STT_FUNC) functions: A list of all function symbols (STT_FUNC)
objects: A list of all variable/object symbols (STT_OBJECT) objects: A list of all variable/object symbols (STT_OBJECT)
code_relocations: A list of all code relocations (.rela.text and .rel.text) code_relocations: A list of all code relocations (.rela.text and .rel.text)
symbol_table_section: The symbol table section (first section with symbol_table_section: The symbol table section (first section with
the type SHT_SYMTAB) the type SHT_SYMTAB)
string_table_section: The string table section (first section with string_table_section: The string table section (first section with
the name .strtab) the name .strtab)
""" """
def __init__(self, data: bytes | bytearray): 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.' assert isinstance(data, (bytes, bytearray)), 'Binary ELF data must be provided as bytes or bytearray.'
self._data = data self._data = data
@ -498,6 +524,7 @@ class elf_file:
Args: Args:
offset: Specify first byte relative to the start of offset: Specify first byte relative to the start of
the ELF file. the ELF file.
num_bytes: Specify the number of bytes to read. num_bytes: Specify the number of bytes to read.
Returns: Returns:
@ -512,7 +539,9 @@ class elf_file:
Args: Args:
offset: Specify first byte of the integer relative to offset: Specify first byte of the integer relative to
the start of the ELF file. the start of the ELF file.
num_bytes: Specify the size of the integer in bytes. num_bytes: Specify the size of the integer in bytes.
signed: Select if the integer is a signed integer. signed: Select if the integer is a signed integer.
Returns: Returns:
@ -529,6 +558,7 @@ class elf_file:
Args: Args:
offset: Specify first byte of the string relative to offset: Specify first byte of the string relative to
the start of the ELF file. the start of the ELF file.
encoding: Encoding used for text decoding. encoding: Encoding used for text decoding.
Returns: Returns:

View File

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