mirror of https://github.com/Nonannet/pelfy.git
Compare commits
6 Commits
8267f6794a
...
7d2028c2bb
Author | SHA1 | Date |
---|---|---|
|
7d2028c2bb | |
|
27e48f8d5b | |
|
da74692664 | |
|
e2a6c1bcd4 | |
|
6b797baf0e | |
|
85d911c5cb |
|
@ -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 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
|
||||||
|
@ -21,34 +29,27 @@ 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
|
||||||
|
|
||||||
|
@ -64,13 +65,11 @@ 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]
|
||||||
|
|
||||||
def read_data(self) -> bytes:
|
@property
|
||||||
|
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':
|
||||||
|
@ -79,17 +78,18 @@ 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'])
|
||||||
|
|
||||||
def read_data_hex(self) -> str:
|
@property
|
||||||
return ' '.join(f'{d:02X}' for d in self.read_data())
|
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':
|
@property
|
||||||
"""List all relocations that are pointing to this symbol.
|
def relocations(self) -> 'relocation_list':
|
||||||
|
"""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 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.
|
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,29 +118,23 @@ 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
|
||||||
|
@ -156,29 +150,27 @@ class elf_section():
|
||||||
self.description = ''
|
self.description = ''
|
||||||
self.type = str(hex(fields['sh_type']))
|
self.type = str(hex(fields['sh_type']))
|
||||||
|
|
||||||
def read_data(self) -> bytes:
|
@property
|
||||||
|
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'])
|
||||||
|
|
||||||
def get_symbols(self) -> 'symbol_list':
|
@property
|
||||||
"""Lists all ELF symbols associated with this section
|
def symbols(self) -> 'symbol_list':
|
||||||
|
"""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))
|
||||||
|
|
||||||
def get_data_hex(self) -> str:
|
@property
|
||||||
data = self.file.read_bytes(self['sh_offset'], self['sh_size'])
|
def data_hex(self) -> str:
|
||||||
return ' '.join(f'{d:02X}' for d in data)
|
"""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:
|
def __getitem__(self, key: str | int) -> int:
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
|
@ -197,38 +189,28 @@ 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
|
||||||
|
@ -365,37 +347,29 @@ 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
|
||||||
|
|
||||||
|
@ -524,7 +498,6 @@ 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:
|
||||||
|
@ -539,9 +512,7 @@ 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:
|
||||||
|
@ -558,7 +529,6 @@ 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:
|
||||||
|
|
|
@ -19,12 +19,22 @@ 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."
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue