mirror of
https://github.com/apache/nuttx.git
synced 2026-05-27 19:36:35 +08:00
riscv/arch_elf.c: Handle PCREL_HI20/LO12_I/S relocations correctly
There is a problem with the current elf loader for risc-v: when a pair of PCREL_HI20 / LO12 relocations are encountered, it is assumed that these will follow each other immediately, as follows: label: auipc a0, %pcrel_hi(symbol) // R_RISCV_PCREL_HI20 load/store a0, %pcrel_lo(label)(a0) // R_RISCV_PCREL_LO12_I/S With this assumption, the hi/lo relocations are both done when a hi20 relocation entry is encountered, first to the current instruction (addr) and to the next instruction (addr + 4). However, this assumption is wrong. There is nothing in the elf relocation specification[1] that mandates this. Thus, the hi/lo relocation always needs to first fixup the hi-part, and when the lo-part is encountered, it needs to find the corresponding hi relocation entry, via the given "label". This necessitates (re-)visiting the relocation entries for the current section as well as looking for "label" in the symbol table. The NuttX elf loader does not allow such operations to be done in the machine specific part, so this patch fixes the relocation issue by introducing an architecture specific cache for the hi20 relocation and symbol table entries. When a lo12 relocation is encountered, the cache can be consulted to find the hi20 part. [1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
This commit is contained in:
@@ -178,6 +178,84 @@ static void _calc_imm(long offset, long *imm_hi, long *imm_lo)
|
||||
*imm_hi = hi;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: _add_hi20
|
||||
*
|
||||
* Description:
|
||||
* Add PCREL_HI20 relocation offset to the LUT. When a PCREL_LO12_I/_S is
|
||||
* encountered, the corresponding PCREL_HI20 value can be found from it.
|
||||
*
|
||||
* Input Parameters:
|
||||
* arch_data - Where the PCREL_HI20 relocations are listed.
|
||||
* hi20_rel - The PCREL_HI20 relocation entry.
|
||||
* hi20_offset - The corresponding offset value.
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void _add_hi20(void *arch_data, uintptr_t hi20_rel,
|
||||
uintptr_t hi20_offset)
|
||||
{
|
||||
arch_elfdata_t *data = (arch_elfdata_t *)arch_data;
|
||||
int i;
|
||||
|
||||
/* Try to find a free slot from the list */
|
||||
|
||||
for (i = 0; i < ARCH_ELF_RELCNT; i++)
|
||||
{
|
||||
struct hi20_rels_s *hi20 = &data->hi20_rels[i];
|
||||
|
||||
if (hi20->hi20_rel == 0)
|
||||
{
|
||||
hi20->hi20_rel = hi20_rel;
|
||||
hi20->hi20_offset = hi20_offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: _find_hi20
|
||||
*
|
||||
* Description:
|
||||
* Find PCREL_HI20 relocation offset from the LUT. When a PCREL_LO12_I/_S
|
||||
* is encountered, the corresponding PCREL_HI20 value is needed to do the
|
||||
* relocation.
|
||||
*
|
||||
* Input Parameters:
|
||||
* arch_data - Where the PCREL_HI20 relocations are listed.
|
||||
* hi20_rel - The PCREL_HI20 relocation entry.
|
||||
*
|
||||
* Returned Value:
|
||||
* The corresponding hi20_offset value.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static uintptr_t _find_hi20(void *arch_data, uintptr_t hi20_rel)
|
||||
{
|
||||
arch_elfdata_t *data = (arch_elfdata_t *)arch_data;
|
||||
int i;
|
||||
|
||||
/* Try to find the hi20 value from the list */
|
||||
|
||||
for (i = 0; i < ARCH_ELF_RELCNT; i++)
|
||||
{
|
||||
struct hi20_rels_s *hi20 = &data->hi20_rels[i];
|
||||
|
||||
if (hi20->hi20_rel == hi20_rel)
|
||||
{
|
||||
/* Found it, we can clear the entry now */
|
||||
|
||||
hi20->hi20_rel = 0;
|
||||
return hi20->hi20_offset;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
@@ -266,14 +344,15 @@ bool up_checkarch(const Elf_Ehdr *ehdr)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int up_relocate(const Elf_Rel *rel, const Elf_Sym *sym, uintptr_t addr)
|
||||
int up_relocate(const Elf_Rel *rel, const Elf_Sym *sym, uintptr_t addr,
|
||||
void *arch_data)
|
||||
{
|
||||
berr("Not implemented\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int up_relocateadd(const Elf_Rela *rel, const Elf_Sym *sym,
|
||||
uintptr_t addr)
|
||||
uintptr_t addr, void *arch_data)
|
||||
{
|
||||
long offset;
|
||||
unsigned int relotype;
|
||||
@@ -317,22 +396,58 @@ int up_relocateadd(const Elf_Rela *rel, const Elf_Sym *sym,
|
||||
break;
|
||||
|
||||
case R_RISCV_PCREL_LO12_I:
|
||||
case R_RISCV_PCREL_LO12_S:
|
||||
{
|
||||
long imm_hi;
|
||||
long imm_lo;
|
||||
|
||||
binfo("%s at %08" PRIxPTR " [%08" PRIx32 "] "
|
||||
"to sym=%p st_value=%08lx\n",
|
||||
_get_rname(relotype),
|
||||
addr, _get_val((uint16_t *)addr),
|
||||
sym, sym->st_value);
|
||||
|
||||
/* NOTE: imm value for mv has been adjusted in previous HI20 */
|
||||
offset = _find_hi20(arch_data, sym->st_value);
|
||||
|
||||
/* Adjust imm for MV(ADDI) / JR (JALR) : I-type */
|
||||
|
||||
_calc_imm(offset, &imm_hi, &imm_lo);
|
||||
|
||||
_add_val((uint16_t *)addr, (int32_t)imm_lo << 20);
|
||||
}
|
||||
break;
|
||||
|
||||
case R_RISCV_PCREL_LO12_S:
|
||||
{
|
||||
uint32_t val;
|
||||
long imm_hi;
|
||||
long imm_lo;
|
||||
|
||||
binfo("%s at %08" PRIxPTR " [%08" PRIx32 "] "
|
||||
"to sym=%p st_value=%08lx\n",
|
||||
_get_rname(relotype),
|
||||
addr, _get_val((uint16_t *)addr),
|
||||
sym, sym->st_value);
|
||||
|
||||
offset = _find_hi20(arch_data, sym->st_value);
|
||||
|
||||
/* Adjust imm for SW : S-type */
|
||||
|
||||
_calc_imm(offset, &imm_hi, &imm_lo);
|
||||
|
||||
val = (((int32_t)imm_lo >> 5) << 25) +
|
||||
(((int32_t)imm_lo & 0x1f) << 7);
|
||||
|
||||
binfo("imm_lo=%ld (%lx), val=%" PRIx32 "\n", imm_lo, imm_lo, val);
|
||||
|
||||
_add_val((uint16_t *)addr, val);
|
||||
}
|
||||
break;
|
||||
|
||||
case R_RISCV_PCREL_HI20:
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_CALL_PLT:
|
||||
{
|
||||
long imm_hi;
|
||||
long imm_lo;
|
||||
|
||||
binfo("%s at %08" PRIxPTR " [%08" PRIx32 "] "
|
||||
"to sym=%p st_value=%08lx\n",
|
||||
_get_rname(relotype),
|
||||
@@ -341,34 +456,41 @@ int up_relocateadd(const Elf_Rela *rel, const Elf_Sym *sym,
|
||||
|
||||
offset = (long)sym->st_value + (long)rel->r_addend - (long)addr;
|
||||
|
||||
_calc_imm(offset, &imm_hi, &imm_lo);
|
||||
|
||||
/* Adjust auipc (add upper immediate to pc) : 20bit */
|
||||
|
||||
_add_val((uint16_t *)addr, imm_hi << 12);
|
||||
|
||||
/* Add the hi20 value to the cache */
|
||||
|
||||
_add_hi20(arch_data, addr, offset);
|
||||
}
|
||||
break;
|
||||
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_CALL_PLT:
|
||||
{
|
||||
long imm_hi;
|
||||
long imm_lo;
|
||||
|
||||
binfo("%s at %08" PRIxPTR " [%08" PRIx32 "] "
|
||||
"to sym=%p st_value=%08lx\n",
|
||||
_get_rname(relotype),
|
||||
addr, _get_val((uint16_t *)addr),
|
||||
sym, sym->st_value);
|
||||
|
||||
offset = (long)sym->st_value + (long)rel->r_addend - (long)addr;
|
||||
|
||||
_calc_imm(offset, &imm_hi, &imm_lo);
|
||||
|
||||
/* Adjust auipc (add upper immediate to pc) : 20bit */
|
||||
|
||||
_add_val((uint16_t *)addr, (imm_hi << 12));
|
||||
_add_val((uint16_t *)addr, imm_hi << 12);
|
||||
|
||||
if ((_get_val((uint16_t *)(addr + 4)) & 0x7f) == OPCODE_SW)
|
||||
{
|
||||
/* Adjust imm for SW : S-type */
|
||||
/* Adjust imm for CALL (JALR) : I-type */
|
||||
|
||||
uint32_t val =
|
||||
(((int32_t)imm_lo >> 5) << 25) +
|
||||
(((int32_t)imm_lo & 0x1f) << 7);
|
||||
|
||||
binfo("imm_lo=%ld (%lx), val=%" PRIx32 "\n",
|
||||
imm_lo, imm_lo, val);
|
||||
|
||||
_add_val((uint16_t *)(addr + 4), val);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Adjust imm for MV(ADDI)/JALR : I-type */
|
||||
|
||||
_add_val((uint16_t *)(addr + 4), ((int32_t)imm_lo << 20));
|
||||
}
|
||||
_add_val((uint16_t *)(addr + 4), (int32_t)imm_lo << 20);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user