From 7bbe425fe4495da438e37e11ee423af88593b005 Mon Sep 17 00:00:00 2001 From: Lauren Murphy Date: Fri, 16 May 2025 14:15:45 -0700 Subject: [PATCH] llext: basic x86 support Adds basic x86 support for LLEXT. Signed-off-by: Lauren Murphy --- arch/x86/core/CMakeLists.txt | 2 + arch/x86/core/elf.c | 142 ++++++++++++++++++++++ cmake/compiler/gcc/target_x86.cmake | 22 ++++ subsys/llext/llext_link.c | 2 +- tests/subsys/llext/no_mem_protection.conf | 1 + tests/subsys/llext/src/syscalls_ext.c | 17 ++- tests/subsys/llext/testcase.yaml | 6 + 7 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 arch/x86/core/elf.c diff --git a/arch/x86/core/CMakeLists.txt b/arch/x86/core/CMakeLists.txt index abbc052a624..c34c6909b01 100644 --- a/arch/x86/core/CMakeLists.txt +++ b/arch/x86/core/CMakeLists.txt @@ -27,6 +27,8 @@ zephyr_library_sources_ifdef(CONFIG_X86_VERY_EARLY_CONSOLE early_serial.c) zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE tls.c) +zephyr_library_sources_ifdef(CONFIG_LLEXT elf.c) + if(CONFIG_X86_64) include(intel64.cmake) else() diff --git a/arch/x86/core/elf.c b/arch/x86/core/elf.c new file mode 100644 index 00000000000..a1660387d0e --- /dev/null +++ b/arch/x86/core/elf.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2025 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(elf, CONFIG_LLEXT_LOG_LEVEL); + +#ifdef CONFIG_64BIT +#define R_X86_64_64 1 +#define R_X86_64_PC32 2 +#define R_X86_64_PLT32 4 +#define R_X86_64_32 10 +#define R_X86_64_32S 11 + +/** + * @brief Architecture specific function for relocating shared elf + * + * Elf files contain a series of relocations described in multiple sections. + * These relocation instructions are architecture specific and each architecture + * supporting modules must implement this. + * + * The relocation codes are well documented: + * + * https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf (intel64) + */ +int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel, + const elf_shdr_t *shdr) +{ + int ret = 0; + 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; + } + + sym_base_addr += rel->r_addend; + + int reloc_type = ELF32_R_TYPE(rel->r_info); + + switch (reloc_type) { + case R_X86_64_PC32: + case R_X86_64_PLT32: + *(uint32_t *)loc = sym_base_addr - loc; + break; + case R_X86_64_64: + case R_X86_64_32: + case R_X86_64_32S: + *(uint32_t *)loc = sym_base_addr; + break; + default: + LOG_ERR("unknown relocation: %u\n", reloc_type); + ret = -ENOEXEC; + break; + } + + return ret; +} +#else +#define R_386_32 1 +#define R_286_PC32 2 + +/** + * @brief Architecture specific function for relocating shared elf + * + * Elf files contain a series of relocations described in multiple sections. + * These relocation instructions are architecture specific and each architecture + * supporting modules must implement this. + * + * The relocation codes are well documented: + * + * https://docs.oracle.com/cd/E19683-01/817-3677/chapter6-26/index.html (ia32) + */ +int arch_elf_relocate(struct llext_loader *ldr, struct llext *ext, elf_rela_t *rel, + const elf_shdr_t *shdr) +{ + int ret = 0; + 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; + + /* x86 uses elf_rel_t records with no addends */ + uintptr_t addend = *(uintptr_t *)loc; + + 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 += addend; + + int reloc_type = ELF32_R_TYPE(rel->r_info); + + switch (reloc_type) { + case R_386_32: + *(uint32_t *)loc = sym_base_addr; + break; + case R_286_PC32: + *(uint32_t *)loc = sym_base_addr - loc; + break; + default: + LOG_ERR("unknown relocation: %u\n", reloc_type); + ret = -ENOEXEC; + break; + } + + return ret; +} +#endif diff --git a/cmake/compiler/gcc/target_x86.cmake b/cmake/compiler/gcc/target_x86.cmake index 8dac67a0068..a2c9f829000 100644 --- a/cmake/compiler/gcc/target_x86.cmake +++ b/cmake/compiler/gcc/target_x86.cmake @@ -15,6 +15,28 @@ else() endif() endif() +# Flags not supported by llext linker +# (regexps are supported and match whole word) +set(LLEXT_REMOVE_FLAGS + -fno-pic + -fno-pie + -ffunction-sections + -fdata-sections + -g.* + -Os +) + +# Force compiler and linker match +if(CONFIG_X86_64) + set(LLEXT_APPEND_FLAGS + -m64 + ) +else() + set(LLEXT_APPEND_FLAGS + -m32 + ) +endif() + # GNU Assembler, by default on non-Linux targets, treats slashes as # start of comments on i386. # (https://sourceware.org/binutils/docs-2.33.1/as/i386_002dChars.html#i386_002dChars) diff --git a/subsys/llext/llext_link.c b/subsys/llext/llext_link.c index fa35a485808..23697f9c3cd 100644 --- a/subsys/llext/llext_link.c +++ b/subsys/llext/llext_link.c @@ -389,7 +389,7 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_load_param *ldr_parm) { uintptr_t sect_base = 0; - elf_rela_t rel; + elf_rela_t rel = {0}; elf_word rel_cnt = 0; const char *name; int i, ret; diff --git a/tests/subsys/llext/no_mem_protection.conf b/tests/subsys/llext/no_mem_protection.conf index 6fbaeac79bb..76ad5cbc92a 100644 --- a/tests/subsys/llext/no_mem_protection.conf +++ b/tests/subsys/llext/no_mem_protection.conf @@ -7,3 +7,4 @@ CONFIG_ARM_MPU=n CONFIG_ARM_AARCH32_MMU=n CONFIG_RISCV_PMP=n CONFIG_ARC_MPU_ENABLE=n +CONFIG_X86_MMU=n diff --git a/tests/subsys/llext/src/syscalls_ext.c b/tests/subsys/llext/src/syscalls_ext.c index d8e6449ee2f..52671d3c292 100644 --- a/tests/subsys/llext/src/syscalls_ext.c +++ b/tests/subsys/llext/src/syscalls_ext.c @@ -8,12 +8,25 @@ * This code demonstrates syscall support. */ + /* This include directive must appear first in the file. + * + * On x86 platforms with demand paging on, Zephyr generates a + * .pinned_text section containing syscalls. The LLEXT loader + * requires .text-like sections to appear close to .text at the + * start of the object file, before .rodata, so they can be + * grouped into a contiguous text region. + * + * Including syscalls_ext.h first ensures the first instance of + * data to be placed in .pinned_text appears before the first instance + * of data for .rodata. As a result, .pinned_text will appear + * before .rodata. + */ +#include "syscalls_ext.h" + #include #include #include -#include "syscalls_ext.h" - void test_entry(void) { int input = 41; diff --git a/tests/subsys/llext/testcase.yaml b/tests/subsys/llext/testcase.yaml index 0fc910e8530..e10686a236a 100644 --- a/tests/subsys/llext/testcase.yaml +++ b/tests/subsys/llext/testcase.yaml @@ -39,6 +39,7 @@ tests: - arm - riscv - arc + - x86 filter: not CONFIG_MPU and not CONFIG_MMU extra_conf_files: ['no_mem_protection.conf'] extra_configs: @@ -56,6 +57,7 @@ tests: - arm64 - arm - riscv + - x86 filter: CONFIG_MMU or CONFIG_RISCV_PMP integration_platforms: - qemu_cortex_a53 # ARM Cortex-A53 (ARMv8-A ISA) @@ -68,6 +70,7 @@ tests: - xtensa - riscv - arc + - x86 integration_platforms: - qemu_xtensa/dc233c # Xtensa ISA filter: not CONFIG_MPU and not CONFIG_MMU @@ -80,6 +83,7 @@ tests: - xtensa - riscv - arc + - x86 platform_exclude: - qemu_arc/qemu_arc_hs5x # See #80949 - nsim/nsim_hs5x # See #80949 @@ -101,6 +105,7 @@ tests: - xtensa - riscv - arc + - x86 integration_platforms: - qemu_xtensa/dc233c # Xtensa ISA filter: not CONFIG_MPU and not CONFIG_MMU @@ -114,6 +119,7 @@ tests: - xtensa - riscv - arc + - x86 platform_exclude: - qemu_arc/qemu_arc_hs5x # See #80949 - nsim/nsim_hs5x # See #80949