From dcd93b0f67f107feb31498791bb2064134cbdc0b Mon Sep 17 00:00:00 2001 From: Marco Casaroli Date: Tue, 25 Aug 2026 11:18:55 +0200 Subject: [PATCH] libs/libc/elf: Load the libraries a module names in DT_NEEDED. A module that names a shared library in DT_NEEDED now gets it loaded and its imports bound against it, rather than being refused. libelf_insert() does the loading, which is what dlopen() calls anyway: the library lands in the module registry like anything else, its exports come back through libelf_getsymbol() -- the same call dlsym() uses -- and a library named by two modules is loaded once. A bare name is looked for along LD_LIBRARY_PATH, where dlopen() looks for it. Undefined symbols resolve against the globally registered symbols first, then the modules this one depends on, then the table exec() supplied. Nothing here calls into dlfcn, because this loader is also the kernel's module loader, which has none. Each library becomes one of the module's dependencies[], and the dependency holds it in place of the reference libelf_insert() took. So a library loaded only for DT_NEEDED is kept by the modules that depend on it, and libelf_undepend() unloads it with the last of them; one that dlopen() or insmod also opened stays until that reference goes too. CONFIG_LIBC_ELF_MAXDEPEND bounds how many libraries a module may name, which is what it already meant. Six things had to be fixed to make it work, none of which a build shows. reldata was a file-scope global. Loading a library from inside libelf_relocatedyn() makes that function reentrant, so the nested load overwrote the outer one's relocation offsets and the module resumed binding with the library's DT_REL. It is now per call. A cross-object call needs the callee's data base, not the caller's. A symbol resolved from an FDPIC library comes back as a descriptor, and R_ARM_FUNCDESC_VALUE was treating it as a code address and pairing it with the importing module's GOT. It now copies both words, so the library runs with its own. An object with no imports has no PLT and so no DT_PLTGOT, but it still has a GOT and still has to be entered with it. Without the fallback its descriptors carried a data base of zero and the library read its globals through a null pointer. R_ARM_FUNCDESC, a pointer to a descriptor, wrapped a library's descriptor in a second one. It now stores the library's descriptor as it is. The flag that says a resolved value is a descriptor was set only for an import and never cleared, so the next relocation against a symbol of the module itself took that symbol for a descriptor too. It is cleared there. libelf_symname() was static, and reading a DT_NEEDED name needs it. A module with DT_NEEDED is refused where CONFIG_LIBC_ELF_MAXDEPEND is zero, since that is where the dependency logic is compiled out. A DT_NEEDED library is one shared instance, its data included, because the loader returns the object already in the registry. A module started with exec() is different: that path loads the module afresh each time, so two running instances have separate data while sharing one copy of the text. Built for mps3-an547:picostest with CONFIG_FDPIC both ways. Run on mps2-an500:xipfs under QEMU: fdpicxip solib loads libcounter.so by name out of DT_NEEDED, two instances share one pinned copy of its text, and the library is unloaded, and its pin given back, when the second one exits. A library also opened with dlopen() stays loaded after its DT_NEEDED user exits, and dlclose() unloads it. With CONFIG_ARCH_ADDRENV the program runs in its own address space, which a library libelf_insert() loads cannot reach, so DT_NEEDED is refused there as before. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli --- arch/arm/include/elf.h | 7 + include/nuttx/lib/elf.h | 10 +- libs/libc/elf/elf.h | 11 ++ libs/libc/elf/elf_bind.c | 241 +++++++++++++++++++++-- libs/libc/elf/elf_depend.c | 9 + libs/libc/elf/elf_remove.c | 8 +- libs/libc/elf/elf_symbols.c | 106 +++++----- libs/libc/machine/arm/armv7-m/arch_elf.c | 30 ++- libs/libc/machine/arm/armv8-m/arch_elf.c | 30 ++- 9 files changed, 364 insertions(+), 88 deletions(-) diff --git a/arch/arm/include/elf.h b/arch/arm/include/elf.h index b0bcf2c676c..813bafc56ca 100644 --- a/arch/arm/include/elf.h +++ b/arch/arm/include/elf.h @@ -296,6 +296,12 @@ #define ARCH_ELFDATA_SET_PLTREL(d, v) (d)->pltrel = (v) +/* Whether the value a relocation resolved to is itself a descriptor, which + * it is when the symbol came from another object rather than from this one. + */ + +#define ARCH_ELFDATA_SET_SYMISDESC(d, v) (d)->symisdesc = (v) + /**************************************************************************** * Public Types ****************************************************************************/ @@ -315,6 +321,7 @@ struct arch_elfdata_s uintptr_t gotbase; /* DT_PLTGOT: this object's data base */ uint16_t ndesc; /* Capacity, in descriptors */ uint16_t usedesc; /* Next free slot */ + uint8_t symisdesc; /* Symbol value is a descriptor, not code */ /* The pool the descriptors are taken from */ diff --git a/include/nuttx/lib/elf.h b/include/nuttx/lib/elf.h index fcbbdc8448c..fbe7269596c 100644 --- a/include/nuttx/lib/elf.h +++ b/include/nuttx/lib/elf.h @@ -92,12 +92,12 @@ * portion of the build */ -/* dlopen() needs a name too: it is the only way to tell that a library is - * already loaded. +/* A name is also the only way to tell that a library is already loaded, so + * dlopen() and DT_NEEDED need one. */ #if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) || \ - defined(CONFIG_LIBC_DLFCN) + defined(CONFIG_LIBC_DLFCN) || CONFIG_LIBC_ELF_MAXDEPEND > 0 # define HAVE_LIBC_ELF_NAMES # define LIBC_ELF_NAMEMAX NAME_MAX #endif @@ -798,7 +798,9 @@ FAR const void *libelf_getsymbol(FAR void *handle, FAR const char *name); * Name: libelf_uninit * * Description: - * Uninitialize module resources. + * Uninitialize module resources. Gives up everything the module holds, + * the DT_NEEDED libraries included, so the caller must hold the last + * reference. * ****************************************************************************/ diff --git a/libs/libc/elf/elf.h b/libs/libc/elf/elf.h index fcf3227cf11..8c661c6a7e6 100644 --- a/libs/libc/elf/elf.h +++ b/libs/libc/elf/elf.h @@ -238,6 +238,17 @@ int libelf_reallocbuffer(FAR struct mod_loadinfo_s *loadinfo, int libelf_freebuffers(FAR struct mod_loadinfo_s *loadinfo); +/**************************************************************************** + * Name: libelf_symname + * + * Description: + * Read a name out of a string table into the I/O buffer. + * + ****************************************************************************/ + +int libelf_symname(FAR struct mod_loadinfo_s *loadinfo, + FAR const Elf_Sym *sym, Elf_Off sh_offset); + /**************************************************************************** * Name: libelf_addr * diff --git a/libs/libc/elf/elf_bind.c b/libs/libc/elf/elf_bind.c index 510b336c614..6d2dccead67 100644 --- a/libs/libc/elf/elf_bind.c +++ b/libs/libc/elf/elf_bind.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -46,6 +47,14 @@ * Pre-processor Definitions ****************************************************************************/ +/* With an address environment the program lives in its own address space, + * which a library loaded by libelf_insert() cannot reach. + */ + +#if CONFIG_LIBC_ELF_MAXDEPEND > 0 && !defined(CONFIG_ARCH_ADDRENV) +# define LIBELF_NEEDED +#endif + #define I_REL 0 /* Index into relxxx[] arrays for relocations */ #define I_PLT 1 /* ... for PLTs */ #define N_RELS 2 /* Number of relxxx[] indexes */ @@ -67,7 +76,13 @@ #if defined(ARCH_ELFDATA) && defined(ARCH_ELFDATA_SET_PLTREL) # define ARCH_ELFDATA_PLTREL(v) ARCH_ELFDATA_SET_PLTREL(&arch_data, v) #else -# define ARCH_ELFDATA_PLTREL(v) +# define ARCH_ELFDATA_PLTREL(v) ((void)(v)) +#endif + +#if defined(ARCH_ELFDATA) && defined(ARCH_ELFDATA_SET_SYMISDESC) +# define ARCH_ELFDATA_SYMISDESC(v) ARCH_ELFDATA_SET_SYMISDESC(&arch_data, v) +#else +# define ARCH_ELFDATA_SYMISDESC(v) ((void)(v)) #endif #if defined(ARCH_ELFDATA) && defined(ARCH_ELFDATA_INIT) @@ -93,7 +108,12 @@ typedef struct int idx; } Elf_SymCache; -struct +/* Where a dynamic object's relocation tables live. Per load, not per file: + * loading a DT_NEEDED library re-enters this function, and a shared + * instance would be overwritten by the nested load. + */ + +struct reldata_s { int stroff; /* offset to string table */ int symoff; /* offset to symbol table */ @@ -102,12 +122,66 @@ struct int reloff[2]; /* offset to the relocation section */ int relsz[2]; /* size of relocation table */ int relrela[2]; /* type of relocation type - 0: DT_REL / 1: DT_RELA */ -} reldata; +}; /**************************************************************************** * Private Functions ****************************************************************************/ +#ifdef LIBELF_NEEDED +/**************************************************************************** + * Name: libelf_insertneeded + * + * Description: + * Load a library a module names in DT_NEEDED. A bare name is looked for + * along LD_LIBRARY_PATH, as dlopen() looks for it, and the module is + * registered under its base name, so a library two modules name is loaded + * once and reference counted. + * + * Returned Value: + * The handle of the loaded library, or NULL. + * + ****************************************************************************/ + +static FAR void *libelf_insertneeded(FAR const char *name) +{ + FAR const char *modname; + + modname = strrchr(name, '/'); + modname = modname != NULL ? modname + 1 : name; + +#ifdef CONFIG_LIBC_ENVPATH + if (name[0] != '/') + { + FAR void *handle = NULL; + FAR char *fullpath; + ENVPATH_HANDLE env; + + env = envpath_init("LD_LIBRARY_PATH"); + if (env != NULL) + { + while ((fullpath = envpath_next(env, name)) != NULL) + { + handle = libelf_insert(fullpath, modname); + lib_free(fullpath); + + if (handle != NULL) + { + break; + } + } + + envpath_release(env); + } + + return handle; + } +#endif + + return libelf_insert(name, modname); +} +#endif + /**************************************************************************** * Name: libelf_readrels * @@ -702,6 +776,13 @@ static int libelf_relocatedyn(FAR struct module_s *modp, int i; int idx_rel; int idx_sym; +#ifdef LIBELF_NEEDED + int j; + uintptr_t libs[CONFIG_LIBC_ELF_MAXDEPEND]; + int nlibs = 0; +#endif + struct reldata_s reldata; + bool symfromlib; /* Define potential architecture specific elf data container */ @@ -764,6 +845,33 @@ static int libelf_relocatedyn(FAR struct module_s *modp, case DT_PLTRELSZ: reldata.relsz[I_PLT] = dyn[i].d_un.d_val; break; + case DT_NEEDED: +#ifdef LIBELF_NEEDED + + /* Remember it; the name lives in the string table, which is + * not located until the loop has seen DT_STRTAB. + */ + + if (nlibs >= CONFIG_LIBC_ELF_MAXDEPEND) + { + berr("ERROR: More than %d DT_NEEDED entries\n", + CONFIG_LIBC_ELF_MAXDEPEND); + lib_free(sym); + lib_free(rels); + lib_free(dyn); + return -ENOEXEC; + } + + libs[nlibs++] = dyn[i].d_un.d_val; + break; +#else + berr("ERROR: Cannot load a DT_NEEDED library\n"); + lib_free(sym); + lib_free(rels); + lib_free(dyn); + return -ENOSYS; +#endif + case DT_PLTGOT: /* The object's data base. Every function descriptor built @@ -842,9 +950,81 @@ static int libelf_relocatedyn(FAR struct module_s *modp, loadinfo->gotbase); } - /* After the loop, because DT_PLTGOT is read there. Both relocation - * tables are walked under this one arch_data, so the pool cursor - * survives from one to the next. + /* Load whatever the object names in DT_NEEDED. This is what dlopen() + * does, through the same libelf_insert(), but the loader is also the + * kernel's module loader, which has no dlfcn. + */ + +#ifdef LIBELF_NEEDED + + symhdr = &loadinfo->shdr[loadinfo->dsymtabidx]; + + for (i = 0; i < nlibs; i++) + { + Elf_Sym namesym; + FAR void *handle; + + /* The name is a string table offset, which is what st_name is, so + * the existing reader can fetch it. + */ + + memset(&namesym, 0, sizeof(namesym)); + namesym.st_name = libs[i]; + + ret = libelf_symname(loadinfo, &namesym, + loadinfo->shdr[symhdr->sh_link].sh_offset); + if (ret < 0) + { + berr("ERROR: DT_NEEDED %d has no name\n", i); + lib_free(sym); + lib_free(rels); + lib_free(dyn); + return ret; + } + + handle = libelf_insertneeded((FAR const char *)loadinfo->iobuffer); + if (handle == NULL) + { + berr("ERROR: Cannot open needed library %s\n", + (FAR char *)loadinfo->iobuffer); + lib_free(sym); + lib_free(rels); + lib_free(dyn); + return -ELIBACC; + } + + binfo("Opened needed library %s\n", (FAR char *)loadinfo->iobuffer); + + /* The dependency holds the library from now on, in place of the + * reference libelf_insert() took, so libelf_undepend() lets it go. + */ + + libelf_registry_lock(); + ret = libelf_depend(modp, handle); + if (ret >= 0) + { + ((FAR struct module_s *)handle)->nopen--; + } + + libelf_registry_unlock(); + + if (ret < 0) + { + berr("ERROR: Cannot depend on %s: %d\n", + (FAR char *)loadinfo->iobuffer, ret); + libelf_remove(handle); + lib_free(sym); + lib_free(rels); + lib_free(dyn); + return ret; + } + } + +#endif + + /* Must follow the tag loop, which is where DT_PLTGOT is read. Both + * relocation tables are walked under this one arch_data, so a cursor in + * it spans the object. */ ARCH_ELFDATA_SETUP(loadinfo); @@ -943,14 +1123,36 @@ static int libelf_relocatedyn(FAR struct module_s *modp, { FAR void *ep; + symfromlib = false; 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. + /* libelf_findglobal() searches only the globally + * registered symbols, and has left the name in the + * I/O buffer. Try the modules this one depends on, + * its DT_NEEDED libraries among them, then the table + * exec() supplied. */ +#ifdef LIBELF_NEEDED + for (j = 0; ep == NULL && j < CONFIG_LIBC_ELF_MAXDEPEND; + j++) + { + FAR struct module_s *dep = modp->dependencies[j]; + + if (dep != NULL) + { + ep = (FAR void *) + libelf_getsymbol(dep, + (FAR char *)loadinfo->iobuffer); + + /* An FDPIC object exports descriptors */ + + symfromlib = ep != NULL && dep->gotbase != 0; + } + } +#endif + if (ep == NULL && exports != NULL) { FAR const struct symtab_s *sm; @@ -998,6 +1200,12 @@ static int libelf_relocatedyn(FAR struct module_s *modp, extsym.st_value = (uintptr_t)ep; + /* Whether the resolved value is itself a descriptor, + * which it is when the symbol came from a library. + */ + + ARCH_ELFDATA_SYMISDESC(symfromlib); + ret = up_relocate(rel, &extsym, addr, ARCH_ELFDATA_PARM); if (ret < 0) { @@ -1011,10 +1219,9 @@ static int libelf_relocatedyn(FAR struct module_s *modp, } 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. + /* A symbol defined inside this object. Its value is + * the symbol's own, translated; the addend stays where + * the relocation type expects it. */ Elf_Sym defsym = sym[idx_sym]; @@ -1022,6 +1229,8 @@ static int libelf_relocatedyn(FAR struct module_s *modp, defsym.st_value = libelf_addr(loadinfo, sym[idx_sym].st_value); + ARCH_ELFDATA_SYMISDESC(false); + addr = libelf_addr(loadinfo, rel->r_offset); if (reldata.relrela[idx_rel] == 1) @@ -1058,6 +1267,8 @@ static int libelf_relocatedyn(FAR struct module_s *modp, dynsym.st_value = libelf_addr(loadinfo, *(FAR uint32_t *)addr); + ARCH_ELFDATA_SYMISDESC(false); + ret = up_relocate(rel, &dynsym, addr, ARCH_ELFDATA_PARM); } @@ -1073,9 +1284,7 @@ static int libelf_relocatedyn(FAR struct module_s *modp, } } - /* Hand back what the relocations consumed. The error paths above do - * not bother: the load is being abandoned, so the cursor has no reader. - */ + /* Hand back what the relocations consumed. */ ARCH_ELFDATA_TEARDOWN(loadinfo); diff --git a/libs/libc/elf/elf_depend.c b/libs/libc/elf/elf_depend.c index c08f1434d93..db4f8b18f67 100644 --- a/libs/libc/elf/elf_depend.c +++ b/libs/libc/elf/elf_depend.c @@ -174,6 +174,15 @@ int libelf_undepend(FAR struct module_s *importer) } importer->dependencies[i] = NULL; + + /* A library loaded for a DT_NEEDED entry is held only by the + * modules that depend on it, so the last of them unloads it. + */ + + if (exporter->dependents == 0 && exporter->nopen == 0) + { + libelf_remove(exporter); + } } } diff --git a/libs/libc/elf/elf_remove.c b/libs/libc/elf/elf_remove.c index 8f7d54bed89..82d64746e28 100644 --- a/libs/libc/elf/elf_remove.c +++ b/libs/libc/elf/elf_remove.c @@ -42,7 +42,9 @@ * Name: libelf_uninit * * Description: - * Uninitialize module resources. + * Uninitialize module resources. Gives up everything the module holds, + * the DT_NEEDED libraries included, so the caller must hold the last + * reference. * ****************************************************************************/ @@ -244,8 +246,8 @@ int libelf_remove(FAR void *handle) } /* Give back a reference. The module goes only when the last one does, - * so an rmmod() cannot pull a module out from under a dlopen() that is - * still holding it. + * so an rmmod() cannot pull a module out from under a dlopen(), or from + * under a module that names it in DT_NEEDED. */ if (modp->nopen > 1) diff --git a/libs/libc/elf/elf_symbols.c b/libs/libc/elf/elf_symbols.c index 3ba6fb5eeda..84143b68ba3 100644 --- a/libs/libc/elf/elf_symbols.c +++ b/libs/libc/elf/elf_symbols.c @@ -77,6 +77,57 @@ extern int nglobals; * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: libelf_symcallback + * + * Description: + * libelf_registry_foreach() callback function. Test if the provided + * module, modp, exports the symbol of interest. If so, return that symbol + * value and setup the module dependency relationship. + * + * Returned Value: + * 0 (OK) is returned on success and a negated errno is returned on + * failure. + * + ****************************************************************************/ + +static int libelf_symcallback(FAR struct module_s *modp, FAR void *arg) +{ + FAR struct mod_exportinfo_s *exportinfo = (FAR struct mod_exportinfo_s *) + arg; + + /* Check if this module exports a symbol of that name */ + + exportinfo->symbol = symtab_findbyname(modp->modinfo.exports, + exportinfo->name, + modp->modinfo.nexports); + + if (exportinfo->symbol != NULL) + { + /* Yes.. save the dependency relationship and return SYM_FOUND to + * stop the traversal. + */ + +#if CONFIG_LIBC_ELF_MAXDEPEND > 0 + int ret = libelf_depend(exportinfo->modp, modp); + + if (ret < 0) + { + berr("ERROR: libelf_depend failed: %d\n", ret); + return ret; + } +#endif + + return SYM_FOUND; + } + + return SYM_NOT_FOUND; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + /**************************************************************************** * Name: libelf_symname * @@ -93,8 +144,8 @@ extern int nglobals; * ****************************************************************************/ -static int libelf_symname(FAR struct mod_loadinfo_s *loadinfo, - FAR const Elf_Sym *sym, Elf_Off sh_offset) +int libelf_symname(FAR struct mod_loadinfo_s *loadinfo, + FAR const Elf_Sym *sym, Elf_Off sh_offset) { FAR uint8_t *buffer; off_t offset; @@ -187,57 +238,6 @@ static int libelf_symname(FAR struct mod_loadinfo_s *loadinfo, return OK; } -/**************************************************************************** - * Name: libelf_symcallback - * - * Description: - * libelf_registry_foreach() callback function. Test if the provided - * module, modp, exports the symbol of interest. If so, return that symbol - * value and setup the module dependency relationship. - * - * Returned Value: - * 0 (OK) is returned on success and a negated errno is returned on - * failure. - * - ****************************************************************************/ - -static int libelf_symcallback(FAR struct module_s *modp, FAR void *arg) -{ - FAR struct mod_exportinfo_s *exportinfo = (FAR struct mod_exportinfo_s *) - arg; - - /* Check if this module exports a symbol of that name */ - - exportinfo->symbol = symtab_findbyname(modp->modinfo.exports, - exportinfo->name, - modp->modinfo.nexports); - - if (exportinfo->symbol != NULL) - { - /* Yes.. save the dependency relationship and return SYM_FOUND to - * stop the traversal. - */ - -#if CONFIG_LIBC_ELF_MAXDEPEND > 0 - int ret = libelf_depend(exportinfo->modp, modp); - - if (ret < 0) - { - berr("ERROR: libelf_depend failed: %d\n", ret); - return ret; - } -#endif - - return SYM_FOUND; - } - - return SYM_NOT_FOUND; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - /**************************************************************************** * Name: libelf_findsymtab * diff --git a/libs/libc/machine/arm/armv7-m/arch_elf.c b/libs/libc/machine/arm/armv7-m/arch_elf.c index e0468c71ede..03e8e9e5981 100644 --- a/libs/libc/machine/arm/armv7-m/arch_elf.c +++ b/libs/libc/machine/arm/armv7-m/arch_elf.c @@ -210,28 +210,36 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, "at addr=%08" PRIxPTR " to sym=%p st_value=%08" PRIx32 "\n", addr, sym, sym->st_value); - if (data->pltrel) + if (data->symisdesc) + { + /* Resolved to a function in another object, which published a + * descriptor of its own. Take both words: the callee has to + * run with its own data base, not ours. + */ + + *desc = *(struct fdpic_desc_s *)sym->st_value; + } + else if (data->pltrel) { /* A lazy descriptor holds its PLT stub address, not an * addend. Overwrite it, do not add to it. */ desc->entry = sym->st_value; + desc->got = data->gotbase; } else { desc->entry = sym->st_value + desc->entry; + desc->got = data->gotbase; } - - desc->got = data->gotbase; } break; case R_ARM_FUNCDESC: { - /* A pointer to a descriptor, which the loader has to supply. - * Carve one out of the pool reserved behind the writable segment - * and store its address. + /* A pointer to a descriptor the loader must supply. Carve one + * from the pool and store its address. */ struct fdpic_desc_s *desc; @@ -249,6 +257,16 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, return -ENOEXEC; } + /* Another object's function comes with a descriptor of its own, + * which is already what this word must point to. + */ + + if (data->symisdesc) + { + *(uint32_t *)addr = sym->st_value; + break; + } + if (data->usedesc >= data->ndesc) { berr("ERROR: Out of function descriptors\n"); diff --git a/libs/libc/machine/arm/armv8-m/arch_elf.c b/libs/libc/machine/arm/armv8-m/arch_elf.c index a0e0ec998c3..479a045d349 100644 --- a/libs/libc/machine/arm/armv8-m/arch_elf.c +++ b/libs/libc/machine/arm/armv8-m/arch_elf.c @@ -210,28 +210,36 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, "at addr=%08" PRIxPTR " to sym=%p st_value=%08" PRIx32 "\n", addr, sym, sym->st_value); - if (data->pltrel) + if (data->symisdesc) + { + /* Resolved to a function in another object, which published a + * descriptor of its own. Take both words: the callee has to + * run with its own data base, not ours. + */ + + *desc = *(struct fdpic_desc_s *)sym->st_value; + } + else if (data->pltrel) { /* A lazy descriptor holds its PLT stub address, not an * addend. Overwrite it, do not add to it. */ desc->entry = sym->st_value; + desc->got = data->gotbase; } else { desc->entry = sym->st_value + desc->entry; + desc->got = data->gotbase; } - - desc->got = data->gotbase; } break; case R_ARM_FUNCDESC: { - /* A pointer to a descriptor, which the loader has to supply. - * Carve one out of the pool reserved behind the writable segment - * and store its address. + /* A pointer to a descriptor the loader must supply. Carve one + * from the pool and store its address. */ struct fdpic_desc_s *desc; @@ -249,6 +257,16 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, return -ENOEXEC; } + /* Another object's function comes with a descriptor of its own, + * which is already what this word must point to. + */ + + if (data->symisdesc) + { + *(uint32_t *)addr = sym->st_value; + break; + } + if (data->usedesc >= data->ndesc) { berr("ERROR: Out of function descriptors\n");