Skip to content

Commit 1d23f53

Browse files
llext: Add parameters to arch_elf_relocate
The RISC-V port of llext requires additional parameters for handling non-adjacent HI20/LO12 relocations in arch_elf_relocate(): the current extension (struct llext), the current extension loader (struct llext_loader), the current section header (elf_shdr_t) and the current symbol (elf_sym_t). This changes the signature of arch_elf_relocate accordingly. Signed-off-by: Eric Ackermann <[email protected]>
1 parent b20aec8 commit 1d23f53

File tree

8 files changed

+333
-105
lines changed

8 files changed

+333
-105
lines changed

arch/arc/core/elf.c

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <zephyr/llext/elf.h>
88
#include <zephyr/llext/llext.h>
9+
#include <zephyr/llext/llext_internal.h>
910
#include <zephyr/llext/loader.h>
1011
#include <zephyr/logging/log.h>
1112
#include <zephyr/sys/util.h>
@@ -31,12 +32,32 @@ LOG_MODULE_REGISTER(elf, CONFIG_LLEXT_LOG_LEVEL);
3132
* https://github.com/foss-for-synopsys-dwc-arc-processors/arc-ABI-manual/blob/master/ARCv2_ABI.pdf
3233
* https://github.com/zephyrproject-rtos/binutils-gdb
3334
*/
34-
int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, uintptr_t sym_base_addr, const char *sym_name,
35-
uintptr_t load_bias)
35+
int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel,
36+
const elf_shdr_t *shdr)
3637
{
3738
int ret = 0;
38-
uint32_t insn = UNALIGNED_GET((uint32_t *)loc);
3939
uint32_t value;
40+
const uintptr_t loc = llext_get_reloc_instruction_location(ldr, ext, shdr, rel);
41+
uint32_t insn = UNALIGNED_GET((uint32_t *)loc);
42+
elf_sym_t sym;
43+
uintptr_t sym_base_addr;
44+
const char *sym_name;
45+
46+
ret = llext_read_symbol(ldr, ext, rel, &sym);
47+
48+
if (ret != 0) {
49+
LOG_ERR("Could not read symbol from binary!");
50+
return ret;
51+
}
52+
53+
sym_name = llext_symbol_name(ldr, ext, &sym);
54+
55+
ret = llext_lookup_symbol(ldr, ext, &sym_base_addr, rel, &sym, sym_name, shdr);
56+
57+
if (ret != 0) {
58+
LOG_ERR("Could not find symbol %s!", sym_name);
59+
return ret;
60+
}
4061

4162
sym_base_addr += rel->r_addend;
4263

arch/arm/core/elf.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <zephyr/llext/elf.h>
99
#include <zephyr/llext/llext.h>
10+
#include <zephyr/llext/llext_internal.h>
1011
#include <zephyr/logging/log.h>
1112
#include <zephyr/sys/util.h>
1213

@@ -316,11 +317,32 @@ static void thm_movs_handler(elf_word reloc_type, uint32_t loc,
316317
* Do NOT mix them with not 'Thumb instructions' in the below switch/case: they are not
317318
* intended to work together.
318319
*/
319-
int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, uintptr_t sym_base_addr,
320-
const char *sym_name, uintptr_t load_bias)
320+
int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel,
321+
const elf_shdr_t *shdr)
321322
{
322323
int ret = 0;
323324
elf_word reloc_type = ELF32_R_TYPE(rel->r_info);
325+
const uintptr_t load_bias = llext_text_start(ext);
326+
const uintptr_t loc = llext_get_reloc_instruction_location(ldr, ext, shdr, rel);
327+
elf_sym_t sym;
328+
uintptr_t sym_base_addr;
329+
const char *sym_name;
330+
331+
ret = llext_read_symbol(ldr, ext, rel, &sym);
332+
333+
if (ret != 0) {
334+
LOG_ERR("Could not read symbol from binary!");
335+
return ret;
336+
}
337+
338+
sym_name = llext_symbol_name(ldr, ext, &sym);
339+
340+
ret = llext_lookup_symbol(ldr, ext, &sym_base_addr, rel, &sym, sym_name, shdr);
341+
342+
if (ret != 0) {
343+
LOG_ERR("Could not find symbol %s!", sym_name);
344+
return ret;
345+
}
324346

325347
LOG_DBG("%d %lx %lx %s", reloc_type, loc, sym_base_addr, sym_name);
326348

arch/arm64/core/elf.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <zephyr/llext/elf.h>
88
#include <zephyr/llext/llext.h>
9+
#include <zephyr/llext/llext_internal.h>
910
#include <zephyr/logging/log.h>
1011
#include <zephyr/sys/util.h>
1112
#include <zephyr/sys/byteorder.h>
@@ -430,12 +431,32 @@ static int imm_reloc_handler(elf_rela_t *rel, elf_word reloc_type, uintptr_t loc
430431
* @retval -ENOTSUP Unsupported relocation
431432
* @retval -ENOEXEC Invalid relocation
432433
*/
433-
int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, uintptr_t sym_base_addr, const char *sym_name,
434-
uintptr_t load_bias)
434+
int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel,
435+
const elf_shdr_t *shdr)
435436
{
436437
int ret = 0;
437438
bool overflow_check = true;
438439
elf_word reloc_type = ELF_R_TYPE(rel->r_info);
440+
const uintptr_t loc = llext_get_reloc_instruction_location(ldr, ext, shdr, rel);
441+
elf_sym_t sym;
442+
uintptr_t sym_base_addr;
443+
const char *sym_name;
444+
445+
ret = llext_read_symbol(ldr, ext, rel, &sym);
446+
447+
if (ret != 0) {
448+
LOG_ERR("Could not read symbol from binary!");
449+
return ret;
450+
}
451+
452+
sym_name = llext_symbol_name(ldr, ext, &sym);
453+
454+
ret = llext_lookup_symbol(ldr, ext, &sym_base_addr, rel, &sym, sym_name, shdr);
455+
456+
if (ret != 0) {
457+
LOG_ERR("Could not find symbol %s!", sym_name);
458+
return ret;
459+
}
439460

440461
switch (reloc_type) {
441462
case R_ARM_NONE:

arch/riscv/core/elf.c

Lines changed: 182 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
*/
99
#include <zephyr/llext/elf.h>
1010
#include <zephyr/llext/llext.h>
11+
#include <zephyr/llext/llext_internal.h>
12+
#include <zephyr/llext/loader.h>
13+
1114
#include <zephyr/logging/log.h>
1215
#include <zephyr/sys/util.h>
1316

@@ -61,18 +64,166 @@ static inline int riscv_relocation_fits(long long jump_target, long long max_dis
6164

6265
static long long last_u_type_jump_target;
6366

67+
static size_t riscv_last_rel_idx;
68+
69+
/**
70+
* @brief On RISC-V, PC-relative relocations (PCREL_LO12_I, PCREL_LO12_S) do not refer to
71+
* the actual symbol. Instead, they refer to the location of a different instruction in the
72+
* same section, which has a PCREL_HI20 relocation. The relocation offset is then computed based
73+
* on the location and symbol from the HI20 relocation. 20 bits from the offset go into the
74+
* instruction that has the HI20 relocation, and 12 bits go into the PCREL_LO12 instruction.
75+
*
76+
* @param[in] ldr llext loader
77+
* @param[in] ext current extension
78+
* @param[in] pcrel_lo12 the elf relocation structure for the PCREL_LO12I/S relocation.
79+
* @param[in] shdr ELF section header for the relocation
80+
* @param[in] sym ELF symbol for PCREL_LO12I
81+
* @param[out] link_addr_out computed link address
82+
*
83+
*/
84+
static int llext_riscv_find_sym_pcrel(struct llext_loader *ldr, struct llext *ext,
85+
const elf_rela_t *pcrel_lo12, const elf_shdr_t *shdr,
86+
const elf_sym_t *sym, intptr_t *link_addr_out)
87+
{
88+
int ret;
89+
elf_rela_t candidate;
90+
uintptr_t candidate_loc;
91+
elf_word reloc_type;
92+
elf_sym_t candidate_sym;
93+
uintptr_t link_addr;
94+
const char *symbol_name;
95+
int iteration_start = riscv_last_rel_idx;
96+
bool is_first = true;
97+
const elf_word rel_cnt = shdr->sh_size / shdr->sh_entsize;
98+
const uintptr_t sect_base = (uintptr_t)llext_loaded_sect_ptr(ldr, ext, shdr->sh_info);
99+
bool found_candidate = false;
100+
101+
if (iteration_start >= rel_cnt) {
102+
/* value left over from a different section */
103+
iteration_start = 0;
104+
}
105+
106+
reloc_type = ELF32_R_TYPE(pcrel_lo12->r_info);
107+
108+
if (reloc_type != R_RISCV_PCREL_LO12_I && reloc_type != R_RISCV_PCREL_LO12_S) {
109+
/* this function does not apply - the symbol is already correct */
110+
return 0;
111+
}
112+
113+
for (int i = iteration_start; i != iteration_start || is_first; i++) {
114+
115+
is_first = false;
116+
117+
/* get each relocation entry */
118+
ret = llext_seek(ldr, shdr->sh_offset + i * shdr->sh_entsize);
119+
if (ret != 0) {
120+
return ret;
121+
}
122+
123+
ret = llext_read(ldr, &candidate, shdr->sh_entsize);
124+
if (ret != 0) {
125+
return ret;
126+
}
127+
128+
/* FIXME currently, RISC-V relocations all fit in ELF_32_R_TYPE */
129+
reloc_type = ELF32_R_TYPE(candidate.r_info);
130+
131+
candidate_loc = sect_base + candidate.r_offset;
132+
133+
/*
134+
* RISC-V ELF specification: "value" of the symbol for the PCREL_LO12 relocation
135+
* is actually the offset of the PCREL_HI20 relocation instruction from section
136+
* start
137+
*/
138+
if (candidate.r_offset == sym->st_value && reloc_type == R_RISCV_PCREL_HI20) {
139+
found_candidate = true;
140+
141+
/*
142+
* start here in next iteration
143+
* it is fairly likely (albeit not guaranteed) that we require PCREL_HI20
144+
* relocations in order
145+
* we can safely write this even if an error occurs after the loop -
146+
* in that case,we can safely abort the execution anyway
147+
*/
148+
riscv_last_rel_idx = i;
149+
150+
break;
151+
}
152+
153+
if (i + 1 >= rel_cnt) {
154+
/* wrap around and search in previously processed indices as well */
155+
i = -1;
156+
}
157+
}
158+
159+
if (!found_candidate) {
160+
LOG_ERR("Could not find R_RISCV_PCREL_HI20 relocation for "
161+
"R_RISCV_PCREL_LO12 relocation!");
162+
return -ENOEXEC;
163+
}
164+
165+
/* we found a match - need to compute the relocation for this instruction */
166+
/* lower 12 bits go to the PCREL_LO12 relocation */
167+
168+
/* get corresponding / "actual" symbol */
169+
ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset +
170+
ELF_R_SYM(candidate.r_info) * sizeof(elf_sym_t));
171+
if (ret != 0) {
172+
return ret;
173+
}
174+
175+
ret = llext_read(ldr, &candidate_sym, sizeof(elf_sym_t));
176+
if (ret != 0) {
177+
return ret;
178+
}
179+
180+
symbol_name = llext_string(ldr, ext, LLEXT_MEM_STRTAB, candidate_sym.st_name);
181+
182+
ret = llext_lookup_symbol(ldr, ext, &link_addr, &candidate, &candidate_sym,
183+
symbol_name, shdr);
184+
185+
if (ret != 0) {
186+
return ret;
187+
}
188+
189+
*link_addr_out = (intptr_t)(link_addr + candidate.r_addend - candidate_loc); /* S + A - P */
190+
191+
/* found the matching entry */
192+
return 0;
193+
}
194+
64195
/**
65196
* @brief RISC-V specific function for relocating partially linked ELF binaries
66197
*
67198
* This implementation follows the official RISC-V specification:
68199
* https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
69200
*
70201
*/
71-
int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc_unsigned, uintptr_t sym_base_addr_unsigned,
72-
const char *sym_name, uintptr_t load_bias)
202+
int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel,
203+
const elf_shdr_t *shdr)
73204
{
74205
/* FIXME currently, RISC-V relocations all fit in ELF_32_R_TYPE */
75206
elf_word reloc_type = ELF32_R_TYPE(rel->r_info);
207+
const uintptr_t load_bias = llext_text_start(ext);
208+
const uintptr_t loc_unsigned = llext_get_reloc_instruction_location(ldr, ext, shdr, rel);
209+
elf_sym_t sym;
210+
uintptr_t sym_base_addr_unsigned;
211+
const char *sym_name;
212+
int ret;
213+
214+
ret = llext_read_symbol(ldr, ext, rel, &sym);
215+
if (ret != 0) {
216+
LOG_ERR("Could not read symbol from binary!");
217+
return ret;
218+
}
219+
220+
sym_name = llext_symbol_name(ldr, ext, &sym);
221+
ret = llext_lookup_symbol(ldr, ext, &sym_base_addr_unsigned, rel, &sym, sym_name, shdr);
222+
223+
if (ret != 0) {
224+
LOG_ERR("Could not find symbol %s!", sym_name);
225+
return ret;
226+
}
76227
/*
77228
* The RISC-V specification uses the following symbolic names for the relocations:
78229
*
@@ -99,7 +250,22 @@ int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc_unsigned, uintptr_t sym_bas
99250
long long original_imm8, jump_target;
100251
int16_t compressed_imm8;
101252
__typeof__(rel->r_addend) target_alignment = 1;
102-
const intptr_t sym_base_addr = (intptr_t)sym_base_addr_unsigned;
253+
intptr_t sym_base_addr = (intptr_t)sym_base_addr_unsigned;
254+
255+
/*
256+
* For HI20/LO12 ("PCREL") relocation pairs, we need a helper function to
257+
* determine the address for the LO12 relocation, as it depends on the
258+
* value in the HI20 relocation.
259+
*/
260+
ret = llext_riscv_find_sym_pcrel(ldr, ext, rel, shdr, &sym, &sym_base_addr);
261+
262+
if (ret != 0) {
263+
LOG_ERR("Failed to resolve RISC-V PCREL relocation for symbol %s at %p "
264+
"with base address %p load address %p type %" PRIu64,
265+
sym_name, (void *)loc, (void *)sym_base_addr, (void *)load_bias,
266+
(uint64_t)reloc_type);
267+
return ret;
268+
}
103269

104270
LOG_DBG("Relocating symbol %s at %p with base address %p load address %p type %" PRIu64,
105271
sym_name, (void *)loc, (void *)sym_base_addr, (void *)load_bias,
@@ -181,38 +347,29 @@ int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc_unsigned, uintptr_t sym_bas
181347
return riscv_relocation_fits(jump_target, RISCV_MAX_JUMP_DISTANCE_U_PLUS_I_TYPE,
182348
reloc_type);
183349
case R_RISCV_PCREL_LO12_I:
184-
/* need the same jump target as preceding U-type relocation */
185-
if (last_u_type_jump_target == 0) {
186-
LOG_ERR("R_RISCV_PCREL_LO12_I relocation without preceding U-type "
187-
"relocation!");
188-
return -ENOEXEC;
189-
}
350+
/*
351+
* Jump target is resolved in llext_riscv_find_sym_pcrel in llext_link.c
352+
* as it depends on other relocations.
353+
*/
190354
modified_operand = UNALIGNED_GET(loc32);
191-
jump_target = last_u_type_jump_target; /* S - P */
192-
last_u_type_jump_target = 0;
193-
imm8 = jump_target;
355+
imm8 = (int32_t)sym_base_addr; /* already computed */
194356
modified_operand = R_RISCV_CLEAR_ITYPE_IMM8(modified_operand);
195357
modified_operand = R_RISCV_SET_ITYPE_IMM8(modified_operand, imm8);
196358
UNALIGNED_PUT(modified_operand, loc32);
197-
return riscv_relocation_fits(jump_target, RISCV_MAX_JUMP_DISTANCE_U_PLUS_I_TYPE,
198-
reloc_type);
359+
/* we have checked that this fits with the associated relocation */
199360
break;
200361
case R_RISCV_PCREL_LO12_S:
201-
/* need the same jump target as preceding U-type relocation */
202-
if (last_u_type_jump_target == 0) {
203-
LOG_ERR("R_RISCV_PCREL_LO12_I relocation without preceding U-type "
204-
"relocation!");
205-
return -ENOEXEC;
206-
}
362+
/*
363+
* Jump target is resolved in llext_riscv_find_sym_pcrel in llext_link.c
364+
* as it depends on other relocations.
365+
*/
207366
modified_operand = UNALIGNED_GET(loc32);
208-
jump_target = last_u_type_jump_target; /* S - P */
209-
last_u_type_jump_target = 0;
210-
imm8 = jump_target;
367+
imm8 = (int32_t)sym_base_addr; /* already computed */
211368
modified_operand = R_RISCV_CLEAR_STYPE_IMM8(modified_operand);
212369
modified_operand = R_RISCV_SET_STYPE_IMM8(modified_operand, imm8);
213370
UNALIGNED_PUT(modified_operand, loc32);
214-
return riscv_relocation_fits(jump_target, RISCV_MAX_JUMP_DISTANCE_U_PLUS_I_TYPE,
215-
reloc_type);
371+
/* we have checked that this fits with the associated relocation */
372+
break;
216373
case R_RISCV_HI20:
217374
jump_target = sym_base_addr + rel->r_addend; /* S + A */
218375
modified_operand = UNALIGNED_GET(loc32);

0 commit comments

Comments
 (0)