Files
python-cle/0001-riscv64-init-support.patch
Keke Ming 799c861a44 SPECS: add python-cle
Signed-off-by: Keke Ming <ming.jvle@gmail.com>
2026-03-03 20:33:36 +08:00

1272 lines
34 KiB
Diff

diff --git a/cle/backends/elf/relocation/__init__.py b/cle/backends/elf/relocation/__init__.py
index d25203b..0e91c0b 100644
--- a/cle/backends/elf/relocation/__init__.py
+++ b/cle/backends/elf/relocation/__init__.py
@@ -9,6 +9,7 @@ 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
@@ -19,6 +20,7 @@ ALL_RELOCATIONS = {
"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/elfreloc.py b/cle/backends/elf/relocation/elfreloc.py
index dffe5cd..4bbdfa8 100644
--- a/cle/backends/elf/relocation/elfreloc.py
+++ b/cle/backends/elf/relocation/elfreloc.py
@@ -25,6 +25,6 @@ class ELFReloc(Relocation):
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
new file mode 100644
index 0000000..b685a00
--- /dev/null
+++ b/cle/backends/elf/relocation/riscv64.py
@@ -0,0 +1,931 @@
+"""
+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,
+ 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:
+ 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) | (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]
+ )
+
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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) -> int:
+ 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",)
diff --git a/tests/test_riscv64_relocations.py b/tests/test_riscv64_relocations.py
new file mode 100644
index 0000000..1ec1e4b
--- /dev/null
+++ b/tests/test_riscv64_relocations.py
@@ -0,0 +1,296 @@
+#!/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()