diff --git a/binfmt/elf.c b/binfmt/elf.c index a4ad873a65add..3a6cdadc46137 100644 --- a/binfmt/elf.c +++ b/binfmt/elf.c @@ -252,6 +252,8 @@ 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.fdpic = loadinfo.fdpic; + binp->mod.gotbase = loadinfo.gotbase; # ifdef CONFIG_BINFMT_CONSTRUCTORS binp->mod.initarr = loadinfo.initarr; binp->mod.finiarr = loadinfo.finiarr; @@ -286,6 +288,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/lib/elf.h b/include/nuttx/lib/elf.h index e30399eede951..80c9d3fb6583e 100644 --- a/include/nuttx/lib/elf.h +++ b/include/nuttx/lib/elf.h @@ -190,6 +190,18 @@ struct module_s uint16_t nsect; /* Number of entries in sectalloc array */ #endif int dynamic; /* Module is a dynamic shared object */ + bool fdpic; /* Module is an FDPIC object: its two + * segments were placed separately and + * the text is media, not an allocation + */ + uintptr_t gotbase; /* An FDPIC object's data base, to + * enter its destructors with + */ +#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 4e0727222dd46..bb5e49b38015f 100644 --- a/libs/libc/elf/elf_bind.c +++ b/libs/libc/elf/elf_bind.c @@ -714,6 +714,24 @@ static int libelf_relocatedyn(FAR struct module_s *modp, case DT_PLTRELSZ: reldata.relsz[I_PLT] = dyn[i].d_un.d_val; break; + case DT_NEEDED: + + /* Nothing loads DT_NEEDED yet, so refuse the module rather + * than let it fault on its first call into the library. + */ + + if (loadinfo->fdpic) + { + berr("ERROR: FDPIC module has a DT_NEEDED entry. Shared " + "libraries are not supported; link it statically.\n"); + lib_free(sym); + lib_free(rels); + lib_free(dyn); + return -ENOEXEC; + } + + break; + case DT_PLTGOT: /* The object's data base. Every function descriptor built diff --git a/libs/libc/elf/elf_insert.c b/libs/libc/elf/elf_insert.c index 09d1cb2623d23..fc1f58fc93445 100644 --- a/libs/libc/elf/elf_insert.c +++ b/libs/libc/elf/elf_insert.c @@ -29,6 +29,7 @@ #include #include +#include #include #include "elf.h" @@ -404,6 +405,12 @@ 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->fdpic = loadinfo.fdpic; + modp->gotbase = loadinfo.gotbase; +#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; @@ -421,11 +428,26 @@ FAR void *libelf_insert(FAR const char *filename, FAR const char *modname) case ET_REL : case ET_DYN : - /* Process any preinit_array entries */ + /* Process any preinit_array entries. An FDPIC object's + * constructors need its own data base, not the loading thread's. + */ array = (FAR void (**)(void))loadinfo.preiarr; for (i = 0; i < loadinfo.nprei; i++) { +#ifdef CONFIG_FDPIC + if (loadinfo.fdpic) + { + struct fdpic_desc_s desc; + + desc.entry = (uintptr_t)array[i]; + desc.got = loadinfo.gotbase; + + fdpic_invoke(0, &desc); + continue; + } +#endif + array[i](); } @@ -434,6 +456,19 @@ 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++) { +#ifdef CONFIG_FDPIC + if (loadinfo.fdpic) + { + struct fdpic_desc_s desc; + + desc.entry = (uintptr_t)array[i]; + desc.got = loadinfo.gotbase; + + fdpic_invoke(0, &desc); + continue; + } +#endif + array[i](); } diff --git a/libs/libc/elf/elf_remove.c b/libs/libc/elf/elf_remove.c index 67722937b3302..85c9904898cf7 100644 --- a/libs/libc/elf/elf_remove.c +++ b/libs/libc/elf/elf_remove.c @@ -29,8 +29,11 @@ #include #include +#include #include +#include "elf/elf.h" + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -64,6 +67,24 @@ int libelf_uninit(FAR struct module_s *modp) array = (FAR void (**)(void))modp->finiarr; for (i = 0; i < modp->nfini; i++) { + /* Like the constructors, an FDPIC object's destructors reach its + * globals through its own data base, which the unloading thread does + * not carry. + */ + +#ifdef CONFIG_FDPIC + if (modp->fdpic) + { + struct fdpic_desc_s desc; + + desc.entry = (uintptr_t)array[i]; + desc.got = modp->gotbase; + + fdpic_invoke(0, &desc); + continue; + } +#endif + array[i](); } @@ -93,6 +114,14 @@ int libelf_uninit(FAR struct module_s *modp) #endif } +#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) @@ -148,6 +177,24 @@ int libelf_uninit(FAR struct module_s *modp) # endif #endif } + else if (modp->fdpic) + { + /* An FDPIC object 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) + { +#if defined(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);