Skip to content

Commit 7c7ecf0

Browse files
committed
feat(aot/riscv): Enhance PCREL relocation handling with HI20/LO12 caching
Implement proper R_RISCV_PCREL_HI20/R_RISCV_PCREL_LO12_{I,S} pairing mechanism for RISC-V AOT relocation processing. Changes: - Add PCREL cache to match HI20 relocations with their corresponding LO12 entries, ensuring correct PC-relative offset computation - Export aot_reloc_reset_cache() to clear cache state - Implement proper LO12 relocation handling that uses cached HI20 offset - Add overflow detection for PCREL cache to fail gracefully with error - Add RV64 immediate validation for HI20 relocations This enhances the RISC-V relocation system to correctly handle PC-relative relocations used by LLVM for position-independent code generation.
1 parent 2a2dd19 commit 7c7ecf0

File tree

3 files changed

+156
-50
lines changed

3 files changed

+156
-50
lines changed

core/iwasm/aot/aot_loader.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3202,6 +3202,14 @@ do_text_relocation(AOTModule *module, AOTRelocationGroup *group,
32023202
return false;
32033203
}
32043204

3205+
#if defined(BUILD_TARGET_RISCV32_ILP32) \
3206+
|| defined(BUILD_TARGET_RISCV32_ILP32F) \
3207+
|| defined(BUILD_TARGET_RISCV32_ILP32D) \
3208+
|| defined(BUILD_TARGET_RISCV64_LP64) \
3209+
|| defined(BUILD_TARGET_RISCV64_LP64D)
3210+
aot_reloc_reset_cache();
3211+
#endif
3212+
32053213
for (i = 0; i < group->relocation_count; i++, relocation++) {
32063214
int32 symbol_index = -1;
32073215
symbol_len = (uint32)strlen(relocation->symbol_name);

core/iwasm/aot/aot_reloc.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,17 @@ apply_relocation(AOTModule *module,
252252
uint64 reloc_offset, int64 reloc_addend,
253253
uint32 reloc_type, void *symbol_addr, int32 symbol_index,
254254
char *error_buf, uint32 error_buf_size);
255+
256+
/**
257+
* Reset any target-specific relocation state.
258+
*
259+
* Some targets (e.g. RISC-V) need to cache information across multiple
260+
* relocation entries (such as PC-relative HI20/LO12 pairs). This function must
261+
* be called by the relocation loader before applying a relocation group/section
262+
* to avoid stale state leaking between groups.
263+
*/
264+
void
265+
aot_reloc_reset_cache(void);
255266
/* clang-format off */
256267

257268
#ifdef __cplusplus

core/iwasm/aot/arch/aot_reloc_riscv.c

Lines changed: 137 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,79 @@ static SymbolMap target_sym_map[] = {
192192
/* clang-format on */
193193
};
194194

195+
/*
196+
* Cache entries for matching R_RISCV_PCREL_HI20 with its corresponding
197+
* R_RISCV_PCREL_LO12_{I,S}. The relocation table is typically ordered by
198+
* increasing offset, so only a small number of "in-flight" HI20 entries are
199+
* expected at any moment; 8 is a conservative fixed bound.
200+
*/
201+
#define PCREL_CACHE_SIZE 8
202+
203+
typedef struct {
204+
uintptr_t hi20_addr;
205+
uintptr_t cached_offset;
206+
} pcrel_cache_entry_t;
207+
208+
static pcrel_cache_entry_t pcrel_cache[PCREL_CACHE_SIZE];
209+
static int pcrel_cache_count = 0;
210+
211+
void
212+
aot_reloc_reset_cache(void)
213+
{
214+
int i;
215+
for (i = 0; i < PCREL_CACHE_SIZE; i++) {
216+
pcrel_cache[i].hi20_addr = 0;
217+
pcrel_cache[i].cached_offset = 0;
218+
}
219+
pcrel_cache_count = 0;
220+
}
221+
222+
static bool
223+
add_hi20_to_cache(uintptr_t hi20_reloc_addr, uintptr_t hi20_offset)
224+
{
225+
int i;
226+
227+
for (i = 0; i < PCREL_CACHE_SIZE; i++) {
228+
if (pcrel_cache[i].hi20_addr == 0) {
229+
pcrel_cache[i].hi20_addr = hi20_reloc_addr;
230+
pcrel_cache[i].cached_offset = hi20_offset;
231+
pcrel_cache_count++;
232+
return true;
233+
}
234+
}
235+
236+
return false;
237+
}
238+
239+
static uintptr_t
240+
find_hi20_in_cache(uintptr_t hi20_reloc_addr)
241+
{
242+
int i;
243+
244+
for (i = 0; i < PCREL_CACHE_SIZE; i++) {
245+
if (pcrel_cache[i].hi20_addr == hi20_reloc_addr) {
246+
pcrel_cache[i].hi20_addr = 0;
247+
pcrel_cache[i].cached_offset = 0;
248+
pcrel_cache_count--;
249+
return pcrel_cache[i].cached_offset;
250+
}
251+
}
252+
return 0;
253+
}
254+
255+
static inline bool
256+
valid_hi20_imm(long imm_hi)
257+
{
258+
#if __riscv_xlen == 64
259+
long hi = imm_hi & ((1 << 20) - 1);
260+
long sign = -((imm_hi >> 19) & 1);
261+
hi = ((hi << 12) | sign << 32) >> 12;
262+
return imm_hi == hi;
263+
#else
264+
return true;
265+
#endif
266+
}
267+
195268
static void
196269
set_error_buf(char *error_buf, uint32 error_buf_size, const char *string)
197270
{
@@ -257,17 +330,17 @@ rv_add_val(uint16 *addr, uint32 val)
257330
/**
258331
* Get imm_hi and imm_lo from given integer
259332
*
260-
* @param imm given integer, signed 32bit
333+
* @param imm given integer (intptr_t, 32-bit on RV32, 64-bit on RV64)
261334
* @param imm_hi signed 20bit
262335
* @param imm_lo signed 12bit
263336
*
264337
*/
265338
static void
266-
rv_calc_imm(int32 imm, int32 *imm_hi, int32 *imm_lo)
339+
rv_calc_imm(intptr_t imm, int32 *imm_hi, int32 *imm_lo)
267340
{
268-
int32 lo;
269-
int32 hi = imm / 4096;
270-
int32 r = imm % 4096;
341+
intptr_t lo;
342+
intptr_t hi = imm / 4096;
343+
intptr_t r = imm % 4096;
271344

272345
if (2047 < r) {
273346
hi++;
@@ -278,8 +351,8 @@ rv_calc_imm(int32 imm, int32 *imm_hi, int32 *imm_lo)
278351

279352
lo = imm - (hi * 4096);
280353

281-
*imm_lo = lo;
282-
*imm_hi = hi;
354+
*imm_lo = (int32)lo;
355+
*imm_hi = (int32)hi;
283356
}
284357

285358
uint32
@@ -325,10 +398,7 @@ typedef struct RelocTypeStrMap {
325398
char *reloc_str;
326399
} RelocTypeStrMap;
327400

328-
#define RELOC_TYPE_MAP(reloc_type) \
329-
{ \
330-
reloc_type, #reloc_type \
331-
}
401+
#define RELOC_TYPE_MAP(reloc_type) { reloc_type, #reloc_type }
332402

333403
static RelocTypeStrMap reloc_type_str_maps[] = {
334404
RELOC_TYPE_MAP(R_RISCV_32), RELOC_TYPE_MAP(R_RISCV_64),
@@ -436,6 +506,21 @@ apply_relocation(AOTModule *module, uint8 *target_section_addr,
436506

437507
rv_calc_imm(val, &imm_hi, &imm_lo);
438508

509+
if (reloc_type == R_RISCV_PCREL_HI20) {
510+
if (!valid_hi20_imm(imm_hi)) {
511+
set_error_buf(error_buf, error_buf_size,
512+
"AOT module load failed: invalid HI20 "
513+
"immediate for RV64");
514+
return false;
515+
}
516+
if (!add_hi20_to_cache((uintptr_t)addr, (uintptr_t)val)) {
517+
set_error_buf(error_buf, error_buf_size,
518+
"AOT module load failed: PCREL relocation "
519+
"cache overflow.");
520+
return false;
521+
}
522+
}
523+
439524
rv_add_val((uint16 *)addr, (imm_hi << 12));
440525
if ((rv_get_val((uint16 *)(addr + 4)) & 0x7f) == RV_OPCODE_SW) {
441526
/* Adjust imm for SW : S-type */
@@ -453,15 +538,20 @@ apply_relocation(AOTModule *module, uint8 *target_section_addr,
453538

454539
case R_RISCV_HI20: /* S + A */
455540
{
456-
val = (int32)((intptr_t)symbol_addr + (intptr_t)reloc_addend);
457-
458541
CHECK_RELOC_OFFSET(sizeof(uint32));
459-
if (val != ((intptr_t)symbol_addr + (intptr_t)reloc_addend)) {
460-
goto fail_addr_out_of_range;
542+
543+
intptr_t val = (intptr_t)symbol_addr + (intptr_t)reloc_addend;
544+
int32_t imm_hi, imm_lo;
545+
rv_calc_imm(val, &imm_hi, &imm_lo);
546+
547+
if (!valid_hi20_imm(imm_hi)) {
548+
set_error_buf(
549+
error_buf, error_buf_size,
550+
"AOT module load failed: invalid HI20 immediate for RV64");
551+
return false;
461552
}
462553

463554
insn = rv_get_val((uint16 *)addr);
464-
rv_calc_imm(val, &imm_hi, &imm_lo);
465555
insn = (insn & 0x00000fff) | (imm_hi << 12);
466556
rv_set_val((uint16 *)addr, insn);
467557
break;
@@ -470,62 +560,59 @@ apply_relocation(AOTModule *module, uint8 *target_section_addr,
470560
case R_RISCV_PCREL_LO12_I: /* S - P */
471561
case R_RISCV_PCREL_LO12_S: /* S - P */
472562
{
473-
/* Already handled in R_RISCV_PCREL_HI20, it should be skipped for
474-
* most cases. But it is still needed for some special cases, e.g.
475-
* ```
476-
* label:
477-
* auipc t0, %pcrel_hi(symbol) # R_RISCV_PCREL_HI20 (symbol)
478-
* lui t1, 1
479-
* lw t2, t0, %pcrel_lo(label) # R_RISCV_PCREL_LO12_I (label)
480-
* add t2, t2, t1
481-
* sw t2, t0, %pcrel_lo(label) # R_RISCV_PCREL_LO12_S (label)
482-
* ```
483-
* In this case, the R_RISCV_PCREL_LO12_I/S relocation should be
484-
* handled after R_RISCV_PCREL_HI20 relocation.
485-
*
486-
* So, if the R_RISCV_PCREL_LO12_I/S relocation is not followed by
487-
* R_RISCV_PCREL_HI20 relocation, it should be handled here but
488-
* not implemented yet.
489-
*/
490-
491-
if ((uintptr_t)addr - (uintptr_t)symbol_addr
492-
- (uintptr_t)reloc_addend
493-
!= 4) {
494-
goto fail_addr_out_of_range;
563+
uintptr_t cached_offset;
564+
565+
cached_offset = find_hi20_in_cache((uintptr_t)addr - 4);
566+
567+
if (cached_offset != 0) {
568+
val = (int32)cached_offset;
569+
}
570+
else {
571+
val = (int32)(intptr_t)((uint8 *)symbol_addr + reloc_addend
572+
- addr - 4);
573+
}
574+
575+
CHECK_RELOC_OFFSET(sizeof(uint32));
576+
577+
rv_calc_imm(val, &imm_hi, &imm_lo);
578+
579+
if (reloc_type == R_RISCV_PCREL_LO12_I) {
580+
rv_add_val((uint16 *)addr, ((int32)imm_lo << 20));
581+
}
582+
else {
583+
val = (((int32)imm_lo >> 5) << 25)
584+
+ (((int32)imm_lo & 0x1f) << 7);
585+
rv_add_val((uint16 *)addr, val);
495586
}
496587
break;
497588
}
498589

499590
case R_RISCV_LO12_I: /* S + A */
500591
{
501-
502-
val = (int32)((intptr_t)symbol_addr + (intptr_t)reloc_addend);
503-
504592
CHECK_RELOC_OFFSET(sizeof(uint32));
505593

506-
if (val != (intptr_t)symbol_addr + (intptr_t)reloc_addend) {
507-
goto fail_addr_out_of_range;
508-
}
509-
510594
addr = target_section_addr + reloc_offset;
511595
insn = rv_get_val((uint16 *)addr);
596+
597+
intptr_t val = (intptr_t)symbol_addr + (intptr_t)reloc_addend;
598+
int32_t imm_hi, imm_lo;
512599
rv_calc_imm(val, &imm_hi, &imm_lo);
600+
513601
insn = (insn & 0x000fffff) | (imm_lo << 20);
514602
rv_set_val((uint16 *)addr, insn);
515603
break;
516604
}
517605

518606
case R_RISCV_LO12_S:
519607
{
520-
val = (int32)((intptr_t)symbol_addr + (intptr_t)reloc_addend);
521-
522608
CHECK_RELOC_OFFSET(sizeof(uint32));
523-
if (val != ((intptr_t)symbol_addr + (intptr_t)reloc_addend)) {
524-
goto fail_addr_out_of_range;
525-
}
526609

527610
addr = target_section_addr + reloc_offset;
611+
612+
intptr_t val = (intptr_t)symbol_addr + (intptr_t)reloc_addend;
613+
int32_t imm_hi, imm_lo;
528614
rv_calc_imm(val, &imm_hi, &imm_lo);
615+
529616
val = (((int32)imm_lo >> 5) << 25) + (((int32)imm_lo & 0x1f) << 7);
530617
rv_add_val((uint16 *)addr, val);
531618
break;

0 commit comments

Comments
 (0)