From 112b6fd9a59ef68e863279ad3949af29882c195b Mon Sep 17 00:00:00 2001 From: anjiahao Date: Wed, 3 Jul 2024 15:33:48 +0800 Subject: [PATCH] modlib:support modlib can load PIC elf Signed-off-by: anjiahao --- arch/arm/src/common/Toolchain.defs | 18 +++++-- binfmt/Kconfig | 2 +- binfmt/elf.c | 20 +++++++- include/nuttx/lib/modlib.h | 1 + libs/libc/modlib/gnu-elf.ld | 7 +++ libs/libc/modlib/modlib_bind.c | 82 +++++++++++++++++++++++++----- libs/libc/modlib/modlib_load.c | 70 +++++++++++++++++++++---- libs/libc/modlib/modlib_symbols.c | 4 ++ 8 files changed, 174 insertions(+), 30 deletions(-) diff --git a/arch/arm/src/common/Toolchain.defs b/arch/arm/src/common/Toolchain.defs index 865efda11c..540e4405d4 100644 --- a/arch/arm/src/common/Toolchain.defs +++ b/arch/arm/src/common/Toolchain.defs @@ -502,11 +502,21 @@ LDMODULEFLAGS = -r -T $(call CONVERT_PATH,$(TOPDIR)/libs/libc/modlib/gnu-elf.ld) # ELF module definitions -CELFFLAGS = $(CFLAGS) -fvisibility=hidden -mlong-calls # --target1-abs -CXXELFFLAGS = $(CXXFLAGS) -fvisibility=hidden +ifeq ($(CONFIG_PIC),y) + CFLAGS += --fixed-r10 + PICFLAGS = -fpic -fPIE -mno-pic-data-is-text-relative \ + -msingle-pic-base -mpic-register=r10 -LDELFFLAGS = -r -e main -LDELFFLAGS += -T $(call CONVERT_PATH,$(TOPDIR)$(DELIM)libs$(DELIM)libc$(DELIM)modlib$(DELIM)gnu-elf.ld) + # Generate an executable elf, need to ignore undefined symbols + 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 diff --git a/binfmt/Kconfig b/binfmt/Kconfig index ae48ecf135..585d878a4c 100644 --- a/binfmt/Kconfig +++ b/binfmt/Kconfig @@ -28,7 +28,7 @@ config BINFMT_LOADABLE Automatically selected if a loadable binary format is selected. config PIC - bool + bool "Executable elf position-independent support" default n ---help--- Automatically selected if the binary format requires position diff --git a/binfmt/elf.c b/binfmt/elf.c index d95557ef36..433cadc8d5 100644 --- a/binfmt/elf.c +++ b/binfmt/elf.c @@ -33,6 +33,7 @@ #include #include +#include #ifdef CONFIG_ELF @@ -119,7 +120,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) + if (loadinfo.ehdr.e_type == ET_REL || loadinfo.gotindex >= 0) { ret = modlib_bind(&binp->mod, &loadinfo, exports, nexports); if (ret != 0) @@ -207,6 +208,23 @@ static int elf_loadbinary(FAR struct binary_s *binp, #endif 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); return OK; diff --git a/include/nuttx/lib/modlib.h b/include/nuttx/lib/modlib.h index 228a24e880..6e406fcb8c 100644 --- a/include/nuttx/lib/modlib.h +++ b/include/nuttx/lib/modlib.h @@ -231,6 +231,7 @@ 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 */ /* Address environment. * diff --git a/libs/libc/modlib/gnu-elf.ld b/libs/libc/modlib/gnu-elf.ld index f78a75a14a..357d7356f1 100644 --- a/libs/libc/modlib/gnu-elf.ld +++ b/libs/libc/modlib/gnu-elf.ld @@ -35,7 +35,9 @@ SECTIONS .init_array : { + _sinit = .; *(.init_array) + _einit = .; } .fini_array : @@ -77,6 +79,11 @@ SECTIONS _ebss = . ; } + .got : + { + *(.got*) + } + /* Stabs debugging sections. */ .stab 0 : { *(.stab) } diff --git a/libs/libc/modlib/modlib_bind.c b/libs/libc/modlib/modlib_bind.c index c136ea3f53..2dc3d36add 100644 --- a/libs/libc/modlib/modlib_bind.c +++ b/libs/libc/modlib/modlib_bind.c @@ -292,8 +292,8 @@ static int modlib_relocate(FAR struct module_s *modp, /* Get the value of the symbol (in sym.st_value) */ ret = modlib_symvalue(modp, loadinfo, sym, - loadinfo->shdr[loadinfo->strtabidx].sh_offset, - exports, nexports); + loadinfo->shdr[loadinfo->strtabidx].sh_offset, + exports, nexports); if (ret < 0) { /* 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. */ - if (rel->r_offset < 0 || - rel->r_offset > dstsec->sh_size) + if (loadinfo->gotindex >= 0) { - 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; - } + if (sym->st_shndx == SHN_UNDEF) + { + /* Symbol type is undefined, we need to set the address + * to the value of the symbol. + */ - 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 */ diff --git a/libs/libc/modlib/modlib_load.c b/libs/libc/modlib/modlib_load.c index 63e12ab382..1baeb66e73 100644 --- a/libs/libc/modlib/modlib_load.c +++ b/libs/libc/modlib/modlib_load.c @@ -62,7 +62,7 @@ static int modlib_section_alloc(FAR struct mod_loadinfo_s *loadinfo, FAR Elf_Shdr *shdr, uint8_t idx) { - if (loadinfo->ehdr.e_type != ET_REL) + if (loadinfo->ehdr.e_type != ET_DYN) { return -EINVAL; } @@ -339,7 +339,8 @@ static inline int modlib_loadfile(FAR struct mod_loadinfo_s *loadinfo) } #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]; } @@ -410,6 +411,9 @@ static inline int modlib_loadfile(FAR struct mod_loadinfo_s *loadinfo) binfo("%d. %08lx->%08lx\n", i, (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; #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; } @@ -461,6 +493,12 @@ int modlib_load(FAR struct mod_loadinfo_s *loadinfo) 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 */ 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. */ - 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) { -# ifdef CONFIG_ARCH_USE_TEXT_HEAP +# ifdef CONFIG_ARCH_USE_TEXT_HEAP loadinfo->textalloc = (uintptr_t) up_textheap_memalign(loadinfo->textalign, loadinfo->textsize + loadinfo->segpad); -# else +# else loadinfo->textalloc = (uintptr_t)lib_memalign(loadinfo->textalign, loadinfo->textsize + loadinfo->segpad); -# endif +# endif if (!loadinfo->textalloc) { 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) { -# ifdef CONFIG_ARCH_USE_DATA_HEAP +# ifdef CONFIG_ARCH_USE_DATA_HEAP loadinfo->datastart = (uintptr_t) up_dataheap_memalign(loadinfo->dataalign, loadinfo->datasize); -# else +# else loadinfo->datastart = (uintptr_t)lib_memalign(loadinfo->dataalign, loadinfo->datasize); -# endif +# endif if (!loadinfo->datastart) { 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; } } -#endif +# endif } else if (loadinfo->ehdr.e_type == ET_DYN) { @@ -535,6 +575,8 @@ int modlib_load(FAR struct mod_loadinfo_s *loadinfo) loadinfo->segpad; } +#endif /* CONFIG_MODLIB_LOADTO_LMA */ + /* Load ELF section data into memory */ ret = modlib_loadfile(loadinfo); @@ -597,6 +639,12 @@ int modlib_load_with_addrenv(FAR struct mod_loadinfo_s *loadinfo) 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 */ modlib_elfsize(loadinfo, false); diff --git a/libs/libc/modlib/modlib_symbols.c b/libs/libc/modlib/modlib_symbols.c index f27a8dbb49..95ba510fa8 100644 --- a/libs/libc/modlib/modlib_symbols.c +++ b/libs/libc/modlib/modlib_symbols.c @@ -445,6 +445,10 @@ int modlib_symvalue(FAR struct module_s *modp, (uintptr_t)(sym->st_value + secbase)); sym->st_value += secbase; + if (loadinfo->gotindex >= 0) + { + sym->st_value -= loadinfo->shdr[sym->st_shndx].sh_offset; + } } break; }