From 04dc50e71afded336404f09f3b38846b1470ffee Mon Sep 17 00:00:00 2001 From: Marco Casaroli Date: Mon, 3 Aug 2026 11:28:49 +0200 Subject: [PATCH] libs/libc/elf: Fix two ways an FDPIC module failed to relocate. Running one for the first time turned up two holes in the ET_DYN path. Neither shows up in a build. An undefined symbol is resolved with libelf_findglobal(), which searches only the table of globally registered symbols. The export table that exec() hands its caller went no further than the ET_REL path, so an ET_DYN module could not import anything the caller supplied. Invisible while such modules resolved everything internally; an FDPIC module imports its libc, and every import failed with "Unable to resolve addr of ext ref printf" although the caller had passed a table containing printf. The export table is now threaded into libelf_relocatedyn() and consulted when the global table has no answer, leaving the existing lookup order intact. A relocation naming a symbol defined inside the object was dropped silently. The code handles a relocation with no symbol, and one against an undefined symbol, but a defined symbol fell through both. That was harmless while every dynamic relocation arriving here had symbol index zero, which is the case for R_ARM_RELATIVE. FDPIC brings the first ones that do not: a pointer to a static function is emitted against the *section* symbol, so the value is the section base and the offset within it -- including the Thumb bit -- is carried as the addend. Deriving a value from the word being patched, as the no-symbol case does, would translate that addend as though it were an address. Confirmed against a real module: .text at 0x23c plus an addend of 0x95 gives 0x2d1, which is the function with its Thumb bit. Also stop libelf_symname() reporting a nameless symbol as an error. A section symbol has no name, and libelf_findsymbol() walks the whole table looking for optional entries such as nx_stacksize, so it meets these routinely and checks for -ESRCH itself. At error level it printed ten or more lines per module load and buried the diagnostics that matter. Built and run on lm3s6965-ek with the examples/elf ROMFS. The ET_REL test modules load as before, and an FDPIC module now loads, relocates, resolves printf and puts from the table exec() supplied, and calls through a function descriptor of its own. Assisted-by: Claude Opus 5 (1M context) Signed-off-by: Marco Casaroli --- libs/libc/elf/elf_bind.c | 59 +++++++++++++++++++++++++++++++++++-- libs/libc/elf/elf_symbols.c | 6 +++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/libs/libc/elf/elf_bind.c b/libs/libc/elf/elf_bind.c index 4abfbdf2a5d..5409b79bfbb 100644 --- a/libs/libc/elf/elf_bind.c +++ b/libs/libc/elf/elf_bind.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "libc.h" @@ -637,7 +638,9 @@ static int libelf_relocateadd(FAR struct module_s *modp, static int libelf_relocatedyn(FAR struct module_s *modp, FAR struct mod_loadinfo_s *loadinfo, - int relidx) + int relidx, + FAR const struct symtab_s *exports, + int nexports) { FAR Elf_Shdr *shdr = &loadinfo->shdr[relidx]; FAR Elf_Shdr *symhdr; @@ -868,6 +871,26 @@ static int libelf_relocatedyn(FAR struct module_s *modp, ep = libelf_findglobal(modp, loadinfo, symhdr, &sym[idx_sym]); + + /* libelf_findglobal() searches only the registered + * symbols. A module from exec() has its own export + * table, and an FDPIC module imports its libc there. + */ + + if (ep == NULL && exports != NULL) + { + FAR const struct symtab_s *sm; + + sm = symtab_findbyname(exports, + (FAR char *) + loadinfo->iobuffer, + nexports); + if (sm != NULL) + { + ep = (FAR void *)sm->sym_value; + } + } + if ((ep == NULL) && (ELF_ST_BIND(sym[idx_sym].st_info) != STB_WEAK)) { @@ -889,6 +912,37 @@ static int libelf_relocatedyn(FAR struct module_s *modp, *(FAR uintptr_t *)addr = (uintptr_t)ep; } + else if (loadinfo->fdpic) + { + /* A relocation naming a symbol inside this object. A + * pointer to a static function is emitted against the + * section symbol, so the offset, Thumb bit included, is + * the addend and must not come from the patched word. + */ + + Elf_Sym defsym = sym[idx_sym]; + + defsym.st_value = libelf_addr(loadinfo, + sym[idx_sym].st_value); + + addr = libelf_addr(loadinfo, rel->r_offset); + + if (reldata.relrela[idx_rel] == 1) + { + addr += rela->r_addend; + } + + ret = up_relocate(rel, &defsym, addr, ARCH_ELFDATA_PARM); + if (ret < 0) + { + berr("ERROR: Section %d reloc %d: " + "Relocation failed: %d\n", relidx, i, ret); + lib_free(sym); + lib_free(rels); + lib_free(dyn); + return ret; + } + } } else { @@ -1004,7 +1058,8 @@ int libelf_bind(FAR struct module_s *modp, switch (loadinfo->shdr[i].sh_type) { case SHT_DYNAMIC: - ret = libelf_relocatedyn(modp, loadinfo, i); + ret = libelf_relocatedyn(modp, loadinfo, i, + exports, nexports); break; case SHT_DYNSYM: loadinfo->dsymtabidx = i; diff --git a/libs/libc/elf/elf_symbols.c b/libs/libc/elf/elf_symbols.c index 39ad66f0858..aa55d595fed 100644 --- a/libs/libc/elf/elf_symbols.c +++ b/libs/libc/elf/elf_symbols.c @@ -107,7 +107,11 @@ static int libelf_symname(FAR struct mod_loadinfo_s *loadinfo, if (sym->st_name == 0) { - berr("ERROR: Symbol has no name\n"); + /* Not a failure. A section symbol has no name, and + * libelf_findsymbol() meets these routinely and checks for -ESRCH. + */ + + binfo("Symbol has no name\n"); return -ESRCH; }