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:
Ville Juven 2023-12-05 12:30:46 +02:00 committed by Xiang Xiao
parent 267d039295
commit 996625ec58
17 changed files with 249 additions and 57 deletions

View File

@ -83,4 +83,25 @@
#define R_RISCV_SET32 56
#define R_RISCV_32_PCREL 57
#define ARCH_ELFDATA 1
#define ARCH_ELF_RELCNT 8
/****************************************************************************
* Public Types
****************************************************************************/
#ifndef __ASSEMBLY__
struct arch_elfdata_s
{
struct hi20_rels_s
{
uintptr_t hi20_rel;
uintptr_t hi20_offset;
}
hi20_rels[ARCH_ELF_RELCNT];
};
typedef struct arch_elfdata_s arch_elfdata_t;
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_RISCV_INCLUDE_ELF_H */

View File

@ -55,6 +55,15 @@
# define elf_dumpbuffer(m,b,n)
#endif
#ifdef ARCH_ELFDATA
# define ARCH_ELFDATA_DEF arch_elfdata_t arch_data; \
memset(&arch_data, 0, sizeof(arch_elfdata_t))
# define ARCH_ELFDATA_PARM &arch_data
#else
# define ARCH_ELFDATA_DEF
# define ARCH_ELFDATA_PARM NULL
#endif
/****************************************************************************
* Private Types
****************************************************************************/
@ -185,6 +194,10 @@ static int elf_relocate(FAR struct elf_loadinfo_s *loadinfo, int relidx,
int i;
int j;
/* Define potential architecture specific elf data container */
ARCH_ELFDATA_DEF;
rels = kmm_malloc(CONFIG_ELF_RELOCATION_BUFFERCOUNT * sizeof(Elf_Rel));
if (rels == NULL)
{
@ -334,7 +347,7 @@ static int elf_relocate(FAR struct elf_loadinfo_s *loadinfo, int relidx,
/* Now perform the architecture-specific relocation */
ret = up_relocate(rel, sym, addr);
ret = up_relocate(rel, sym, addr, ARCH_ELFDATA_PARM);
if (ret < 0)
{
berr("ERROR: Section %d reloc %d: Relocation failed: %d\n",
@ -370,6 +383,10 @@ static int elf_relocateadd(FAR struct elf_loadinfo_s *loadinfo, int relidx,
int i;
int j;
/* Define potential architecture specific elf data container */
ARCH_ELFDATA_DEF;
relas = kmm_malloc(CONFIG_ELF_RELOCATION_BUFFERCOUNT * sizeof(Elf_Rela));
if (relas == NULL)
{
@ -519,7 +536,7 @@ static int elf_relocateadd(FAR struct elf_loadinfo_s *loadinfo, int relidx,
/* Now perform the architecture-specific relocation */
ret = up_relocateadd(rela, sym, addr);
ret = up_relocateadd(rela, sym, addr, ARCH_ELFDATA_PARM);
if (ret < 0)
{
berr("ERROR: Section %d reloc %d: Relocation failed: %d\n",

View File

@ -142,9 +142,9 @@ bool up_checkarch(FAR const Elf_Ehdr *hdr);
#ifdef CONFIG_LIBC_ARCH_ELF
int up_relocate(FAR const Elf_Rel *rel, FAR const Elf_Sym *sym,
uintptr_t addr);
int up_relocateadd(FAR const Elf_Rela *rel,
FAR const Elf_Sym *sym, uintptr_t addr);
uintptr_t addr, FAR void *arch_data);
int up_relocateadd(FAR const Elf_Rela *rel, FAR const Elf_Sym *sym,
uintptr_t addr, FAR void *arch_data);
#endif
/****************************************************************************

View File

@ -123,7 +123,8 @@ bool up_checkarch(const Elf32_Ehdr *ehdr)
*
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr,
void *arch_data)
{
int32_t offset;
unsigned int relotype;
@ -477,7 +478,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
berr("ERROR: RELA relocation not supported\n");
return -ENOSYS;

View File

@ -110,7 +110,8 @@ bool up_checkarch(const Elf32_Ehdr *ehdr)
*
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr,
void *arch_data)
{
int32_t offset;
uint32_t upper_insn;
@ -513,7 +514,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
berr("ERROR: RELA relocation not supported\n");
return -ENOSYS;

View File

@ -123,7 +123,8 @@ bool up_checkarch(const Elf32_Ehdr *ehdr)
*
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr,
void *arch_data)
{
int32_t offset;
unsigned int relotype;
@ -474,7 +475,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
berr("ERROR: RELA relocation not supported\n");
return -ENOSYS;

View File

@ -110,7 +110,8 @@ bool up_checkarch(const Elf32_Ehdr *ehdr)
*
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr,
void *arch_data)
{
int32_t offset;
uint32_t upper_insn;
@ -513,7 +514,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
berr("ERROR: RELA relocation not supported\n");
return -ENOSYS;

View File

@ -123,7 +123,8 @@ bool up_checkarch(const Elf32_Ehdr *ehdr)
*
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr,
void *arch_data)
{
int32_t offset;
unsigned int relotype;
@ -474,7 +475,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
berr("ERROR: RELA relocation not supported\n");
return -ENOSYS;

View File

@ -110,7 +110,8 @@ bool up_checkarch(const Elf32_Ehdr *ehdr)
*
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr,
void *arch_data)
{
int32_t offset;
uint32_t upper_insn;
@ -513,7 +514,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
berr("ERROR: RELA relocation not supported\n");
return -ENOSYS;

View File

@ -465,14 +465,15 @@ bool up_checkarch(const Elf64_Ehdr *ehdr)
*
****************************************************************************/
int up_relocate(const Elf64_Rel *rel, const Elf64_Sym *sym, uintptr_t addr)
int up_relocate(const Elf64_Rel *rel, const Elf64_Sym *sym, uintptr_t addr,
void *arch_data)
{
berr("ERROR: REL relocation not supported\n");
return -ENOSYS;
}
int up_relocateadd(const Elf64_Rela *rel, const Elf64_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
bool overflow_check = true;
uint64_t val;

View File

@ -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;

View File

@ -87,7 +87,8 @@ bool up_checkarch(const Elf32_Ehdr *hdr)
*
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr,
void *arch_data)
{
uint32_t *ptr = (uint32_t *)addr;
@ -119,7 +120,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
berr("ERROR: Not supported\n");
return -ENOSYS;

View File

@ -140,14 +140,15 @@ bool up_checkarch(const Elf64_Ehdr *ehdr)
*
****************************************************************************/
int up_relocate(const Elf64_Rel *rel, const Elf64_Sym *sym, uintptr_t addr)
int up_relocate(const Elf64_Rel *rel, const Elf64_Sym *sym, uintptr_t addr,
FAR void *arch_data)
{
berr("Not implemented\n");
return -ENOSYS;
}
int up_relocateadd(const Elf64_Rela *rel, const Elf64_Sym *sym,
uintptr_t addr)
uintptr_t addr, FAR void *arch_data)
{
unsigned int relotype;
uint64_t value;

View File

@ -121,7 +121,7 @@ bool up_checkarch(const Elf32_Ehdr *ehdr)
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
unsigned int relotype;
@ -141,7 +141,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym,
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
unsigned int relotype;
uint32_t value;

View File

@ -95,7 +95,8 @@ bool up_checkarch(const Elf32_Ehdr *hdr)
*
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr,
void *arch_data)
{
uint32_t *ptr = (uint32_t *)addr;
@ -126,7 +127,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
bwarn("WARNING: Not supported\n");
return -ENOSYS;

View File

@ -126,7 +126,8 @@ bool up_checkarch(const Elf32_Ehdr *ehdr)
*
****************************************************************************/
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr,
void *arch_data)
{
unsigned int relotype;
@ -159,7 +160,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr)
}
int up_relocateadd(const Elf32_Rela *rel, const Elf32_Sym *sym,
uintptr_t addr)
uintptr_t addr, void *arch_data)
{
unsigned int relotype;
unsigned char *p;

View File

@ -44,6 +44,15 @@
#define I_PLT 1 /* ... for PLTs */
#define N_RELS 2 /* Number of relxxx[] indexes */
#ifdef ARCH_ELFDATA
# define ARCH_ELFDATA_DEF arch_elfdata_t arch_data; \
memset(&arch_data, 0, sizeof(arch_elfdata_t))
# define ARCH_ELFDATA_PARM &arch_data
#else
# define ARCH_ELFDATA_DEF
# define ARCH_ELFDATA_PARM NULL
#endif
/****************************************************************************
* Private Types
****************************************************************************/
@ -181,6 +190,10 @@ static int modlib_relocate(FAR struct module_s *modp,
int i;
int j;
/* Define potential architecture specific elf data container */
ARCH_ELFDATA_DEF;
rels = lib_malloc(CONFIG_MODLIB_RELOCATION_BUFFERCOUNT * sizeof(Elf_Rel));
if (!rels)
{
@ -331,7 +344,7 @@ static int modlib_relocate(FAR struct module_s *modp,
/* Now perform the architecture-specific relocation */
ret = up_relocate(rel, sym, addr);
ret = up_relocate(rel, sym, addr, ARCH_ELFDATA_PARM);
if (ret < 0)
{
berr("ERROR: Section %d reloc %d: Relocation failed: %d\n",
@ -368,6 +381,10 @@ static int modlib_relocateadd(FAR struct module_s *modp,
int i;
int j;
/* Define potential architecture specific elf data container */
ARCH_ELFDATA_DEF;
relas = lib_malloc(CONFIG_MODLIB_RELOCATION_BUFFERCOUNT *
sizeof(Elf_Rela));
if (!relas)
@ -519,7 +536,7 @@ static int modlib_relocateadd(FAR struct module_s *modp,
/* Now perform the architecture-specific relocation */
ret = up_relocateadd(rela, sym, addr);
ret = up_relocateadd(rela, sym, addr, ARCH_ELFDATA_PARM);
if (ret < 0)
{
berr("ERROR: Section %d reloc %d: Relocation failed: %d\n",
@ -568,6 +585,10 @@ static int modlib_relocatedyn(FAR struct module_s *modp,
int idx_rel;
int idx_sym;
/* Define potential architecture specific elf data container */
ARCH_ELFDATA_DEF;
dyn = lib_malloc(shdr->sh_size);
ret = modlib_read(loadinfo, (FAR uint8_t *)dyn, shdr->sh_size,
shdr->sh_offset);
@ -778,7 +799,7 @@ static int modlib_relocatedyn(FAR struct module_s *modp,
loadinfo->datasec + loadinfo->datastart;
}
ret = up_relocate(rel, &dynsym, addr);
ret = up_relocate(rel, &dynsym, addr, ARCH_ELFDATA_PARM);
}
if (ret < 0)