diff --git a/binfmt/elf.c b/binfmt/elf.c index fc896300538..a4ad873a65a 100644 --- a/binfmt/elf.c +++ b/binfmt/elf.c @@ -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; } diff --git a/boards/arm/mps/mps3-an547/src/mps3_bringup.c b/boards/arm/mps/mps3-an547/src/mps3_bringup.c index 5c7edfb523c..e3c044aacb2 100644 --- a/boards/arm/mps/mps3-an547/src/mps3_bringup.c +++ b/boards/arm/mps/mps3-an547/src/mps3_bringup.c @@ -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; diff --git a/include/nuttx/lib/elf.h b/include/nuttx/lib/elf.h index 7eee93fe61e..e30399eede9 100644 --- a/include/nuttx/lib/elf.h +++ b/include/nuttx/lib/elf.h @@ -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. diff --git a/libs/libc/elf/elf_bind.c b/libs/libc/elf/elf_bind.c index 5409b79bfbb..4e0727222dd 100644 --- a/libs/libc/elf/elf_bind.c +++ b/libs/libc/elf/elf_bind.c @@ -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; diff --git a/libs/libc/elf/elf_load.c b/libs/libc/elf/elf_load.c index 6abdff25424..8536e8e5c68 100644 --- a/libs/libc/elf/elf_load.c +++ b/libs/libc/elf/elf_load.c @@ -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); diff --git a/libs/libc/elf/elf_symbols.c b/libs/libc/elf/elf_symbols.c index b1feda244c5..3ba6fb5eeda 100644 --- a/libs/libc/elf/elf_symbols.c +++ b/libs/libc/elf/elf_symbols.c @@ -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; }