From 3466dab8047ac4d90630ed3afeb4309ef0ca8c60 Mon Sep 17 00:00:00 2001 From: Eric Ackermann Date: Tue, 18 Feb 2025 09:16:52 +0100 Subject: [PATCH] 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 --- arch/arc/core/elf.c | 27 +++++- arch/arm/core/elf.c | 26 +++++- arch/arm64/core/elf.c | 25 +++++- arch/riscv/core/elf.c | 30 ++++++- include/zephyr/llext/llext.h | 13 +-- include/zephyr/llext/llext_internal.h | 37 +++++++- subsys/llext/llext_link.c | 123 ++++++++++++++------------ subsys/llext/llext_priv.h | 6 -- 8 files changed, 206 insertions(+), 81 deletions(-) diff --git a/arch/arc/core/elf.c b/arch/arc/core/elf.c index 9f9f1073431..db2a74e164e 100644 --- a/arch/arc/core/elf.c +++ b/arch/arc/core/elf.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -31,12 +32,32 @@ LOG_MODULE_REGISTER(elf, CONFIG_LLEXT_LOG_LEVEL); * https://github.com/foss-for-synopsys-dwc-arc-processors/arc-ABI-manual/blob/master/ARCv2_ABI.pdf * https://github.com/zephyrproject-rtos/binutils-gdb */ -int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, uintptr_t sym_base_addr, const char *sym_name, - uintptr_t load_bias) +int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel, + const elf_shdr_t *shdr) { int ret = 0; - uint32_t insn = UNALIGNED_GET((uint32_t *)loc); uint32_t value; + const uintptr_t loc = llext_get_reloc_instruction_location(ldr, ext, shdr->sh_info, rel); + uint32_t insn = UNALIGNED_GET((uint32_t *)loc); + elf_sym_t sym; + uintptr_t sym_base_addr; + const char *sym_name; + + ret = llext_read_symbol(ldr, ext, rel, &sym); + + if (ret != 0) { + LOG_ERR("Could not read symbol from binary!"); + return ret; + } + + sym_name = llext_symbol_name(ldr, ext, &sym); + + ret = llext_lookup_symbol(ldr, ext, &sym_base_addr, rel, &sym, sym_name, shdr); + + if (ret != 0) { + LOG_ERR("Could not find symbol %s!", sym_name); + return ret; + } sym_base_addr += rel->r_addend; diff --git a/arch/arm/core/elf.c b/arch/arm/core/elf.c index 5fdc5471a6b..d44e7dd2e2c 100644 --- a/arch/arm/core/elf.c +++ b/arch/arm/core/elf.c @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -316,11 +317,32 @@ static void thm_movs_handler(elf_word reloc_type, uint32_t loc, * Do NOT mix them with not 'Thumb instructions' in the below switch/case: they are not * intended to work together. */ -int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, uintptr_t sym_base_addr, - const char *sym_name, uintptr_t load_bias) +int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel, + const elf_shdr_t *shdr) { int ret = 0; elf_word reloc_type = ELF32_R_TYPE(rel->r_info); + const uintptr_t load_bias = (uintptr_t)ext->mem[LLEXT_MEM_TEXT]; + const uintptr_t loc = llext_get_reloc_instruction_location(ldr, ext, shdr->sh_info, rel); + elf_sym_t sym; + uintptr_t sym_base_addr; + const char *sym_name; + + ret = llext_read_symbol(ldr, ext, rel, &sym); + + if (ret != 0) { + LOG_ERR("Could not read symbol from binary!"); + return ret; + } + + sym_name = llext_symbol_name(ldr, ext, &sym); + + ret = llext_lookup_symbol(ldr, ext, &sym_base_addr, rel, &sym, sym_name, shdr); + + if (ret != 0) { + LOG_ERR("Could not find symbol %s!", sym_name); + return ret; + } LOG_DBG("%d %lx %lx %s", reloc_type, loc, sym_base_addr, sym_name); diff --git a/arch/arm64/core/elf.c b/arch/arm64/core/elf.c index 66e9f21fc06..c60c99fad46 100644 --- a/arch/arm64/core/elf.c +++ b/arch/arm64/core/elf.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -430,12 +431,32 @@ static int imm_reloc_handler(elf_rela_t *rel, elf_word reloc_type, uintptr_t loc * @retval -ENOTSUP Unsupported relocation * @retval -ENOEXEC Invalid relocation */ -int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, uintptr_t sym_base_addr, const char *sym_name, - uintptr_t load_bias) +int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel, + const elf_shdr_t *shdr) { int ret = 0; bool overflow_check = true; elf_word reloc_type = ELF_R_TYPE(rel->r_info); + const uintptr_t loc = llext_get_reloc_instruction_location(ldr, ext, shdr->sh_info, rel); + elf_sym_t sym; + uintptr_t sym_base_addr; + const char *sym_name; + + ret = llext_read_symbol(ldr, ext, rel, &sym); + + if (ret != 0) { + LOG_ERR("Could not read symbol from binary!"); + return ret; + } + + sym_name = llext_symbol_name(ldr, ext, &sym); + + ret = llext_lookup_symbol(ldr, ext, &sym_base_addr, rel, &sym, sym_name, shdr); + + if (ret != 0) { + LOG_ERR("Could not find symbol %s!", sym_name); + return ret; + } switch (reloc_type) { case R_ARM_NONE: diff --git a/arch/riscv/core/elf.c b/arch/riscv/core/elf.c index e0630876dd4..db3ae005cd6 100644 --- a/arch/riscv/core/elf.c +++ b/arch/riscv/core/elf.c @@ -8,6 +8,9 @@ */ #include #include +#include +#include + #include #include @@ -68,11 +71,32 @@ static long long last_u_type_jump_target; * https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc * */ -int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc_unsigned, uintptr_t sym_base_addr_unsigned, - const char *sym_name, uintptr_t load_bias) +int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel, + const elf_shdr_t *shdr) { /* FIXME currently, RISC-V relocations all fit in ELF_32_R_TYPE */ elf_word reloc_type = ELF32_R_TYPE(rel->r_info); + const uintptr_t load_bias = (uintptr_t)ext->mem[LLEXT_MEM_TEXT]; + const uintptr_t loc_unsigned = llext_get_reloc_instruction_location(ldr, ext, + shdr->sh_info, rel); + elf_sym_t sym; + uintptr_t sym_base_addr_unsigned; + const char *sym_name; + int ret; + + ret = llext_read_symbol(ldr, ext, rel, &sym); + if (ret != 0) { + LOG_ERR("Could not read symbol from binary!"); + return ret; + } + + sym_name = llext_symbol_name(ldr, ext, &sym); + ret = llext_lookup_symbol(ldr, ext, &sym_base_addr_unsigned, rel, &sym, sym_name, shdr); + + if (ret != 0) { + LOG_ERR("Could not find symbol %s!", sym_name); + return ret; + } /* * The RISC-V specification uses the following symbolic names for the relocations: * @@ -99,7 +123,7 @@ int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc_unsigned, uintptr_t sym_bas long long original_imm8, jump_target; int16_t compressed_imm8; __typeof__(rel->r_addend) target_alignment = 1; - const intptr_t sym_base_addr = (intptr_t)sym_base_addr_unsigned; + intptr_t sym_base_addr = (intptr_t)sym_base_addr_unsigned; LOG_DBG("Relocating symbol %s at %p with base address %p load address %p type %" PRIu64, sym_name, (void *)loc, (void *)sym_base_addr, (void *)load_bias, diff --git a/include/zephyr/llext/llext.h b/include/zephyr/llext/llext.h index a77248afda1..30b0bc1ee8f 100644 --- a/include/zephyr/llext/llext.h +++ b/include/zephyr/llext/llext.h @@ -354,18 +354,19 @@ int llext_add_domain(struct llext *ext, struct k_mem_domain *domain); * symbolic data such as a section, function, or object. These relocations * are architecture specific and each architecture supporting LLEXT must * implement this. + * Arguments sym_base_addr, sym_name can be computed from the sym parameter, + * but these parameters are provided redundantly to increase efficiency. * + * @param[in] ldr Extension loader + * @param[in] ext Extension being relocated refers to * @param[in] rel Relocation data provided by ELF - * @param[in] loc Address of opcode to rewrite - * @param[in] sym_base_addr Address of symbol referenced by relocation - * @param[in] sym_name Name of symbol referenced by relocation - * @param[in] load_bias `.text` load address + * @param[in] shdr Header of the ELF section currently being located * @retval 0 Success * @retval -ENOTSUP Unsupported relocation * @retval -ENOEXEC Invalid relocation */ -int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, - uintptr_t sym_base_addr, const char *sym_name, uintptr_t load_bias); +int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel, + const elf_shdr_t *shdr); /** * @brief Locates an ELF section in the file. diff --git a/include/zephyr/llext/llext_internal.h b/include/zephyr/llext/llext_internal.h index b5365aa5a75..c196ddd25dd 100644 --- a/include/zephyr/llext/llext_internal.h +++ b/include/zephyr/llext/llext_internal.h @@ -11,6 +11,8 @@ extern "C" { #endif +#include + /** * @file * @brief Private header for linkable loadable extensions @@ -18,8 +20,6 @@ extern "C" { /** @cond ignore */ -struct llext_loader; -struct llext; struct llext_elf_sect_map { enum llext_mem mem_idx; @@ -28,6 +28,39 @@ struct llext_elf_sect_map { const void *llext_loaded_sect_ptr(struct llext_loader *ldr, struct llext *ext, unsigned int sh_ndx); + +static inline const char *llext_string(const struct llext_loader *ldr, const struct llext *ext, + enum llext_mem mem_idx, unsigned int idx) +{ + return (const char *)ext->mem[mem_idx] + idx; +} + +static inline uintptr_t llext_get_reloc_instruction_location(struct llext_loader *ldr, + struct llext *ext, + int shndx, + const elf_rela_t *rela) +{ + return (uintptr_t) llext_loaded_sect_ptr(ldr, ext, shndx) + rela->r_offset; +} + +static inline const char *llext_symbol_name(struct llext_loader *ldr, struct llext *ext, + const elf_sym_t *sym) +{ + return llext_string(ldr, ext, LLEXT_MEM_STRTAB, sym->st_name); +} +/* + * Determine address of a symbol. + */ +int llext_lookup_symbol(struct llext_loader *ldr, struct llext *ext, uintptr_t *link_addr, + const elf_rela_t *rel, const elf_sym_t *sym, const char *name, + const elf_shdr_t *shdr); + +/* + * Read the symbol entry corresponding to a relocation from the binary. + */ +int llext_read_symbol(struct llext_loader *ldr, struct llext *ext, const elf_rela_t *rel, + elf_sym_t *sym); + /** @endcond */ #ifdef __cplusplus diff --git a/subsys/llext/llext_link.c b/subsys/llext/llext_link.c index 1fa77b54407..794a212c2c1 100644 --- a/subsys/llext/llext_link.c +++ b/subsys/llext/llext_link.c @@ -26,8 +26,8 @@ LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL); #define SYM_NAME_OR_SLID(name, slid) name #endif -__weak int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc, - uintptr_t sym_base_addr, const char *sym_name, uintptr_t load_bias) +__weak int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel, + const elf_shdr_t *shdr) { return -ENOTSUP; } @@ -142,22 +142,31 @@ static const void *llext_find_extension_sym(const char *sym_name, struct llext * return se.addr; } -/** - * @brief Determine address of a symbol. - * - * @param ext llext extension - * @param ldr llext loader - * @param link_addr (output) resolved address - * @param rel relocation entry - * @param sym symbol entry - * @param name symbol name - * @param shdr section header - * - * @return 0 for OK, negative for error +/* + * Read the symbol entry corresponding to a relocation from the binary. */ -static int llext_lookup_symbol(struct llext *ext, struct llext_loader *ldr, uintptr_t *link_addr, - const elf_rela_t *rel, const elf_sym_t *sym, const char *name, - const elf_shdr_t *shdr) +int llext_read_symbol(struct llext_loader *ldr, struct llext *ext, const elf_rela_t *rel, + elf_sym_t *sym) +{ + int ret; + + ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset + + ELF_R_SYM(rel->r_info) * sizeof(elf_sym_t)); + if (ret != 0) { + return ret; + } + + ret = llext_read(ldr, sym, sizeof(elf_sym_t)); + + return ret; +} + +/* + * Determine address of a symbol. + */ +int llext_lookup_symbol(struct llext_loader *ldr, struct llext *ext, uintptr_t *link_addr, + const elf_rela_t *rel, const elf_sym_t *sym, const char *name, + const elf_shdr_t *shdr) { if (ELF_R_SYM(rel->r_info) == 0) { /* @@ -219,7 +228,6 @@ static int llext_lookup_symbol(struct llext *ext, struct llext_loader *ldr, uint return 0; } - static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr_t *shdr, const struct llext_load_param *ldr_parm, elf_shdr_t *tgt) { @@ -359,7 +367,6 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_l { uintptr_t sect_base = 0; elf_rela_t rel; - elf_sym_t sym; elf_word rel_cnt = 0; const char *name; int i, ret; @@ -446,7 +453,7 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_l return -ENOEXEC; } - sect_base = (uintptr_t)llext_loaded_sect_ptr(ldr, ext, shdr->sh_info); + sect_base = (uintptr_t) llext_loaded_sect_ptr(ldr, ext, shdr->sh_info); for (int j = 0; j < rel_cnt; j++) { /* get each relocation entry */ @@ -460,47 +467,49 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_l return ret; } - /* get corresponding symbol */ - ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset - + ELF_R_SYM(rel.r_info) * sizeof(elf_sym_t)); - if (ret != 0) { - return ret; +#ifdef CONFIG_LLEXT_LOG_LEVEL + if (CONFIG_LLEXT_LOG_LEVEL >= LOG_LEVEL_INF) { + uintptr_t link_addr; + uintptr_t op_loc = + llext_get_reloc_instruction_location(ldr, ext, + shdr->sh_info, + &rel); + elf_sym_t sym; + + ret = llext_read_symbol(ldr, ext, &rel, &sym); + + if (ret != 0) { + return ret; + } + + name = llext_symbol_name(ldr, ext, &sym); + + ret = llext_lookup_symbol(ldr, ext, &link_addr, &rel, &sym, name, + shdr); + + if (ret != 0) { + LOG_ERR("Could not find symbol %s!", name); + return ret; + } + + LOG_DBG("relocation %d:%d info 0x%zx (type %zd, sym %zd) offset %zd" + " sym_name %s sym_type %d sym_bind %d sym_ndx %d", + i, j, (size_t)rel.r_info, (size_t)ELF_R_TYPE(rel.r_info), + (size_t)ELF_R_SYM(rel.r_info), (size_t)rel.r_offset, + name, ELF_ST_TYPE(sym.st_info), + ELF_ST_BIND(sym.st_info), sym.st_shndx); + + LOG_INF("writing relocation symbol %s type %zd sym %zd at addr " + "0x%lx addr 0x%lx", + name, (size_t)ELF_R_TYPE(rel.r_info), + (size_t)ELF_R_SYM(rel.r_info), + op_loc, link_addr); } +#endif /* CONFIG_LLEXT_LOG_LEVEL */ - ret = llext_read(ldr, &sym, sizeof(elf_sym_t)); - if (ret != 0) { - return ret; - } - - name = llext_string(ldr, ext, LLEXT_MEM_STRTAB, sym.st_name); - - LOG_DBG("relocation %d:%d info 0x%zx (type %zd, sym %zd) offset %zd " - "sym_name %s sym_type %d sym_bind %d sym_ndx %d", - i, j, (size_t)rel.r_info, (size_t)ELF_R_TYPE(rel.r_info), - (size_t)ELF_R_SYM(rel.r_info), (size_t)rel.r_offset, - name, ELF_ST_TYPE(sym.st_info), - ELF_ST_BIND(sym.st_info), sym.st_shndx); - - uintptr_t link_addr, op_loc; - - op_loc = sect_base + rel.r_offset; - - ret = llext_lookup_symbol(ext, ldr, &link_addr, &rel, &sym, name, shdr); - - if (ret != 0) { - LOG_ERR("Failed to lookup symbol in rela section %d entry %d!", i, - j); - return ret; - } - - LOG_INF("writing relocation symbol %s type %zd sym %zd at addr 0x%lx " - "addr 0x%lx", - name, (size_t)ELF_R_TYPE(rel.r_info), (size_t)ELF_R_SYM(rel.r_info), - op_loc, link_addr); /* relocation */ - ret = arch_elf_relocate(&rel, op_loc, link_addr, name, - (uintptr_t)ext->mem[LLEXT_MEM_TEXT]); + ret = arch_elf_relocate(ldr, ext, &rel, shdr); if (ret != 0) { return ret; } diff --git a/subsys/llext/llext_priv.h b/subsys/llext/llext_priv.h index b5b71fa31f6..66a7c6c2dea 100644 --- a/subsys/llext/llext_priv.h +++ b/subsys/llext/llext_priv.h @@ -49,12 +49,6 @@ static inline void llext_free(void *ptr) int do_llext_load(struct llext_loader *ldr, struct llext *ext, const struct llext_load_param *ldr_parm); -static inline const char *llext_string(const struct llext_loader *ldr, const struct llext *ext, - enum llext_mem mem_idx, unsigned int idx) -{ - return (char *)ext->mem[mem_idx] + idx; -} - /* * Relocation (llext_link.c) */