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) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
This commit is contained in:
Marco Casaroli 2026-08-03 11:28:49 +02:00 committed by Alan C. Assis
parent 450cfad383
commit 04dc50e71a
2 changed files with 62 additions and 3 deletions

View file

@ -35,6 +35,7 @@
#include <nuttx/arch.h>
#include <nuttx/cache.h>
#include <nuttx/elf.h>
#include <nuttx/symtab.h>
#include <nuttx/lib/elf.h>
#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;

View file

@ -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;
}