summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlessandro Gatti <a.gatti@frob.it>2024-06-23 08:18:04 +0200
committerDamien George <damien@micropython.org>2024-12-23 10:02:20 +1100
commit6760e00817ff6cb449134418411850fc577d0f9c (patch)
treec5d618a92a6504a48c58a8c5517387bf03a0c669
parent136058496f05b4b2ab5e63829b301671dc8b9a59 (diff)
tools/mpy_ld.py: Add native modules support for RV32 code.
This commit adds support for RV32IMC native modules, as in embedding native code into a self-contained MPY module and and make its exported functions available to the MicroPython environment. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
-rw-r--r--docs/develop/natmod.rst5
-rw-r--r--examples/natmod/README.md2
-rw-r--r--examples/natmod/btree/Makefile2
-rw-r--r--examples/natmod/deflate/Makefile2
-rw-r--r--examples/natmod/features0/Makefile3
-rw-r--r--examples/natmod/features1/Makefile2
-rw-r--r--examples/natmod/features3/Makefile2
-rw-r--r--examples/natmod/features4/Makefile2
-rw-r--r--examples/natmod/heapq/Makefile2
-rw-r--r--examples/natmod/random/Makefile2
-rw-r--r--examples/natmod/re/Makefile2
-rw-r--r--py/dynruntime.mk16
-rwxr-xr-xtools/mpy_ld.py399
13 files changed, 425 insertions, 16 deletions
diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst
index 502ea1c4c..ba45e4305 100644
--- a/docs/develop/natmod.rst
+++ b/docs/develop/natmod.rst
@@ -39,7 +39,8 @@ options for the ``ARCH`` variable, see below):
* ``armv7emsp`` (ARM Thumb 2, single precision float, eg Cortex-M4F, Cortex-M7)
* ``armv7emdp`` (ARM Thumb 2, double precision float, eg Cortex-M7)
* ``xtensa`` (non-windowed, eg ESP8266)
-* ``xtensawin`` (windowed with window size 8, eg ESP32)
+* ``xtensawin`` (windowed with window size 8, eg ESP32, ESP32S3)
+* ``rv32imc`` (RISC-V 32 bits with compressed instructions, eg ESP32C3, ESP32C6)
When compiling and linking the native .mpy file the architecture must be chosen
and the corresponding file can only be imported on that architecture. For more
@@ -172,7 +173,7 @@ The file ``Makefile`` contains:
# Source files (.c or .py)
SRC = factorial.c
- # Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin)
+ # Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
# Include to get the rules for compiling and linking the module
diff --git a/examples/natmod/README.md b/examples/natmod/README.md
index f72d1c049..ca6b887d0 100644
--- a/examples/natmod/README.md
+++ b/examples/natmod/README.md
@@ -65,7 +65,7 @@ your system package manager or installed from PyPI in a virtual environment
with `pip`.
Each example provides a Makefile. You should specify the `ARCH` argument to
-make (one of x86, x64, armv6m, armv7m, xtensa, xtensawin):
+make (one of x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc):
```
$ cd features0
diff --git a/examples/natmod/btree/Makefile b/examples/natmod/btree/Makefile
index b5846f900..ff130d61b 100644
--- a/examples/natmod/btree/Makefile
+++ b/examples/natmod/btree/Makefile
@@ -7,7 +7,7 @@ MOD = btree_$(ARCH)
# Source files (.c or .py)
SRC = btree_c.c
-# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
+# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
BTREE_DIR = $(MPY_DIR)/lib/berkeley-db-1.xx
diff --git a/examples/natmod/deflate/Makefile b/examples/natmod/deflate/Makefile
index 86ef29b63..504130d57 100644
--- a/examples/natmod/deflate/Makefile
+++ b/examples/natmod/deflate/Makefile
@@ -7,7 +7,7 @@ MOD = deflate_$(ARCH)
# Source files (.c or .py)
SRC = deflate.c
-# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
+# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
include $(MPY_DIR)/py/dynruntime.mk
diff --git a/examples/natmod/features0/Makefile b/examples/natmod/features0/Makefile
index 57490df90..fb01b8d03 100644
--- a/examples/natmod/features0/Makefile
+++ b/examples/natmod/features0/Makefile
@@ -7,8 +7,9 @@ MOD = features0
# Source files (.c or .py)
SRC = features0.c
-# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
+# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
# Include to get the rules for compiling and linking the module
include $(MPY_DIR)/py/dynruntime.mk
+
diff --git a/examples/natmod/features1/Makefile b/examples/natmod/features1/Makefile
index 010640daf..490405110 100644
--- a/examples/natmod/features1/Makefile
+++ b/examples/natmod/features1/Makefile
@@ -7,7 +7,7 @@ MOD = features1
# Source files (.c or .py)
SRC = features1.c
-# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
+# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
# Include to get the rules for compiling and linking the module
diff --git a/examples/natmod/features3/Makefile b/examples/natmod/features3/Makefile
index 4a5f71b8f..3573f41ca 100644
--- a/examples/natmod/features3/Makefile
+++ b/examples/natmod/features3/Makefile
@@ -7,7 +7,7 @@ MOD = features3
# Source files (.c or .py)
SRC = features3.c
-# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
+# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
# Include to get the rules for compiling and linking the module
diff --git a/examples/natmod/features4/Makefile b/examples/natmod/features4/Makefile
index f76a31a7c..34fc3a7ef 100644
--- a/examples/natmod/features4/Makefile
+++ b/examples/natmod/features4/Makefile
@@ -7,7 +7,7 @@ MOD = features4
# Source files (.c or .py)
SRC = features4.c
-# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
+# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
# Include to get the rules for compiling and linking the module
diff --git a/examples/natmod/heapq/Makefile b/examples/natmod/heapq/Makefile
index af45b472d..61e2fc8fc 100644
--- a/examples/natmod/heapq/Makefile
+++ b/examples/natmod/heapq/Makefile
@@ -7,7 +7,7 @@ MOD = heapq_$(ARCH)
# Source files (.c or .py)
SRC = heapq.c
-# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
+# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
include $(MPY_DIR)/py/dynruntime.mk
diff --git a/examples/natmod/random/Makefile b/examples/natmod/random/Makefile
index 5c50227b1..8abdb66dc 100644
--- a/examples/natmod/random/Makefile
+++ b/examples/natmod/random/Makefile
@@ -7,7 +7,7 @@ MOD = random_$(ARCH)
# Source files (.c or .py)
SRC = random.c
-# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
+# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
include $(MPY_DIR)/py/dynruntime.mk
diff --git a/examples/natmod/re/Makefile b/examples/natmod/re/Makefile
index 1ba540110..56b08b988 100644
--- a/examples/natmod/re/Makefile
+++ b/examples/natmod/re/Makefile
@@ -7,7 +7,7 @@ MOD = re_$(ARCH)
# Source files (.c or .py)
SRC = re.c
-# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
+# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
ARCH = x64
include $(MPY_DIR)/py/dynruntime.mk
diff --git a/py/dynruntime.mk b/py/dynruntime.mk
index 62db43ad1..71679954e 100644
--- a/py/dynruntime.mk
+++ b/py/dynruntime.mk
@@ -99,6 +99,22 @@ CROSS = xtensa-esp32-elf-
CFLAGS +=
MICROPY_FLOAT_IMPL ?= float
+else ifeq ($(ARCH),rv32imc)
+
+# rv32imc
+CROSS = riscv64-unknown-elf-
+CFLAGS += -march=rv32imac -mabi=ilp32 -mno-relax
+# If Picolibc is available then select it explicitly. Ubuntu 22.04 ships its
+# bare metal RISC-V toolchain with Picolibc rather than Newlib, and the default
+# is "nosys" so a value must be provided. To avoid having per-distro
+# workarounds, always select Picolibc if available.
+PICOLIBC_SPECS = $(shell $(CROSS)gcc --print-file-name=picolibc.specs)
+ifneq ($(PICOLIBC_SPECS),picolibc.specs)
+CFLAGS += --specs=$(PICOLIBC_SPECS)
+endif
+
+MICROPY_FLOAT_IMPL ?= none
+
else
$(error architecture '$(ARCH)' not supported)
endif
diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py
index c77c46d44..54295208f 100755
--- a/tools/mpy_ld.py
+++ b/tools/mpy_ld.py
@@ -47,6 +47,7 @@ MP_NATIVE_ARCH_ARMV7EMSP = 7
MP_NATIVE_ARCH_ARMV7EMDP = 8
MP_NATIVE_ARCH_XTENSA = 9
MP_NATIVE_ARCH_XTENSAWIN = 10
+MP_NATIVE_ARCH_RV32IMC = 11
MP_PERSISTENT_OBJ_STR = 5
MP_SCOPE_FLAG_VIPERRELOC = 0x10
MP_SCOPE_FLAG_VIPERRODATA = 0x20
@@ -56,6 +57,7 @@ MP_FUN_TABLE_MP_TYPE_TYPE_OFFSET = 73
# ELF constants
R_386_32 = 1
+R_RISCV_32 = 1
R_X86_64_64 = 1
R_XTENSA_32 = 1
R_386_PC32 = 2
@@ -70,15 +72,57 @@ R_386_GOTOFF = 9
R_386_GOTPC = 10
R_ARM_THM_CALL = 10
R_XTENSA_ASM_EXPAND = 11
+R_RISCV_BRANCH = 16
+R_RISCV_JAL = 17
+R_RISCV_CALL = 18
+R_RISCV_CALL_PLT = 19
R_XTENSA_DIFF32 = 19
R_XTENSA_SLOT0_OP = 20
+R_RISCV_GOT_HI20 = 20
+R_RISCV_TLS_GD_HI20 = 22
+R_RISCV_PCREL_HI20 = 23
+R_RISCV_PCREL_LO12_I = 24
+R_RISCV_PCREL_LO12_S = 25
R_ARM_BASE_PREL = 25 # aka R_ARM_GOTPC
R_ARM_GOT_BREL = 26 # aka R_ARM_GOT32
R_ARM_THM_JUMP24 = 30
+R_RISCV_HI20 = 26
+R_RISCV_LO12_I = 27
+R_RISCV_LO12_S = 28
+R_RISCV_TPREL_HI20 = 29
+R_RISCV_TPREL_LO12_I = 30
+R_RISCV_TPREL_LO12_S = 31
+R_RISCV_TPREL_ADD = 32
+R_RISCV_ADD8 = 33
+R_RISCV_ADD16 = 34
+R_RISCV_ADD32 = 35
+R_RISCV_ADD64 = 36
+R_RISCV_SUB8 = 37
+R_RISCV_SUB16 = 38
+R_RISCV_SUB32 = 39
+R_RISCV_SUB64 = 40
+R_RISCV_GOT32_PCREL = 41
R_X86_64_GOTPCREL = 9
R_X86_64_REX_GOTPCRELX = 42
R_386_GOT32X = 43
+R_RISCV_ALIGN = 43
+R_RISCV_RVC_BRANCH = 44
+R_RISCV_RVC_JUMP = 45
+R_RISCV_RELAX = 51
+R_RISCV_SUB6 = 52
+R_RISCV_SET6 = 53
+R_RISCV_SET8 = 54
+R_RISCV_SET16 = 55
+R_RISCV_SET32 = 56
+R_RISCV_32_PCREL = 57
+R_RISCV_PLT32 = 59
R_XTENSA_PDIFF32 = 59
+R_RISCV_SET_ULEB128 = 60
+R_RISCV_SUB_ULEB128 = 61
+R_RISCV_TLSDESC_HI20 = 62
+R_RISCC_TLSDESC_LOAD_LO12 = 63
+R_RISCV_TLSDESC_ADD_LO12 = 64
+R_RISCV_TLSDESC_CALL = 65
################################################################################
# Architecture configuration
@@ -130,6 +174,18 @@ def asm_jump_xtensa(entry):
return struct.pack("<BH", jump_op & 0xFF, jump_op >> 8)
+def asm_jump_rv32(entry):
+ # This could be 6 bytes shorter, but the code currently cannot
+ # support a trampoline with varying length depending on the offset.
+
+ # auipc t6, HI(entry)
+ # jalr zero, t6, LO(entry)
+ upper, lower = split_riscv_address(entry)
+ return struct.pack(
+ "<II", (upper | 0x00000F97) & 0xFFFFFFFF, ((lower << 20) | 0x000F8067) & 0xFFFFFFFF
+ )
+
+
class ArchData:
def __init__(self, name, mpy_feature, word_size, arch_got, asm_jump, *, separate_rodata=False):
self.name = name
@@ -199,6 +255,13 @@ ARCH_DATA = {
asm_jump_xtensa,
separate_rodata=True,
),
+ "rv32imc": ArchData(
+ "EM_RISCV",
+ MP_NATIVE_ARCH_RV32IMC << 2,
+ 4,
+ (R_RISCV_32, R_RISCV_GOT_HI20, R_RISCV_GOT32_PCREL),
+ asm_jump_rv32,
+ ),
}
################################################################################
@@ -219,6 +282,21 @@ def pack_u24le(data, offset, value):
data[offset + 2] = value >> 16 & 0xFF
+def split_riscv_address(value):
+ # The address can be represented with just the lowest 12 bits
+ if value < 0 and value > -2048:
+ value = 4096 + value
+ return 0, value
+ # 2s complement
+ if value < 0:
+ value = 0x100000000 + value
+ upper, lower = (value & 0xFFFFF000), (value & 0xFFF)
+ if lower & 0x800 != 0:
+ # Reverse lower part sign extension
+ upper += 0x1000
+ return upper & 0xFFFFFFFF, lower & 0xFFFFFFFF
+
+
def xxd(text):
for i in range(0, len(text), 16):
print("{:08x}:".format(i), end="")
@@ -346,7 +424,7 @@ def build_got_generic(env):
for r in sec.reloc:
s = r.sym
if not (
- s.entry["st_info"]["bind"] == "STB_GLOBAL"
+ s.entry["st_info"]["bind"] in ("STB_GLOBAL", "STB_WEAK")
and r["r_info_type"] in env.arch.arch_got
):
continue
@@ -487,6 +565,8 @@ def do_relocation_text(env, text_addr, r):
# Default relocation type and name for logging
reloc_type = "le32"
log_name = None
+ addr = None
+ value = None
if (
env.arch.name == "EM_386"
@@ -590,12 +670,46 @@ def do_relocation_text(env, text_addr, r):
return
assert 0
+ elif env.arch.name == "EM_RISCV" and r_info_type in (
+ R_RISCV_TLS_GD_HI20,
+ R_RISCV_TLSDESC_HI20,
+ R_RISCV_TLSDESC_ADD_LO12,
+ R_RISCV_TLSDESC_CALL,
+ ):
+ # TLS relocations are not supported.
+ raise LinkError("{}: RISC-V TLS relocation: {}".format(s.filename, s.name))
+
+ elif env.arch.name == "EM_RISCV" and r_info_type in (
+ R_RISCV_TPREL_HI20,
+ R_RISCV_TPREL_LO12_I,
+ R_RISCV_TPREL_LO12_S,
+ R_RISCV_TPREL_ADD,
+ ):
+ # ThreadPointer-relative relocations are not supported.
+ raise LinkError("{}: RISC-V TP-relative relocation: {}".format(s.filename, s.name))
+
+ elif env.arch.name == "EM_RISCV" and r_info_type in (R_RISCV_SET_ULEB128, R_RISCV_SUB_ULEB128):
+ # 128-bit value relocations are not supported
+ raise LinkError("{}: RISC-V ULEB128 relocation: {}".format(s.filename, s.name))
+
+ elif env.arch.name == "EM_RISCV" and r_info_type in (R_RISCV_RELAX, R_RISCV_ALIGN):
+ # To keep things simple, no relocations are relaxed and thus no
+ # size optimisation is performed even if there is the chance, along
+ # with no offsets to fix up.
+ return
+
+ elif env.arch.name == "EM_RISCV":
+ (addr, value) = process_riscv32_relocation(env, text_addr, r)
+
else:
# Unknown/unsupported relocation
assert 0, r_info_type
# Write relocation
- if reloc_type == "le32":
+ if env.arch.name == "EM_RISCV":
+ # This case is already handled by `process_riscv_relocation`.
+ pass
+ elif reloc_type == "le32":
(existing,) = struct.unpack_from("<I", env.full_text, r_offset)
struct.pack_into("<I", env.full_text, r_offset, (existing + reloc) & 0xFFFFFFFF)
elif reloc_type == "thumb_b":
@@ -623,7 +737,10 @@ def do_relocation_text(env, text_addr, r):
log_name = s.section.name
else:
log_name = s.name
- log(LOG_LEVEL_3, " {:08x} {} -> {:08x}".format(r_offset, log_name, addr))
+ if addr is not None:
+ log(LOG_LEVEL_3, " {:08x} {} -> {:08x}".format(r_offset, log_name, addr))
+ else:
+ log(LOG_LEVEL_3, " {:08x} {} == {:08x}".format(r_offset, log_name, value))
def do_relocation_data(env, text_addr, r):
@@ -646,12 +763,16 @@ def do_relocation_data(env, text_addr, r):
and r_info_type == R_ARM_ABS32
or env.arch.name == "EM_XTENSA"
and r_info_type == R_XTENSA_32
+ or env.arch.name == "EM_RISCV"
+ and r_info_type == R_RISCV_32
):
# Relocation in data.rel.ro to internal/external symbol
if env.arch.word_size == 4:
struct_type = "<I"
elif env.arch.word_size == 8:
struct_type = "<Q"
+ if hasattr(s, "resolved"):
+ s = s.resolved
sec = s.section
assert r_offset % env.arch.word_size == 0
addr = sec.addr + s["st_value"] + r_addend
@@ -684,6 +805,276 @@ def do_relocation_data(env, text_addr, r):
assert 0, r_info_type
+RISCV_RELOCATIONS_TYPE_MAP = {
+ R_RISCV_ADD8: ("riscv_addsub", "B", 8, 1),
+ R_RISCV_ADD16: ("riscv_addsub", "<H", 16, 1),
+ R_RISCV_ADD32: ("riscv_addsub", "<I", 32, 1),
+ R_RISCV_ADD64: ("riscv_addsub", "<Q", 64, 1),
+ R_RISCV_SUB6: ("riscv_addsub", "B", 6, -1),
+ R_RISCV_SUB8: ("riscv_addsub", "B", 8, -1),
+ R_RISCV_SUB16: ("riscv_addsub", "<H", 16, -1),
+ R_RISCV_SUB32: ("riscv_addsub", "<I", 32, -1),
+ R_RISCV_SUB64: ("riscv_addsub", "<Q", 64, -1),
+ R_RISCV_SET6: ("riscv_set6", "B", 6),
+ R_RISCV_SET8: ("riscv_set8", "B", 8),
+ R_RISCV_SET16: ("riscv_set16", "<H", 16),
+ R_RISCV_SET32: ("riscv_set32", "<I", 32),
+ R_RISCV_JAL: "riscv_j",
+ R_RISCV_BRANCH: "riscv_b",
+ R_RISCV_RVC_BRANCH: "riscv_cb",
+ R_RISCV_RVC_JUMP: "riscv_cj",
+ R_RISCV_CALL: "riscv_call",
+ R_RISCV_CALL_PLT: "riscv_call",
+ R_RISCV_PCREL_LO12_I: "riscv_lo12i",
+ R_RISCV_PCREL_LO12_S: "riscv_lo12s",
+ R_RISCV_LO12_I: "riscv_lo12i",
+ R_RISCV_LO12_S: "riscv_lo12s",
+ R_RISCV_32_PCREL: "riscv_32pcrel",
+ R_RISCV_PLT32: "riscv_32pcrel",
+}
+
+
+def process_riscv32_relocation(env, text_addr, r):
+ assert env.arch.name == "EM_RISCV"
+
+ addr = None
+ value = None
+ s = r.sym
+
+ if hasattr(s, "resolved"):
+ s = s.resolved
+
+ r_offset = r["r_offset"] + text_addr
+ r_info_type = r["r_info_type"]
+ try:
+ r_addend = r["r_addend"]
+ except KeyError:
+ r_addend = 0
+
+ if r_info_type == R_RISCV_GOT_HI20:
+ got_entry = env.got_entries[s.name]
+ addr = env.got_section.addr + got_entry.offset
+ reloc = addr + r_addend - r_offset
+ r.computed_reloc = reloc
+ reloc_type = "riscv_hi20"
+
+ elif r_info_type == R_RISCV_GOT32_PCREL:
+ got_entry = env.got_entries[s.name]
+ addr = env.got_section.addr + got_entry.offset
+ value = addr + r_addend - r_offset
+ reloc_type = "riscv_set32"
+
+ elif r_info_type == R_RISCV_PCREL_HI20:
+ addr = s.section.addr + s["st_value"]
+ reloc = addr + r_addend - r_offset
+ r.computed_reloc = reloc
+ reloc_type = "riscv_hi20"
+
+ elif r_info_type == R_RISCV_HI20:
+ addr = s.section.addr + s["st_value"]
+ reloc = addr + r_addend
+ r.computed_reloc = reloc
+ reloc_type = "riscv_hi20"
+
+ elif r_info_type in (
+ R_RISCV_PCREL_LO12_I,
+ R_RISCV_PCREL_LO12_S,
+ R_RISCV_LO12_I,
+ R_RISCV_LO12_S,
+ ):
+ parent = None
+ for potential_parent in s.section.reloc:
+ if potential_parent["r_offset"] != s["st_value"]:
+ continue
+ if potential_parent["r_info_type"] not in (
+ R_RISCV_GOT_HI20,
+ R_RISCV_PCREL_HI20,
+ R_RISCV_HI20,
+ ):
+ continue
+ parent = potential_parent
+ break
+ if parent is None:
+ assert 0, r
+ addr = s.section.addr + s["st_value"]
+ reloc = parent.computed_reloc
+ reloc_type = RISCV_RELOCATIONS_TYPE_MAP[r_info_type]
+
+ elif r_info_type in (
+ R_RISCV_JAL,
+ R_RISCV_RVC_BRANCH,
+ R_RISCV_RVC_JUMP,
+ R_RISCV_CALL,
+ R_RISCV_CALL_PLT,
+ R_RISCV_BRANCH,
+ R_RISCV_32_PCREL,
+ R_RISCV_PLT32,
+ ):
+ addr = s.section.addr + s["st_value"]
+ reloc = addr + r_addend - r_offset
+ reloc_type = RISCV_RELOCATIONS_TYPE_MAP[r_info_type]
+
+ elif r_info_type in (
+ R_RISCV_ADD8,
+ R_RISCV_ADD16,
+ R_RISCV_ADD32,
+ R_RISCV_ADD64,
+ R_RISCV_SUB6,
+ R_RISCV_SUB8,
+ R_RISCV_SUB16,
+ R_RISCV_SUB32,
+ R_RISCV_SUB64,
+ R_RISCV_SET6,
+ R_RISCV_SET8,
+ R_RISCV_SET16,
+ R_RISCV_SET32,
+ ):
+ value = s.section.addr + s["st_value"] + r_addend
+ reloc_type, *reloc_args = RISCV_RELOCATIONS_TYPE_MAP[r_info_type]
+
+ else:
+ # Unknown/unsupported relocation
+ assert 0, r_info_type
+
+ # Write relocation
+ if reloc_type == "riscv_hi20":
+ # Patch the upper 20 bits of the opcode
+ upper, _ = split_riscv_address(reloc)
+ (existing,) = struct.unpack_from("<I", env.full_text, r_offset)
+ struct.pack_into(
+ "<I",
+ env.full_text,
+ r_offset,
+ ((existing & 0xFFF) | upper) & 0xFFFFFFFF,
+ )
+ elif reloc_type == "riscv_lo12i":
+ # Patch the lower 12 bits of an I-opcode immediate.
+ _, lower = split_riscv_address(reloc)
+ (existing,) = struct.unpack_from("<I", env.full_text, r_offset)
+ struct.pack_into(
+ "<I",
+ env.full_text,
+ r_offset,
+ ((existing & 0xFFFFF) | ((lower & 0xFFF) << 20)) & 0xFFFFFFFF,
+ )
+ elif reloc_type == "riscv_lo12s":
+ # Patch the lower 12 bits of an S-opcode immediate.
+ _, lower = split_riscv_address(reloc)
+ (existing,) = struct.unpack_from("<I", env.full_text, r_offset)
+ struct.pack_into(
+ "<I",
+ env.full_text,
+ r_offset,
+ ((existing & 0xFE000F80) | ((lower & 0xFE0) << 20) | ((lower & 0x1F) << 7))
+ & 0xFFFFFFFF,
+ )
+ elif reloc_type == "riscv_cb":
+ # Patch the target of a compressed branch opcode
+ (existing,) = struct.unpack_from("<H", env.full_text, r_offset)
+ struct.pack_into(
+ "<H",
+ env.full_text,
+ r_offset,
+ (
+ (existing & 0xE383)
+ | ((reloc & 0x100) << 4)
+ | ((reloc & 0xC0) >> 1)
+ | ((reloc & 0x20) >> 3)
+ | ((reloc & 0x18) << 7)
+ | ((reloc & 0x06) << 2)
+ )
+ & 0xFFFF,
+ )
+ elif reloc_type == "riscv_cj":
+ # Patch the target of a compressed jump opcode
+ (existing,) = struct.unpack_from("<H", env.full_text, r_offset)
+ struct.pack_into(
+ "<H",
+ env.full_text,
+ r_offset,
+ (
+ (existing & 0xE003)
+ | ((reloc & 0x800) << 1)
+ | ((reloc & 0x400) >> 2)
+ | ((reloc & 0x300) << 1)
+ | ((reloc & 0x80) >> 1)
+ | ((reloc & 0x40) << 1)
+ | ((reloc & 0x20) >> 3)
+ | ((reloc & 0x10) << 7)
+ | ((reloc & 0x0E) << 2)
+ )
+ & 0xFFFF,
+ )
+ elif reloc_type == "riscv_call":
+ # Patch a pair of opcodes forming a call operation
+ upper, lower = split_riscv_address(reloc)
+ (existing,) = struct.unpack_from("<I", env.full_text, r_offset)
+ struct.pack_into(
+ "<I",
+ env.full_text,
+ r_offset,
+ ((existing & 0xFFF) | upper) & 0xFFFFFFFF,
+ )
+ (existing,) = struct.unpack_from("<I", env.full_text, r_offset + 4)
+ struct.pack_into(
+ "<I",
+ env.full_text,
+ r_offset + 4,
+ ((existing & 0xFFFFF) | (lower << 20)) & 0xFFFFFFFF,
+ )
+ elif reloc_type == "riscv_b":
+ # Patch a conditional opcode
+ (existing,) = struct.unpack_from("<I", env.full_text, r_offset)
+ struct.pack_into(
+ "<I",
+ env.full_text,
+ r_offset,
+ (
+ (existing & 0x01FFF07F)
+ | ((reloc & 0x1000) << 19)
+ | ((reloc & 0x800) >> 4)
+ | ((reloc & 0x7E0) << 20)
+ | ((reloc & 0x1E) << 7)
+ )
+ & 0xFFFFFFFF,
+ )
+ elif reloc_type == "riscv_j":
+ # Patch a jump/jump with link opcode
+ (existing,) = struct.unpack_from("<I", env.full_text, r_offset)
+ struct.pack_into(
+ "<I",
+ env.full_text,
+ r_offset,
+ (
+ (existing & 0xFFF)
+ | ((reloc & 0x100000) << 11)
+ | (reloc & 0xFF000)
+ | ((reloc & 0x800) << 9)
+ | ((reloc & 0x7FE) << 20)
+ ),
+ )
+ elif reloc_type == "riscv_addsub":
+ (fmt, bits, multiplier) = reloc_args
+ (existing,) = struct.unpack_from(fmt, env.full_text, r_offset)
+ mask = (1 << bits) - 1
+ value = (existing & mask) + (value * multiplier)
+ if value < 0:
+ value = (1 << bits) + value
+ struct.pack_into(fmt, env.full_text, r_offset, (existing & ~mask) | (value & mask))
+ elif reloc_type == "riscv_set":
+ (fmt, bits) = reloc_args
+ (existing,) = struct.unpack_from(fmt, env.full_text, r_offset)
+ mask = (1 << bits) - 1
+ struct.pack_into(fmt, env.full_text, r_offset, (existing & ~mask) | (value & mask))
+ elif reloc_type == "riscv_32pcrel":
+ # Write the distance from the current PC
+ struct.pack_into("<I", env.full_text, r_offset, reloc & 0xFFFFFFFF)
+ else:
+ assert 0, reloc_type
+
+ return addr, value
+
+
def load_object_file(env, felf):
with open(felf, "rb") as f:
elf = elffile.ELFFile(f)
@@ -727,7 +1118,7 @@ def load_object_file(env, felf):
if shndx in sections_shndx:
# Symbol with associated section
sym.section = sections_shndx[shndx]
- if sym["st_info"]["bind"] == "STB_GLOBAL":
+ if sym["st_info"]["bind"] in ("STB_GLOBAL", "STB_WEAK"):
# Defined global symbol
if sym.name in env.known_syms and not sym.name.startswith(
"__x86.get_pc_thunk."