modlib:support modlib can load PIC elf

Signed-off-by: anjiahao <anjiahao@xiaomi.com>
This commit is contained in:
anjiahao 2024-07-03 15:33:48 +08:00 committed by GUIDINGLI
parent cfc90ad1f3
commit 112b6fd9a5
8 changed files with 174 additions and 30 deletions

View file

@ -502,11 +502,21 @@ LDMODULEFLAGS = -r -T $(call CONVERT_PATH,$(TOPDIR)/libs/libc/modlib/gnu-elf.ld)
# ELF module definitions # ELF module definitions
CELFFLAGS = $(CFLAGS) -fvisibility=hidden -mlong-calls # --target1-abs ifeq ($(CONFIG_PIC),y)
CXXELFFLAGS = $(CXXFLAGS) -fvisibility=hidden CFLAGS += --fixed-r10
PICFLAGS = -fpic -fPIE -mno-pic-data-is-text-relative \
-msingle-pic-base -mpic-register=r10
LDELFFLAGS = -r -e main # Generate an executable elf, need to ignore undefined symbols
LDELFFLAGS += -T $(call CONVERT_PATH,$(TOPDIR)$(DELIM)libs$(DELIM)libc$(DELIM)modlib$(DELIM)gnu-elf.ld) LDELFFLAGS += --unresolved-symbols=ignore-in-object-files --emit-relocs
else
LDELFFLAGS += -r
endif
CELFFLAGS = $(CFLAGS) $(PICFLAGS) -fvisibility=hidden -mlong-calls # --target1-abs
CXXELFFLAGS = $(CXXFLAGS) $(PICFLAGS) -fvisibility=hidden
LDELFFLAGS += -e main -T $(call CONVERT_PATH,$(TOPDIR)$(DELIM)libs$(DELIM)libc$(DELIM)modlib$(DELIM)gnu-elf.ld)
# Zig toolchain # Zig toolchain

View file

@ -28,7 +28,7 @@ config BINFMT_LOADABLE
Automatically selected if a loadable binary format is selected. Automatically selected if a loadable binary format is selected.
config PIC config PIC
bool bool "Executable elf position-independent support"
default n default n
---help--- ---help---
Automatically selected if the binary format requires position Automatically selected if the binary format requires position

View file

@ -33,6 +33,7 @@
#include <nuttx/arch.h> #include <nuttx/arch.h>
#include <nuttx/binfmt/binfmt.h> #include <nuttx/binfmt/binfmt.h>
#include <nuttx/kmalloc.h>
#ifdef CONFIG_ELF #ifdef CONFIG_ELF
@ -119,7 +120,7 @@ static int elf_loadbinary(FAR struct binary_s *binp,
/* Bind the program to the exported symbol table */ /* Bind the program to the exported symbol table */
if (loadinfo.ehdr.e_type == ET_REL) if (loadinfo.ehdr.e_type == ET_REL || loadinfo.gotindex >= 0)
{ {
ret = modlib_bind(&binp->mod, &loadinfo, exports, nexports); ret = modlib_bind(&binp->mod, &loadinfo, exports, nexports);
if (ret != 0) if (ret != 0)
@ -207,6 +208,23 @@ static int elf_loadbinary(FAR struct binary_s *binp,
#endif #endif
modlib_dumpentrypt(&loadinfo); modlib_dumpentrypt(&loadinfo);
#ifdef CONFIG_PIC
if (loadinfo.gotindex >= 0)
{
FAR struct dspace_s *dspaces = kmm_zalloc(sizeof(struct dspace_s));
if (dspaces == NULL)
{
ret = -ENOMEM;
goto errout_with_load;
}
dspaces->region = (FAR void *)loadinfo.shdr[loadinfo.gotindex].sh_addr;
dspaces->crefs = 1;
binp->picbase = (FAR void *)dspaces;
}
#endif
modlib_uninitialize(&loadinfo); modlib_uninitialize(&loadinfo);
return OK; return OK;

View file

@ -231,6 +231,7 @@ struct mod_loadinfo_s
uint16_t buflen; /* size of iobuffer[] */ uint16_t buflen; /* size of iobuffer[] */
int filfd; /* Descriptor for the file being loaded */ int filfd; /* Descriptor for the file being loaded */
int nexports; /* ET_DYN - Number of symbols exported */ int nexports; /* ET_DYN - Number of symbols exported */
int gotindex; /* Index to the GOT section */
/* Address environment. /* Address environment.
* *

View file

@ -35,7 +35,9 @@ SECTIONS
.init_array : .init_array :
{ {
_sinit = .;
*(.init_array) *(.init_array)
_einit = .;
} }
.fini_array : .fini_array :
@ -77,6 +79,11 @@ SECTIONS
_ebss = . ; _ebss = . ;
} }
.got :
{
*(.got*)
}
/* Stabs debugging sections. */ /* Stabs debugging sections. */
.stab 0 : { *(.stab) } .stab 0 : { *(.stab) }

View file

@ -292,8 +292,8 @@ static int modlib_relocate(FAR struct module_s *modp,
/* Get the value of the symbol (in sym.st_value) */ /* Get the value of the symbol (in sym.st_value) */
ret = modlib_symvalue(modp, loadinfo, sym, ret = modlib_symvalue(modp, loadinfo, sym,
loadinfo->shdr[loadinfo->strtabidx].sh_offset, loadinfo->shdr[loadinfo->strtabidx].sh_offset,
exports, nexports); exports, nexports);
if (ret < 0) if (ret < 0)
{ {
/* The special error -ESRCH is returned only in one condition: /* The special error -ESRCH is returned only in one condition:
@ -333,19 +333,75 @@ static int modlib_relocate(FAR struct module_s *modp,
/* Calculate the relocation address. */ /* Calculate the relocation address. */
if (rel->r_offset < 0 || if (loadinfo->gotindex >= 0)
rel->r_offset > dstsec->sh_size)
{ {
berr("ERROR: Section %d reloc %d: " if (sym->st_shndx == SHN_UNDEF)
"Relocation address out of range, " {
"offset %" PRIuPTR " size %ju\n", /* Symbol type is undefined, we need to set the address
relidx, i, (uintptr_t)rel->r_offset, * to the value of the symbol.
(uintmax_t)dstsec->sh_size); */
ret = -EINVAL;
break;
}
addr = dstsec->sh_addr + rel->r_offset; FAR Elf_Shdr *gotsec = &loadinfo->shdr[loadinfo->gotindex];
FAR uintptr_t *gotaddr = (FAR uintptr_t *)(gotsec->sh_addr +
*((FAR uintptr_t *)(dstsec->sh_addr + rel->r_offset)));
*gotaddr = sym->st_value;
continue;
}
if ((dstsec->sh_flags & SHF_WRITE) == 0)
{
/* Skip relocations for read-only sections */
continue;
}
/* Use the GOT to store the address */
if (rel->r_offset - dstsec->sh_offset >
dstsec->sh_size)
{
berr("ERROR: Section %d reloc %d: "
"Relocation address out of range, "
"offset %" PRIuPTR " size %ju\n",
relidx, i, (uintptr_t)rel->r_offset,
(uintmax_t)dstsec->sh_size);
ret = -EINVAL;
break;
}
addr = dstsec->sh_addr + rel->r_offset - dstsec->sh_offset;
if (ELF_ST_TYPE(sym->st_info) == STT_SECTION)
{
/* Symbol type is section, we need clear the address
* and keep the original value.
*/
*(FAR uintptr_t *)addr -=
loadinfo->shdr[sym->st_shndx].sh_offset;
}
else
{
/* Normal symbol, just keep it zero */
*(FAR uintptr_t *)addr = 0;
}
}
else
{
if (rel->r_offset > dstsec->sh_size)
{
berr("ERROR: Section %d reloc %d: "
"Relocation address out of range, "
"offset %" PRIuPTR " size %ju\n",
relidx, i, (uintptr_t)rel->r_offset,
(uintmax_t)dstsec->sh_size);
ret = -EINVAL;
break;
}
addr = dstsec->sh_addr + rel->r_offset;
}
/* Now perform the architecture-specific relocation */ /* Now perform the architecture-specific relocation */

View file

@ -62,7 +62,7 @@
static int modlib_section_alloc(FAR struct mod_loadinfo_s *loadinfo, static int modlib_section_alloc(FAR struct mod_loadinfo_s *loadinfo,
FAR Elf_Shdr *shdr, uint8_t idx) FAR Elf_Shdr *shdr, uint8_t idx)
{ {
if (loadinfo->ehdr.e_type != ET_REL) if (loadinfo->ehdr.e_type != ET_DYN)
{ {
return -EINVAL; return -EINVAL;
} }
@ -339,7 +339,8 @@ static inline int modlib_loadfile(FAR struct mod_loadinfo_s *loadinfo)
} }
#ifdef CONFIG_ARCH_USE_SEPARATED_SECTION #ifdef CONFIG_ARCH_USE_SEPARATED_SECTION
if (loadinfo->ehdr.e_type == ET_REL) if (loadinfo->ehdr.e_type == ET_REL ||
loadinfo->ehdr.e_type == ET_EXEC)
{ {
pptr = (FAR uint8_t **)&loadinfo->sectalloc[i]; pptr = (FAR uint8_t **)&loadinfo->sectalloc[i];
} }
@ -410,6 +411,9 @@ static inline int modlib_loadfile(FAR struct mod_loadinfo_s *loadinfo)
binfo("%d. %08lx->%08lx\n", i, binfo("%d. %08lx->%08lx\n", i,
(unsigned long)shdr->sh_addr, (unsigned long)*pptr); (unsigned long)shdr->sh_addr, (unsigned long)*pptr);
/* Use offset to remember the original file address */
shdr->sh_offset = (uintptr_t)shdr->sh_addr;
shdr->sh_addr = (uintptr_t)*pptr; shdr->sh_addr = (uintptr_t)*pptr;
#ifdef CONFIG_ARCH_USE_SEPARATED_SECTION #ifdef CONFIG_ARCH_USE_SEPARATED_SECTION
@ -425,6 +429,34 @@ static inline int modlib_loadfile(FAR struct mod_loadinfo_s *loadinfo)
} }
} }
/* Update GOT table */
if (loadinfo->gotindex >= 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);
for (; got < end; got++)
{
for (i = 0; i < loadinfo->ehdr.e_shnum; i++)
{
FAR Elf_Shdr *shdr = &loadinfo->shdr[i];
if ((shdr->sh_flags & SHF_ALLOC) == 0)
{
continue;
}
if (*got >= shdr->sh_offset &&
*got < shdr->sh_offset + shdr->sh_size)
{
*got += shdr->sh_addr - shdr->sh_offset;
}
}
}
}
return OK; return OK;
} }
@ -461,6 +493,12 @@ int modlib_load(FAR struct mod_loadinfo_s *loadinfo)
goto errout_with_buffers; goto errout_with_buffers;
} }
loadinfo->gotindex = modlib_findsection(loadinfo, ".got");
if (loadinfo->gotindex >= 0)
{
binfo("GOT section found! index %d\n", loadinfo->gotindex);
}
/* Determine total size to allocate */ /* Determine total size to allocate */
modlib_elfsize(loadinfo, true); modlib_elfsize(loadinfo, true);
@ -474,21 +512,23 @@ int modlib_load(FAR struct mod_loadinfo_s *loadinfo)
* GOT. Therefore we cannot do two different allocations. * GOT. Therefore we cannot do two different allocations.
*/ */
if (loadinfo->ehdr.e_type == ET_REL) #ifndef CONFIG_MODLIB_LOADTO_LMA
if (loadinfo->ehdr.e_type == ET_REL || loadinfo->ehdr.e_type == ET_EXEC)
{ {
#ifndef CONFIG_ARCH_USE_SEPARATED_SECTION # ifndef CONFIG_ARCH_USE_SEPARATED_SECTION
if (loadinfo->textsize > 0) if (loadinfo->textsize > 0)
{ {
# ifdef CONFIG_ARCH_USE_TEXT_HEAP # ifdef CONFIG_ARCH_USE_TEXT_HEAP
loadinfo->textalloc = (uintptr_t) loadinfo->textalloc = (uintptr_t)
up_textheap_memalign(loadinfo->textalign, up_textheap_memalign(loadinfo->textalign,
loadinfo->textsize + loadinfo->textsize +
loadinfo->segpad); loadinfo->segpad);
# else # else
loadinfo->textalloc = (uintptr_t)lib_memalign(loadinfo->textalign, loadinfo->textalloc = (uintptr_t)lib_memalign(loadinfo->textalign,
loadinfo->textsize + loadinfo->textsize +
loadinfo->segpad); loadinfo->segpad);
# endif # endif
if (!loadinfo->textalloc) if (!loadinfo->textalloc)
{ {
berr("ERROR: Failed to allocate memory for the module text\n"); berr("ERROR: Failed to allocate memory for the module text\n");
@ -499,14 +539,14 @@ int modlib_load(FAR struct mod_loadinfo_s *loadinfo)
if (loadinfo->datasize > 0) if (loadinfo->datasize > 0)
{ {
# ifdef CONFIG_ARCH_USE_DATA_HEAP # ifdef CONFIG_ARCH_USE_DATA_HEAP
loadinfo->datastart = (uintptr_t) loadinfo->datastart = (uintptr_t)
up_dataheap_memalign(loadinfo->dataalign, up_dataheap_memalign(loadinfo->dataalign,
loadinfo->datasize); loadinfo->datasize);
# else # else
loadinfo->datastart = (uintptr_t)lib_memalign(loadinfo->dataalign, loadinfo->datastart = (uintptr_t)lib_memalign(loadinfo->dataalign,
loadinfo->datasize); loadinfo->datasize);
# endif # endif
if (!loadinfo->datastart) if (!loadinfo->datastart)
{ {
berr("ERROR: Failed to allocate memory for the module data\n"); berr("ERROR: Failed to allocate memory for the module data\n");
@ -514,7 +554,7 @@ int modlib_load(FAR struct mod_loadinfo_s *loadinfo)
goto errout_with_buffers; goto errout_with_buffers;
} }
} }
#endif # endif
} }
else if (loadinfo->ehdr.e_type == ET_DYN) else if (loadinfo->ehdr.e_type == ET_DYN)
{ {
@ -535,6 +575,8 @@ int modlib_load(FAR struct mod_loadinfo_s *loadinfo)
loadinfo->segpad; loadinfo->segpad;
} }
#endif /* CONFIG_MODLIB_LOADTO_LMA */
/* Load ELF section data into memory */ /* Load ELF section data into memory */
ret = modlib_loadfile(loadinfo); ret = modlib_loadfile(loadinfo);
@ -597,6 +639,12 @@ int modlib_load_with_addrenv(FAR struct mod_loadinfo_s *loadinfo)
goto errout_with_buffers; goto errout_with_buffers;
} }
loadinfo->gotindex = modlib_findsection(loadinfo, ".got");
if (loadinfo->gotindex >= 0)
{
binfo("GOT section found! index %d\n", loadinfo->gotindex);
}
/* Determine total size to allocate */ /* Determine total size to allocate */
modlib_elfsize(loadinfo, false); modlib_elfsize(loadinfo, false);

View file

@ -445,6 +445,10 @@ int modlib_symvalue(FAR struct module_s *modp,
(uintptr_t)(sym->st_value + secbase)); (uintptr_t)(sym->st_value + secbase));
sym->st_value += secbase; sym->st_value += secbase;
if (loadinfo->gotindex >= 0)
{
sym->st_value -= loadinfo->shdr[sym->st_shndx].sh_offset;
}
} }
break; break;
} }