From a9a1b1a5259f169ff68292113aac89e3a2c0d4f3 Mon Sep 17 00:00:00 2001 From: Jvle Date: Tue, 30 Dec 2025 16:16:09 +0800 Subject: [PATCH 1/4] riscv64: add ELF relocation support Signed-off-by: Jvle --- cle/backends/elf/relocation/__init__.py | 2 + cle/backends/elf/relocation/riscv64.py | 871 ++++++++++++++++++++++++ 2 files changed, 873 insertions(+) create mode 100644 cle/backends/elf/relocation/riscv64.py diff --git a/cle/backends/elf/relocation/__init__.py b/cle/backends/elf/relocation/__init__.py index d25203b58..2931ae834 100644 --- a/cle/backends/elf/relocation/__init__.py +++ b/cle/backends/elf/relocation/__init__.py @@ -5,6 +5,7 @@ from .amd64 import relocation_table_amd64 from .arm import relocation_table_arm from .arm64 import relocation_table_arm64 +from .riscv64 import relocation_table_riscv64 from .i386 import relocation_table_i386 from .mips import relocation_table_mips from .ppc import relocation_table_ppc @@ -19,6 +20,7 @@ "AARCH64": relocation_table_arm64, "ARMEL": relocation_table_arm, "ARMHF": relocation_table_arm, + "RISCV64": relocation_table_riscv64, "X86": relocation_table_i386, "MIPS32": relocation_table_mips, "MIPS64": relocation_table_mips, diff --git a/cle/backends/elf/relocation/riscv64.py b/cle/backends/elf/relocation/riscv64.py new file mode 100644 index 000000000..7a4154020 --- /dev/null +++ b/cle/backends/elf/relocation/riscv64.py @@ -0,0 +1,871 @@ +""" +Relocations for RISCV64 + +Reference: +1. https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#relocations +2. https://docs.riscv.org/reference/isa/_attachments/riscv-unprivileged.pdf + +""" + +from __future__ import annotations + +import logging + +from .elfreloc import ELFReloc +from .generic import ( + GenericAbsoluteAddendReloc, + GenericCopyReloc, + GenericIRelativeReloc, + GenericJumpslotReloc, + RelocTruncate32Mixin, + RelocGOTMixin, +) + +log = logging.getLogger(name=__name__) + +class R_RISCV_NONE(ELFReloc): + """ + Relocation Type: 0 + Calculation: None + """ + def relocate(self): + return True + +class R_RISCV_32(RelocTruncate32Mixin, GenericAbsoluteAddendReloc): + """ + Relocation Type: 1 + Calculation: S + A + """ + +class R_RISCV_64(GenericAbsoluteAddendReloc): + """ + Relocation Type: 2 + Calculation: S + A + """ + +class R_RISCV_RELATIVE(ELFReloc): + """ + Relocation Type: 3 + Calculation: B + A + """ + AUTO_HANDLE_NONE = True + + @property + def value(self): + return self.owner.mapped_base + self.addend + +class R_RISCV_COPY(GenericCopyReloc): + """ + Relocation Type: 4 + Calculation: None + """ + +class R_RISCV_JUMP_SLOT(GenericJumpslotReloc): + """ + Relocation Type: 5 + Calculation: S + """ + +class R_RISCV_BRANCH(ELFReloc): + """ + Relocation Type: 16 + Calculation: S + A - P + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + P = self.rebased_addr + return S + A - P + + def relocate(self): + if not self.resolved: + return False + + val = self.value + + if val & 0x1: + log.warning("Unaligned BRANCH relocation") + + imm = val >> 1 + if not -(1 << 12) <= imm < (1 << 12): + log.warning("BRANCH relocation out of range") + + instr = self.owner.memory.unpack_word(self.relative_addr, size=4) + + instr &= ~( + (1 << 31) | # imm[12] + (0x3F << 25) | # imm[10:5] + (0xF << 8) | # imm[4:1] + (1 << 7) # imm[11] + ) + + instr |= ( + ((imm >> 11) & 0x1) << 31 | # imm[12] + ((imm >> 4) & 0x3F) << 25 | # imm[10:5] + ((imm >> 0) & 0xF) << 8 | # imm[4:1] + ((imm >> 10) & 0x1) << 7 # imm[11] + ) + + self.owner.memory.pack_word(self.relative_addr, instr, size=4) + return True + + +class R_RISCV_JAL(ELFReloc): + """ + Relocation Type: 17 + Calculation: S + A - P + """ + AUTO_HANDLE_NONE = False + + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + P = self.rebased_addr + return S + A - P + + def relocate(self): + if not self.resolved: + return False + + val = self.value + if not -(1 << 20) <= val < (1 << 20): + log.warning("JAL relocation out of range") + + imm = val >> 1 + + instr = self.owner.memory.unpack_word(self.relative_addr, size=4) + instr &= 0xFFF + + instr |= ( + ((imm >> 19) & 0x1) << 31 | # imm[20] + ((imm >> 0) & 0x3FF) << 21 | # imm[10:1] + ((imm >> 10) & 0x1) << 20 | # imm[11] + ((imm >> 11) & 0xFF) << 12 # imm[19:12] + ) + + self.owner.memory.pack_word(self.relative_addr, instr, size=4) + return True + +class R_RISCV_CALL_PLT(ELFReloc): + """ + Relocation Type: 19 + Calculation: S + A - P + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + P = self.rebased_addr + return S + A - P + + def relocate(self): + if not self.resolved: + return False + + val = self.value + # U + I Type instruction pair + hi20 = (val + 0x800) >> 12 + lo12 = val & 0xFFF + + instr_hi = self.owner.memory.unpack_word(self.relative_addr, size=4) + instr_hi &= 0x00000FFF + instr_hi |= (hi20 & 0xFFFFF) << 12 + self.owner.memory.pack_word(self.relative_addr, instr_hi, size=4) + + instr_lo = self.owner.memory.unpack_word(self.relative_addr + 4, size=4) + instr_lo &= 0x000FFFFF + instr_lo |= (lo12 & 0xFFF) << 20 + self.owner.memory.pack_word(self.relative_addr + 4, instr_lo, size=4) + + return True + +class R_RISCV_CALL(R_RISCV_CALL_PLT): + """ + Relocation Type: 18 + Calculation: S + A - P + """ + def relocate(self): + log.debug("R_RISCV_CALL encountered, treating as CALL_PLT") + return super().relocate() + +class R_RISCV_GOT_HI20(RelocGOTMixin, ELFReloc): + """ + Relocation Type: 20 + Calculation: G + GOT + A - P + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + P = self.rebased_addr + return S + A - P + + def resolve(self, obj, extern_object=None): + return RelocGOTMixin.resolve(self, symbol=obj, extern_object=extern_object) + + def relocate(self): + if not self.resolved: + return False + + val = self.value + hi20 = (val + 0x800) >> 12 + + instr = self.owner.memory.unpack_word(self.relative_addr, size=4) + instr &= 0x00000FFF + instr |= (hi20 & 0xFFFFF) << 12 + + self.owner.memory.pack_word(self.relative_addr, instr, size=4) + return True + +class R_RISCV_PCREL_HI20(ELFReloc): + """ + Relocation Type: 23 + Calculation: S + A - P + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + P = self.rebased_addr + return S + A - P + + def relocate(self): + if not self.resolved: + return False + + val = self.value + hi20 = (val + 0x800) >> 12 + + instr = self.owner.memory.unpack_word(self.relative_addr, size=4) + instr &= 0x00000FFF + instr |= (hi20 & 0xFFFFF) << 12 + + self.owner.memory.pack_word(self.relative_addr, instr, size=4) + return True + +def _find_paired_hi20(self): + # TODO: We don't implement R_RISCV_TLS_GOT_HI20 now + label_addr = self.resolvedby.rebased_addr + for rr in self.owner.relocs: + if rr.rebased_addr != label_addr: + continue + if isinstance(rr, (R_RISCV_PCREL_HI20, R_RISCV_GOT_HI20)): + return rr + return None + + +class R_RISCV_PCREL_LO12_I(ELFReloc): + """ + Relocation Type: 24 + """ + def relocate(self): + if not self.resolved or self.resolvedby is None: + return False + + hi = _find_paired_hi20(self) + if hi is None or not hi.resolved: + log.warning("PCREL_LO12_I without matching HI20 at %#x", self.resolvedby.rebased_addr) + return False + + off = hi.value + lo12 = (off + self.addend) & 0xFFF + + instr = self.owner.memory.unpack_word(self.relative_addr, size=4) + instr &= ~(0xFFF << 20) + instr |= lo12 << 20 + self.owner.memory.pack_word(self.relative_addr, instr, size=4) + return True + + +class R_RISCV_PCREL_LO12_S(ELFReloc): + """ + Relocation Type: 25 + """ + def relocate(self): + if not self.resolved or self.resolvedby is None: + return False + + hi = _find_paired_hi20(self) + if hi is None or not hi.resolved: + log.warning("PCREL_LO12_S without matching HI20 at %#x", self.resolvedby.rebased_addr) + return False + + off = hi.value + lo12 = (off + self.addend) & 0xFFF + imm_11_5 = (lo12 >> 5) & 0x7F + imm_4_0 = lo12 & 0x1F + + instr = self.owner.memory.unpack_word(self.relative_addr, size=4) + instr &= ~((0x7F << 25) | (0x1F << 7)) + instr |= (imm_11_5 << 25) | (imm_4_0 << 7) + self.owner.memory.pack_word(self.relative_addr, instr, size=4) + return True + +class R_RISCV_HI20(GenericAbsoluteAddendReloc): + """ + Relocation Type: 26 + Calculation: S + A + """ + def relocate(self): + if not self.resolved: + return False + + val = self.value + hi20 = (val + 0x800) >> 12 + instr = self.owner.memory.unpack_word(self.relative_addr, size=4) + instr = (instr & 0x00000FFF) | ((hi20 & 0xFFFFF) << 12) + self.owner.memory.pack_word(self.relative_addr, instr, size=4) + return True + +class R_RISCV_LO12_I(ELFReloc): + """ + Relocation Type: 27 + Calculation: S + A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + val = self.value + lo12 = val & 0xFFF + + instr = self.owner.memory.unpack_word(self.relative_addr, size=4) + instr &= ~(0xFFF << 20) + instr |= lo12 << 20 + + self.owner.memory.pack_word(self.relative_addr, instr, size=4) + return True + +class R_RISCV_LO12_S(ELFReloc): + """ + Relocation Type: 28 + Calculation: S + A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + val = self.value + lo12 = val & 0xFFF + + instr = self.owner.memory.unpack_word(self.relative_addr, size=4) + instr &= ~((0x7F << 25) | (0x1F << 7)) + instr |= ((lo12 >> 5) & 0x7F) << 25 + instr |= (lo12 & 0x1F) << 7 + + self.owner.memory.pack_word(self.relative_addr, instr, size=4) + return True + +class R_RISCV_ADD8(ELFReloc): + """ + Relocation Type: 33 + Calculation: V + S + A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + V = self.owner.memory.unpack_word(self.relative_addr, size=1) + new_val = (V + self.value) & 0xFF + self.owner.memory.pack_word(self.relative_addr, new_val, size=1) + return True + +class R_RISCV_ADD16(ELFReloc): + """ + Relocation Type: 34 + Calculation: V + S + A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + def relocate(self): + if not self.resolved: + return False + + V = self.owner.memory.unpack_word(self.relative_addr, size=2) + new_val = (V + self.value) & 0xFFFF + self.owner.memory.pack_word(self.relative_addr, new_val, size=2) + return True + +class R_RISCV_ADD32(ELFReloc): + """ + Relocation Type: 35 + Calculation: V + S + A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + V = self.owner.memory.unpack_word(self.relative_addr, size=4) + new_val = (V + self.value) & 0xFFFFFFFF + self.owner.memory.pack_word(self.relative_addr, new_val, size=4) + return True + +class R_RISCV_ADD64(ELFReloc): + """ + Relocation Type: 36 + Calculation: V + S + A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + V = self.owner.memory.unpack_word(self.relative_addr, size=8) + new_val = (V + self.value) & 0xFFFFFFFFFFFFFFFF + self.owner.memory.pack_word(self.relative_addr, new_val, size=8) + return True + +class R_RISCV_SUB8(ELFReloc): + """ + Relocation Type: 37 + Calculation: V - S - A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + V = self.owner.memory.unpack_word(self.relative_addr, size=1) + new_val = (V - self.value) & 0xFF + self.owner.memory.pack_word(self.relative_addr, new_val, size=1) + return True + +class R_RISCV_SUB16(ELFReloc): + """ + Relocation Type: 38 + Calculation: V - S - A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + V = self.owner.memory.unpack_word(self.relative_addr, size=2) + new_val = (V - self.value) & 0xFFFF + self.owner.memory.pack_word(self.relative_addr, new_val, size=2) + return True + +class R_RISCV_SUB32(ELFReloc): + """ + Relocation Type: 39 + Calculation: V - S - A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + V = self.owner.memory.unpack_word(self.relative_addr, size=4) + new_val = (V - self.value) & 0xFFFFFFFF + self.owner.memory.pack_word(self.relative_addr, new_val, size=4) + return True + +class R_RISCV_SUB64(ELFReloc): + """ + Relocation Type: 40 + Calculation: V - S - A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + v = self.owner.memory.unpack_word(self.relative_addr, size=8) + new_val = (v - self.value) & 0xFFFFFFFFFFFFFFFF + self.owner.memory.pack_word(self.relative_addr, new_val, size=8) + return True + +class R_RISCV_ALIGN(ELFReloc): + """ + Relocation Type: 43 + Calculation: None + """ + AUTO_HANDLE_NONE = True + def relocate(self): + return True + +class R_RISCV_RVC_BRANCH(ELFReloc): + """ + Relocation Type: 44 + Calculation: S + A - P + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + P = self.rebased_addr + return S + A - P + + def relocate(self): + if not self.resolved: + return False + + val = self.value + + # C.B* offsets are multiples of 2 + if val & 0x1: + log.warning("Unaligned RVC branch target") + + imm = val >> 1 + + if not -256 <= val < 256: + log.warning("RVC branch out of range") + + instr = self.owner.memory.unpack_word(self.relative_addr, size=2) + instr &= ~0x1C7C + instr |= ( + ((imm >> 7) & 0x1) << 12 | # val[8] + ((imm >> 2) & 0x3) << 10 | # val[4:3] + ((imm >> 5) & 0x3) << 5 | # val[7:6] + ((imm >> 0) & 0x3) << 3 | # val[2:1] + ((imm >> 4) & 0x1) << 2 # val[5] + ) + self.owner.memory.pack_word(self.relative_addr, instr, size=2) + return True + + +class R_RISCV_RVC_JUMP(ELFReloc): + """ + Relocation Type: 45 + Calculation: S + A - P + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + P = self.rebased_addr + return S + A - P + + def relocate(self): + if not self.resolved: + return False + + val = self.value + imm = val >> 1 + + instr = self.owner.memory.unpack_word(self.relative_addr, size=2) + + instr &= ~0X1FFC + instr |= ( + ((imm >> 10) & 1) << 12 | # imm[11] + ((imm >> 3) & 1) << 11 | # imm[4] + ((imm >> 7) & 0x3) << 9 | # imm[9:8] + ((imm >> 9) & 1) << 8 | # imm[10] + ((imm >> 5) & 1) << 7 | # imm[6] + ((imm >> 6) & 1) << 6 | # imm[7] + ((imm >> 0) & 0x7) << 3 | # imm[3:1] + ((imm >> 4) & 1) << 2 # imm[5] + ) + self.owner.memory.pack_word(self.relative_addr, instr, size=2) + return True + + +class R_RISCV_RELAX(ELFReloc): + """ + Relocation Type: 51 + Calculation: None + """ + AUTO_HANDLE_NONE = True + def relocate(self): + return True + +class R_RISCV_SUB6(ELFReloc): + """ + Relocation Type: 52 + Calculation: V - S - A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return S + A + + def relocate(self): + if not self.resolved: + return False + + V = self.owner.memory.unpack_word(self.relative_addr, size=1) + old_6bit_val = V & 0x3F + new_6bit_val = (old_6bit_val - self.value) & 0x3F + new_val = (V & 0xC0) | new_6bit_val + self.owner.memory.pack_word(self.relative_addr, new_val, size=1) + return True + +class R_RISCV_SET6(ELFReloc): + """ + Relocation Type: 53 + Calculation: S + A + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + return (S + A) & 0x3F + + def relocate(self): + if not self.resolved: + return False + + V = self.owner.memory.unpack_word(self.relative_addr, size=1) + new_val = (V & 0xC0) | self.value + self.owner.memory.pack_word(self.relative_addr, new_val, size=1) + return True + +class R_RISCV_SET8(GenericAbsoluteAddendReloc): + """ + Relocation Type: 54 + Calculation: S + A + """ + def relocate(self): + if not self.resolved: + return False + + self.owner.memory.pack_word(self.relative_addr, self.value & 0xFF, size=1) + return True + +class R_RISCV_SET16(GenericAbsoluteAddendReloc): + """ + Relocation Type: 55 + Calculation: S + A + """ + def relocate(self): + if not self.resolved: + return False + + self.owner.memory.pack_word(self.relative_addr, self.value & 0xFFFF, size=2) + return True + +class R_RISCV_SET32(RelocTruncate32Mixin, GenericAbsoluteAddendReloc): + """ + Relocation Type: 56 + Calculation: S + A + """ + +class R_RISCV_32_PCREL(ELFReloc): + """ + Relocation Type: 57 + Calculation: S + A - P + """ + @property + def value(self): + if self.resolvedby is None: + return 0 + + S = self.resolvedby.rebased_addr + A = self.addend + P = self.rebased_addr + return S + A - P + + def relocate(self): + val = self.value + self.owner.memory.pack_word(self.relative_addr, val & 0xFFFFFFFF, size=4) + return True + +class R_RISCV_IRELATIVE(GenericIRelativeReloc): + """ + Relocation Type: 58 + Calculation: ifunc_resolver(B + A) + """ + +class R_RISCV_SET_ULEB128(ELFReloc): + """ + Relocation Type: 60 + Calculation: S + A + """ + AUTO_HANDLE_NONE = True + def relocate(self): + return True + + +class R_RISCV_SUB_ULEB128(ELFReloc): + """ + Relocation Type: 61 + Calculation: V - S - A + """ + AUTO_HANDLE_NONE = True + def relocate(self): + return True + + +relocation_table_riscv64 = { + 0: R_RISCV_NONE, + 1: R_RISCV_32, + 2: R_RISCV_64, + 3: R_RISCV_RELATIVE, + 4: R_RISCV_COPY, + 5: R_RISCV_JUMP_SLOT, + # 6: R_RISCV_TLS_DTPMOD32, + # 7: R_RISCV_TLS_DTPMOD64, + # 8: R_RISCV_TLS_DTPREL32, + # 9: R_RISCV_TLS_DTPREL64, + # 10: R_RISCV_TLS_TPREL32, + # 11: R_RISCV_TLS_TPREL64, + # 12: R_RISCV_TLSDESC + 16: R_RISCV_BRANCH, + 17: R_RISCV_JAL, + 18: R_RISCV_CALL, + 19: R_RISCV_CALL_PLT, + 20: R_RISCV_GOT_HI20, + # 21: R_RISCV_TLS_GOT_HI20, + # 22: R_RISCV_TLS_GD_HI20, + 23: R_RISCV_PCREL_HI20, + 24: R_RISCV_PCREL_LO12_I, + 25: R_RISCV_PCREL_LO12_S, + 26: R_RISCV_HI20, + 27: R_RISCV_LO12_I, + 28: R_RISCV_LO12_S, + # 29: R_RISCV_TPREL_HI20, + # 30: R_RISCV_TPREL_LO12_I, + # 31: R_RISCV_TPREL_LO12_S, + # 32: R_RISCV_TPREL_ADD, + 33: R_RISCV_ADD8, + 34: R_RISCV_ADD16, + 35: R_RISCV_ADD32, + 36: R_RISCV_ADD64, + 37: R_RISCV_SUB8, + 38: R_RISCV_SUB16, + 39: R_RISCV_SUB32, + 40: R_RISCV_SUB64, + # 41: R_RISCV_GOT32_PCREL, + # 42: Reserved + 43: R_RISCV_ALIGN, + 44: R_RISCV_RVC_BRANCH, + 45: R_RISCV_RVC_JUMP, + + # 46-50: Reserved + + 51: R_RISCV_RELAX, + 52: R_RISCV_SUB6, + 53: R_RISCV_SET6, + 54: R_RISCV_SET8, + 55: R_RISCV_SET16, + 56: R_RISCV_SET32, + 57: R_RISCV_32_PCREL, + 58: R_RISCV_IRELATIVE, + # 59: R_RISCV_PLT32, + 60: R_RISCV_SET_ULEB128, + 61: R_RISCV_SUB_ULEB128, + # 62: R_RISCV_TLSDESC_HI20, + # 63: R_RISCV_TLSDESC_LOAD_LO12, + # 64: R_RISCV_TLSDESC_ADD_LO12, + # 65: R_RISCV_TLSDESC_CALL, + + # 66-190: Reserved + + # 191: R_RISCV_VENDOR, + + # 192-255: Reserved +} + + +__all__ = ("relocation_table_riscv64",) \ No newline at end of file From 3129e63424eaebc9da7e63b20335812034691d01 Mon Sep 17 00:00:00 2001 From: Jvle Date: Wed, 31 Dec 2025 17:35:26 +0800 Subject: [PATCH 2/4] riscv64: add relocation test script Signed-off-by: Jvle --- tests/test_riscv64_relocations.py | 287 ++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 tests/test_riscv64_relocations.py diff --git a/tests/test_riscv64_relocations.py b/tests/test_riscv64_relocations.py new file mode 100644 index 000000000..8b59b47da --- /dev/null +++ b/tests/test_riscv64_relocations.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python +from __future__ import annotations + +import os +import struct +import unittest + +import cle +from cle.backends.elf.relocation import riscv64 as riscv + +def get_real_instr(r): + try: + if r.relative_addr % 2 != 0: + return None + probing = r.owner.memory.unpack_word(r.relative_addr, size=2) + if (probing & 0x3) != 0x3: + return probing + return r.owner.memory.unpack_word(r.relative_addr, size=4) + except (KeyError, struct.error): + return None + +def sign_extend(x, bits): + m = 1 << (bits - 1) + return (x ^ m) - m + +def decode_u_imm20(insn32): + return insn32 & 0xFFFFF000 + +def decode_i_imm12_raw(insn32): + return (insn32 >> 20) & 0xFFF + +def decode_s_imm12_raw(insn32: int) -> int: + imm = ((insn32 >> 25) & 0x7F) << 5 | ((insn32 >> 7) & 0x1F) + return imm & 0xFFF + +def decode_b_off(insn32): + imm = ( + ((insn32 >> 31) & 0x1) << 12 | + ((insn32 >> 7) & 0x1) << 11 | + ((insn32 >> 25) & 0x3F) << 5 | + ((insn32 >> 8) & 0xF) << 1 + ) + return sign_extend(imm, 13) + +def decode_j_off(insn32): + imm = ( + ((insn32 >> 31) & 0x1) << 20 | + ((insn32 >> 21) & 0x3FF) << 1 | + ((insn32 >> 20) & 0x1) << 11 | + ((insn32 >> 12) & 0xFF) << 12 + ) + return sign_extend(imm, 21) + +def decode_cb_off(insn16): + off = ( + ((insn16 >> 12) & 1) << 8 | # off[8] + ((insn16 >> 6) & 1) << 7 | # off[7] + ((insn16 >> 5) & 1) << 6 | # off[6] + ((insn16 >> 2) & 1) << 5 | # off[5] + ((insn16 >> 11) & 1) << 4 | # off[4] + ((insn16 >> 10) & 1) << 3 | # off[3] + ((insn16 >> 4) & 1) << 2 | # off[2] + ((insn16 >> 3) & 1) << 1 # off[1] + ) + return sign_extend(off, 9) + +def decode_cj_off(insn16): + off = ( + ((insn16 >> 12) & 1) << 11 | # off[11] + ((insn16 >> 11) & 1) << 4 | # off[4] + ((insn16 >> 9) & 0x3) << 8 | # off[9:8] + ((insn16 >> 8) & 1) << 10 | # off[10] + ((insn16 >> 7) & 1) << 6 | # off[6] + ((insn16 >> 6) & 1) << 7 | # off[7] + ((insn16 >> 3) & 0x7) << 1 | # off[3:1] + ((insn16 >> 2) & 1) << 5 # off[5] + ) + return sign_extend(off, 12) + +def expect_abs(r): + assert r.resolvedby is not None + return r.resolvedby.rebased_addr + r.addend + +def expect_pcrel(r, P: int | None = None): + assert r.resolvedby is not None + S = r.resolvedby.rebased_addr + A = r.addend + if P is None: + P = r.rebased_addr + return S + A - P + +def find_paired_hi20(obj, label_addr: int): + """ + For PCREL_LO12*, resolvedby usually points to a label at the HI20 site (AUIPC). + We find a HI20/GOT_HI20 relocation whose rebased_addr == label_addr. + """ + hi_types = [] + # TODO: We don't implement R_RISCV_TLS_GOT_HI20 now. + for name in ("R_RISCV_PCREL_HI20", "R_RISCV_GOT_HI20", "R_RISCV_TLS_GOT_HI20"): + if hasattr(riscv, name): + hi_types.append(getattr(riscv, name)) + + for rr in obj.relocs: + if rr.rebased_addr == label_addr and any(isinstance(rr, t) for t in hi_types): + return rr + return None + +def run_reloc_test_on_file(file_path, base_addr=0x210000): + try: + loader = cle.Loader(file_path, main_opts={"base_addr": base_addr}) + except Exception as e: + raise AssertionError(f"Failed to load {file_path}: {e}") from e + + obj = loader.main_object + relocations = obj.relocs + + instruction_reloc_types = ( + riscv.R_RISCV_PCREL_HI20, + riscv.R_RISCV_PCREL_LO12_I, + riscv.R_RISCV_PCREL_LO12_S, + riscv.R_RISCV_HI20, + riscv.R_RISCV_LO12_I, + riscv.R_RISCV_LO12_S, + riscv.R_RISCV_CALL, + riscv.R_RISCV_CALL_PLT, + riscv.R_RISCV_JAL, + riscv.R_RISCV_BRANCH, + riscv.R_RISCV_RVC_JUMP, + riscv.R_RISCV_RVC_BRANCH, + ) + + validated = 0 + + for r in relocations: + if isinstance(r, riscv.R_RISCV_NONE): + continue + + if not r.resolved: + continue + + # Data relocations + if isinstance(r, riscv.R_RISCV_64): + assert r.resolvedby is not None + data = r.owner.memory.unpack_word(r.relative_addr, size=8) + expected = expect_abs(r) + assert data == expected, (r, hex(data), hex(expected)) + validated += 1 + continue + + if isinstance(r, riscv.R_RISCV_32): + assert r.resolvedby is not None + data = r.owner.memory.unpack_word(r.relative_addr, size=4) + expected = expect_abs(r) & 0xFFFFFFFF + assert data == expected, (r, hex(data), hex(expected)) + validated += 1 + continue + + if not isinstance(r, instruction_reloc_types): + continue + + instr = get_real_instr(r) + if instr is None: + raise AssertionError(f"Unable to read instruction for relocation: {r!r}") + + if isinstance(r, riscv.R_RISCV_PCREL_HI20): + assert (instr & 0x7F) == 0b0010111 + off = expect_pcrel(r) + hi_exp = (((off + 0x800) >> 12) & 0xFFFFF) << 12 + hi_enc = decode_u_imm20(instr) + assert hi_enc == hi_exp, (r, hex(hi_enc), hex(hi_exp)) + validated += 1 + elif isinstance(r, riscv.R_RISCV_PCREL_LO12_I): + assert instr & 0x7F in {0b0010011, 0b0000011, 0b0000111, 0b1100111} + assert r.resolvedby is not None + label_addr = r.resolvedby.rebased_addr + hi = find_paired_hi20(obj, label_addr) + assert hi is not None and hi.resolved, f"LO12_I without matching HI20 at {label_addr:#x}: {r!r}" + off = expect_pcrel(hi) + lo_exp = (off + r.addend) & 0xFFF + lo_enc = decode_i_imm12_raw(instr) + assert lo_enc == lo_exp, (r, hex(lo_enc), hex(lo_exp)) + validated += 1 + elif isinstance(r, riscv.R_RISCV_PCREL_LO12_S): + assert (instr & 0x7F) == 0b0100011 + assert r.resolvedby is not None + label_addr = r.resolvedby.rebased_addr + hi = find_paired_hi20(obj, label_addr) + assert hi is not None and hi.resolved, f"LO12_S without matching HI20 at {label_addr:#x}: {r!r}" + + off = expect_pcrel(hi) + lo_exp = (off + r.addend) & 0xFFF + lo_enc = decode_s_imm12_raw(instr) + assert lo_enc == lo_exp, (r, hex(lo_enc), hex(lo_exp)) + validated += 1 + elif isinstance(r, riscv.R_RISCV_HI20): + assert instr & 0x7F in {0b0010111, 0b0110111} + val = expect_abs(r) + hi_exp = (((val + 0x800) >> 12) & 0xFFFFF) << 12 + hi_enc = decode_u_imm20(instr) + assert hi_enc == hi_exp, (r, hex(hi_enc), hex(hi_exp)) + validated += 1 + elif isinstance(r, riscv.R_RISCV_LO12_I): + assert instr & 0x7F in {0b0010011, 0b0000011, 0b0000111, 0b1100111} + val = expect_abs(r) + lo_exp = val & 0xFFF + lo_enc = decode_i_imm12_raw(instr) + assert lo_enc == lo_exp, (r, hex(lo_enc), hex(lo_exp)) + validated += 1 + elif isinstance(r, riscv.R_RISCV_LO12_S): + assert (instr & 0x7F) == 0b0100011 + val = expect_abs(r) + lo_exp = val & 0xFFF + lo_enc = decode_s_imm12_raw(instr) + assert lo_enc == lo_exp, (r, hex(lo_enc), hex(lo_exp)) + validated += 1 + elif isinstance(r, (riscv.R_RISCV_CALL, riscv.R_RISCV_CALL_PLT)): + assert (instr & 0x7F) == 0b0010111 + next_instr = r.owner.memory.unpack_word(r.relative_addr + 4, size=4) + assert (next_instr & 0x7F) == 0b1100111 + + off = expect_pcrel(r) + hi_exp = (((off + 0x800) >> 12) & 0xFFFFF) << 12 + hi_enc = decode_u_imm20(instr) + assert hi_enc == hi_exp, (r, hex(hi_enc), hex(hi_exp)) + + lo_exp = off & 0xFFF + lo_enc = decode_i_imm12_raw(next_instr) + assert lo_enc == lo_exp, (r, hex(lo_enc), hex(lo_exp)) + validated += 1 + elif isinstance(r, riscv.R_RISCV_JAL): + assert (instr & 0x7F) == 0b1101111 + off_enc = decode_j_off(instr) + off_exp = expect_pcrel(r) + assert off_enc == off_exp, (r, hex(off_enc), hex(off_exp)) + validated += 1 + elif isinstance(r, riscv.R_RISCV_BRANCH): + assert (instr & 0x7F) == 0b1100011 + off_enc = decode_b_off(instr) + off_exp = expect_pcrel(r) + assert off_enc == off_exp, (r, hex(off_enc), hex(off_exp)) + validated += 1 + elif isinstance(r, riscv.R_RISCV_RVC_JUMP): + assert (instr & 0x3) == 0b01 + assert (instr >> 13) & 0x7 in {0b101, 0b001} + off_enc = decode_cj_off(instr) + off_exp = expect_pcrel(r) + assert off_enc == off_exp, (r, hex(off_enc), hex(off_exp)) + validated += 1 + elif isinstance(r, riscv.R_RISCV_RVC_BRANCH): + assert (instr & 0x3) == 0b01 + assert (instr >> 13) & 0x7 in {0b110, 0b111} + off_enc = decode_cb_off(instr) + off_exp = expect_pcrel(r) + assert off_enc == off_exp, (r, hex(off_enc), hex(off_exp)) + validated += 1 + + assert validated > 0, f"No relocations validated for {file_path}" + + +def test_riscv64_all_relocations() -> None: + riscv_test_dir = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", + "..", + "binaries", + "tests", + "riscv64", + ) + + if not os.path.isdir(riscv_test_dir): + raise unittest.SkipTest(f"Directory not found: {riscv_test_dir}") + + test_files = [ + os.path.join(riscv_test_dir, f) + for f in os.listdir(riscv_test_dir) + if f.endswith((".o", ".so")) + ] + + if not test_files: + raise unittest.SkipTest(f"No .o or .so files found in {riscv_test_dir}") + + for file_path in sorted(test_files): + run_reloc_test_on_file(file_path) + + +if __name__ == "__main__": + test_riscv64_all_relocations() From 3b0341b8b6d627ac39ed20b66870a6f8833852f5 Mon Sep 17 00:00:00 2001 From: Jvle Date: Sat, 3 Jan 2026 10:47:03 +0800 Subject: [PATCH 3/4] typing: add return type annotations to ELFReloc.value Signed-off-by: Jvle --- cle/backends/elf/relocation/elfreloc.py | 2 +- cle/backends/elf/relocation/riscv64.py | 42 ++++++++++++------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/cle/backends/elf/relocation/elfreloc.py b/cle/backends/elf/relocation/elfreloc.py index dffe5cdbb..4bbdfa800 100644 --- a/cle/backends/elf/relocation/elfreloc.py +++ b/cle/backends/elf/relocation/elfreloc.py @@ -25,6 +25,6 @@ def addend(self): return self._addend @property - def value(self): # pylint: disable=no-self-use + def value(self) -> int: # pylint: disable=no-self-use log.error("Value property of Relocation must be overridden by subclass!") return 0 diff --git a/cle/backends/elf/relocation/riscv64.py b/cle/backends/elf/relocation/riscv64.py index 7a4154020..0b1031095 100644 --- a/cle/backends/elf/relocation/riscv64.py +++ b/cle/backends/elf/relocation/riscv64.py @@ -51,7 +51,7 @@ class R_RISCV_RELATIVE(ELFReloc): AUTO_HANDLE_NONE = True @property - def value(self): + def value(self) -> int: return self.owner.mapped_base + self.addend class R_RISCV_COPY(GenericCopyReloc): @@ -72,7 +72,7 @@ class R_RISCV_BRANCH(ELFReloc): Calculation: S + A - P """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -122,7 +122,7 @@ class R_RISCV_JAL(ELFReloc): AUTO_HANDLE_NONE = False @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -160,7 +160,7 @@ class R_RISCV_CALL_PLT(ELFReloc): Calculation: S + A - P """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -205,7 +205,7 @@ class R_RISCV_GOT_HI20(RelocGOTMixin, ELFReloc): Calculation: G + GOT + A - P """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -237,7 +237,7 @@ class R_RISCV_PCREL_HI20(ELFReloc): Calculation: S + A - P """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -340,7 +340,7 @@ class R_RISCV_LO12_I(ELFReloc): Calculation: S + A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -368,7 +368,7 @@ class R_RISCV_LO12_S(ELFReloc): Calculation: S + A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -397,7 +397,7 @@ class R_RISCV_ADD8(ELFReloc): Calculation: V + S + A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -420,7 +420,7 @@ class R_RISCV_ADD16(ELFReloc): Calculation: V + S + A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -442,7 +442,7 @@ class R_RISCV_ADD32(ELFReloc): Calculation: V + S + A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -465,7 +465,7 @@ class R_RISCV_ADD64(ELFReloc): Calculation: V + S + A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -488,7 +488,7 @@ class R_RISCV_SUB8(ELFReloc): Calculation: V - S - A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -511,7 +511,7 @@ class R_RISCV_SUB16(ELFReloc): Calculation: V - S - A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -534,7 +534,7 @@ class R_RISCV_SUB32(ELFReloc): Calculation: V - S - A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -557,7 +557,7 @@ class R_RISCV_SUB64(ELFReloc): Calculation: V - S - A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 S = self.resolvedby.rebased_addr @@ -588,7 +588,7 @@ class R_RISCV_RVC_BRANCH(ELFReloc): Calculation: S + A - P """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -631,7 +631,7 @@ class R_RISCV_RVC_JUMP(ELFReloc): Calculation: S + A - P """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -679,7 +679,7 @@ class R_RISCV_SUB6(ELFReloc): Calculation: V - S - A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -704,7 +704,7 @@ class R_RISCV_SET6(ELFReloc): Calculation: S + A """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 @@ -757,7 +757,7 @@ class R_RISCV_32_PCREL(ELFReloc): Calculation: S + A - P """ @property - def value(self): + def value(self) -> int: if self.resolvedby is None: return 0 From 54d6b26a487fb4589fa24c11f11741c7271d1ff9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 3 Jan 2026 03:03:53 +0000 Subject: [PATCH 4/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cle/backends/elf/relocation/__init__.py | 2 +- cle/backends/elf/relocation/riscv64.py | 150 +++++++++++++++++------- tests/test_riscv64_relocations.py | 67 ++++++----- 3 files changed, 144 insertions(+), 75 deletions(-) diff --git a/cle/backends/elf/relocation/__init__.py b/cle/backends/elf/relocation/__init__.py index 2931ae834..0e91c0b4c 100644 --- a/cle/backends/elf/relocation/__init__.py +++ b/cle/backends/elf/relocation/__init__.py @@ -5,11 +5,11 @@ from .amd64 import relocation_table_amd64 from .arm import relocation_table_arm from .arm64 import relocation_table_arm64 -from .riscv64 import relocation_table_riscv64 from .i386 import relocation_table_i386 from .mips import relocation_table_mips from .ppc import relocation_table_ppc from .ppc64 import relocation_table_ppc64 +from .riscv64 import relocation_table_riscv64 from .s390x import relocation_table_s390x from .sparc import relocation_table_sparc diff --git a/cle/backends/elf/relocation/riscv64.py b/cle/backends/elf/relocation/riscv64.py index 0b1031095..b685a0015 100644 --- a/cle/backends/elf/relocation/riscv64.py +++ b/cle/backends/elf/relocation/riscv64.py @@ -17,60 +17,70 @@ GenericCopyReloc, GenericIRelativeReloc, GenericJumpslotReloc, - RelocTruncate32Mixin, RelocGOTMixin, + RelocTruncate32Mixin, ) log = logging.getLogger(name=__name__) + class R_RISCV_NONE(ELFReloc): """ Relocation Type: 0 Calculation: None """ + def relocate(self): return True + class R_RISCV_32(RelocTruncate32Mixin, GenericAbsoluteAddendReloc): """ Relocation Type: 1 Calculation: S + A """ + class R_RISCV_64(GenericAbsoluteAddendReloc): """ Relocation Type: 2 Calculation: S + A """ + class R_RISCV_RELATIVE(ELFReloc): """ Relocation Type: 3 Calculation: B + A """ + AUTO_HANDLE_NONE = True @property def value(self) -> int: return self.owner.mapped_base + self.addend + class R_RISCV_COPY(GenericCopyReloc): """ Relocation Type: 4 Calculation: None """ + class R_RISCV_JUMP_SLOT(GenericJumpslotReloc): """ Relocation Type: 5 Calculation: S """ + class R_RISCV_BRANCH(ELFReloc): """ Relocation Type: 16 Calculation: S + A - P """ + @property def value(self) -> int: if self.resolvedby is None: @@ -96,18 +106,13 @@ def relocate(self): instr = self.owner.memory.unpack_word(self.relative_addr, size=4) - instr &= ~( - (1 << 31) | # imm[12] - (0x3F << 25) | # imm[10:5] - (0xF << 8) | # imm[4:1] - (1 << 7) # imm[11] - ) + instr &= ~((1 << 31) | (0x3F << 25) | (0xF << 8) | (1 << 7)) # imm[12] # imm[10:5] # imm[4:1] # imm[11] instr |= ( - ((imm >> 11) & 0x1) << 31 | # imm[12] - ((imm >> 4) & 0x3F) << 25 | # imm[10:5] - ((imm >> 0) & 0xF) << 8 | # imm[4:1] - ((imm >> 10) & 0x1) << 7 # imm[11] + ((imm >> 11) & 0x1) << 31 # imm[12] + | ((imm >> 4) & 0x3F) << 25 # imm[10:5] + | ((imm >> 0) & 0xF) << 8 # imm[4:1] + | ((imm >> 10) & 0x1) << 7 # imm[11] ) self.owner.memory.pack_word(self.relative_addr, instr, size=4) @@ -119,6 +124,7 @@ class R_RISCV_JAL(ELFReloc): Relocation Type: 17 Calculation: S + A - P """ + AUTO_HANDLE_NONE = False @property @@ -145,20 +151,22 @@ def relocate(self): instr &= 0xFFF instr |= ( - ((imm >> 19) & 0x1) << 31 | # imm[20] - ((imm >> 0) & 0x3FF) << 21 | # imm[10:1] - ((imm >> 10) & 0x1) << 20 | # imm[11] - ((imm >> 11) & 0xFF) << 12 # imm[19:12] + ((imm >> 19) & 0x1) << 31 # imm[20] + | ((imm >> 0) & 0x3FF) << 21 # imm[10:1] + | ((imm >> 10) & 0x1) << 20 # imm[11] + | ((imm >> 11) & 0xFF) << 12 # imm[19:12] ) self.owner.memory.pack_word(self.relative_addr, instr, size=4) return True - + + class R_RISCV_CALL_PLT(ELFReloc): """ Relocation Type: 19 Calculation: S + A - P """ + @property def value(self) -> int: if self.resolvedby is None: @@ -189,21 +197,25 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr + 4, instr_lo, size=4) return True - + + class R_RISCV_CALL(R_RISCV_CALL_PLT): """ Relocation Type: 18 Calculation: S + A - P """ + def relocate(self): log.debug("R_RISCV_CALL encountered, treating as CALL_PLT") return super().relocate() + class R_RISCV_GOT_HI20(RelocGOTMixin, ELFReloc): """ Relocation Type: 20 Calculation: G + GOT + A - P """ + @property def value(self) -> int: if self.resolvedby is None: @@ -231,11 +243,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, instr, size=4) return True + class R_RISCV_PCREL_HI20(ELFReloc): """ Relocation Type: 23 Calculation: S + A - P """ + @property def value(self) -> int: if self.resolvedby is None: @@ -260,6 +274,7 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, instr, size=4) return True + def _find_paired_hi20(self): # TODO: We don't implement R_RISCV_TLS_GOT_HI20 now label_addr = self.resolvedby.rebased_addr @@ -275,6 +290,7 @@ class R_RISCV_PCREL_LO12_I(ELFReloc): """ Relocation Type: 24 """ + def relocate(self): if not self.resolved or self.resolvedby is None: return False @@ -298,6 +314,7 @@ class R_RISCV_PCREL_LO12_S(ELFReloc): """ Relocation Type: 25 """ + def relocate(self): if not self.resolved or self.resolvedby is None: return False @@ -318,11 +335,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, instr, size=4) return True + class R_RISCV_HI20(GenericAbsoluteAddendReloc): """ Relocation Type: 26 Calculation: S + A """ + def relocate(self): if not self.resolved: return False @@ -334,11 +353,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, instr, size=4) return True + class R_RISCV_LO12_I(ELFReloc): """ Relocation Type: 27 Calculation: S + A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -361,12 +382,14 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, instr, size=4) return True - + + class R_RISCV_LO12_S(ELFReloc): """ Relocation Type: 28 Calculation: S + A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -391,11 +414,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, instr, size=4) return True + class R_RISCV_ADD8(ELFReloc): """ Relocation Type: 33 Calculation: V + S + A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -413,12 +438,14 @@ def relocate(self): new_val = (V + self.value) & 0xFF self.owner.memory.pack_word(self.relative_addr, new_val, size=1) return True - + + class R_RISCV_ADD16(ELFReloc): """ Relocation Type: 34 Calculation: V + S + A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -427,6 +454,7 @@ def value(self) -> int: S = self.resolvedby.rebased_addr A = self.addend return S + A + def relocate(self): if not self.resolved: return False @@ -435,12 +463,14 @@ def relocate(self): new_val = (V + self.value) & 0xFFFF self.owner.memory.pack_word(self.relative_addr, new_val, size=2) return True - + + class R_RISCV_ADD32(ELFReloc): """ Relocation Type: 35 Calculation: V + S + A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -459,11 +489,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, new_val, size=4) return True + class R_RISCV_ADD64(ELFReloc): """ Relocation Type: 36 Calculation: V + S + A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -482,16 +514,18 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, new_val, size=8) return True + class R_RISCV_SUB8(ELFReloc): """ Relocation Type: 37 Calculation: V - S - A """ + @property def value(self) -> int: if self.resolvedby is None: return 0 - + S = self.resolvedby.rebased_addr A = self.addend return S + A @@ -505,11 +539,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, new_val, size=1) return True + class R_RISCV_SUB16(ELFReloc): """ Relocation Type: 38 Calculation: V - S - A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -528,11 +564,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, new_val, size=2) return True + class R_RISCV_SUB32(ELFReloc): """ Relocation Type: 39 Calculation: V - S - A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -551,11 +589,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, new_val, size=4) return True + class R_RISCV_SUB64(ELFReloc): """ Relocation Type: 40 Calculation: V - S - A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -573,20 +613,25 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, new_val, size=8) return True + class R_RISCV_ALIGN(ELFReloc): """ Relocation Type: 43 Calculation: None """ + AUTO_HANDLE_NONE = True + def relocate(self): return True - + + class R_RISCV_RVC_BRANCH(ELFReloc): """ Relocation Type: 44 Calculation: S + A - P """ + @property def value(self) -> int: if self.resolvedby is None: @@ -615,11 +660,11 @@ def relocate(self): instr = self.owner.memory.unpack_word(self.relative_addr, size=2) instr &= ~0x1C7C instr |= ( - ((imm >> 7) & 0x1) << 12 | # val[8] - ((imm >> 2) & 0x3) << 10 | # val[4:3] - ((imm >> 5) & 0x3) << 5 | # val[7:6] - ((imm >> 0) & 0x3) << 3 | # val[2:1] - ((imm >> 4) & 0x1) << 2 # val[5] + ((imm >> 7) & 0x1) << 12 # val[8] + | ((imm >> 2) & 0x3) << 10 # val[4:3] + | ((imm >> 5) & 0x3) << 5 # val[7:6] + | ((imm >> 0) & 0x3) << 3 # val[2:1] + | ((imm >> 4) & 0x1) << 2 # val[5] ) self.owner.memory.pack_word(self.relative_addr, instr, size=2) return True @@ -630,6 +675,7 @@ class R_RISCV_RVC_JUMP(ELFReloc): Relocation Type: 45 Calculation: S + A - P """ + @property def value(self) -> int: if self.resolvedby is None: @@ -649,16 +695,16 @@ def relocate(self): instr = self.owner.memory.unpack_word(self.relative_addr, size=2) - instr &= ~0X1FFC + instr &= ~0x1FFC instr |= ( - ((imm >> 10) & 1) << 12 | # imm[11] - ((imm >> 3) & 1) << 11 | # imm[4] - ((imm >> 7) & 0x3) << 9 | # imm[9:8] - ((imm >> 9) & 1) << 8 | # imm[10] - ((imm >> 5) & 1) << 7 | # imm[6] - ((imm >> 6) & 1) << 6 | # imm[7] - ((imm >> 0) & 0x7) << 3 | # imm[3:1] - ((imm >> 4) & 1) << 2 # imm[5] + ((imm >> 10) & 1) << 12 # imm[11] + | ((imm >> 3) & 1) << 11 # imm[4] + | ((imm >> 7) & 0x3) << 9 # imm[9:8] + | ((imm >> 9) & 1) << 8 # imm[10] + | ((imm >> 5) & 1) << 7 # imm[6] + | ((imm >> 6) & 1) << 6 # imm[7] + | ((imm >> 0) & 0x7) << 3 # imm[3:1] + | ((imm >> 4) & 1) << 2 # imm[5] ) self.owner.memory.pack_word(self.relative_addr, instr, size=2) return True @@ -669,20 +715,24 @@ class R_RISCV_RELAX(ELFReloc): Relocation Type: 51 Calculation: None """ + AUTO_HANDLE_NONE = True + def relocate(self): return True - + + class R_RISCV_SUB6(ELFReloc): """ Relocation Type: 52 Calculation: V - S - A """ + @property def value(self) -> int: if self.resolvedby is None: return 0 - + S = self.resolvedby.rebased_addr A = self.addend return S + A @@ -698,11 +748,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, new_val, size=1) return True + class R_RISCV_SET6(ELFReloc): """ Relocation Type: 53 Calculation: S + A """ + @property def value(self) -> int: if self.resolvedby is None: @@ -721,11 +773,13 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, new_val, size=1) return True + class R_RISCV_SET8(GenericAbsoluteAddendReloc): """ Relocation Type: 54 Calculation: S + A """ + def relocate(self): if not self.resolved: return False @@ -733,29 +787,34 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, self.value & 0xFF, size=1) return True + class R_RISCV_SET16(GenericAbsoluteAddendReloc): """ Relocation Type: 55 Calculation: S + A """ + def relocate(self): if not self.resolved: return False self.owner.memory.pack_word(self.relative_addr, self.value & 0xFFFF, size=2) return True - + + class R_RISCV_SET32(RelocTruncate32Mixin, GenericAbsoluteAddendReloc): """ Relocation Type: 56 Calculation: S + A """ + class R_RISCV_32_PCREL(ELFReloc): """ Relocation Type: 57 Calculation: S + A - P """ + @property def value(self) -> int: if self.resolvedby is None: @@ -771,18 +830,22 @@ def relocate(self): self.owner.memory.pack_word(self.relative_addr, val & 0xFFFFFFFF, size=4) return True + class R_RISCV_IRELATIVE(GenericIRelativeReloc): """ Relocation Type: 58 Calculation: ifunc_resolver(B + A) """ + class R_RISCV_SET_ULEB128(ELFReloc): """ Relocation Type: 60 Calculation: S + A """ + AUTO_HANDLE_NONE = True + def relocate(self): return True @@ -792,7 +855,9 @@ class R_RISCV_SUB_ULEB128(ELFReloc): Relocation Type: 61 Calculation: V - S - A """ + AUTO_HANDLE_NONE = True + def relocate(self): return True @@ -841,9 +906,7 @@ def relocate(self): 43: R_RISCV_ALIGN, 44: R_RISCV_RVC_BRANCH, 45: R_RISCV_RVC_JUMP, - # 46-50: Reserved - 51: R_RISCV_RELAX, 52: R_RISCV_SUB6, 53: R_RISCV_SET6, @@ -859,13 +922,10 @@ def relocate(self): # 63: R_RISCV_TLSDESC_LOAD_LO12, # 64: R_RISCV_TLSDESC_ADD_LO12, # 65: R_RISCV_TLSDESC_CALL, - # 66-190: Reserved - # 191: R_RISCV_VENDOR, - # 192-255: Reserved } -__all__ = ("relocation_table_riscv64",) \ No newline at end of file +__all__ = ("relocation_table_riscv64",) diff --git a/tests/test_riscv64_relocations.py b/tests/test_riscv64_relocations.py index 8b59b47da..1ec1e4bc6 100644 --- a/tests/test_riscv64_relocations.py +++ b/tests/test_riscv64_relocations.py @@ -8,6 +8,7 @@ import cle from cle.backends.elf.relocation import riscv64 as riscv + def get_real_instr(r): try: if r.relative_addr % 2 != 0: @@ -19,68 +20,78 @@ def get_real_instr(r): except (KeyError, struct.error): return None + def sign_extend(x, bits): m = 1 << (bits - 1) return (x ^ m) - m + def decode_u_imm20(insn32): return insn32 & 0xFFFFF000 + def decode_i_imm12_raw(insn32): return (insn32 >> 20) & 0xFFF + def decode_s_imm12_raw(insn32: int) -> int: imm = ((insn32 >> 25) & 0x7F) << 5 | ((insn32 >> 7) & 0x1F) return imm & 0xFFF + def decode_b_off(insn32): imm = ( - ((insn32 >> 31) & 0x1) << 12 | - ((insn32 >> 7) & 0x1) << 11 | - ((insn32 >> 25) & 0x3F) << 5 | - ((insn32 >> 8) & 0xF) << 1 + ((insn32 >> 31) & 0x1) << 12 + | ((insn32 >> 7) & 0x1) << 11 + | ((insn32 >> 25) & 0x3F) << 5 + | ((insn32 >> 8) & 0xF) << 1 ) return sign_extend(imm, 13) + def decode_j_off(insn32): imm = ( - ((insn32 >> 31) & 0x1) << 20 | - ((insn32 >> 21) & 0x3FF) << 1 | - ((insn32 >> 20) & 0x1) << 11 | - ((insn32 >> 12) & 0xFF) << 12 + ((insn32 >> 31) & 0x1) << 20 + | ((insn32 >> 21) & 0x3FF) << 1 + | ((insn32 >> 20) & 0x1) << 11 + | ((insn32 >> 12) & 0xFF) << 12 ) return sign_extend(imm, 21) + def decode_cb_off(insn16): off = ( - ((insn16 >> 12) & 1) << 8 | # off[8] - ((insn16 >> 6) & 1) << 7 | # off[7] - ((insn16 >> 5) & 1) << 6 | # off[6] - ((insn16 >> 2) & 1) << 5 | # off[5] - ((insn16 >> 11) & 1) << 4 | # off[4] - ((insn16 >> 10) & 1) << 3 | # off[3] - ((insn16 >> 4) & 1) << 2 | # off[2] - ((insn16 >> 3) & 1) << 1 # off[1] + ((insn16 >> 12) & 1) << 8 # off[8] + | ((insn16 >> 6) & 1) << 7 # off[7] + | ((insn16 >> 5) & 1) << 6 # off[6] + | ((insn16 >> 2) & 1) << 5 # off[5] + | ((insn16 >> 11) & 1) << 4 # off[4] + | ((insn16 >> 10) & 1) << 3 # off[3] + | ((insn16 >> 4) & 1) << 2 # off[2] + | ((insn16 >> 3) & 1) << 1 # off[1] ) return sign_extend(off, 9) + def decode_cj_off(insn16): off = ( - ((insn16 >> 12) & 1) << 11 | # off[11] - ((insn16 >> 11) & 1) << 4 | # off[4] - ((insn16 >> 9) & 0x3) << 8 | # off[9:8] - ((insn16 >> 8) & 1) << 10 | # off[10] - ((insn16 >> 7) & 1) << 6 | # off[6] - ((insn16 >> 6) & 1) << 7 | # off[7] - ((insn16 >> 3) & 0x7) << 1 | # off[3:1] - ((insn16 >> 2) & 1) << 5 # off[5] + ((insn16 >> 12) & 1) << 11 # off[11] + | ((insn16 >> 11) & 1) << 4 # off[4] + | ((insn16 >> 9) & 0x3) << 8 # off[9:8] + | ((insn16 >> 8) & 1) << 10 # off[10] + | ((insn16 >> 7) & 1) << 6 # off[6] + | ((insn16 >> 6) & 1) << 7 # off[7] + | ((insn16 >> 3) & 0x7) << 1 # off[3:1] + | ((insn16 >> 2) & 1) << 5 # off[5] ) return sign_extend(off, 12) + def expect_abs(r): assert r.resolvedby is not None return r.resolvedby.rebased_addr + r.addend + def expect_pcrel(r, P: int | None = None): assert r.resolvedby is not None S = r.resolvedby.rebased_addr @@ -89,6 +100,7 @@ def expect_pcrel(r, P: int | None = None): P = r.rebased_addr return S + A - P + def find_paired_hi20(obj, label_addr: int): """ For PCREL_LO12*, resolvedby usually points to a label at the HI20 site (AUIPC). @@ -105,6 +117,7 @@ def find_paired_hi20(obj, label_addr: int): return rr return None + def run_reloc_test_on_file(file_path, base_addr=0x210000): try: loader = cle.Loader(file_path, main_opts={"base_addr": base_addr}) @@ -270,11 +283,7 @@ def test_riscv64_all_relocations() -> None: if not os.path.isdir(riscv_test_dir): raise unittest.SkipTest(f"Directory not found: {riscv_test_dir}") - test_files = [ - os.path.join(riscv_test_dir, f) - for f in os.listdir(riscv_test_dir) - if f.endswith((".o", ".so")) - ] + test_files = [os.path.join(riscv_test_dir, f) for f in os.listdir(riscv_test_dir) if f.endswith((".o", ".so"))] if not test_files: raise unittest.SkipTest(f"No .o or .so files found in {riscv_test_dir}")