Compare commits

..

8 Commits

Author SHA1 Message Date
Nicolas d7e7876afd fixed usage of offset_in_section instead of st_value 2026-01-27 16:57:39 +01:00
Nicolas 5f0c36fef4 code style and typehints fixed 2026-01-27 16:37:27 +01:00
Nicolas aab7f83cd9 _get_rel_addend function updated for ARM in thumb mode 2026-01-27 14:49:47 +01:00
Nicolas 7376e505d5 relocation data for ARM updated 2026-01-27 14:49:47 +01:00
Nicolas 024f86d4bc size attribute added to symbol class 2026-01-27 13:52:33 +01:00
Nicolas 700813ff31 Version bumped to 1.0.8 2026-01-27 13:40:50 +01:00
Nicolas 7ccd3852d1 Added thumb_mode, offset_in_section and offset_in_file property to
elf_symbol class
2026-01-27 13:38:24 +01:00
Nicolas 5dccaa30d6 armv7thumb example for testing added 2026-01-27 12:42:11 +01:00
5 changed files with 104 additions and 24 deletions

View File

@ -1,6 +1,6 @@
[project] [project]
name = "pelfy" name = "pelfy"
version = "1.0.7" version = "1.0.8"
authors = [ authors = [
{ name="Nicolas Kruse", email="nicolas.kruse@nonan.net" }, { name="Nicolas Kruse", email="nicolas.kruse@nonan.net" },
] ]

View File

@ -390,38 +390,48 @@ relocation_table_types = {
}, },
"EM_ARM": { "EM_ARM": {
0: ("R_ARM_NONE", 0, ""), 0: ("R_ARM_NONE", 0, ""),
1: ("R_ARM_PC24", 24, "S - P + A"), 1: ("R_ARM_PC24", 24, "S - P + A"),
2: ("R_ARM_ABS32", 32, "S + A"), 2: ("R_ARM_ABS32", 32, "S + A"),
3: ("R_ARM_REL32", 32, "S - P + A"), 3: ("R_ARM_REL32", 32, "S - P + A"),
4: ("R_ARM_PC13", 13, "S - P + A"), 4: ("R_ARM_LDR_PC_G0", 12, "S - P + A"),
5: ("R_ARM_ABS16", 16, "S + A"), 5: ("R_ARM_ABS16", 16, "S + A"),
6: ("R_ARM_ABS12", 12, "S + A"), 6: ("R_ARM_ABS12", 12, "S + A"),
7: ("R_ARM_THM_ABS5", 5, "S + A"), 7: ("R_ARM_THM_ABS5", 5, "S + A"),
8: ("R_ARM_ABS8", 8, "S + A"), 8: ("R_ARM_ABS8", 8, "S + A"),
9: ("R_ARM_SBREL32", 32, "S - B + A"), 9: ("R_ARM_SBREL32", 32, "S - B + A"),
10: ("R_ARM_THM_PC22", 22, "S - P + A"),
10: ("R_ARM_THM_CALL", 22, "S - P + A"),
11: ("R_ARM_THM_PC8", 8, "S - P + A"), 11: ("R_ARM_THM_PC8", 8, "S - P + A"),
12: ("Reserved", 0, ""), 12: ("R_ARM_BREL_ADJ", 0, ""),
13: ("R_ARM_SWI24", 24, "S + A"), 13: ("R_ARM_TLS_DESC", 0, ""),
14: ("R_ARM_THM_SWI8", 8, "S + A"), 14: ("R_ARM_THM_SWI8", 8, "S + A"),
15: ("R_ARM_XPC25", 25, ""), 15: ("R_ARM_XPC25", 25, ""),
16: ("R_ARM_THM_XPC22", 22, ""), 16: ("R_ARM_THM_XPC22", 22, ""),
28: ("R_ARM_CALL", 24, "((S + A) - P) >> 2"), 28: ("R_ARM_CALL", 24, "((S + A) - P) >> 2"),
29: ("R_ARM_JUMP24", 24, "((S + A) - P) >> 2"), 29: ("R_ARM_JUMP24", 24, "((S + A) - P) >> 2"),
30: ("R_ARM_TLS_DESC", 0, ""), 30: ("R_ARM_THM_JUMP24", 24, "((S + A) - P) >> 1"),
32: ("R_ARM_ALU_PCREL_7_0", 7, "(S - P + A) & 0x000000FF"),
33: ("R_ARM_ALU_PCREL_15_8", 15, "(S - P + A) & 0x0000FF00"), 32: ("R_ARM_ALU_PCREL_7_0", 8, "(S - P + A) & 0x000000FF"),
34: ("R_ARM_ALU_PCREL_23_15", 23, "(S - P + A) & 0x00FF0000"), 33: ("R_ARM_ALU_PCREL_15_8", 8, "(S - P + A) & 0x0000FF00"),
35: ("R_ARM_LDR_SBREL_11_0", 11, "(S - B + A) & 0x00000FFF"), 34: ("R_ARM_ALU_PCREL_23_15", 9, "(S - P + A) & 0x00FF8000"),
36: ("R_ARM_ALU_SBREL_19_12", 19, "(S - B + A) & 0x000FF000"),
37: ("R_ARM_ALU_SBREL_27_20", 27, "(S - B + A) & 0x0FF00000"), 35: ("R_ARM_LDR_SBREL_11_0", 12, "(S - B + A) & 0x00000FFF"),
38: ("R_ARM_RELABS32", 32, "S + A or S - P + A"), 36: ("R_ARM_ALU_SBREL_19_12", 8, "(S - B + A) & 0x000FF000"),
39: ("R_ARM_ROSEGREL32", 32, "S - E + A"), 37: ("R_ARM_ALU_SBREL_27_20", 8, "(S - B + A) & 0x0FF00000"),
38: ("R_ARM_TARGET1", 32, "implementation defined"),
39: ("R_ARM_SBREL31", 31, "S - B + A"),
40: ("R_ARM_V4BX", 0, ""), 40: ("R_ARM_V4BX", 0, ""),
41: ("R_ARM_STKCHK", 0, ""), 41: ("R_ARM_TARGET2", 32, "implementation defined"),
42: ("R_ARM_THM_STKCHK", 0, ""), 42: ("R_ARM_PREL31", 31, "S - P + A"),
43: ("R_ARM_MOVW_ABS_NC", 16, "S + A"), 43: ("R_ARM_MOVW_ABS_NC", 16, "S + A"),
44: ("R_ARM_MOVT_ABS", 16, "S + A") 44: ("R_ARM_MOVT_ABS", 16, "S + A"),
}, },
"EM_AARCH64": { "EM_AARCH64": {
0: ("R_AARCH64_NONE", 0, ""), 0: ("R_AARCH64_NONE", 0, ""),

View File

@ -26,6 +26,53 @@ def open_elf_file(file_path: str) -> 'elf_file':
return elf_file(f.read()) return elf_file(f.read())
def _decode_thumb_branch_imm(field: int, bits: int) -> int:
"""
Decode Thumb-2 wide branch immediate.
bits: 22 (R_ARM_THM_PC22) or 24 (R_ARM_THM_JUMP24)
"""
h1 = (field >> 16) & 0xFFFF
h2 = field & 0xFFFF
S = (h1 >> 10) & 1
imm10 = h1 & 0x03FF
J1 = (h2 >> 13) & 1
J2 = (h2 >> 11) & 1
imm11 = h2 & 0x07FF
# Decode J1/J2 → I1/I2
I1 = (~(J1 ^ S)) & 1
I2 = (~(J2 ^ S)) & 1
if bits == 24:
imm = (
(S << 23) |
(I1 << 22) |
(I2 << 21) |
(imm10 << 11) |
(imm11 << 0)
)
sign_bit = 23
else:
assert bits == 22
imm = (
(S << 21) |
(I1 << 20) |
(I2 << 19) |
(imm10 << 9) |
(imm11 << 0)
)
sign_bit = 21
# Sign extend
if imm & (1 << sign_bit):
imm |= ~((1 << (sign_bit + 1)) - 1)
# Thumb branch offsets are halfword aligned
return imm << 1
class elf_symbol(): class elf_symbol():
"""A class for representing data of an ELF symbol """A class for representing data of an ELF symbol
@ -38,6 +85,11 @@ class elf_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
thumb_mode: Indicate if symbol is a ARM thumb function
offset_in_section: Position of first symbol byte
relative to section start
offset_in_file: Position of first symbol byte in object file
size: size of symbol in bytes
fields: All symbol header fields as dict fields: All symbol header fields as dict
""" """
@ -64,19 +116,20 @@ 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]
self.thumb_mode = bool((file.architecture == 'EM_ARM') & fields['st_value'] & 1)
self.offset_in_section = fields['st_value'] & ~int(self.thumb_mode)
self.offset_in_file = self.section['sh_offset'] + self.offset_in_section if self.section else 0
self.size = self.fields['st_size']
@property @property
def 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:
sections[symbol.st_shndx].sh_offset + symbol.st_value
""" """
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':
return b'\x00' * self['st_size'] return b'\x00' * self['st_size']
else: else:
offset = self.section['sh_offset'] + self['st_value'] return self.file.read_bytes(self.offset_in_file, self['st_size'])
return self.file.read_bytes(offset, self['st_size'])
@property @property
def data_hex(self) -> str: def data_hex(self) -> str:
@ -95,7 +148,7 @@ class elf_symbol():
assert self.section and self.section.type == 'SHT_PROGBITS' assert self.section and self.section.type == 'SHT_PROGBITS'
for reloc in self.file.get_relocations(): for reloc in self.file.get_relocations():
if reloc.target_section == self.section: if reloc.target_section == self.section:
offset = reloc['r_offset'] - self['st_value'] offset = reloc['r_offset'] - self.offset_in_section
if 0 <= offset < self['st_size']: if 0 <= offset < self['st_size']:
ret.append(reloc) ret.append(reloc)
return relocation_list(ret) return relocation_list(ret)
@ -537,8 +590,12 @@ class elf_file:
if imm24 & 0x800000: if imm24 & 0x800000:
imm24 |= ~0xFFFFFF imm24 |= ~0xFFFFFF
return imm24 << 2 return imm24 << 2
if name == 'R_ARM_THM_PC22':
return _decode_thumb_branch_imm(field, 22)
if name in ('R_ARM_THM_JUMP24', 'R_ARM_THM_CALL'):
return _decode_thumb_branch_imm(field, 24)
if '_THM_' in name: if '_THM_' in name:
print('Warning: Thumb relocation addend extraction is not implemented') print(f'Warning: Thumb relocation addend extraction is for {name} not implemented')
return 0 return 0
if '_MIPS_' in name: if '_MIPS_' in name:
print('Warning: MIPS relocations addend extraction is not implemented') print('Warning: MIPS relocations addend extraction is not implemented')

Binary file not shown.

View File

@ -38,7 +38,20 @@ def test_simple_c() -> None:
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."
assert 'imageWidth' in elf.objects or 'read_float_ret' in elf.objects, path is_arm = 'arm' in path
test_function = elf.functions[-3]
assert test_function.section
assert is_arm == ('.ARM.attributes' in elf.sections), path # Is 32 bit ARM
assert is_arm == (elf.architecture == 'EM_ARM'), (path, elf.architecture)
assert test_function.thumb_mode == ('thumb' in path or 'arm-linux-gnueabihf-gcc-12-O' in path), path
if test_function.thumb_mode:
# In 32 Bit ARM mode the leased segnificant bit in st_value indicates thumb mode
assert test_function.offset_in_file == (test_function.fields['st_value'] & ~1) + test_function.section['sh_offset']
else:
assert test_function.offset_in_file == test_function.fields['st_value'] + test_function.section['sh_offset']
assert ('imageWidth' in elf.objects) or ('read_float_ret' in elf.objects) or ('pow_int_float' in elf.functions), path
assert 'leet456456456n4ghn4hf56n4f' not in elf.objects assert 'leet456456456n4ghn4hf56n4f' not in elf.objects
assert 0 in elf.objects assert 0 in elf.objects
assert 1000 not in elf.objects assert 1000 not in elf.objects