Compare commits

...

17 Commits

Author SHA1 Message Date
Nicolas Kruse b9f9032872
Merge pull request #4 from Nonannet/dev
Dev
2026-03-02 21:53:51 +01:00
Nicolas 345b0e6db8 Removed warning for non-implemented ARM Thumb relocations, raises error now 2026-03-01 10:10:38 +01:00
Nicolas 2be97956ab Type hints fixed for backwards compatibility for python 3.9 2026-02-28 21:24:10 +01:00
Nicolas a8139e3739 addend calculation for R_ARM_THM_PC22, R_ARM_THM_JUMP24 and R_ARM_THM_CALL fixed 2026-02-28 21:21:06 +01:00
Nicolas 5a77c72064 EM_ARM data updated from ARM reference manual 2026-02-28 20:57:43 +01:00
Nicolas 940488b660 Extended test for ARM thumb relocations and added reference disassembly 2026-02-27 17:58:55 +01:00
Nicolas 9a4cbe92af Fixed addend extraction for R_ARM_THM_MOVW_ABS_NC and R_ARM_THM_MOVT_ABS 2026-02-27 17:58:15 +01:00
Nicolas 20b00109e9 Added test for 32 bit ARM relocations 2026-02-27 17:57:28 +01:00
Nicolas 5663e7f5b0 Fixed name R_ARM_THM_MOVT_ABS_NC -> R_ARM_THM_MOVT_ABS 2026-02-27 15:02:55 +01:00
Nicolas 39bb639c1f stencils_armv7thumb_O3_THM_MOVW.o test object added for thumb code using R_ARM_THM_MOVW_ABS_NC and R_ARM_THM_MOVT_ABS_NC instead of litteral pools. new addend extractions added, but untested 2026-02-27 14:47:21 +01:00
Nicolas 86de67a7f8 Thumb relocations R_ARM_THM_MOVW_ABS_NC and R_ARM_THM_MOVT_ABS_NC added 2026-02-26 10:20:13 +01:00
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
9 changed files with 8532 additions and 35 deletions

View File

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

View File

@ -390,38 +390,42 @@ relocation_table_types = {
},
"EM_ARM": {
0: ("R_ARM_NONE", 0, ""),
1: ("R_ARM_PC24", 24, "S - P + A"),
2: ("R_ARM_ABS32", 32, "S + A"),
3: ("R_ARM_REL32", 32, "S - P + A"),
4: ("R_ARM_PC13", 13, "S - P + A"),
1: ("R_ARM_PC24", 24, "((S + A) | T) - P"),
2: ("R_ARM_ABS32", 32, "(S + A) | T"),
3: ("R_ARM_REL32", 32, "((S + A) | T) - P"),
4: ("R_ARM_LDR_PC_G0", 12, "S + A - P"),
5: ("R_ARM_ABS16", 16, "S + A"),
6: ("R_ARM_ABS12", 12, "S + A"),
7: ("R_ARM_THM_ABS5", 5, "S + A"),
8: ("R_ARM_ABS8", 8, "S + A"),
9: ("R_ARM_SBREL32", 32, "S - B + A"),
10: ("R_ARM_THM_PC22", 22, "S - P + A"),
11: ("R_ARM_THM_PC8", 8, "S - P + A"),
12: ("Reserved", 0, ""),
13: ("R_ARM_SWI24", 24, "S + A"),
9: ("R_ARM_SBREL32", 32, "((S + A) | T) - B(S)"),
10: ("R_ARM_THM_CALL", 22, "((S + A) | T) - P"),
11: ("R_ARM_THM_PC8", 8, "S + A - Pa"),
12: ("R_ARM_BREL_ADJ", 0, "ChangeIn[B(S)] + A"),
13: ("R_ARM_TLS_DESC", 0, ""),
14: ("R_ARM_THM_SWI8", 8, "S + A"),
15: ("R_ARM_XPC25", 25, ""),
16: ("R_ARM_THM_XPC22", 22, ""),
28: ("R_ARM_CALL", 24, "((S + A) - P) >> 2"),
29: ("R_ARM_JUMP24", 24, "((S + A) - P) >> 2"),
30: ("R_ARM_TLS_DESC", 0, ""),
32: ("R_ARM_ALU_PCREL_7_0", 7, "(S - P + A) & 0x000000FF"),
33: ("R_ARM_ALU_PCREL_15_8", 15, "(S - P + A) & 0x0000FF00"),
34: ("R_ARM_ALU_PCREL_23_15", 23, "(S - P + A) & 0x00FF0000"),
35: ("R_ARM_LDR_SBREL_11_0", 11, "(S - B + A) & 0x00000FFF"),
36: ("R_ARM_ALU_SBREL_19_12", 19, "(S - B + A) & 0x000FF000"),
37: ("R_ARM_ALU_SBREL_27_20", 27, "(S - B + A) & 0x0FF00000"),
38: ("R_ARM_RELABS32", 32, "S + A or S - P + A"),
39: ("R_ARM_ROSEGREL32", 32, "S - E + A"),
17: ("R_ARM_TLS_DTPMOD32", 0, "Module[S]"),
18: ("R_ARM_TLS_DTPOFF32", 0, "S + A - TLS"),
19: ("R_ARM_TLS_TPOFF32", 0, "S + A - tp"),
20: ("R_ARM_COPY", 0, ""),
21: ("R_ARM_GLOB_DAT", 0, "(S + A) | T"),
22: ("R_ARM_JUMP_SLOT", 0, "(S + A) | T"),
23: ("R_ARM_RELATIVE", 0, "B(S) + A"),
24: ("R_ARM_GOTOFF32", 0, "((S + A) | T) - GOT_ORG"),
25: ("R_ARM_BASE_PREL", 0, "B(S) + A - P"),
26: ("R_ARM_GOT_BREL", 0, "GOT(S) + A - GOT_ORG"),
27: ("R_ARM_PLT32", 0, "((S + A) | T) - P"),
28: ("R_ARM_CALL", 24, "((S + A) | T) - P"),
29: ("R_ARM_JUMP24", 24, "((S + A) | T) - P"),
30: ("R_ARM_THM_JUMP24", 24, "((S + A) | T) - P"),
40: ("R_ARM_V4BX", 0, ""),
41: ("R_ARM_STKCHK", 0, ""),
42: ("R_ARM_THM_STKCHK", 0, ""),
43: ("R_ARM_MOVW_ABS_NC", 16, "S + A"),
44: ("R_ARM_MOVT_ABS", 16, "S + A")
42: ("R_ARM_PREL31", 31, "((S + A) | T) - P"),
43: ("R_ARM_MOVW_ABS_NC", 16, "(S + A) | T"),
44: ("R_ARM_MOVT_ABS", 16, "S + A"),
47: ("R_ARM_THM_MOVW_ABS_NC", 16, "(S + A) | T"),
48: ("R_ARM_THM_MOVT_ABS", 16, "S + A"),
},
"EM_AARCH64": {
0: ("R_AARCH64_NONE", 0, ""),

View File

@ -9,6 +9,7 @@ Typical usage example:
from . import _fields_data as fdat
from . import _output_formatter
from typing import TypeVar, Literal, Iterable, Generic, Iterator, Generator, Optional, Union
import warnings
_T = TypeVar('_T')
@ -26,6 +27,14 @@ def open_elf_file(file_path: str) -> 'elf_file':
return elf_file(f.read())
def _sign_extend(value: int, bits: int) -> int:
"""Sign-extend a value with given bit width to 32 bits."""
sign_bit = 1 << (bits - 1)
if value & sign_bit:
return value - (1 << bits)
return value
class elf_symbol():
"""A class for representing data of an ELF symbol
@ -42,6 +51,7 @@ class elf_symbol():
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
"""
@ -71,19 +81,17 @@ class elf_symbol():
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
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
"""
assert self.section, 'This symbol is not associated to a data section'
if self.section.type == 'SHT_NOBITS':
return b'\x00' * self['st_size']
else:
offset = self.section['sh_offset'] + self['st_value']
return self.file.read_bytes(offset, self['st_size'])
return self.file.read_bytes(self.offset_in_file, self['st_size'])
@property
def data_hex(self) -> str:
@ -102,7 +110,7 @@ class elf_symbol():
assert self.section and self.section.type == 'SHT_PROGBITS'
for reloc in self.file.get_relocations():
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']:
ret.append(reloc)
return relocation_list(ret)
@ -528,7 +536,7 @@ class elf_file:
if reloc_types and 'A' in reloc_types[relocation_type][2]:
name = reloc_types[relocation_type][0]
sh = self.sections[reloc_section['sh_info']]
field = self.read_int(r_offset + sh['sh_offset'], 4, True)
field = self.read_int(r_offset + sh['sh_offset'], 4, False)
if name in ('R_386_PC32', 'R_386_32', 'R_X86_64_PC32', 'R_X86_64_PLT32', 'R_ARM_REL32', 'R_ARM_ABS32'):
return field
if name == 'R_ARM_MOVW_ABS_NC':
@ -544,11 +552,45 @@ class elf_file:
if imm24 & 0x800000:
imm24 |= ~0xFFFFFF
return imm24 << 2
if '_THM_' in name:
print('Warning: Thumb relocation addend extraction is not implemented')
return 0
if name == 'R_ARM_THM_PC22':
# BL Encoding T2
h1 = field & 0xFFFF
h2 = (field >> 16) & 0xFFFF
s = (h1 >> 10) & 0x1 # Bit 10 of h1
imm10H = h1 & 0x3FF # Bits 9..0 of h1
j1 = (h2 >> 13) & 0x1 # Bit 13 of h2
j2 = (h2 >> 11) & 0x1 # Bit 11 of h2
imm10L = (h2 >> 1) & 0x3FF # Bits 10..1 of h2
i1 = ~(j1 ^ s) & 0x1
i2 = ~(j2 ^ s) & 0x1
imm22 = (s << 21) | (i1 << 20) | (i2 << 19) | (imm10H << 9) | imm10L
imm32 = _sign_extend(imm22 << 1, 23) # Shift left by 1, then sign-extend 23 bits
return imm32
if name in ('R_ARM_THM_JUMP24', 'R_ARM_THM_CALL'):
# B.W Encoding T4
h1 = field & 0xFFFF
h2 = (field >> 16) & 0xFFFF
s = (h1 >> 10) & 0x1 # Bit 10 of h1
imm10 = h1 & 0x3FF # Bits 9..0 of h1
j1 = (h2 >> 13) & 0x1 # Bit 13 of h2
j2 = (h2 >> 11) & 0x1 # Bit 11 of h2
imm11 = h2 & 0x7FF # Bits 10..0 of h2
i1 = ~(j1 ^ s) & 0x1
i2 = ~(j2 ^ s) & 0x1
imm24 = (s << 23) | (i1 << 22) | (i2 << 21) | (imm10 << 11) | imm11
imm32 = _sign_extend(imm24 << 1, 25) # Shift left by 1, then sign-extend 25 bits
return imm32
if name == 'R_ARM_THM_MOVW_ABS_NC' or name == 'R_ARM_THM_MOVT_ABS':
i = (field >> 10) & 1
imm4 = field & 0xF
imm3 = (field >> 28) & 0x7
imm8 = (field >> 16) & 0xFF
imm16 = imm8 | (imm3 << 8) | (i << 11) | (imm4 << 12)
if name == 'R_ARM_THM_MOVT_ABS':
return imm16 << 16
return imm16
if '_MIPS_' in name:
print('Warning: MIPS relocations addend extraction is not implemented')
warnings.warn('Warning: MIPS relocations addend extraction is not implemented', stacklevel=2)
return 0
raise NotImplementedError(f"Relocation addend extraction for {name} is not implemented")

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

62
tests/test_arm_addend.py Normal file
View File

@ -0,0 +1,62 @@
import pelfy._main as _main
import os
def test_arm_addend_extraction() -> None:
# Path to the test object file
obj_path = os.path.join('tests', 'obj', 'stencils_armv7_O3.o')
elf = _main.open_elf_file(obj_path)
# Collect all ARM relocations
reloc_addends: list[tuple[str, int, int, str]] = []
for reloc in elf.get_relocations():
if reloc.type.startswith('R_ARM'):
reloc_addends.append((reloc.type, reloc['r_offset'], reloc['r_addend'], reloc.symbol.name))
# Reference values from stencils_armv7_O3.asm (addend = 0 for V4BX, -8 for JUMP24/CALL)
reference = [
('R_ARM_V4BX', 0xB4, 0, ''),
('R_ARM_V4BX', 0xC0, 0, ''),
('R_ARM_V4BX', 0xD0, 0, ''),
('R_ARM_V4BX', 0x114, 0, ''),
('R_ARM_V4BX', 0x144, 0, ''),
('R_ARM_V4BX', 0x148, 0, ''),
('R_ARM_JUMP24', 0x124, -8, '__aeabi_idiv0'),
('R_ARM_JUMP24', 0x14, -8, 'auxsub_get_42'),
('R_ARM_CALL', 0x10, -8, 'result_int'),
('R_ARM_JUMP24', 0xC, -8, 'result_float_int'),
('R_ARM_JUMP24', 0xC, -8, 'result_float_float'),
('R_ARM_JUMP24', 0xC, -8, 'result_int_int'),
('R_ARM_JUMP24', 0xC, -8, 'result_int_float'),
('R_ARM_CALL', 0xC, -8, 'aux_get_42'),
('R_ARM_JUMP24', 0x14, -8, 'result_float'),
('R_ARM_CALL', 0x4, -8, 'aux_get_42'),
('R_ARM_JUMP24', 0xC, -8, 'result_float'),
('R_ARM_JUMP24', 0x4, -8, 'result_int'),
('R_ARM_JUMP24', 0x4, -8, 'result_float'),
('R_ARM_JUMP24', 0x2C, -8, 'result_float'),
('R_ARM_CALL', 0x30, -8, 'sqrtf'),
('R_ARM_JUMP24', 0x24, -8, 'result_float'),
('R_ARM_CALL', 0x28, -8, 'sqrtf'),
('R_ARM_CALL', 0xC, -8, 'expf'),
('R_ARM_JUMP24', 0x14, -8, 'result_float'),
('R_ARM_CALL', 0x4, -8, 'expf'),
('R_ARM_JUMP24', 0xC, -8, 'result_float'),
('R_ARM_CALL', 0xC, -8, 'logf'),
('R_ARM_JUMP24', 0x14, -8, 'result_float'),
('R_ARM_CALL', 0x4, -8, 'logf'),
('R_ARM_JUMP24', 0xC, -8, 'result_float'),
('R_ARM_CALL', 0xC, -8, 'sinf'),
('R_ARM_JUMP24', 0x14, -8, 'result_float'),
('R_ARM_CALL', 0x4, -8, 'sinf'),
('R_ARM_JUMP24', 0xC, -8, 'result_float'),
]
# For each reference, check that at least one matching relocation has the expected addend
for ref_type, ref_offset, ref_addend, ref_symbol in reference:
found = False
addend = None
for typ, offset, addend, symbol in reloc_addends:
if typ == ref_type and offset == ref_offset and symbol == ref_symbol and addend == ref_addend:
found = True
break
assert found, f"Missing or incorrect addend for {ref_type} offset=0x{ref_offset:X} symbol={ref_symbol} (value={addend}, expected {ref_addend})"

View File

@ -0,0 +1,67 @@
import pelfy._main as _main
import os
from typing import Union
def test_thumb_addend_extraction() -> None:
# Path to the test object file
obj_path = os.path.join('tests', 'obj', 'stencils_armv7thumb_O3_THM_MOVW.o')
elf = _main.open_elf_file(obj_path)
# Collect all relocations of interest
reloc_addends: list[tuple[str, int, int, str]] = []
for reloc in elf.get_relocations():
if reloc.type.startswith('R_ARM_THM'):
reloc_addends.append((reloc.type, reloc['r_offset'], reloc['r_addend'], reloc.symbol.name))
# Reference values from the .asm file (addend = 0 for all Thumb relocations)
reference: list[tuple[str, Union[int, None], int, str]] = [
('R_ARM_THM_MOVW_ABS_NC', None, 0, 'dummy_int'),
('R_ARM_THM_MOVT_ABS', None, 0, 'dummy_int'),
('R_ARM_THM_MOVW_ABS_NC', None, 0, 'dummy_float'),
('R_ARM_THM_MOVT_ABS', None, 0, 'dummy_float'),
('R_ARM_THM_JUMP24', 0x14, -4, 'auxsub_get_42'),
('R_ARM_THM_CALL', 0xA, -4, 'result_int'),
('R_ARM_THM_JUMP24', 0xA, -4, 'result_float_int'),
('R_ARM_THM_JUMP24', 0xC, -4, 'result_float_float'),
('R_ARM_THM_JUMP24', 0xA, -4, 'result_int_int'),
('R_ARM_THM_JUMP24', 0xC, -4, 'result_int_float'),
('R_ARM_THM_CALL', 0xA, -4, 'aux_get_42'),
('R_ARM_THM_JUMP24', 0x12, -4, 'result_float'),
('R_ARM_THM_CALL', 0x2, -4, 'aux_get_42'),
('R_ARM_THM_JUMP24', 0xA, -4, 'result_float'),
('R_ARM_THM_JUMP24', 0x2, -4, 'result_int'),
('R_ARM_THM_JUMP24', 0x4, -4, 'result_float'),
('R_ARM_THM_JUMP24', 0x28, -4, 'result_float'),
('R_ARM_THM_CALL', 0x2C, -4, 'sqrtf'),
('R_ARM_THM_JUMP24', 0x20, -4, 'result_float'),
('R_ARM_THM_CALL', 0x24, -4, 'sqrtf'),
('R_ARM_THM_CALL', 0xA, -4, 'expf'),
('R_ARM_THM_JUMP24', 0x12, -4, 'result_float'),
('R_ARM_THM_CALL', 0x2, -4, 'expf'),
('R_ARM_THM_JUMP24', 0xA, -4, 'result_float'),
('R_ARM_THM_CALL', 0xA, -4, 'logf'),
('R_ARM_THM_JUMP24', 0x12, -4, 'result_float'),
('R_ARM_THM_CALL', 0x2, -4, 'logf'),
('R_ARM_THM_JUMP24', 0xA, -4, 'result_float'),
('R_ARM_THM_CALL', 0xA, -4, 'sinf'),
('R_ARM_THM_JUMP24', 0x12, -4, 'result_float'),
('R_ARM_THM_CALL', 0x2, -4, 'sinf'),
('R_ARM_THM_JUMP24', 0xA, -4, 'result_float'),
('R_ARM_THM_CALL', 0xA, -4, 'cosf'),
('R_ARM_THM_JUMP24', 0x12, -4, 'result_float'),
('R_ARM_THM_CALL', 0x2, -4, 'cosf'),
('R_ARM_THM_JUMP24', 0xA, -4, 'result_float'),
('R_ARM_THM_CALL', 0xA, -4, 'tanf'),
('R_ARM_THM_JUMP24', 0x12, -4, 'result_float'),
('R_ARM_THM_CALL', 0x2, -4, 'tanf'),
]
# For each reference, check that at least one matching relocation has the expected addend
for ref_type, _, ref_addend, ref_symbol in reference:
found = False
addend = 0
for typ, offset, addend, symbol in reloc_addends:
if typ == ref_type and symbol == ref_symbol and addend == ref_addend:
found = True
break
assert found, f"Missing or incorrect addend for {ref_type} {ref_symbol} (value={addend:X}, expected {ref_addend})"