libs/libc/elf, binfmt: Describe the GOT by base and size, not by index.

gotindex named the .got section header, and every user then reached through
shdr[] for what it actually wanted.  Only one of the five wanted the index.

gotbase and gotsize say it directly.  gotsize is the extent of .got and is
also what says the object has one, and gotbase is where the GOT ended up:
the placed address of .got for an ordinary object, or DT_PLTGOT for an FDPIC
one, which libelf_bind() already reads.  Both are set in libelf_loadfile(),
after the sections are placed, so gotbase is the address the object will be
read at rather than the one it was linked for.

The GOT walk in libelf_loadfile() now runs only when there is a base, which
also keeps it off an FDPIC object.  An FDPIC object's sections are never
placed, so .got carried a link time sh_addr there, and the walk read and
wrote through it.  Its GOT is relocated through its own relocations.

The check that gates libelf_xipacquire() runs before the load, when neither
field is set, so it looks the section up by name.  It hands the index it
found to libelf_loadfile(), which is the only reason that function takes
one: the object is searched once, not twice.

One behaviour changes: a .got that exists but is empty now reads as no GOT.
There is nothing for any of the five users to do with an empty one.

Built for pimoroni-pico-2-plus with CONFIG_PIC, CONFIG_ELF and
CONFIG_LIBC_ELF, and for mps3-an547:bl, which is the board that read the
index.  Run on QEMU with mps3-an547:picostest, which loads PIC ELF modules
from a romfs: hello prints, and ostest reaches the timed mutex test, the
same as before the change.

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-09-03 01:00:13 +02:00 committed by Alan C. Assis
parent 1c72098fd5
commit 0518ccb9ca
6 changed files with 60 additions and 25 deletions

View file

@ -139,7 +139,7 @@ static int elf_loadbinary(FAR struct binary_s *binp,
/* Bind the program to the exported symbol table */
if (loadinfo.ehdr.e_type == ET_REL || loadinfo.gotindex >= 0)
if (loadinfo.ehdr.e_type == ET_REL || loadinfo.gotsize != 0)
{
ret = libelf_bind(&binp->mod, &loadinfo, exports, nexports);
if (ret != 0)
@ -270,7 +270,7 @@ static int elf_loadbinary(FAR struct binary_s *binp,
libelf_dumpentrypt(&loadinfo);
#ifdef CONFIG_PIC
if (loadinfo.gotindex >= 0)
if (loadinfo.gotsize != 0)
{
FAR struct dspace_s *dspaces = kmm_zalloc(sizeof(struct dspace_s));
@ -280,7 +280,7 @@ static int elf_loadbinary(FAR struct binary_s *binp,
goto errout_with_load;
}
dspaces->region = (FAR void *)loadinfo.shdr[loadinfo.gotindex].sh_addr;
dspaces->region = (FAR void *)loadinfo.gotbase;
dspaces->crefs = 1;
binp->picbase = (FAR void *)dspaces;
}

View file

@ -147,7 +147,7 @@ int board_boot_image(const char *path, uint32_t hdr_size)
}
bss = libelf_findsection(&loadinfo, ".bss");
got = loadinfo.shdr[loadinfo.gotindex].sh_addr;
got = loadinfo.gotbase;
msp = loadinfo.shdr[bss].sh_addr + loadinfo.shdr[bss].sh_size +
CONFIG_IDLETHREAD_STACKSIZE;

View file

@ -262,7 +262,6 @@ struct mod_loadinfo_s
uint16_t buflen; /* size of iobuffer[] */
int filfd; /* Descriptor for the file being loaded */
int nexports; /* ET_DYN - Number of symbols exported */
int gotindex; /* Index to the GOT section */
uintptr_t xipbase; /* if elf is position independent, and use
* romfs/tmps, we can try get xipbase,
* skip the copy.
@ -278,11 +277,14 @@ struct mod_loadinfo_s
FAR struct file *pinfile;
#endif
/* The object's data base, from DT_PLTGOT. An FDPIC module runs with this
* in the PIC base register.
/* The object's GOT. gotbase is where it ended up: the placed address of
* .got, or DT_PLTGOT for an FDPIC object, which runs with it in the PIC
* base register. gotsize is the extent of .got, and is also what says
* the object has one at all.
*/
uintptr_t gotbase;
size_t gotsize;
/* Pool of function descriptors behind the writable segment. Reserved
* when the segment is sized, and bounded by the relocation count.

View file

@ -337,7 +337,7 @@ static int libelf_relocate(FAR struct module_s *modp,
/* Calculate the relocation address. */
if (loadinfo->gotindex >= 0)
if (loadinfo->gotsize != 0)
{
if (sym->st_shndx == SHN_UNDEF)
{
@ -345,8 +345,7 @@ static int libelf_relocate(FAR struct module_s *modp,
* to the value of the symbol.
*/
FAR Elf_Shdr *gotsec = &loadinfo->shdr[loadinfo->gotindex];
FAR uintptr_t *gotaddr = (FAR uintptr_t *)(gotsec->sh_addr +
FAR uintptr_t *gotaddr = (FAR uintptr_t *)(loadinfo->gotbase +
*((FAR uintptr_t *)(dstsec->sh_addr + rel->r_offset)));
*gotaddr = sym->st_value;

View file

@ -366,13 +366,19 @@ static void libelf_set_emptysect_vma(FAR struct mod_loadinfo_s *loadinfo,
* Read the section data into memory. Section addresses in the shdr[] are
* updated to point to the corresponding position in the memory.
*
* Input Parameters:
* loadinfo - The load state.
* gotidx - Section index of .got, which the caller has already looked
* up, or a negative value if the object has none.
*
* Returned Value:
* 0 (OK) is returned on success and a negated errno is returned on
* failure.
*
****************************************************************************/
static inline int libelf_loadfile(FAR struct mod_loadinfo_s *loadinfo)
static inline int libelf_loadfile(FAR struct mod_loadinfo_s *loadinfo,
int gotidx)
{
FAR uint8_t *text = (FAR uint8_t *)loadinfo->textalloc;
FAR uint8_t *data = (FAR uint8_t *)loadinfo->datastart;
@ -546,13 +552,29 @@ skipload:
}
}
/* Update GOT table */
/* Note the GOT. The sections are placed by now, thus .got carries the
* address it will be read at. An FDPIC object's sections are never
* placed, and libelf_bind() takes its base from DT_PLTGOT instead.
*/
if (loadinfo->gotindex >= 0)
if (gotidx >= 0)
{
FAR Elf_Shdr *gotshdr = &loadinfo->shdr[loadinfo->gotindex];
FAR uintptr_t *got = (FAR uintptr_t *)gotshdr->sh_addr;
FAR uintptr_t *end = got + gotshdr->sh_size / sizeof(uintptr_t);
loadinfo->gotsize = loadinfo->shdr[gotidx].sh_size;
if (!loadinfo->fdpic)
{
loadinfo->gotbase = loadinfo->shdr[gotidx].sh_addr;
}
}
/* Update GOT table. An FDPIC object's entries are relocated through its
* own relocations, so there is nothing to do for one here.
*/
if (loadinfo->gotbase != 0)
{
FAR uintptr_t *got = (FAR uintptr_t *)loadinfo->gotbase;
FAR uintptr_t *end = got + loadinfo->gotsize / sizeof(uintptr_t);
for (; got < end; got++)
{
@ -699,6 +721,7 @@ void libelf_pinrelease(FAR struct file **pinfile)
int libelf_load(FAR struct mod_loadinfo_s *loadinfo)
{
int gotidx;
int ret;
int i;
@ -714,10 +737,15 @@ int libelf_load(FAR struct mod_loadinfo_s *loadinfo)
goto errout_with_buffers;
}
loadinfo->gotindex = libelf_findsection(loadinfo, ".got");
if (loadinfo->gotindex >= 0)
/* An object with a GOT is position independent, thus its read-only part
* may be able to stay where the filesystem holds it. Keep the index:
* libelf_loadfile() notes the section once it has placed it.
*/
gotidx = libelf_findsection(loadinfo, ".got");
if (gotidx >= 0)
{
binfo("GOT section found! index %d\n", loadinfo->gotindex);
binfo("GOT section found! index %d\n", gotidx);
libelf_xipacquire(loadinfo);
}
@ -889,7 +917,7 @@ int libelf_load(FAR struct mod_loadinfo_s *loadinfo)
/* Load ELF section data into memory */
ret = libelf_loadfile(loadinfo);
ret = libelf_loadfile(loadinfo, gotidx);
if (ret < 0)
{
berr("ERROR: libelf_loadfile failed: %d\n", ret);
@ -935,6 +963,7 @@ errout_with_buffers:
#ifdef CONFIG_ARCH_ADDRENV
int libelf_load_with_addrenv(FAR struct mod_loadinfo_s *loadinfo)
{
int gotidx;
int ret;
binfo("loadinfo: %p\n", loadinfo);
@ -949,10 +978,15 @@ int libelf_load_with_addrenv(FAR struct mod_loadinfo_s *loadinfo)
goto errout_with_buffers;
}
loadinfo->gotindex = libelf_findsection(loadinfo, ".got");
if (loadinfo->gotindex >= 0)
/* An object with a GOT is position independent, thus its read-only part
* may be able to stay where the filesystem holds it. Keep the index:
* libelf_loadfile() notes the section once it has placed it.
*/
gotidx = libelf_findsection(loadinfo, ".got");
if (gotidx >= 0)
{
binfo("GOT section found! index %d\n", loadinfo->gotindex);
binfo("GOT section found! index %d\n", gotidx);
libelf_xipacquire(loadinfo);
}
@ -980,7 +1014,7 @@ int libelf_load_with_addrenv(FAR struct mod_loadinfo_s *loadinfo)
goto errout_with_buffers;
}
ret = libelf_loadfile(loadinfo);
ret = libelf_loadfile(loadinfo, gotidx);
if (ret < 0)
{
berr("ERROR: libelf_loadfile failed: %d\n", ret);

View file

@ -451,7 +451,7 @@ int libelf_symvalue(FAR struct module_s *modp,
(uintptr_t)(sym->st_value + secbase));
sym->st_value += secbase;
if (loadinfo->gotindex >= 0)
if (loadinfo->gotsize != 0)
{
sym->st_value -= loadinfo->shdr[sym->st_shndx].sh_offset;
}