From 5ef0ab7585a3e5dcccf4ad971e4d19a23f0127d7 Mon Sep 17 00:00:00 2001 From: Yevhen Boichuk Date: Mon, 9 Mar 2026 12:39:31 +0200 Subject: [PATCH 1/4] feaf: add elfloader --- lib/lilka/src/lilka.h | 1 + lib/lilka/src/lilka/display.cpp | 1 - lib/lilka/src/lilka/dynloader.cpp | 683 ++++++++++++++++++++++++++++++ lib/lilka/src/lilka/dynloader.h | 304 +++++++++++++ 4 files changed, 988 insertions(+), 1 deletion(-) create mode 100644 lib/lilka/src/lilka/dynloader.cpp create mode 100644 lib/lilka/src/lilka/dynloader.h diff --git a/lib/lilka/src/lilka.h b/lib/lilka/src/lilka.h index da0ebd3..5f75a5e 100644 --- a/lib/lilka/src/lilka.h +++ b/lib/lilka/src/lilka.h @@ -17,6 +17,7 @@ #include "lilka/fmath.h" #include "lilka/audio.h" #include "lilka/sdk.h" +#include "lilka/dynloader.h" namespace lilka { /// Ініціалізація Лілки diff --git a/lib/lilka/src/lilka/display.cpp b/lib/lilka/src/lilka/display.cpp index 1b7ff33..cca79e9 100644 --- a/lib/lilka/src/lilka/display.cpp +++ b/lib/lilka/src/lilka/display.cpp @@ -45,7 +45,6 @@ void Display::begin() { #endif setFont(FONT_10x20); setUTF8Print(true); - fillScreen(lilka::colors::Black); serial.log("display ok"); } diff --git a/lib/lilka/src/lilka/dynloader.cpp b/lib/lilka/src/lilka/dynloader.cpp new file mode 100644 index 0000000..5d7f1f8 --- /dev/null +++ b/lib/lilka/src/lilka/dynloader.cpp @@ -0,0 +1,683 @@ +/** + * @file dynloader.cpp + * @brief Dynamic ELF Loader implementation for Lilka (ESP32-S3) + * + * Adapted from espressif/elf_loader v1.3.1 (Apache-2.0) + * https://components.espressif.com/components/espressif/elf_loader + * + * This implementation is specifically tailored for ESP32-S3 with PSRAM, + * using bus-address mirroring to execute code from PSRAM via the + * instruction bus. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "dynloader.h" +#include "serial.h" + +#include +#include +#include +#include +#include +#include + +#include "esp_heap_caps.h" +#include "esp_idf_version.h" +#include "soc/soc.h" + +/* ── ESP32-S3 PSRAM bus-address offset ──────────────────────────────────── */ + +/* + * On ESP32-S3, PSRAM is mapped to both the data bus and instruction bus. + * Data bus: SOC_DROM_LOW (0x3C000000) + * Instruction bus: SOC_IROM_LOW (0x42000000) + * + * To execute code allocated via the data bus, we add this offset to + * remap the address to the instruction bus. + */ +#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ESP32S3) || (LILKA_VERSION == 2) +#define LILKA_TEXT_OFFSET (SOC_IROM_LOW - SOC_DROM_LOW) +#else +#define LILKA_TEXT_OFFSET 0 +#endif + +/* ── Forward declarations ───────────────────────────────────────────────── */ + +extern "C" { + +/* Cache writeback - needed after loading code into PSRAM */ +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +extern void Cache_WriteBack_All(void); +#else +extern void esp_spiram_writeback_cache(void); +#endif +extern void spi_flash_disable_interrupts_caches_and_other_cpu(void); +extern void spi_flash_enable_interrupts_caches_and_other_cpu(void); + +} // extern "C" + +/* ── Global symbol tables ───────────────────────────────────────────────── */ + +static const lilka_dynsym_t *g_sym_tables[LILKA_DYNSYM_MAX_TABLES] = {}; + +int lilka_dynloader_register_symbols(const lilka_dynsym_t *table) { + if (!table) return -EINVAL; + for (int i = 0; i < LILKA_DYNSYM_MAX_TABLES; i++) { + if (g_sym_tables[i] == table) return -EEXIST; + if (g_sym_tables[i] == nullptr) { + g_sym_tables[i] = table; + return 0; + } + } + return -ENOMEM; +} + +int lilka_dynloader_unregister_symbols(const lilka_dynsym_t *table) { + if (!table) return -EINVAL; + for (int i = 0; i < LILKA_DYNSYM_MAX_TABLES; i++) { + if (g_sym_tables[i] == table) { + g_sym_tables[i] = nullptr; + return 0; + } + } + return -EINVAL; +} + +uintptr_t lilka_dynloader_find_symbol(const char *name) { + if (!name) return 0; + for (int i = 0; i < LILKA_DYNSYM_MAX_TABLES; i++) { + if (!g_sym_tables[i]) continue; + const lilka_dynsym_t *sym = g_sym_tables[i]; + while (sym->name) { + if (strcmp(sym->name, name) == 0) { + return (uintptr_t)sym->addr; + } + sym++; + } + } + return 0; +} + +/* ── Memory allocation ──────────────────────────────────────────────────── */ + +static void *elf_malloc(uint32_t n, bool exec) { + (void)exec; + /* On ESP32-S3 with PSRAM, we allocate from SPIRAM for both text and data. + * Text will be remapped to instruction bus via address offset. */ + uint32_t caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT; + return heap_caps_malloc(n, caps); +} + +static void elf_free(void *ptr) { + heap_caps_free(ptr); +} + +/* ── Address remapping ──────────────────────────────────────────────────── */ + +/// Remap data-bus address to instruction-bus address if it falls within .text +static inline uintptr_t remap_text(lilka_elf_t *elf, uintptr_t sym) { + lilka_elf_sec_t *sec = &elf->sec[LILKA_ELF_SEC_TEXT]; + if (sym >= sec->addr && sym < (sec->addr + sec->size)) { + return sym + LILKA_TEXT_OFFSET; + } + return sym; +} + +/* ── Symbol mapping ─────────────────────────────────────────────────────── */ + +/// Map a virtual address from the ELF to the physical address in memory +static uintptr_t elf_map_sym(lilka_elf_t *elf, uintptr_t sym) { + for (int i = 0; i < LILKA_ELF_SECS; i++) { + if (elf->sec[i].size && + sym >= elf->sec[i].v_addr && + sym < (elf->sec[i].v_addr + elf->sec[i].size)) { + return sym - elf->sec[i].v_addr + elf->sec[i].addr; + } + } + return 0; +} + +/* ── Xtensa relocations ────────────────────────────────────────────────── */ + +static int elf_arch_relocate(lilka_elf_t *elf, const lilka_elf32_rela_t *rela, + const lilka_elf32_sym_t *sym, uint32_t addr) { + uint32_t *where = (uint32_t *)elf_map_sym(elf, rela->offset); + if (!where) { + lilka::serial.log("dynloader: reloc where=NULL offset=0x%x", rela->offset); + return -EINVAL; + } + + switch (LILKA_ELF_R_TYPE(rela->info)) { + case R_XTENSA_RELATIVE: + *where = remap_text(elf, elf_map_sym(elf, *where)); + break; + case R_XTENSA_RTLD: + /* Runtime linker marker - nothing to do */ + break; + case R_XTENSA_GLOB_DAT: + case R_XTENSA_JMP_SLOT: + *where = remap_text(elf, addr); + break; + case R_XTENSA_32: + *where = remap_text(elf, addr + rela->addend); + break; + case R_XTENSA_NONE: + break; + default: + lilka::serial.log("dynloader: unsupported reloc type %d", LILKA_ELF_R_TYPE(rela->info)); + return -EINVAL; + } + + return 0; +} + +/* ── Cache flush ────────────────────────────────────────────────────────── */ + +static void elf_flush_cache(void) { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + Cache_WriteBack_All(); +#else + esp_spiram_writeback_cache(); +#endif + spi_flash_disable_interrupts_caches_and_other_cpu(); + spi_flash_enable_interrupts_caches_and_other_cpu(); +} + +/* ── Section loading (bus-address mirror mode for ESP32-S3) ─────────────── */ + +static int elf_load_sections(lilka_elf_t *elf, const uint8_t *pbuf) { + const lilka_elf32_hdr_t *ehdr = (const lilka_elf32_hdr_t *)pbuf; + const lilka_elf32_shdr_t *shdr = + (const lilka_elf32_shdr_t *)(pbuf + ehdr->shoff); + const char *shstrtab = + (const char *)pbuf + shdr[ehdr->shstrndx].offset; + + /* Find relevant sections */ + for (uint32_t i = 0; i < ehdr->shnum; i++) { + const char *name = shstrtab + shdr[i].name; + + if (shdr[i].type == LILKA_SHT_PROGBITS && + (shdr[i].flags & LILKA_SHF_ALLOC)) { + + if ((shdr[i].flags & LILKA_SHF_EXECINSTR) && + strcmp(".text", name) == 0) { + elf->sec[LILKA_ELF_SEC_TEXT].v_addr = shdr[i].addr; + elf->sec[LILKA_ELF_SEC_TEXT].size = + LILKA_ELF_ALIGN(shdr[i].size, 4); + elf->sec[LILKA_ELF_SEC_TEXT].offset = shdr[i].offset; + } else if ((shdr[i].flags & LILKA_SHF_WRITE) && + strcmp(".data", name) == 0) { + elf->sec[LILKA_ELF_SEC_DATA].v_addr = shdr[i].addr; + elf->sec[LILKA_ELF_SEC_DATA].size = shdr[i].size; + elf->sec[LILKA_ELF_SEC_DATA].offset = shdr[i].offset; + } else if (strcmp(".rodata", name) == 0) { + elf->sec[LILKA_ELF_SEC_RODATA].v_addr = shdr[i].addr; + elf->sec[LILKA_ELF_SEC_RODATA].size = shdr[i].size; + elf->sec[LILKA_ELF_SEC_RODATA].offset = shdr[i].offset; + } else if (strcmp(".data.rel.ro", name) == 0) { + elf->sec[LILKA_ELF_SEC_DRLRO].v_addr = shdr[i].addr; + elf->sec[LILKA_ELF_SEC_DRLRO].size = shdr[i].size; + elf->sec[LILKA_ELF_SEC_DRLRO].offset = shdr[i].offset; + } + } else if (shdr[i].type == LILKA_SHT_NOBITS && + (shdr[i].flags & (LILKA_SHF_ALLOC | LILKA_SHF_WRITE)) == + (LILKA_SHF_ALLOC | LILKA_SHF_WRITE) && + strcmp(".bss", name) == 0) { + elf->sec[LILKA_ELF_SEC_BSS].v_addr = shdr[i].addr; + elf->sec[LILKA_ELF_SEC_BSS].size = shdr[i].size; + elf->sec[LILKA_ELF_SEC_BSS].offset = shdr[i].offset; + } + } + + /* Validate: we need at least a .text section */ + if (!elf->sec[LILKA_ELF_SEC_TEXT].size) { + lilka::serial.log("dynloader: no .text section found"); + return -EINVAL; + } + + /* Allocate text section in PSRAM */ + elf->ptext = + (unsigned char *)elf_malloc(elf->sec[LILKA_ELF_SEC_TEXT].size, true); + if (!elf->ptext) { + lilka::serial.log("dynloader: failed to alloc %u bytes for .text", + elf->sec[LILKA_ELF_SEC_TEXT].size); + return -ENOMEM; + } + + /* Allocate data sections */ + uint32_t data_size = elf->sec[LILKA_ELF_SEC_DATA].size + + elf->sec[LILKA_ELF_SEC_RODATA].size + + elf->sec[LILKA_ELF_SEC_BSS].size + + elf->sec[LILKA_ELF_SEC_DRLRO].size; + if (data_size) { + elf->pdata = (unsigned char *)elf_malloc(data_size, false); + if (!elf->pdata) { + lilka::serial.log("dynloader: failed to alloc %u bytes for data", + data_size); + elf_free(elf->ptext); + elf->ptext = nullptr; + return -ENOMEM; + } + } + + /* Copy .text into executable memory */ + elf->sec[LILKA_ELF_SEC_TEXT].addr = (uintptr_t)elf->ptext; + memcpy(elf->ptext, pbuf + elf->sec[LILKA_ELF_SEC_TEXT].offset, + elf->sec[LILKA_ELF_SEC_TEXT].size); + + /* Copy data sections */ + if (data_size) { + uint8_t *p = elf->pdata; + + if (elf->sec[LILKA_ELF_SEC_DATA].size) { + elf->sec[LILKA_ELF_SEC_DATA].addr = (uintptr_t)p; + memcpy(p, pbuf + elf->sec[LILKA_ELF_SEC_DATA].offset, + elf->sec[LILKA_ELF_SEC_DATA].size); + p += elf->sec[LILKA_ELF_SEC_DATA].size; + } + if (elf->sec[LILKA_ELF_SEC_RODATA].size) { + elf->sec[LILKA_ELF_SEC_RODATA].addr = (uintptr_t)p; + memcpy(p, pbuf + elf->sec[LILKA_ELF_SEC_RODATA].offset, + elf->sec[LILKA_ELF_SEC_RODATA].size); + p += elf->sec[LILKA_ELF_SEC_RODATA].size; + } + if (elf->sec[LILKA_ELF_SEC_DRLRO].size) { + elf->sec[LILKA_ELF_SEC_DRLRO].addr = (uintptr_t)p; + memcpy(p, pbuf + elf->sec[LILKA_ELF_SEC_DRLRO].offset, + elf->sec[LILKA_ELF_SEC_DRLRO].size); + p += elf->sec[LILKA_ELF_SEC_DRLRO].size; + } + if (elf->sec[LILKA_ELF_SEC_BSS].size) { + elf->sec[LILKA_ELF_SEC_BSS].addr = (uintptr_t)p; + memset(p, 0, elf->sec[LILKA_ELF_SEC_BSS].size); + } + } + + /* Set entry point (remap to instruction bus for execution) */ + uintptr_t entry_addr = ehdr->entry + + elf->sec[LILKA_ELF_SEC_TEXT].addr - + elf->sec[LILKA_ELF_SEC_TEXT].v_addr; + elf->entry = (int (*)(int, char *[]))remap_text(elf, entry_addr); + + return 0; +} + +/* ── Core API implementation ────────────────────────────────────────────── */ + +int lilka_elf_init(lilka_elf_t *elf) { + if (!elf) return -EINVAL; + memset(elf, 0, sizeof(lilka_elf_t)); + return 0; +} + +int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size) { + if (!elf || !pbuf || size < sizeof(lilka_elf32_hdr_t)) return -EINVAL; + + const lilka_elf32_hdr_t *ehdr = (const lilka_elf32_hdr_t *)pbuf; + + /* Validate ELF magic */ + if (ehdr->ident[0] != 0x7f || ehdr->ident[1] != 'E' || + ehdr->ident[2] != 'L' || ehdr->ident[3] != 'F') { + lilka::serial.log("dynloader: invalid ELF magic"); + return -EINVAL; + } + + /* Validate 32-bit, little-endian */ + if (ehdr->ident[4] != 1 || ehdr->ident[5] != 1) { + lilka::serial.log("dynloader: expected 32-bit little-endian ELF"); + return -EINVAL; + } + + /* Load sections into memory */ + int ret = elf_load_sections(elf, pbuf); + if (ret) return ret; + + lilka::serial.log("dynloader: entry=%p", elf->entry); + + /* Process relocations */ + const lilka_elf32_shdr_t *shdr = + (const lilka_elf32_shdr_t *)(pbuf + ehdr->shoff); + const char *shstrtab = + (const char *)pbuf + shdr[ehdr->shstrndx].offset; + + for (uint32_t i = 0; i < ehdr->shnum; i++) { + if (shdr[i].type == LILKA_SHT_RELA) { + uint32_t nr_reloc = + shdr[i].size / sizeof(lilka_elf32_rela_t); + const lilka_elf32_rela_t *rela = + (const lilka_elf32_rela_t *)(pbuf + shdr[i].offset); + const lilka_elf32_sym_t *symtab = + (const lilka_elf32_sym_t *)(pbuf + shdr[shdr[i].link].offset); + const char *strtab = + (const char *)(pbuf + shdr[shdr[shdr[i].link].link].offset); + + for (uint32_t j = 0; j < nr_reloc; j++) { + lilka_elf32_rela_t rela_buf; + memcpy(&rela_buf, &rela[j], sizeof(lilka_elf32_rela_t)); + + const lilka_elf32_sym_t *sym = + &symtab[LILKA_ELF_R_SYM(rela_buf.info)]; + int type = LILKA_ELF_R_TYPE(rela_buf.info); + uintptr_t addr = 0; + + if (type == LILKA_STT_COMMON || type == LILKA_STT_OBJECT || + type == LILKA_STT_SECTION) { + const char *sym_name = strtab + sym->name; + if (sym_name[0]) { + addr = lilka_dynloader_find_symbol(sym_name); + /* If not found externally, check local .data */ + if (!addr && sym->shndx != LILKA_SHN_UNDEF) { + addr = (uintptr_t)(elf->sec[LILKA_ELF_SEC_DATA].addr + + sym->value - + elf->sec[LILKA_ELF_SEC_DATA].v_addr); + } + if (!addr) { + lilka::serial.log("dynloader: unresolved symbol: %s", sym_name); + return -ENOSYS; + } + } + } else if (type == LILKA_STT_FILE) { + const char *func_name = strtab + sym->name; + if (sym->value) { + addr = elf_map_sym(elf, sym->value); + } else { + addr = lilka_dynloader_find_symbol(func_name); + } + /* Check local text */ + if (!addr && sym->shndx != LILKA_SHN_UNDEF) { + addr = (uintptr_t)(elf->sec[LILKA_ELF_SEC_TEXT].addr + + sym->value - + elf->sec[LILKA_ELF_SEC_TEXT].v_addr); + } + if (!addr) { + lilka::serial.log("dynloader: unresolved function: %s", func_name); + return -ENOSYS; + } + } + + ret = elf_arch_relocate(elf, &rela_buf, sym, addr); + if (ret) return ret; + } + } + /* Parse .dynsym for exported symbols */ + else if (shdr[i].type == LILKA_SHT_DYNSYM) { + const lilka_elf32_sym_t *dsymtab = + (const lilka_elf32_sym_t *)(pbuf + shdr[i].offset); + const char *dstrtab = + (const char *)(pbuf + shdr[shdr[i].link].offset); + uint32_t nsyms = shdr[i].size / sizeof(lilka_elf32_sym_t); + + /* Count global functions */ + uint16_t count = 0; + for (uint32_t j = 0; j < nsyms; j++) { + if (LILKA_ELF_ST_BIND(dsymtab[j].info) == LILKA_STB_GLOBAL && + LILKA_ELF_ST_TYPE(dsymtab[j].info) == LILKA_STT_FUNC) { + count++; + } + } + + if (count) { + elf->symtab = (lilka_dynsym_t *)elf_malloc( + count * sizeof(lilka_dynsym_t), false); + if (!elf->symtab) return -ENOMEM; + memset(elf->symtab, 0, count * sizeof(lilka_dynsym_t)); + + uint16_t idx = 0; + for (uint32_t j = 0; j < nsyms && idx < count; j++) { + if (LILKA_ELF_ST_BIND(dsymtab[j].info) == LILKA_STB_GLOBAL && + LILKA_ELF_ST_TYPE(dsymtab[j].info) == LILKA_STT_FUNC) { + /* Resolve address within loaded sections */ + elf->symtab[idx].addr = (void *)( + elf->ptext + dsymtab[j].value - + elf->sec[LILKA_ELF_SEC_TEXT].v_addr); + /* Copy name */ + size_t len = + strlen(dstrtab + dsymtab[j].name) + 1; + char *nm = (char *)elf_malloc(len, false); + if (!nm) { + elf->sym_count = idx; + return -ENOMEM; + } + memcpy(nm, dstrtab + dsymtab[j].name, len); + elf->symtab[idx].name = nm; + lilka::serial.log("dynloader: export[%d] %s", idx, nm); + idx++; + } + } + elf->sym_count = count; + } + } + } + + /* Flush PSRAM cache to ensure code is visible to instruction bus */ + elf_flush_cache(); + + return 0; +} + +int lilka_elf_run(lilka_elf_t *elf, int argc, char *argv[]) { + if (!elf || !elf->entry) return -EINVAL; + return elf->entry(argc, argv); +} + +void lilka_elf_deinit(lilka_elf_t *elf) { + if (!elf) return; + + if (elf->ptext) { + elf_free(elf->ptext); + elf->ptext = nullptr; + } + if (elf->pdata) { + elf_free(elf->pdata); + elf->pdata = nullptr; + } + if (elf->sym_count && elf->symtab) { + for (int i = 0; i < elf->sym_count; i++) { + if (elf->symtab[i].name) { + elf_free((void *)elf->symtab[i].name); + } + } + elf_free(elf->symtab); + elf->symtab = nullptr; + } + elf->sym_count = 0; + memset(elf, 0, sizeof(lilka_elf_t)); +} + +/* ── High-level helper ──────────────────────────────────────────────────── */ + +int lilka_dynloader_run(const char *path, int argc, char *argv[]) { + if (!path) return -EINVAL; + + /* Open and read the .so file */ + FILE *f = fopen(path, "rb"); + if (!f) { + lilka::serial.log("dynloader: failed to open %s", path); + return -EIO; + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize <= 0) { + fclose(f); + return -EIO; + } + + uint8_t *buf = (uint8_t *)elf_malloc(fsize, false); + if (!buf) { + fclose(f); + lilka::serial.log("dynloader: failed to alloc %ld bytes for file", fsize); + return -ENOMEM; + } + + size_t rd = fread(buf, 1, fsize, f); + fclose(f); + + if ((long)rd != fsize) { + elf_free(buf); + return -EIO; + } + + /* Load, relocate, run */ + lilka_elf_t elf; + lilka_elf_init(&elf); + + int ret = lilka_elf_relocate(&elf, buf, fsize); + if (ret == 0) { + ret = lilka_elf_run(&elf, argc, argv); + } + + lilka_elf_deinit(&elf); + elf_free(buf); + return ret; +} + +/* ── Default libc symbol table ──────────────────────────────────────────── */ + +/* Standard C functions exported for use by dynamically loaded apps */ +extern "C" { +extern int __ltdf2(double, double); +extern unsigned int __fixunsdfsi(double); +extern int __gtdf2(double, double); +extern double __floatunsidf(unsigned int); +extern double __divdf3(double, double); +} + +static const lilka_dynsym_t g_libc_symbols[] = { + /* string.h */ + LILKA_DYNSYM_EXPORT(memset), + LILKA_DYNSYM_EXPORT(memcpy), + LILKA_DYNSYM_EXPORT(memmove), + LILKA_DYNSYM_EXPORT(memcmp), + LILKA_DYNSYM_EXPORT(strlen), + LILKA_DYNSYM_EXPORT(strcmp), + LILKA_DYNSYM_EXPORT(strncmp), + LILKA_DYNSYM_EXPORT(strcpy), + LILKA_DYNSYM_EXPORT(strncpy), + LILKA_DYNSYM_EXPORT(strcat), + LILKA_DYNSYM_EXPORT(strchr), + LILKA_DYNSYM_EXPORT(strrchr), + LILKA_DYNSYM_EXPORT(strtol), + LILKA_DYNSYM_EXPORT(strtod), + LILKA_DYNSYM_EXPORT(strerror), + + /* stdio.h */ + LILKA_DYNSYM_EXPORT(printf), + LILKA_DYNSYM_EXPORT(snprintf), + LILKA_DYNSYM_EXPORT(vsnprintf), + LILKA_DYNSYM_EXPORT(puts), + LILKA_DYNSYM_EXPORT(putchar), + + /* stdlib.h */ + LILKA_DYNSYM_EXPORT(malloc), + LILKA_DYNSYM_EXPORT(calloc), + LILKA_DYNSYM_EXPORT(realloc), + LILKA_DYNSYM_EXPORT(free), + LILKA_DYNSYM_EXPORT(rand), + LILKA_DYNSYM_EXPORT(srand), + { "abs", (void*)(int(*)(int))abs }, + + /* math (compiler builtins) */ + LILKA_DYNSYM_EXPORT(__ltdf2), + LILKA_DYNSYM_EXPORT(__fixunsdfsi), + LILKA_DYNSYM_EXPORT(__gtdf2), + LILKA_DYNSYM_EXPORT(__floatunsidf), + LILKA_DYNSYM_EXPORT(__divdf3), + + /* FreeRTOS / system */ + LILKA_DYNSYM_EXPORT(usleep), + LILKA_DYNSYM_EXPORT(vTaskDelay), + + LILKA_DYNSYM_END +}; + +/* Auto-register libc symbols at startup */ +__attribute__((constructor)) +static void _register_libc_symbols() { + lilka_dynloader_register_symbols(g_libc_symbols); +} + +/* ── C++ DynLoader class implementation ─────────────────────────────────── */ + +namespace lilka { + +int DynLoader::load(const char *path) { + if (loaded) unload(); + + errorMsg = nullptr; + + FILE *f = fopen(path, "rb"); + if (!f) { + errorMsg = "Failed to open .so file"; + serial.log("dynloader: %s: %s", errorMsg, path); + return -EIO; + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize <= 0) { + fclose(f); + errorMsg = "Invalid file size"; + return -EIO; + } + + fileData = (uint8_t *)elf_malloc(fsize, false); + if (!fileData) { + fclose(f); + errorMsg = "Not enough memory for file"; + return -ENOMEM; + } + fileSize = fsize; + + size_t rd = fread(fileData, 1, fsize, f); + fclose(f); + + if ((long)rd != fsize) { + elf_free(fileData); + fileData = nullptr; + errorMsg = "Failed to read file"; + return -EIO; + } + + int ret = lilka_elf_relocate(&elf, fileData, fileSize); + if (ret != 0) { + errorMsg = "ELF relocation failed"; + serial.log("dynloader: relocation failed: %d", ret); + lilka_elf_deinit(&elf); + elf_free(fileData); + fileData = nullptr; + return ret; + } + + loaded = true; + serial.log("dynloader: loaded successfully, entry=%p", elf.entry); + return 0; +} + +int DynLoader::execute(int argc, char *argv[]) { + if (!loaded) { + errorMsg = "No .so loaded"; + return -EINVAL; + } + return lilka_elf_run(&elf, argc, argv); +} + +void DynLoader::unload() { + if (!loaded) return; + lilka_elf_deinit(&elf); + if (fileData) { + elf_free(fileData); + fileData = nullptr; + } + fileSize = 0; + loaded = false; + errorMsg = nullptr; + lilka_elf_init(&elf); +} + +} // namespace lilka diff --git a/lib/lilka/src/lilka/dynloader.h b/lib/lilka/src/lilka/dynloader.h new file mode 100644 index 0000000..d7dfad0 --- /dev/null +++ b/lib/lilka/src/lilka/dynloader.h @@ -0,0 +1,304 @@ +/** + * @file dynloader.h + * @brief Dynamic ELF Loader for Lilka (ESP32-S3) + * + * Loads .so (ELF shared object) files from the SD card into PSRAM + * and executes them as dynamically linked apps within KeiraOS. + * + * Based on espressif/elf_loader (Apache-2.0) + * https://components.espressif.com/components/espressif/elf_loader + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef LILKA_DYNLOADER_H +#define LILKA_DYNLOADER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ── ELF types (32-bit Xtensa) ──────────────────────────────────────────── */ + +#define EI_NIDENT 16 + +typedef unsigned int Elf32_Addr; +typedef unsigned int Elf32_Off; +typedef unsigned int Elf32_Word; +typedef unsigned short Elf32_Half; +typedef int Elf32_Sword; + +typedef struct { + unsigned char ident[EI_NIDENT]; + Elf32_Half type; + Elf32_Half machine; + Elf32_Word version; + Elf32_Addr entry; + Elf32_Off phoff; + Elf32_Off shoff; + Elf32_Word flags; + Elf32_Half ehsize; + Elf32_Half phentsize; + Elf32_Half phnum; + Elf32_Half shentsize; + Elf32_Half shnum; + Elf32_Half shstrndx; +} lilka_elf32_hdr_t; + +typedef struct { + Elf32_Word name; + Elf32_Word type; + Elf32_Word flags; + Elf32_Addr addr; + Elf32_Off offset; + Elf32_Word size; + Elf32_Word link; + Elf32_Word info; + Elf32_Word addralign; + Elf32_Word entsize; +} lilka_elf32_shdr_t; + +typedef struct { + Elf32_Word name; + Elf32_Addr value; + Elf32_Word size; + unsigned char info; + unsigned char other; + Elf32_Half shndx; +} lilka_elf32_sym_t; + +typedef struct { + Elf32_Addr offset; + Elf32_Word info; + Elf32_Sword addend; +} lilka_elf32_rela_t; + +/* ── Section indices ─────────────────────────────────────────────────────── */ + +#define LILKA_ELF_SEC_TEXT 0 +#define LILKA_ELF_SEC_BSS 1 +#define LILKA_ELF_SEC_DATA 2 +#define LILKA_ELF_SEC_RODATA 3 +#define LILKA_ELF_SEC_DRLRO 4 +#define LILKA_ELF_SECS 5 + +/* ── ELF constants ───────────────────────────────────────────────────────── */ + +/* Section types */ +#define LILKA_SHT_NULL 0 +#define LILKA_SHT_PROGBITS 1 +#define LILKA_SHT_SYMTAB 2 +#define LILKA_SHT_STRTAB 3 +#define LILKA_SHT_RELA 4 +#define LILKA_SHT_NOBITS 8 +#define LILKA_SHT_DYNSYM 11 + +/* Section flags */ +#define LILKA_SHF_WRITE 1 +#define LILKA_SHF_ALLOC 2 +#define LILKA_SHF_EXECINSTR 4 + +/* Symbol binding / type */ +#define LILKA_STB_GLOBAL 1 +#define LILKA_STT_NOTYPE 0 +#define LILKA_STT_OBJECT 1 +#define LILKA_STT_FUNC 2 +#define LILKA_STT_SECTION 3 +#define LILKA_STT_FILE 4 +#define LILKA_STT_COMMON 5 + +#define LILKA_SHN_UNDEF 0 + +#define LILKA_ELF_ST_BIND(i) ((i) >> 4) +#define LILKA_ELF_ST_TYPE(i) ((i) & 0xf) +#define LILKA_ELF_R_SYM(i) ((i) >> 8) +#define LILKA_ELF_R_TYPE(i) ((unsigned char)(i)) + +/* Xtensa relocation types */ +#define R_XTENSA_NONE 0 +#define R_XTENSA_32 1 +#define R_XTENSA_RTLD 2 +#define R_XTENSA_GLOB_DAT 3 +#define R_XTENSA_JMP_SLOT 4 +#define R_XTENSA_RELATIVE 5 + +/* Alignment macro */ +#define LILKA_ELF_ALIGN(a, s) (((a) + ((s) - 1)) & ~((s) - 1)) + +/* ── ELF section descriptor ─────────────────────────────────────────────── */ + +typedef struct { + uintptr_t v_addr; /* virtual address in the ELF */ + uint32_t offset; /* offset in the ELF file */ + uintptr_t addr; /* physical address in memory */ + uint32_t size; /* section size */ +} lilka_elf_sec_t; + +/* ── Symbol export entry ─────────────────────────────────────────────────── */ + +typedef struct { + const char *name; + void *addr; +} lilka_dynsym_t; + +/* Helper macros */ +#define LILKA_DYNSYM_EXPORT(sym) { #sym, (void*)&sym } +#define LILKA_DYNSYM_END { NULL, NULL } + +/* ── DynLoader state ─────────────────────────────────────────────────────── */ + +typedef struct { + /* Memory pointers */ + unsigned char *ptext; /* text (code) buffer in PSRAM (data-bus addr) */ + unsigned char *pdata; /* data+rodata+bss buffer */ + + /* Section descriptors */ + lilka_elf_sec_t sec[LILKA_ELF_SECS]; + + /* Entry point (instruction-bus address) */ + int (*entry)(int argc, char *argv[]); + + /* Exported symbols from the loaded .so */ + uint16_t sym_count; + lilka_dynsym_t *symtab; +} lilka_elf_t; + +/* ── Public API ──────────────────────────────────────────────────────────── */ + +/** + * @brief Maximum number of registered symbol tables. + */ +#define LILKA_DYNSYM_MAX_TABLES 32 + +/** + * @brief Register a symbol table array for resolution during ELF loading. + * + * @param table Array of lilka_dynsym_t terminated with LILKA_DYNSYM_END. + * @return 0 on success, negative errno on failure. + */ +int lilka_dynloader_register_symbols(const lilka_dynsym_t *table); + +/** + * @brief Unregister a previously registered symbol table. + * + * @param table Pointer passed to lilka_dynloader_register_symbols(). + * @return 0 on success, negative errno on failure. + */ +int lilka_dynloader_unregister_symbols(const lilka_dynsym_t *table); + +/** + * @brief Resolve a symbol name in all registered tables. + * + * @param name Symbol name to look up. + * @return Address of the symbol, or 0 if not found. + */ +uintptr_t lilka_dynloader_find_symbol(const char *name); + +/** + * @brief Initialize an ELF loader context. + * + * @param elf Pointer to lilka_elf_t structure. + * @return 0 on success, negative errno on failure. + */ +int lilka_elf_init(lilka_elf_t *elf); + +/** + * @brief Load and relocate an ELF from a memory buffer. + * + * @param elf Initialized lilka_elf_t structure. + * @param pbuf Pointer to the raw ELF file data. + * @param size Size of the ELF data. + * @return 0 on success, negative errno on failure. + */ +int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size); + +/** + * @brief Execute the loaded ELF's entry point. + * + * @param elf Relocated ELF context. + * @param argc Argument count. + * @param argv Argument vector. + * @return Return value from the ELF's main/app_main. + */ +int lilka_elf_run(lilka_elf_t *elf, int argc, char *argv[]); + +/** + * @brief Deinitialize the ELF loader and free all resources. + * + * @param elf ELF context to clean up. + */ +void lilka_elf_deinit(lilka_elf_t *elf); + +/** + * @brief High-level: load a .so file from a filesystem path, relocate, + * execute with given arguments, and clean up. + * + * @param path Full filesystem path (e.g., "/sd/apps/demo.so"). + * @param argc Argument count for the app. + * @param argv Argument values for the app. + * @return 0 on success, negative errno on failure. + */ +int lilka_dynloader_run(const char *path, int argc, char *argv[]); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* ── C++ wrapper ─────────────────────────────────────────────────────────── */ + +#ifdef __cplusplus +namespace lilka { + +/// High-level C++ wrapper around the ELF dynamic loader. +class DynLoader { +public: + DynLoader() : loaded(false), errorMsg(nullptr) { + lilka_elf_init(&elf); + } + + ~DynLoader() { + unload(); + } + + /// Register a symbol table for resolution. + static int registerSymbols(const lilka_dynsym_t *table) { + return lilka_dynloader_register_symbols(table); + } + + /// Unregister a symbol table. + static int unregisterSymbols(const lilka_dynsym_t *table) { + return lilka_dynloader_unregister_symbols(table); + } + + /// Load a .so file from the given filesystem path. + /// @return 0 on success, negative on error. + int load(const char *path); + + /// Execute the loaded app's entry point. + /// @return Return value from the app. + int execute(int argc, char *argv[]); + + /// Unload the app and free all resources. + void unload(); + + /// Get the last error message (or nullptr). + const char *getError() const { return errorMsg; } + + /// Check if a .so is currently loaded. + bool isLoaded() const { return loaded; } + +private: + lilka_elf_t elf; + uint8_t *fileData = nullptr; + size_t fileSize = 0; + bool loaded; + const char *errorMsg; +}; + +} // namespace lilka +#endif + +#endif // LILKA_DYNLOADER_H From bd66f52b8e412c0614ff20a956ab9be206ee63d3 Mon Sep 17 00:00:00 2001 From: Yevhen Boichuk Date: Mon, 9 Mar 2026 19:50:01 +0200 Subject: [PATCH 2/4] fix: fix elfloader mapping errors --- lib/lilka/src/lilka/dynloader.cpp | 345 ++++++++++++++++++------------ 1 file changed, 207 insertions(+), 138 deletions(-) diff --git a/lib/lilka/src/lilka/dynloader.cpp b/lib/lilka/src/lilka/dynloader.cpp index 5d7f1f8..bc6256e 100644 --- a/lib/lilka/src/lilka/dynloader.cpp +++ b/lib/lilka/src/lilka/dynloader.cpp @@ -37,9 +37,9 @@ * remap the address to the instruction bus. */ #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ESP32S3) || (LILKA_VERSION == 2) -#define LILKA_TEXT_OFFSET (SOC_IROM_LOW - SOC_DROM_LOW) +# define LILKA_TEXT_OFFSET (SOC_IROM_LOW - SOC_DROM_LOW) #else -#define LILKA_TEXT_OFFSET 0 +# define LILKA_TEXT_OFFSET 0 #endif /* ── Forward declarations ───────────────────────────────────────────────── */ @@ -59,9 +59,9 @@ extern void spi_flash_enable_interrupts_caches_and_other_cpu(void); /* ── Global symbol tables ───────────────────────────────────────────────── */ -static const lilka_dynsym_t *g_sym_tables[LILKA_DYNSYM_MAX_TABLES] = {}; +static const lilka_dynsym_t* g_sym_tables[LILKA_DYNSYM_MAX_TABLES] = {}; -int lilka_dynloader_register_symbols(const lilka_dynsym_t *table) { +int lilka_dynloader_register_symbols(const lilka_dynsym_t* table) { if (!table) return -EINVAL; for (int i = 0; i < LILKA_DYNSYM_MAX_TABLES; i++) { if (g_sym_tables[i] == table) return -EEXIST; @@ -73,7 +73,7 @@ int lilka_dynloader_register_symbols(const lilka_dynsym_t *table) { return -ENOMEM; } -int lilka_dynloader_unregister_symbols(const lilka_dynsym_t *table) { +int lilka_dynloader_unregister_symbols(const lilka_dynsym_t* table) { if (!table) return -EINVAL; for (int i = 0; i < LILKA_DYNSYM_MAX_TABLES; i++) { if (g_sym_tables[i] == table) { @@ -84,11 +84,11 @@ int lilka_dynloader_unregister_symbols(const lilka_dynsym_t *table) { return -EINVAL; } -uintptr_t lilka_dynloader_find_symbol(const char *name) { +uintptr_t lilka_dynloader_find_symbol(const char* name) { if (!name) return 0; for (int i = 0; i < LILKA_DYNSYM_MAX_TABLES; i++) { if (!g_sym_tables[i]) continue; - const lilka_dynsym_t *sym = g_sym_tables[i]; + const lilka_dynsym_t* sym = g_sym_tables[i]; while (sym->name) { if (strcmp(sym->name, name) == 0) { return (uintptr_t)sym->addr; @@ -101,25 +101,100 @@ uintptr_t lilka_dynloader_find_symbol(const char *name) { /* ── Memory allocation ──────────────────────────────────────────────────── */ -static void *elf_malloc(uint32_t n, bool exec) { - (void)exec; - /* On ESP32-S3 with PSRAM, we allocate from SPIRAM for both text and data. - * Text will be remapped to instruction bus via address offset. */ - uint32_t caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT; - return heap_caps_malloc(n, caps); +static void* elf_malloc(uint32_t n, bool exec) { + if (exec) { + /* + * Allocate executable code in D/IRAM (internal RAM). + * D/IRAM supports both instruction fetch AND data load/store + * at the same address, which is required for Xtensa L32R + * instructions that load constants from literal pools + * embedded in .text. + * + * PSRAM I-bus (0x42000000+) only supports instruction fetch, + * so L32R from PSRAM causes LoadStoreError. + */ + void* p = heap_caps_malloc(n, MALLOC_CAP_EXEC); + if (p) return p; + lilka::serial.log("dynloader [WARN] IRAM alloc failed (%u bytes), falling back to PSRAM", n); + } + /* Data sections go to PSRAM */ + return heap_caps_malloc(n, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); } -static void elf_free(void *ptr) { +static void elf_free(void* ptr) { heap_caps_free(ptr); } +/* + * IRAM-safe memory operations. + * + * On ESP32-S3, the IRAM instruction bus (0x4037xxxx) only supports + * 32-bit word-aligned access. Standard memcpy/memset use byte + * load/store internally and will cause LoadStoreError on IRAM. + * + * These helpers read bytes from src (which may be unaligned) and + * write 32-bit words to the IRAM destination. + */ + +/// Word-aligned copy to IRAM. dst must be 4-byte aligned. src may be unaligned. +static void iram_cpy(void *dst, const void *src, size_t n) { + volatile uint32_t *d = (volatile uint32_t *)dst; + const uint8_t *s = (const uint8_t *)src; + size_t words = n / 4; + size_t tail = n % 4; + + for (size_t i = 0; i < words; i++) { + uint32_t w = (uint32_t)s[0] + | ((uint32_t)s[1] << 8) + | ((uint32_t)s[2] << 16) + | ((uint32_t)s[3] << 24); + *d++ = w; + s += 4; + } + if (tail) { + uint32_t w = 0; + for (size_t i = 0; i < tail; i++) + w |= ((uint32_t)s[i]) << (i * 8); + *d = w; + } +} + +/// Word-aligned fill for IRAM. ptr must be 4-byte aligned. +static void iram_set(void *ptr, uint8_t val, size_t n) { + volatile uint32_t *d = (volatile uint32_t *)ptr; + uint32_t fill = (uint32_t)val | ((uint32_t)val << 8) + | ((uint32_t)val << 16) | ((uint32_t)val << 24); + size_t words = (n + 3) / 4; + for (size_t i = 0; i < words; i++) + d[i] = fill; +} + +/// Check if address is in IRAM (instruction-bus only, no byte access) +static inline bool is_iram_addr(uintptr_t addr) { + return addr >= SOC_IRAM_LOW && addr < SOC_IRAM_HIGH; +} + /* ── Address remapping ──────────────────────────────────────────────────── */ -/// Remap data-bus address to instruction-bus address if it falls within .text -static inline uintptr_t remap_text(lilka_elf_t *elf, uintptr_t sym) { - lilka_elf_sec_t *sec = &elf->sec[LILKA_ELF_SEC_TEXT]; +/// Remap data-bus address to instruction-bus address if it falls within .text. +/// Only applies when .text is in PSRAM data-bus range (< 0x40000000). +/// IRAM addresses (>= 0x40000000) are already on the instruction bus — no remapping needed. +static inline uintptr_t remap_text(lilka_elf_t* elf, uintptr_t sym) { + lilka_elf_sec_t* sec = &elf->sec[LILKA_ELF_SEC_TEXT]; if (sym >= sec->addr && sym < (sec->addr + sec->size)) { - return sym + LILKA_TEXT_OFFSET; +#if LILKA_TEXT_OFFSET != 0 + /* + * ESP32-S3 address map: + * PSRAM data bus: 0x3C000000 - 0x3DFFFFFF (needs +TEXT_OFFSET to reach I-bus) + * IRAM I-bus: 0x40370000 - 0x403E0000 (already executable, no remap) + * + * Only apply the PSRAM text offset if .text is below 0x40000000 + * (i.e. in the data-bus address space). + */ + if (sec->addr < 0x40000000) { + return sym + LILKA_TEXT_OFFSET; + } +#endif } return sym; } @@ -127,11 +202,9 @@ static inline uintptr_t remap_text(lilka_elf_t *elf, uintptr_t sym) { /* ── Symbol mapping ─────────────────────────────────────────────────────── */ /// Map a virtual address from the ELF to the physical address in memory -static uintptr_t elf_map_sym(lilka_elf_t *elf, uintptr_t sym) { +static uintptr_t elf_map_sym(lilka_elf_t* elf, uintptr_t sym) { for (int i = 0; i < LILKA_ELF_SECS; i++) { - if (elf->sec[i].size && - sym >= elf->sec[i].v_addr && - sym < (elf->sec[i].v_addr + elf->sec[i].size)) { + if (elf->sec[i].size && sym >= elf->sec[i].v_addr && sym < (elf->sec[i].v_addr + elf->sec[i].size)) { return sym - elf->sec[i].v_addr + elf->sec[i].addr; } } @@ -140,9 +213,10 @@ static uintptr_t elf_map_sym(lilka_elf_t *elf, uintptr_t sym) { /* ── Xtensa relocations ────────────────────────────────────────────────── */ -static int elf_arch_relocate(lilka_elf_t *elf, const lilka_elf32_rela_t *rela, - const lilka_elf32_sym_t *sym, uint32_t addr) { - uint32_t *where = (uint32_t *)elf_map_sym(elf, rela->offset); +static int elf_arch_relocate( + lilka_elf_t* elf, const lilka_elf32_rela_t* rela, const lilka_elf32_sym_t* sym, uint32_t addr +) { + uint32_t* where = (uint32_t*)elf_map_sym(elf, rela->offset); if (!where) { lilka::serial.log("dynloader: reloc where=NULL offset=0x%x", rela->offset); return -EINVAL; @@ -152,21 +226,21 @@ static int elf_arch_relocate(lilka_elf_t *elf, const lilka_elf32_rela_t *rela, case R_XTENSA_RELATIVE: *where = remap_text(elf, elf_map_sym(elf, *where)); break; - case R_XTENSA_RTLD: - /* Runtime linker marker - nothing to do */ - break; - case R_XTENSA_GLOB_DAT: - case R_XTENSA_JMP_SLOT: - *where = remap_text(elf, addr); - break; - case R_XTENSA_32: - *where = remap_text(elf, addr + rela->addend); - break; - case R_XTENSA_NONE: - break; - default: - lilka::serial.log("dynloader: unsupported reloc type %d", LILKA_ELF_R_TYPE(rela->info)); - return -EINVAL; + case R_XTENSA_RTLD: + /* Runtime linker marker - nothing to do */ + break; + case R_XTENSA_GLOB_DAT: + case R_XTENSA_JMP_SLOT: + *where = remap_text(elf, addr); + break; + case R_XTENSA_32: + *where = remap_text(elf, addr + rela->addend); + break; + case R_XTENSA_NONE: + break; + default: + lilka::serial.log("dynloader: unsupported reloc type %d", LILKA_ELF_R_TYPE(rela->info)); + return -EINVAL; } return 0; @@ -180,34 +254,43 @@ static void elf_flush_cache(void) { #else esp_spiram_writeback_cache(); #endif - spi_flash_disable_interrupts_caches_and_other_cpu(); - spi_flash_enable_interrupts_caches_and_other_cpu(); + /* + * NOTE: spi_flash_disable/enable_interrupts_caches_and_other_cpu() + * was removed here. Those calls are intended for SPI flash write + * protection and disable caches + interrupts on BOTH CPUs, which + * causes a TG1WDT_SYS_RST (watchdog reset on the other core). + * + * Cache_WriteBack_All() alone is sufficient: it flushes dirty + * data-cache lines to PSRAM so the instruction bus can fetch + * the freshly loaded code. Since the .text region was just + * allocated, there are no stale instruction-cache entries. + */ } /* ── Section loading (bus-address mirror mode for ESP32-S3) ─────────────── */ -static int elf_load_sections(lilka_elf_t *elf, const uint8_t *pbuf) { - const lilka_elf32_hdr_t *ehdr = (const lilka_elf32_hdr_t *)pbuf; - const lilka_elf32_shdr_t *shdr = - (const lilka_elf32_shdr_t *)(pbuf + ehdr->shoff); - const char *shstrtab = - (const char *)pbuf + shdr[ehdr->shstrndx].offset; +static int elf_load_sections(lilka_elf_t* elf, const uint8_t* pbuf) { + const lilka_elf32_hdr_t* ehdr = (const lilka_elf32_hdr_t*)pbuf; + const lilka_elf32_shdr_t* shdr = (const lilka_elf32_shdr_t*)(pbuf + ehdr->shoff); + const char* shstrtab = (const char*)pbuf + shdr[ehdr->shstrndx].offset; /* Find relevant sections */ for (uint32_t i = 0; i < ehdr->shnum; i++) { - const char *name = shstrtab + shdr[i].name; - - if (shdr[i].type == LILKA_SHT_PROGBITS && - (shdr[i].flags & LILKA_SHF_ALLOC)) { + const char* name = shstrtab + shdr[i].name; - if ((shdr[i].flags & LILKA_SHF_EXECINSTR) && - strcmp(".text", name) == 0) { + if (shdr[i].type == LILKA_SHT_PROGBITS && (shdr[i].flags & LILKA_SHF_ALLOC)) { + if ((shdr[i].flags & LILKA_SHF_EXECINSTR) && strcmp(".text", name) == 0) { elf->sec[LILKA_ELF_SEC_TEXT].v_addr = shdr[i].addr; - elf->sec[LILKA_ELF_SEC_TEXT].size = - LILKA_ELF_ALIGN(shdr[i].size, 4); + /* + * Use actual section size for VMA range checking (not aligned). + * Aligning to 4 bytes can extend the .text VMA range to overlap + * with adjacent sections (e.g. .rodata), causing elf_map_sym() + * to resolve .rodata addresses through .text — wrong memory type. + * Memory allocation is aligned separately below. + */ + elf->sec[LILKA_ELF_SEC_TEXT].size = shdr[i].size; elf->sec[LILKA_ELF_SEC_TEXT].offset = shdr[i].offset; - } else if ((shdr[i].flags & LILKA_SHF_WRITE) && - strcmp(".data", name) == 0) { + } else if ((shdr[i].flags & LILKA_SHF_WRITE) && strcmp(".data", name) == 0) { elf->sec[LILKA_ELF_SEC_DATA].v_addr = shdr[i].addr; elf->sec[LILKA_ELF_SEC_DATA].size = shdr[i].size; elf->sec[LILKA_ELF_SEC_DATA].offset = shdr[i].offset; @@ -221,8 +304,7 @@ static int elf_load_sections(lilka_elf_t *elf, const uint8_t *pbuf) { elf->sec[LILKA_ELF_SEC_DRLRO].offset = shdr[i].offset; } } else if (shdr[i].type == LILKA_SHT_NOBITS && - (shdr[i].flags & (LILKA_SHF_ALLOC | LILKA_SHF_WRITE)) == - (LILKA_SHF_ALLOC | LILKA_SHF_WRITE) && + (shdr[i].flags & (LILKA_SHF_ALLOC | LILKA_SHF_WRITE)) == (LILKA_SHF_ALLOC | LILKA_SHF_WRITE) && strcmp(".bss", name) == 0) { elf->sec[LILKA_ELF_SEC_BSS].v_addr = shdr[i].addr; elf->sec[LILKA_ELF_SEC_BSS].size = shdr[i].size; @@ -236,56 +318,62 @@ static int elf_load_sections(lilka_elf_t *elf, const uint8_t *pbuf) { return -EINVAL; } - /* Allocate text section in PSRAM */ - elf->ptext = - (unsigned char *)elf_malloc(elf->sec[LILKA_ELF_SEC_TEXT].size, true); + /* Allocate text section — align allocation size to 4 bytes */ + uint32_t text_alloc_size = LILKA_ELF_ALIGN(elf->sec[LILKA_ELF_SEC_TEXT].size, 4); + elf->ptext = (unsigned char*)elf_malloc(text_alloc_size, true); if (!elf->ptext) { - lilka::serial.log("dynloader: failed to alloc %u bytes for .text", - elf->sec[LILKA_ELF_SEC_TEXT].size); + lilka::serial.log("dynloader: failed to alloc %u bytes for .text", text_alloc_size); return -ENOMEM; } + bool text_in_iram = is_iram_addr((uintptr_t)elf->ptext); + + /* Zero padding bytes — use IRAM-safe write if needed */ + if (text_in_iram) { + iram_set(elf->ptext, 0, text_alloc_size); + } else { + memset(elf->ptext, 0, text_alloc_size); + } /* Allocate data sections */ - uint32_t data_size = elf->sec[LILKA_ELF_SEC_DATA].size + - elf->sec[LILKA_ELF_SEC_RODATA].size + - elf->sec[LILKA_ELF_SEC_BSS].size + - elf->sec[LILKA_ELF_SEC_DRLRO].size; + uint32_t data_size = elf->sec[LILKA_ELF_SEC_DATA].size + elf->sec[LILKA_ELF_SEC_RODATA].size + + elf->sec[LILKA_ELF_SEC_BSS].size + elf->sec[LILKA_ELF_SEC_DRLRO].size; if (data_size) { - elf->pdata = (unsigned char *)elf_malloc(data_size, false); + elf->pdata = (unsigned char*)elf_malloc(data_size, false); if (!elf->pdata) { - lilka::serial.log("dynloader: failed to alloc %u bytes for data", - data_size); + lilka::serial.log("dynloader: failed to alloc %u bytes for data", data_size); elf_free(elf->ptext); elf->ptext = nullptr; return -ENOMEM; } } - /* Copy .text into executable memory */ + /* Copy .text into executable memory — use IRAM-safe copy if needed */ elf->sec[LILKA_ELF_SEC_TEXT].addr = (uintptr_t)elf->ptext; - memcpy(elf->ptext, pbuf + elf->sec[LILKA_ELF_SEC_TEXT].offset, - elf->sec[LILKA_ELF_SEC_TEXT].size); + if (text_in_iram) { + iram_cpy(elf->ptext, pbuf + elf->sec[LILKA_ELF_SEC_TEXT].offset, + elf->sec[LILKA_ELF_SEC_TEXT].size); + } else { + memcpy(elf->ptext, pbuf + elf->sec[LILKA_ELF_SEC_TEXT].offset, + elf->sec[LILKA_ELF_SEC_TEXT].size); + } /* Copy data sections */ if (data_size) { - uint8_t *p = elf->pdata; + uint8_t* p = elf->pdata; if (elf->sec[LILKA_ELF_SEC_DATA].size) { elf->sec[LILKA_ELF_SEC_DATA].addr = (uintptr_t)p; - memcpy(p, pbuf + elf->sec[LILKA_ELF_SEC_DATA].offset, - elf->sec[LILKA_ELF_SEC_DATA].size); + memcpy(p, pbuf + elf->sec[LILKA_ELF_SEC_DATA].offset, elf->sec[LILKA_ELF_SEC_DATA].size); p += elf->sec[LILKA_ELF_SEC_DATA].size; } if (elf->sec[LILKA_ELF_SEC_RODATA].size) { elf->sec[LILKA_ELF_SEC_RODATA].addr = (uintptr_t)p; - memcpy(p, pbuf + elf->sec[LILKA_ELF_SEC_RODATA].offset, - elf->sec[LILKA_ELF_SEC_RODATA].size); + memcpy(p, pbuf + elf->sec[LILKA_ELF_SEC_RODATA].offset, elf->sec[LILKA_ELF_SEC_RODATA].size); p += elf->sec[LILKA_ELF_SEC_RODATA].size; } if (elf->sec[LILKA_ELF_SEC_DRLRO].size) { elf->sec[LILKA_ELF_SEC_DRLRO].addr = (uintptr_t)p; - memcpy(p, pbuf + elf->sec[LILKA_ELF_SEC_DRLRO].offset, - elf->sec[LILKA_ELF_SEC_DRLRO].size); + memcpy(p, pbuf + elf->sec[LILKA_ELF_SEC_DRLRO].offset, elf->sec[LILKA_ELF_SEC_DRLRO].size); p += elf->sec[LILKA_ELF_SEC_DRLRO].size; } if (elf->sec[LILKA_ELF_SEC_BSS].size) { @@ -295,30 +383,27 @@ static int elf_load_sections(lilka_elf_t *elf, const uint8_t *pbuf) { } /* Set entry point (remap to instruction bus for execution) */ - uintptr_t entry_addr = ehdr->entry + - elf->sec[LILKA_ELF_SEC_TEXT].addr - - elf->sec[LILKA_ELF_SEC_TEXT].v_addr; - elf->entry = (int (*)(int, char *[]))remap_text(elf, entry_addr); + uintptr_t entry_addr = ehdr->entry + elf->sec[LILKA_ELF_SEC_TEXT].addr - elf->sec[LILKA_ELF_SEC_TEXT].v_addr; + elf->entry = (int (*)(int, char*[]))remap_text(elf, entry_addr); return 0; } /* ── Core API implementation ────────────────────────────────────────────── */ -int lilka_elf_init(lilka_elf_t *elf) { +int lilka_elf_init(lilka_elf_t* elf) { if (!elf) return -EINVAL; memset(elf, 0, sizeof(lilka_elf_t)); return 0; } -int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size) { +int lilka_elf_relocate(lilka_elf_t* elf, const uint8_t* pbuf, size_t size) { if (!elf || !pbuf || size < sizeof(lilka_elf32_hdr_t)) return -EINVAL; - const lilka_elf32_hdr_t *ehdr = (const lilka_elf32_hdr_t *)pbuf; + const lilka_elf32_hdr_t* ehdr = (const lilka_elf32_hdr_t*)pbuf; /* Validate ELF magic */ - if (ehdr->ident[0] != 0x7f || ehdr->ident[1] != 'E' || - ehdr->ident[2] != 'L' || ehdr->ident[3] != 'F') { + if (ehdr->ident[0] != 0x7f || ehdr->ident[1] != 'E' || ehdr->ident[2] != 'L' || ehdr->ident[3] != 'F') { lilka::serial.log("dynloader: invalid ELF magic"); return -EINVAL; } @@ -336,40 +421,31 @@ int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size) { lilka::serial.log("dynloader: entry=%p", elf->entry); /* Process relocations */ - const lilka_elf32_shdr_t *shdr = - (const lilka_elf32_shdr_t *)(pbuf + ehdr->shoff); - const char *shstrtab = - (const char *)pbuf + shdr[ehdr->shstrndx].offset; + const lilka_elf32_shdr_t* shdr = (const lilka_elf32_shdr_t*)(pbuf + ehdr->shoff); + const char* shstrtab = (const char*)pbuf + shdr[ehdr->shstrndx].offset; for (uint32_t i = 0; i < ehdr->shnum; i++) { if (shdr[i].type == LILKA_SHT_RELA) { - uint32_t nr_reloc = - shdr[i].size / sizeof(lilka_elf32_rela_t); - const lilka_elf32_rela_t *rela = - (const lilka_elf32_rela_t *)(pbuf + shdr[i].offset); - const lilka_elf32_sym_t *symtab = - (const lilka_elf32_sym_t *)(pbuf + shdr[shdr[i].link].offset); - const char *strtab = - (const char *)(pbuf + shdr[shdr[shdr[i].link].link].offset); + uint32_t nr_reloc = shdr[i].size / sizeof(lilka_elf32_rela_t); + const lilka_elf32_rela_t* rela = (const lilka_elf32_rela_t*)(pbuf + shdr[i].offset); + const lilka_elf32_sym_t* symtab = (const lilka_elf32_sym_t*)(pbuf + shdr[shdr[i].link].offset); + const char* strtab = (const char*)(pbuf + shdr[shdr[shdr[i].link].link].offset); for (uint32_t j = 0; j < nr_reloc; j++) { lilka_elf32_rela_t rela_buf; memcpy(&rela_buf, &rela[j], sizeof(lilka_elf32_rela_t)); - const lilka_elf32_sym_t *sym = - &symtab[LILKA_ELF_R_SYM(rela_buf.info)]; + const lilka_elf32_sym_t* sym = &symtab[LILKA_ELF_R_SYM(rela_buf.info)]; int type = LILKA_ELF_R_TYPE(rela_buf.info); uintptr_t addr = 0; - if (type == LILKA_STT_COMMON || type == LILKA_STT_OBJECT || - type == LILKA_STT_SECTION) { - const char *sym_name = strtab + sym->name; + if (type == LILKA_STT_COMMON || type == LILKA_STT_OBJECT || type == LILKA_STT_SECTION) { + const char* sym_name = strtab + sym->name; if (sym_name[0]) { addr = lilka_dynloader_find_symbol(sym_name); /* If not found externally, check local .data */ if (!addr && sym->shndx != LILKA_SHN_UNDEF) { - addr = (uintptr_t)(elf->sec[LILKA_ELF_SEC_DATA].addr + - sym->value - + addr = (uintptr_t)(elf->sec[LILKA_ELF_SEC_DATA].addr + sym->value - elf->sec[LILKA_ELF_SEC_DATA].v_addr); } if (!addr) { @@ -378,7 +454,7 @@ int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size) { } } } else if (type == LILKA_STT_FILE) { - const char *func_name = strtab + sym->name; + const char* func_name = strtab + sym->name; if (sym->value) { addr = elf_map_sym(elf, sym->value); } else { @@ -386,8 +462,7 @@ int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size) { } /* Check local text */ if (!addr && sym->shndx != LILKA_SHN_UNDEF) { - addr = (uintptr_t)(elf->sec[LILKA_ELF_SEC_TEXT].addr + - sym->value - + addr = (uintptr_t)(elf->sec[LILKA_ELF_SEC_TEXT].addr + sym->value - elf->sec[LILKA_ELF_SEC_TEXT].v_addr); } if (!addr) { @@ -402,10 +477,8 @@ int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size) { } /* Parse .dynsym for exported symbols */ else if (shdr[i].type == LILKA_SHT_DYNSYM) { - const lilka_elf32_sym_t *dsymtab = - (const lilka_elf32_sym_t *)(pbuf + shdr[i].offset); - const char *dstrtab = - (const char *)(pbuf + shdr[shdr[i].link].offset); + const lilka_elf32_sym_t* dsymtab = (const lilka_elf32_sym_t*)(pbuf + shdr[i].offset); + const char* dstrtab = (const char*)(pbuf + shdr[shdr[i].link].offset); uint32_t nsyms = shdr[i].size / sizeof(lilka_elf32_sym_t); /* Count global functions */ @@ -418,8 +491,7 @@ int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size) { } if (count) { - elf->symtab = (lilka_dynsym_t *)elf_malloc( - count * sizeof(lilka_dynsym_t), false); + elf->symtab = (lilka_dynsym_t*)elf_malloc(count * sizeof(lilka_dynsym_t), false); if (!elf->symtab) return -ENOMEM; memset(elf->symtab, 0, count * sizeof(lilka_dynsym_t)); @@ -428,13 +500,11 @@ int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size) { if (LILKA_ELF_ST_BIND(dsymtab[j].info) == LILKA_STB_GLOBAL && LILKA_ELF_ST_TYPE(dsymtab[j].info) == LILKA_STT_FUNC) { /* Resolve address within loaded sections */ - elf->symtab[idx].addr = (void *)( - elf->ptext + dsymtab[j].value - - elf->sec[LILKA_ELF_SEC_TEXT].v_addr); + elf->symtab[idx].addr = + (void*)(elf->ptext + dsymtab[j].value - elf->sec[LILKA_ELF_SEC_TEXT].v_addr); /* Copy name */ - size_t len = - strlen(dstrtab + dsymtab[j].name) + 1; - char *nm = (char *)elf_malloc(len, false); + size_t len = strlen(dstrtab + dsymtab[j].name) + 1; + char* nm = (char*)elf_malloc(len, false); if (!nm) { elf->sym_count = idx; return -ENOMEM; @@ -456,12 +526,12 @@ int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size) { return 0; } -int lilka_elf_run(lilka_elf_t *elf, int argc, char *argv[]) { +int lilka_elf_run(lilka_elf_t* elf, int argc, char* argv[]) { if (!elf || !elf->entry) return -EINVAL; return elf->entry(argc, argv); } -void lilka_elf_deinit(lilka_elf_t *elf) { +void lilka_elf_deinit(lilka_elf_t* elf) { if (!elf) return; if (elf->ptext) { @@ -475,7 +545,7 @@ void lilka_elf_deinit(lilka_elf_t *elf) { if (elf->sym_count && elf->symtab) { for (int i = 0; i < elf->sym_count; i++) { if (elf->symtab[i].name) { - elf_free((void *)elf->symtab[i].name); + elf_free((void*)elf->symtab[i].name); } } elf_free(elf->symtab); @@ -487,11 +557,11 @@ void lilka_elf_deinit(lilka_elf_t *elf) { /* ── High-level helper ──────────────────────────────────────────────────── */ -int lilka_dynloader_run(const char *path, int argc, char *argv[]) { +int lilka_dynloader_run(const char* path, int argc, char* argv[]) { if (!path) return -EINVAL; /* Open and read the .so file */ - FILE *f = fopen(path, "rb"); + FILE* f = fopen(path, "rb"); if (!f) { lilka::serial.log("dynloader: failed to open %s", path); return -EIO; @@ -506,7 +576,7 @@ int lilka_dynloader_run(const char *path, int argc, char *argv[]) { return -EIO; } - uint8_t *buf = (uint8_t *)elf_malloc(fsize, false); + uint8_t* buf = (uint8_t*)elf_malloc(fsize, false); if (!buf) { fclose(f); lilka::serial.log("dynloader: failed to alloc %ld bytes for file", fsize); @@ -578,7 +648,7 @@ static const lilka_dynsym_t g_libc_symbols[] = { LILKA_DYNSYM_EXPORT(free), LILKA_DYNSYM_EXPORT(rand), LILKA_DYNSYM_EXPORT(srand), - { "abs", (void*)(int(*)(int))abs }, + {"abs", (void*)(int (*)(int))abs}, /* math (compiler builtins) */ LILKA_DYNSYM_EXPORT(__ltdf2), @@ -595,8 +665,7 @@ static const lilka_dynsym_t g_libc_symbols[] = { }; /* Auto-register libc symbols at startup */ -__attribute__((constructor)) -static void _register_libc_symbols() { +__attribute__((constructor)) static void _register_libc_symbols() { lilka_dynloader_register_symbols(g_libc_symbols); } @@ -604,12 +673,12 @@ static void _register_libc_symbols() { namespace lilka { -int DynLoader::load(const char *path) { +int DynLoader::load(const char* path) { if (loaded) unload(); errorMsg = nullptr; - FILE *f = fopen(path, "rb"); + FILE* f = fopen(path, "rb"); if (!f) { errorMsg = "Failed to open .so file"; serial.log("dynloader: %s: %s", errorMsg, path); @@ -626,7 +695,7 @@ int DynLoader::load(const char *path) { return -EIO; } - fileData = (uint8_t *)elf_malloc(fsize, false); + fileData = (uint8_t*)elf_malloc(fsize, false); if (!fileData) { fclose(f); errorMsg = "Not enough memory for file"; @@ -659,7 +728,7 @@ int DynLoader::load(const char *path) { return 0; } -int DynLoader::execute(int argc, char *argv[]) { +int DynLoader::execute(int argc, char* argv[]) { if (!loaded) { errorMsg = "No .so loaded"; return -EINVAL; From 18eaa25bd002928851f375b13a33e76a5b05022b Mon Sep 17 00:00:00 2001 From: Yevhen Boichuk Date: Mon, 9 Mar 2026 19:55:21 +0200 Subject: [PATCH 3/4] fix: fix cpp/clang checks --- lib/lilka/src/lilka/dynloader.cpp | 77 +++++++-------- lib/lilka/src/lilka/dynloader.h | 154 ++++++++++++++++-------------- lib/lilka/src/lilka/resources.cpp | 3 +- 3 files changed, 118 insertions(+), 116 deletions(-) diff --git a/lib/lilka/src/lilka/dynloader.cpp b/lib/lilka/src/lilka/dynloader.cpp index bc6256e..31d291a 100644 --- a/lib/lilka/src/lilka/dynloader.cpp +++ b/lib/lilka/src/lilka/dynloader.cpp @@ -137,17 +137,14 @@ static void elf_free(void* ptr) { */ /// Word-aligned copy to IRAM. dst must be 4-byte aligned. src may be unaligned. -static void iram_cpy(void *dst, const void *src, size_t n) { - volatile uint32_t *d = (volatile uint32_t *)dst; - const uint8_t *s = (const uint8_t *)src; +static void iram_cpy(void* dst, const void* src, size_t n) { + volatile uint32_t* d = reinterpret_cast(dst); + const uint8_t* s = reinterpret_cast(src); size_t words = n / 4; - size_t tail = n % 4; + size_t tail = n % 4; for (size_t i = 0; i < words; i++) { - uint32_t w = (uint32_t)s[0] - | ((uint32_t)s[1] << 8) - | ((uint32_t)s[2] << 16) - | ((uint32_t)s[3] << 24); + uint32_t w = (uint32_t)s[0] | ((uint32_t)s[1] << 8) | ((uint32_t)s[2] << 16) | ((uint32_t)s[3] << 24); *d++ = w; s += 4; } @@ -160,10 +157,9 @@ static void iram_cpy(void *dst, const void *src, size_t n) { } /// Word-aligned fill for IRAM. ptr must be 4-byte aligned. -static void iram_set(void *ptr, uint8_t val, size_t n) { - volatile uint32_t *d = (volatile uint32_t *)ptr; - uint32_t fill = (uint32_t)val | ((uint32_t)val << 8) - | ((uint32_t)val << 16) | ((uint32_t)val << 24); +static void iram_set(void* ptr, uint8_t val, size_t n) { + volatile uint32_t* d = reinterpret_cast(ptr); + uint32_t fill = (uint32_t)val | ((uint32_t)val << 8) | ((uint32_t)val << 16) | ((uint32_t)val << 24); size_t words = (n + 3) / 4; for (size_t i = 0; i < words; i++) d[i] = fill; @@ -180,7 +176,7 @@ static inline bool is_iram_addr(uintptr_t addr) { /// Only applies when .text is in PSRAM data-bus range (< 0x40000000). /// IRAM addresses (>= 0x40000000) are already on the instruction bus — no remapping needed. static inline uintptr_t remap_text(lilka_elf_t* elf, uintptr_t sym) { - lilka_elf_sec_t* sec = &elf->sec[LILKA_ELF_SEC_TEXT]; + const lilka_elf_sec_t* sec = &elf->sec[LILKA_ELF_SEC_TEXT]; if (sym >= sec->addr && sym < (sec->addr + sec->size)) { #if LILKA_TEXT_OFFSET != 0 /* @@ -216,16 +212,16 @@ static uintptr_t elf_map_sym(lilka_elf_t* elf, uintptr_t sym) { static int elf_arch_relocate( lilka_elf_t* elf, const lilka_elf32_rela_t* rela, const lilka_elf32_sym_t* sym, uint32_t addr ) { - uint32_t* where = (uint32_t*)elf_map_sym(elf, rela->offset); + uint32_t* where = reinterpret_cast(elf_map_sym(elf, rela->offset)); if (!where) { lilka::serial.log("dynloader: reloc where=NULL offset=0x%x", rela->offset); return -EINVAL; } switch (LILKA_ELF_R_TYPE(rela->info)) { - case R_XTENSA_RELATIVE: - *where = remap_text(elf, elf_map_sym(elf, *where)); - break; + case R_XTENSA_RELATIVE: + *where = remap_text(elf, elf_map_sym(elf, *where)); + break; case R_XTENSA_RTLD: /* Runtime linker marker - nothing to do */ break; @@ -270,9 +266,9 @@ static void elf_flush_cache(void) { /* ── Section loading (bus-address mirror mode for ESP32-S3) ─────────────── */ static int elf_load_sections(lilka_elf_t* elf, const uint8_t* pbuf) { - const lilka_elf32_hdr_t* ehdr = (const lilka_elf32_hdr_t*)pbuf; - const lilka_elf32_shdr_t* shdr = (const lilka_elf32_shdr_t*)(pbuf + ehdr->shoff); - const char* shstrtab = (const char*)pbuf + shdr[ehdr->shstrndx].offset; + const lilka_elf32_hdr_t* ehdr = reinterpret_cast(pbuf); + const lilka_elf32_shdr_t* shdr = reinterpret_cast(pbuf + ehdr->shoff); + const char* shstrtab = reinterpret_cast(pbuf) + shdr[ehdr->shstrndx].offset; /* Find relevant sections */ for (uint32_t i = 0; i < ehdr->shnum; i++) { @@ -320,7 +316,7 @@ static int elf_load_sections(lilka_elf_t* elf, const uint8_t* pbuf) { /* Allocate text section — align allocation size to 4 bytes */ uint32_t text_alloc_size = LILKA_ELF_ALIGN(elf->sec[LILKA_ELF_SEC_TEXT].size, 4); - elf->ptext = (unsigned char*)elf_malloc(text_alloc_size, true); + elf->ptext = static_cast(elf_malloc(text_alloc_size, true)); if (!elf->ptext) { lilka::serial.log("dynloader: failed to alloc %u bytes for .text", text_alloc_size); return -ENOMEM; @@ -338,7 +334,7 @@ static int elf_load_sections(lilka_elf_t* elf, const uint8_t* pbuf) { uint32_t data_size = elf->sec[LILKA_ELF_SEC_DATA].size + elf->sec[LILKA_ELF_SEC_RODATA].size + elf->sec[LILKA_ELF_SEC_BSS].size + elf->sec[LILKA_ELF_SEC_DRLRO].size; if (data_size) { - elf->pdata = (unsigned char*)elf_malloc(data_size, false); + elf->pdata = static_cast(elf_malloc(data_size, false)); if (!elf->pdata) { lilka::serial.log("dynloader: failed to alloc %u bytes for data", data_size); elf_free(elf->ptext); @@ -350,11 +346,9 @@ static int elf_load_sections(lilka_elf_t* elf, const uint8_t* pbuf) { /* Copy .text into executable memory — use IRAM-safe copy if needed */ elf->sec[LILKA_ELF_SEC_TEXT].addr = (uintptr_t)elf->ptext; if (text_in_iram) { - iram_cpy(elf->ptext, pbuf + elf->sec[LILKA_ELF_SEC_TEXT].offset, - elf->sec[LILKA_ELF_SEC_TEXT].size); + iram_cpy(elf->ptext, pbuf + elf->sec[LILKA_ELF_SEC_TEXT].offset, elf->sec[LILKA_ELF_SEC_TEXT].size); } else { - memcpy(elf->ptext, pbuf + elf->sec[LILKA_ELF_SEC_TEXT].offset, - elf->sec[LILKA_ELF_SEC_TEXT].size); + memcpy(elf->ptext, pbuf + elf->sec[LILKA_ELF_SEC_TEXT].offset, elf->sec[LILKA_ELF_SEC_TEXT].size); } /* Copy data sections */ @@ -384,7 +378,7 @@ static int elf_load_sections(lilka_elf_t* elf, const uint8_t* pbuf) { /* Set entry point (remap to instruction bus for execution) */ uintptr_t entry_addr = ehdr->entry + elf->sec[LILKA_ELF_SEC_TEXT].addr - elf->sec[LILKA_ELF_SEC_TEXT].v_addr; - elf->entry = (int (*)(int, char*[]))remap_text(elf, entry_addr); + elf->entry = reinterpret_cast(remap_text(elf, entry_addr)); return 0; } @@ -400,7 +394,7 @@ int lilka_elf_init(lilka_elf_t* elf) { int lilka_elf_relocate(lilka_elf_t* elf, const uint8_t* pbuf, size_t size) { if (!elf || !pbuf || size < sizeof(lilka_elf32_hdr_t)) return -EINVAL; - const lilka_elf32_hdr_t* ehdr = (const lilka_elf32_hdr_t*)pbuf; + const lilka_elf32_hdr_t* ehdr = reinterpret_cast(pbuf); /* Validate ELF magic */ if (ehdr->ident[0] != 0x7f || ehdr->ident[1] != 'E' || ehdr->ident[2] != 'L' || ehdr->ident[3] != 'F') { @@ -421,15 +415,15 @@ int lilka_elf_relocate(lilka_elf_t* elf, const uint8_t* pbuf, size_t size) { lilka::serial.log("dynloader: entry=%p", elf->entry); /* Process relocations */ - const lilka_elf32_shdr_t* shdr = (const lilka_elf32_shdr_t*)(pbuf + ehdr->shoff); - const char* shstrtab = (const char*)pbuf + shdr[ehdr->shstrndx].offset; + const lilka_elf32_shdr_t* shdr = reinterpret_cast(pbuf + ehdr->shoff); for (uint32_t i = 0; i < ehdr->shnum; i++) { if (shdr[i].type == LILKA_SHT_RELA) { uint32_t nr_reloc = shdr[i].size / sizeof(lilka_elf32_rela_t); - const lilka_elf32_rela_t* rela = (const lilka_elf32_rela_t*)(pbuf + shdr[i].offset); - const lilka_elf32_sym_t* symtab = (const lilka_elf32_sym_t*)(pbuf + shdr[shdr[i].link].offset); - const char* strtab = (const char*)(pbuf + shdr[shdr[shdr[i].link].link].offset); + const lilka_elf32_rela_t* rela = reinterpret_cast(pbuf + shdr[i].offset); + const lilka_elf32_sym_t* symtab = + reinterpret_cast(pbuf + shdr[shdr[i].link].offset); + const char* strtab = reinterpret_cast(pbuf + shdr[shdr[shdr[i].link].link].offset); for (uint32_t j = 0; j < nr_reloc; j++) { lilka_elf32_rela_t rela_buf; @@ -477,8 +471,8 @@ int lilka_elf_relocate(lilka_elf_t* elf, const uint8_t* pbuf, size_t size) { } /* Parse .dynsym for exported symbols */ else if (shdr[i].type == LILKA_SHT_DYNSYM) { - const lilka_elf32_sym_t* dsymtab = (const lilka_elf32_sym_t*)(pbuf + shdr[i].offset); - const char* dstrtab = (const char*)(pbuf + shdr[shdr[i].link].offset); + const lilka_elf32_sym_t* dsymtab = reinterpret_cast(pbuf + shdr[i].offset); + const char* dstrtab = reinterpret_cast(pbuf + shdr[shdr[i].link].offset); uint32_t nsyms = shdr[i].size / sizeof(lilka_elf32_sym_t); /* Count global functions */ @@ -491,7 +485,7 @@ int lilka_elf_relocate(lilka_elf_t* elf, const uint8_t* pbuf, size_t size) { } if (count) { - elf->symtab = (lilka_dynsym_t*)elf_malloc(count * sizeof(lilka_dynsym_t), false); + elf->symtab = static_cast(elf_malloc(count * sizeof(lilka_dynsym_t), false)); if (!elf->symtab) return -ENOMEM; memset(elf->symtab, 0, count * sizeof(lilka_dynsym_t)); @@ -501,10 +495,10 @@ int lilka_elf_relocate(lilka_elf_t* elf, const uint8_t* pbuf, size_t size) { LILKA_ELF_ST_TYPE(dsymtab[j].info) == LILKA_STT_FUNC) { /* Resolve address within loaded sections */ elf->symtab[idx].addr = - (void*)(elf->ptext + dsymtab[j].value - elf->sec[LILKA_ELF_SEC_TEXT].v_addr); + static_cast(elf->ptext + dsymtab[j].value - elf->sec[LILKA_ELF_SEC_TEXT].v_addr); /* Copy name */ size_t len = strlen(dstrtab + dsymtab[j].name) + 1; - char* nm = (char*)elf_malloc(len, false); + char* nm = static_cast(elf_malloc(len, false)); if (!nm) { elf->sym_count = idx; return -ENOMEM; @@ -545,7 +539,8 @@ void lilka_elf_deinit(lilka_elf_t* elf) { if (elf->sym_count && elf->symtab) { for (int i = 0; i < elf->sym_count; i++) { if (elf->symtab[i].name) { - elf_free((void*)elf->symtab[i].name); + // cppcheck-suppress cstyleCast + elf_free(const_cast(elf->symtab[i].name)); } } elf_free(elf->symtab); @@ -576,7 +571,7 @@ int lilka_dynloader_run(const char* path, int argc, char* argv[]) { return -EIO; } - uint8_t* buf = (uint8_t*)elf_malloc(fsize, false); + uint8_t* buf = static_cast(elf_malloc(fsize, false)); if (!buf) { fclose(f); lilka::serial.log("dynloader: failed to alloc %ld bytes for file", fsize); @@ -695,7 +690,7 @@ int DynLoader::load(const char* path) { return -EIO; } - fileData = (uint8_t*)elf_malloc(fsize, false); + fileData = static_cast(elf_malloc(fsize, false)); if (!fileData) { fclose(f); errorMsg = "Not enough memory for file"; diff --git a/lib/lilka/src/lilka/dynloader.h b/lib/lilka/src/lilka/dynloader.h index d7dfad0..e25fb27 100644 --- a/lib/lilka/src/lilka/dynloader.h +++ b/lib/lilka/src/lilka/dynloader.h @@ -25,27 +25,27 @@ extern "C" { #define EI_NIDENT 16 -typedef unsigned int Elf32_Addr; -typedef unsigned int Elf32_Off; -typedef unsigned int Elf32_Word; +typedef unsigned int Elf32_Addr; +typedef unsigned int Elf32_Off; +typedef unsigned int Elf32_Word; typedef unsigned short Elf32_Half; -typedef int Elf32_Sword; +typedef int Elf32_Sword; typedef struct { unsigned char ident[EI_NIDENT]; - Elf32_Half type; - Elf32_Half machine; - Elf32_Word version; - Elf32_Addr entry; - Elf32_Off phoff; - Elf32_Off shoff; - Elf32_Word flags; - Elf32_Half ehsize; - Elf32_Half phentsize; - Elf32_Half phnum; - Elf32_Half shentsize; - Elf32_Half shnum; - Elf32_Half shstrndx; + Elf32_Half type; + Elf32_Half machine; + Elf32_Word version; + Elf32_Addr entry; + Elf32_Off phoff; + Elf32_Off shoff; + Elf32_Word flags; + Elf32_Half ehsize; + Elf32_Half phentsize; + Elf32_Half phnum; + Elf32_Half shentsize; + Elf32_Half shnum; + Elf32_Half shstrndx; } lilka_elf32_hdr_t; typedef struct { @@ -53,7 +53,7 @@ typedef struct { Elf32_Word type; Elf32_Word flags; Elf32_Addr addr; - Elf32_Off offset; + Elf32_Off offset; Elf32_Word size; Elf32_Word link; Elf32_Word info; @@ -62,17 +62,17 @@ typedef struct { } lilka_elf32_shdr_t; typedef struct { - Elf32_Word name; - Elf32_Addr value; - Elf32_Word size; + Elf32_Word name; + Elf32_Addr value; + Elf32_Word size; unsigned char info; unsigned char other; - Elf32_Half shndx; + Elf32_Half shndx; } lilka_elf32_sym_t; typedef struct { - Elf32_Addr offset; - Elf32_Word info; + Elf32_Addr offset; + Elf32_Word info; Elf32_Sword addend; } lilka_elf32_rela_t; @@ -102,28 +102,28 @@ typedef struct { #define LILKA_SHF_EXECINSTR 4 /* Symbol binding / type */ -#define LILKA_STB_GLOBAL 1 -#define LILKA_STT_NOTYPE 0 -#define LILKA_STT_OBJECT 1 -#define LILKA_STT_FUNC 2 -#define LILKA_STT_SECTION 3 -#define LILKA_STT_FILE 4 -#define LILKA_STT_COMMON 5 - -#define LILKA_SHN_UNDEF 0 - -#define LILKA_ELF_ST_BIND(i) ((i) >> 4) -#define LILKA_ELF_ST_TYPE(i) ((i) & 0xf) +#define LILKA_STB_GLOBAL 1 +#define LILKA_STT_NOTYPE 0 +#define LILKA_STT_OBJECT 1 +#define LILKA_STT_FUNC 2 +#define LILKA_STT_SECTION 3 +#define LILKA_STT_FILE 4 +#define LILKA_STT_COMMON 5 + +#define LILKA_SHN_UNDEF 0 + +#define LILKA_ELF_ST_BIND(i) ((i) >> 4) +#define LILKA_ELF_ST_TYPE(i) ((i) & 0xf) #define LILKA_ELF_R_SYM(i) ((i) >> 8) -#define LILKA_ELF_R_TYPE(i) ((unsigned char)(i)) +#define LILKA_ELF_R_TYPE(i) ((unsigned char)(i)) /* Xtensa relocation types */ -#define R_XTENSA_NONE 0 -#define R_XTENSA_32 1 -#define R_XTENSA_RTLD 2 -#define R_XTENSA_GLOB_DAT 3 -#define R_XTENSA_JMP_SLOT 4 -#define R_XTENSA_RELATIVE 5 +#define R_XTENSA_NONE 0 +#define R_XTENSA_32 1 +#define R_XTENSA_RTLD 2 +#define R_XTENSA_GLOB_DAT 3 +#define R_XTENSA_JMP_SLOT 4 +#define R_XTENSA_RELATIVE 5 /* Alignment macro */ #define LILKA_ELF_ALIGN(a, s) (((a) + ((s) - 1)) & ~((s) - 1)) @@ -131,39 +131,41 @@ typedef struct { /* ── ELF section descriptor ─────────────────────────────────────────────── */ typedef struct { - uintptr_t v_addr; /* virtual address in the ELF */ - uint32_t offset; /* offset in the ELF file */ - uintptr_t addr; /* physical address in memory */ - uint32_t size; /* section size */ + uintptr_t v_addr; /* virtual address in the ELF */ + uint32_t offset; /* offset in the ELF file */ + uintptr_t addr; /* physical address in memory */ + uint32_t size; /* section size */ } lilka_elf_sec_t; /* ── Symbol export entry ─────────────────────────────────────────────────── */ typedef struct { - const char *name; - void *addr; + const char* name; + void* addr; } lilka_dynsym_t; /* Helper macros */ -#define LILKA_DYNSYM_EXPORT(sym) { #sym, (void*)&sym } -#define LILKA_DYNSYM_END { NULL, NULL } +#define LILKA_DYNSYM_EXPORT(sym) \ + { #sym, (void*)&sym } +#define LILKA_DYNSYM_END \ + { NULL, NULL } /* ── DynLoader state ─────────────────────────────────────────────────────── */ typedef struct { /* Memory pointers */ - unsigned char *ptext; /* text (code) buffer in PSRAM (data-bus addr) */ - unsigned char *pdata; /* data+rodata+bss buffer */ + unsigned char* ptext; /* text (code) buffer in PSRAM (data-bus addr) */ + unsigned char* pdata; /* data+rodata+bss buffer */ /* Section descriptors */ lilka_elf_sec_t sec[LILKA_ELF_SECS]; /* Entry point (instruction-bus address) */ - int (*entry)(int argc, char *argv[]); + int (*entry)(int argc, char* argv[]); /* Exported symbols from the loaded .so */ - uint16_t sym_count; - lilka_dynsym_t *symtab; + uint16_t sym_count; + lilka_dynsym_t* symtab; } lilka_elf_t; /* ── Public API ──────────────────────────────────────────────────────────── */ @@ -179,7 +181,7 @@ typedef struct { * @param table Array of lilka_dynsym_t terminated with LILKA_DYNSYM_END. * @return 0 on success, negative errno on failure. */ -int lilka_dynloader_register_symbols(const lilka_dynsym_t *table); +int lilka_dynloader_register_symbols(const lilka_dynsym_t* table); /** * @brief Unregister a previously registered symbol table. @@ -187,7 +189,7 @@ int lilka_dynloader_register_symbols(const lilka_dynsym_t *table); * @param table Pointer passed to lilka_dynloader_register_symbols(). * @return 0 on success, negative errno on failure. */ -int lilka_dynloader_unregister_symbols(const lilka_dynsym_t *table); +int lilka_dynloader_unregister_symbols(const lilka_dynsym_t* table); /** * @brief Resolve a symbol name in all registered tables. @@ -195,7 +197,7 @@ int lilka_dynloader_unregister_symbols(const lilka_dynsym_t *table); * @param name Symbol name to look up. * @return Address of the symbol, or 0 if not found. */ -uintptr_t lilka_dynloader_find_symbol(const char *name); +uintptr_t lilka_dynloader_find_symbol(const char* name); /** * @brief Initialize an ELF loader context. @@ -203,7 +205,7 @@ uintptr_t lilka_dynloader_find_symbol(const char *name); * @param elf Pointer to lilka_elf_t structure. * @return 0 on success, negative errno on failure. */ -int lilka_elf_init(lilka_elf_t *elf); +int lilka_elf_init(lilka_elf_t* elf); /** * @brief Load and relocate an ELF from a memory buffer. @@ -213,7 +215,7 @@ int lilka_elf_init(lilka_elf_t *elf); * @param size Size of the ELF data. * @return 0 on success, negative errno on failure. */ -int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size); +int lilka_elf_relocate(lilka_elf_t* elf, const uint8_t* pbuf, size_t size); /** * @brief Execute the loaded ELF's entry point. @@ -223,14 +225,14 @@ int lilka_elf_relocate(lilka_elf_t *elf, const uint8_t *pbuf, size_t size); * @param argv Argument vector. * @return Return value from the ELF's main/app_main. */ -int lilka_elf_run(lilka_elf_t *elf, int argc, char *argv[]); +int lilka_elf_run(lilka_elf_t* elf, int argc, char* argv[]); /** * @brief Deinitialize the ELF loader and free all resources. * * @param elf ELF context to clean up. */ -void lilka_elf_deinit(lilka_elf_t *elf); +void lilka_elf_deinit(lilka_elf_t* elf); /** * @brief High-level: load a .so file from a filesystem path, relocate, @@ -241,7 +243,7 @@ void lilka_elf_deinit(lilka_elf_t *elf); * @param argv Argument values for the app. * @return 0 on success, negative errno on failure. */ -int lilka_dynloader_run(const char *path, int argc, char *argv[]); +int lilka_dynloader_run(const char* path, int argc, char* argv[]); #ifdef __cplusplus } /* extern "C" */ @@ -264,38 +266,42 @@ class DynLoader { } /// Register a symbol table for resolution. - static int registerSymbols(const lilka_dynsym_t *table) { + static int registerSymbols(const lilka_dynsym_t* table) { return lilka_dynloader_register_symbols(table); } /// Unregister a symbol table. - static int unregisterSymbols(const lilka_dynsym_t *table) { + static int unregisterSymbols(const lilka_dynsym_t* table) { return lilka_dynloader_unregister_symbols(table); } /// Load a .so file from the given filesystem path. /// @return 0 on success, negative on error. - int load(const char *path); + int load(const char* path); /// Execute the loaded app's entry point. /// @return Return value from the app. - int execute(int argc, char *argv[]); + int execute(int argc, char* argv[]); /// Unload the app and free all resources. void unload(); /// Get the last error message (or nullptr). - const char *getError() const { return errorMsg; } + const char* getError() const { + return errorMsg; + } /// Check if a .so is currently loaded. - bool isLoaded() const { return loaded; } + bool isLoaded() const { + return loaded; + } private: lilka_elf_t elf; - uint8_t *fileData = nullptr; - size_t fileSize = 0; - bool loaded; - const char *errorMsg; + uint8_t* fileData = nullptr; + size_t fileSize = 0; + bool loaded; + const char* errorMsg; }; } // namespace lilka diff --git a/lib/lilka/src/lilka/resources.cpp b/lib/lilka/src/lilka/resources.cpp index c0828da..dc4b792 100644 --- a/lib/lilka/src/lilka/resources.cpp +++ b/lib/lilka/src/lilka/resources.cpp @@ -86,7 +86,8 @@ Image* Resources::loadImage(String filename, int32_t transparentColor, int32_t p if (fread(fileSignature, 1, 4, file) == 4) { if (fileSignature[0] == 'B' && fileSignature[1] == 'M') { image = loadImageBMP(file, transparentColor, pivotX, pivotY); - } else if (fileSignature[0] == 0x89 && fileSignature[1] == 'P' && fileSignature[2] == 'N' && fileSignature[3] == 'G') { + } else if (fileSignature[0] == 0x89 && fileSignature[1] == 'P' && fileSignature[2] == 'N' && + fileSignature[3] == 'G') { image = loadImagePNG(file, transparentColor, pivotX, pivotY); } else { // serial.err("Invalid file type: %d %d %d %d\n", fileSignature[0], fileSignature[1], fileSignature[2], fileSignature[3]); From 9298f4fbebf1471d8c59ffab7c74512a7d5f58d8 Mon Sep 17 00:00:00 2001 From: Yevhen Boichuk Date: Mon, 9 Mar 2026 20:01:43 +0200 Subject: [PATCH 4/4] fix: apply clang-format-17 formatting --- lib/lilka/src/lilka/dynloader.cpp | 4 +--- lib/lilka/src/lilka/dynloader.h | 2 +- lib/lilka/src/lilka/resources.cpp | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/lilka/src/lilka/dynloader.cpp b/lib/lilka/src/lilka/dynloader.cpp index 31d291a..2dd23cb 100644 --- a/lib/lilka/src/lilka/dynloader.cpp +++ b/lib/lilka/src/lilka/dynloader.cpp @@ -299,9 +299,7 @@ static int elf_load_sections(lilka_elf_t* elf, const uint8_t* pbuf) { elf->sec[LILKA_ELF_SEC_DRLRO].size = shdr[i].size; elf->sec[LILKA_ELF_SEC_DRLRO].offset = shdr[i].offset; } - } else if (shdr[i].type == LILKA_SHT_NOBITS && - (shdr[i].flags & (LILKA_SHF_ALLOC | LILKA_SHF_WRITE)) == (LILKA_SHF_ALLOC | LILKA_SHF_WRITE) && - strcmp(".bss", name) == 0) { + } else if (shdr[i].type == LILKA_SHT_NOBITS && (shdr[i].flags & (LILKA_SHF_ALLOC | LILKA_SHF_WRITE)) == (LILKA_SHF_ALLOC | LILKA_SHF_WRITE) && strcmp(".bss", name) == 0) { elf->sec[LILKA_ELF_SEC_BSS].v_addr = shdr[i].addr; elf->sec[LILKA_ELF_SEC_BSS].size = shdr[i].size; elf->sec[LILKA_ELF_SEC_BSS].offset = shdr[i].offset; diff --git a/lib/lilka/src/lilka/dynloader.h b/lib/lilka/src/lilka/dynloader.h index e25fb27..8326c68 100644 --- a/lib/lilka/src/lilka/dynloader.h +++ b/lib/lilka/src/lilka/dynloader.h @@ -126,7 +126,7 @@ typedef struct { #define R_XTENSA_RELATIVE 5 /* Alignment macro */ -#define LILKA_ELF_ALIGN(a, s) (((a) + ((s) - 1)) & ~((s) - 1)) +#define LILKA_ELF_ALIGN(a, s) (((a) + ((s)-1)) & ~((s)-1)) /* ── ELF section descriptor ─────────────────────────────────────────────── */ diff --git a/lib/lilka/src/lilka/resources.cpp b/lib/lilka/src/lilka/resources.cpp index dc4b792..c0828da 100644 --- a/lib/lilka/src/lilka/resources.cpp +++ b/lib/lilka/src/lilka/resources.cpp @@ -86,8 +86,7 @@ Image* Resources::loadImage(String filename, int32_t transparentColor, int32_t p if (fread(fileSignature, 1, 4, file) == 4) { if (fileSignature[0] == 'B' && fileSignature[1] == 'M') { image = loadImageBMP(file, transparentColor, pivotX, pivotY); - } else if (fileSignature[0] == 0x89 && fileSignature[1] == 'P' && fileSignature[2] == 'N' && - fileSignature[3] == 'G') { + } else if (fileSignature[0] == 0x89 && fileSignature[1] == 'P' && fileSignature[2] == 'N' && fileSignature[3] == 'G') { image = loadImagePNG(file, transparentColor, pivotX, pivotY); } else { // serial.err("Invalid file type: %d %d %d %d\n", fileSignature[0], fileSignature[1], fileSignature[2], fileSignature[3]);