This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit e666653b807286d3481d9566d7460bfde046b37a Author: Marco Casaroli <[email protected]> AuthorDate: Mon Aug 3 11:34:35 2026 +0200 libs/libc/elf: Publish FDPIC functions as descriptors for dlsym. A module that dlopen()s a library gets back function addresses from dlsym() and calls them. Under FDPIC a bare code address is not enough: the callee needs its own data base as well, so what dlsym() returns has to be a function descriptor. The exported symbol table carries no type information -- symtab_s is a name and a value, and its own comment says typing would have to be added to support anything but function pointers -- so by the time dlsym() is asked there is no way to tell a function from an object. libelf_insertsymtab() is the last point that can: st_info is still in hand there. So an FDPIC object's exported functions are published as the address of a descriptor carved from the module's pool, and dlopen(), dlsym() and the module registry need no knowledge of FDPIC at all. The pool is sized for the dynamic symbol table as well as the relocations, since both can draw from it. That leaves the symbol values themselves, which were wrong for any ET_DYN object. libelf_loadsymtab() adds the symbol's section address to its value, which is right for ET_REL, where the section address is where the section was actually placed and the value is relative to it. In a shared object both are already full link-time addresses, so adding them counts the section twice. It needs translating onto wherever the object was placed instead. Library data is shared between everything that dlopen()s it, because the registry holds one instance per name. Giving each user its own copy would mean teaching the registry about instances, which is a much larger change to shared code; an executable loaded through exec() already gets its own data, since that path loads a fresh copy each time. Built and run on lm3s6965-ek with the examples/elf ROMFS; the FDPIC module continues to load, relocate and call through its own descriptors. Assisted-by: Claude Opus 5 (1M context) <[email protected]> Signed-off-by: Marco Casaroli <[email protected]> --- libs/libc/elf/elf_insert.c | 16 ++++++++++++++-- libs/libc/elf/elf_load.c | 8 +++++--- libs/libc/elf/elf_symbols.c | 20 ++++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/libs/libc/elf/elf_insert.c b/libs/libc/elf/elf_insert.c index 5b646115248..d9e7739e8de 100644 --- a/libs/libc/elf/elf_insert.c +++ b/libs/libc/elf/elf_insert.c @@ -247,9 +247,21 @@ static int libelf_loadsymtab(FAR struct module_s *modp, if (sym[i].st_shndx != SHN_UNDEF && sym[i].st_shndx < loadinfo->ehdr.e_shnum) { - FAR Elf_Shdr *s = &loadinfo->shdr[sym[i].st_shndx]; + if (loadinfo->ehdr.e_type == ET_DYN) + { + /* A shared object's symbol value is already the full + * link-time address. It only needs translating onto where + * the object was placed. + */ - sym[i].st_value = sym[i].st_value + s->sh_addr; + sym[i].st_value = libelf_addr(loadinfo, sym[i].st_value); + } + else + { + FAR Elf_Shdr *s = &loadinfo->shdr[sym[i].st_shndx]; + + sym[i].st_value = sym[i].st_value + s->sh_addr; + } } } diff --git a/libs/libc/elf/elf_load.c b/libs/libc/elf/elf_load.c index 4fbdb1cba63..b63ad6ed601 100644 --- a/libs/libc/elf/elf_load.c +++ b/libs/libc/elf/elf_load.c @@ -243,8 +243,9 @@ static void libelf_elfsize(FAR struct mod_loadinfo_s *loadinfo, bool alloc) } /* Reserve the descriptor pool. R_ARM_FUNCDESC asks the loader to - * manufacture a descriptor after the segment is placed, and the - * relocation count bounds how many. + * manufacture a descriptor after the segment is placed, and a library + * publishes one per exported function for dlsym(). The relocation and + * dynamic symbol counts bound how many. */ if (loadinfo->fdpic) @@ -255,7 +256,8 @@ static void libelf_elfsize(FAR struct mod_loadinfo_s *loadinfo, bool alloc) { FAR Elf_Shdr *shdr = &loadinfo->shdr[i]; - if (shdr->sh_type == SHT_REL && shdr->sh_entsize != 0) + if ((shdr->sh_type == SHT_REL || shdr->sh_type == SHT_DYNSYM) && + shdr->sh_entsize != 0) { nrels += shdr->sh_size / shdr->sh_entsize; } diff --git a/libs/libc/elf/elf_symbols.c b/libs/libc/elf/elf_symbols.c index aa55d595fed..488777962f5 100644 --- a/libs/libc/elf/elf_symbols.c +++ b/libs/libc/elf/elf_symbols.c @@ -34,6 +34,7 @@ #include <nuttx/debug.h> #include <nuttx/symtab.h> +#include <nuttx/fdpic.h> #include <nuttx/lib/elf.h> #include "libc.h" @@ -542,6 +543,25 @@ int libelf_insertsymtab(FAR struct module_s *modp, strdup((FAR char *)loadinfo->iobuffer); symbol[j].sym_value = (FAR const void *)(uintptr_t)sym[i].st_value; + + /* Publish an FDPIC function as a descriptor, so dlsym() + * hands back something callable. Only here does st_info + * still say what is a function. + */ + + if (loadinfo->fdpic && + ELF_ST_TYPE(sym[i].st_info) == STT_FUNC && + loadinfo->usedesc < loadinfo->ndesc) + { + FAR struct fdpic_desc_s *desc = + loadinfo->descpool + loadinfo->usedesc++; + + desc->entry = sym[i].st_value; + desc->got = loadinfo->gotbase; + + symbol[j].sym_value = (FAR const void *)desc; + } + j++; } }
