diff --git a/arch/risc-v/include/elf.h b/arch/risc-v/include/elf.h index 6fd222b6feebc..2eb6270fb1258 100644 --- a/arch/risc-v/include/elf.h +++ b/arch/risc-v/include/elf.h @@ -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 */ diff --git a/arch/risc-v/src/common/riscv_pmp.c b/arch/risc-v/src/common/riscv_pmp.c index 2f63c2596db6d..3a9daa2ec24ee 100644 --- a/arch/risc-v/src/common/riscv_pmp.c +++ b/arch/risc-v/src/common/riscv_pmp.c @@ -32,7 +32,6 @@ #include #include #include -#include #include "riscv_internal.h" @@ -89,6 +88,32 @@ typedef struct pmp_entry_s pmp_entry_t; * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: log2ceil + * + * Description: + * Calculate the up-rounded power-of-two for input. + * + * Input Parameters: + * x - Argument to calculate the power-of-two from. + * + * Returned Value: + * Power-of-two for argument, rounded up. + * + ****************************************************************************/ + +static uintptr_t log2ceil(uintptr_t x) +{ + uintptr_t pot = 0; + + for (x = x - 1; x; x >>= 1) + { + pot++; + } + + return pot; +} + /**************************************************************************** * Name: pmp_check_region_attrs * @@ -143,7 +168,7 @@ static bool pmp_check_region_attrs(uintptr_t base, uintptr_t size, /* Get the power-of-two for size, rounded up */ - if ((base & ((UINT64_C(1) << LOG2_CEIL(size)) - 1)) != 0) + if ((base & ((UINT64_C(1) << log2ceil(size)) - 1)) != 0) { /* The start address is not properly aligned with size */ diff --git a/binfmt/libelf/libelf_bind.c b/binfmt/libelf/libelf_bind.c index 564e53f0e1f1c..d6a26305e0047 100644 --- a/binfmt/libelf/libelf_bind.c +++ b/binfmt/libelf/libelf_bind.c @@ -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", diff --git a/include/nuttx/elf.h b/include/nuttx/elf.h index c0f8d9fffcba4..c7ba1337a3618 100644 --- a/include/nuttx/elf.h +++ b/include/nuttx/elf.h @@ -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 /**************************************************************************** diff --git a/libs/libc/machine/risc-v/arch_elf.c b/libs/libc/machine/risc-v/arch_elf.c index 96b5e1a4a1586..b559230d23aeb 100644 --- a/libs/libc/machine/risc-v/arch_elf.c +++ b/libs/libc/machine/risc-v/arch_elf.c @@ -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,58 +396,101 @@ 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_HI20: - case R_RISCV_CALL: - case R_RISCV_CALL_PLT: + 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 = (long)sym->st_value + (long)rel->r_addend - (long)addr; + 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: + { 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 */ + /* Add the hi20 value to the cache */ - uint32_t val = - (((int32_t)imm_lo >> 5) << 25) + - (((int32_t)imm_lo & 0x1f) << 7); + _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 */ - binfo("imm_lo=%ld (%lx), val=%" PRIx32 "\n", - imm_lo, imm_lo, val); + _add_val((uint16_t *)addr, imm_hi << 12); - _add_val((uint16_t *)(addr + 4), val); - } - else - { - /* Adjust imm for MV(ADDI)/JALR : I-type */ + /* Adjust imm for CALL (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;