diff --git a/binfmt/elf.c b/binfmt/elf.c index a4ad873a65a..bf20ec4127e 100644 --- a/binfmt/elf.c +++ b/binfmt/elf.c @@ -252,6 +252,7 @@ static int elf_loadbinary(FAR struct binary_s *binp, binp->mod.textalloc = (FAR void *)loadinfo.textalloc; binp->mod.dataalloc = (FAR void *)loadinfo.datastart; + binp->mod.gotbase = loadinfo.fdpic ? loadinfo.gotbase : 0; # ifdef CONFIG_BINFMT_CONSTRUCTORS binp->mod.initarr = loadinfo.initarr; binp->mod.finiarr = loadinfo.finiarr; @@ -286,6 +287,15 @@ static int elf_loadbinary(FAR struct binary_s *binp, } #endif +#ifdef HAVE_LIBC_ELF_PIN + /* Past the last thing that can fail, so the module owns the pin: it is + * given back when the task that runs the module exits. + */ + + binp->mod.pinfile = loadinfo.pinfile; + loadinfo.pinfile = NULL; +#endif + libelf_uninitialize(&loadinfo); return OK; diff --git a/include/nuttx/fdpic.h b/include/nuttx/fdpic.h index 26185e8dd21..01435d285d6 100644 --- a/include/nuttx/fdpic.h +++ b/include/nuttx/fdpic.h @@ -159,6 +159,36 @@ static inline void fdpic_invoke(uintptr_t arg, } } +/**************************************************************************** + * Name: fdpic_call + * + * Description: + * Call a function of a module the caller is not running in, such as a + * constructor of a module being loaded or a destructor of one being + * unloaded, with the data base that function needs. + * + * A zero base means the function is not a module's, or the caller already + * carries the right one, and it is called directly. A non-FDPIC object + * has a GOT too, so the caller decides which base to pass, not this. + * + * Input Parameters: + * arg - The one word argument. + * fn - The function to call. + * got - The data base to enter it with, or zero. + * + ****************************************************************************/ + +static inline void fdpic_call(uintptr_t arg, CODE void (*fn)(void), + uintptr_t got) +{ + struct fdpic_desc_s desc; + + desc.entry = (uintptr_t)fn; + desc.got = got; + + fdpic_invoke(arg, &desc); +} + #else # define fdpic_base() (0) @@ -167,6 +197,8 @@ static inline void fdpic_invoke(uintptr_t arg, ((desc)->entry = (uintptr_t)(fn), (desc)->got = 0) # define fdpic_invoke(arg, desc) \ (((CODE void (*)(uintptr_t))(desc)->entry)(arg)) +# define fdpic_call(arg, fn, got) \ + ((void)(got), ((CODE void (*)(uintptr_t))(fn))(arg)) #endif /* CONFIG_FDPIC */ diff --git a/include/nuttx/lib/elf.h b/include/nuttx/lib/elf.h index e30399eede9..fcbbdc8448c 100644 --- a/include/nuttx/lib/elf.h +++ b/include/nuttx/lib/elf.h @@ -190,6 +190,19 @@ struct module_s uint16_t nsect; /* Number of entries in sectalloc array */ #endif int dynamic; /* Module is a dynamic shared object */ + uintptr_t gotbase; /* An FDPIC object's data base, to + * enter its destructors with. Zero + * for anything else, so it also says + * which kind of object this is: an + * FDPIC one placed its two segments + * separately and its text may be + * media rather than an allocation + */ +#ifdef HAVE_LIBC_ELF_PIN + FAR struct file *pinfile; /* Holds the XIP pin on the text until + * the module is unloaded + */ +#endif #if defined(CONFIG_FS_PROCFS) && !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE) size_t textsize; /* Size of the kernel .text memory allocation */ size_t datasize; /* Size of the kernel .bss/.data memory allocation */ diff --git a/libs/libc/elf/elf_bind.c b/libs/libc/elf/elf_bind.c index fae5b5f3cec..f59c413f117 100644 --- a/libs/libc/elf/elf_bind.c +++ b/libs/libc/elf/elf_bind.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -798,6 +799,19 @@ static int libelf_relocatedyn(FAR struct module_s *modp, } } + /* An object with no imports has no PLT and so no DT_PLTGOT, but it still + * has a GOT and still has to be entered with it: the linker puts it + * immediately after the dynamic section. + */ + + if (loadinfo->fdpic && loadinfo->gotbase == 0) + { + loadinfo->gotbase = libelf_addr(loadinfo, + shdr->sh_addr + shdr->sh_size); + binfo("No DT_PLTGOT; taking the GOT at %08" PRIxPTR "\n", + loadinfo->gotbase); + } + /* After the loop, because DT_PLTGOT is read there. Both relocation * tables are walked under this one arch_data, so the pool cursor * survives from one to the next. diff --git a/libs/libc/elf/elf_insert.c b/libs/libc/elf/elf_insert.c index 09d1cb2623d..bee8f09254c 100644 --- a/libs/libc/elf/elf_insert.c +++ b/libs/libc/elf/elf_insert.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -404,6 +405,11 @@ FAR void *libelf_insert(FAR const char *filename, FAR const char *modname) modp->textalloc = (FAR void *)loadinfo.textalloc; modp->dataalloc = (FAR void *)loadinfo.datastart; + modp->gotbase = loadinfo.fdpic ? loadinfo.gotbase : 0; +#ifdef HAVE_LIBC_ELF_PIN + modp->pinfile = loadinfo.pinfile; + loadinfo.pinfile = NULL; +#endif #ifdef CONFIG_ARCH_USE_SEPARATED_SECTION modp->sectalloc = (FAR void **)loadinfo.sectalloc; modp->nsect = loadinfo.ehdr.e_shnum; @@ -414,7 +420,11 @@ FAR void *libelf_insert(FAR const char *filename, FAR const char *modname) modp->datasize = loadinfo.datasize; #endif - /* Call the module initializer */ + /* Call the module initializer. An FDPIC object's constructors reach its + * globals through its own data base, which the loading thread does not + * carry; for anything else modp->gotbase is zero and they are called + * directly. + */ switch (loadinfo.ehdr.e_type) { @@ -426,7 +436,7 @@ FAR void *libelf_insert(FAR const char *filename, FAR const char *modname) array = (FAR void (**)(void))loadinfo.preiarr; for (i = 0; i < loadinfo.nprei; i++) { - array[i](); + fdpic_call(0, array[i], modp->gotbase); } /* Process any init_array entries */ @@ -434,7 +444,7 @@ FAR void *libelf_insert(FAR const char *filename, FAR const char *modname) array = (FAR void (**)(void))loadinfo.initarr; for (i = 0; i < loadinfo.ninit; i++) { - array[i](); + fdpic_call(0, array[i], modp->gotbase); } modp->initarr = loadinfo.initarr; diff --git a/libs/libc/elf/elf_remove.c b/libs/libc/elf/elf_remove.c index 8393e6b1f83..849d49a0998 100644 --- a/libs/libc/elf/elf_remove.c +++ b/libs/libc/elf/elf_remove.c @@ -28,9 +28,12 @@ #include #include +#include #include #include +#include "elf/elf.h" + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -59,12 +62,15 @@ int libelf_uninit(FAR struct module_s *modp) } #endif - /* Is there an uninitializer? */ + /* Is there an uninitializer? Like the constructors, an FDPIC object's + * destructors reach its globals through its own data base, which the + * unloading thread does not carry. + */ array = (FAR void (**)(void))modp->finiarr; for (i = 0; i < modp->nfini; i++) { - array[i](); + fdpic_call(0, array[i], modp->gotbase); } if (modp->modinfo.uninitializer != NULL) @@ -95,6 +101,14 @@ int libelf_uninit(FAR struct module_s *modp) modp->modinfo.exports = NULL; modp->modinfo.nexports = 0; +#ifdef HAVE_LIBC_ELF_PIN + /* Give the pin back before the text goes out of use. This does nothing if + * the loader took no pin. + */ + + libelf_pinrelease(&modp->pinfile); +#endif + /* Release resources held by the module */ if (modp->textalloc != NULL || modp->dataalloc != NULL) @@ -150,6 +164,24 @@ int libelf_uninit(FAR struct module_s *modp) # endif #endif } + else if (modp->gotbase != 0) + { + /* An FDPIC object, which placed its two segments separately. Free + * each one. If the text stayed on the media, it was never + * allocated, thus leave it. + */ + + if (modp->xipbase == 0) + { +#ifdef CONFIG_ARCH_USE_TEXT_HEAP + up_textheap_free((FAR void *)modp->textalloc); +#else + lib_free((FAR void *)modp->textalloc); +#endif + } + + lib_free((FAR void *)modp->dataalloc); + } else { lib_free((FAR void *)modp->textalloc);