This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit c52c86a6c573371a7b0048af76bf866440b72a25
Author: Marco Casaroli <[email protected]>
AuthorDate: Tue Oct 13 06:57:06 2026 +0200

    libs/libc/elf: Keep a module registry per task group.
    
    One global registry held every ELF object that dlopen(), insmod() and
    the DT_NEEDED loader loaded.  Thus a library was one instance for the
    whole system, and all task groups shared its data.
    
    A registry is now a list head, and a module is on it through a list
    node, so a module can leave its list without knowing the head.  The
    libelf functions get the registry from the caller and do not lock it:
    the caller holds the lock of that registry.
    
    - dlopen(), dlclose() and dlsym() use the registry of the calling task
      group, ta_modules in struct task_info_s.  POSIX makes a dlopen() handle
      valid in the whole process, so the registry is per task group and not
      per thread.  Its lock, ta_modlock, is recursive because the
      constructors of a library run with it held and can call dlopen().
    - insmod(), rmmod(), modsym(), modhandle() and /proc/modules use the
      registry of the kernel modules, which is now in sched/module.
    - The global symbol table has a lock of its own.
    
    A DT_NEEDED library goes on the registry of the module that needs it,
    which libelf_bind() gets in struct mod_loadinfo_s.  A program that exec()
    loads has no task group yet, so its libraries go on a list in struct
    binary_s.  In a flat build, exec_module() moves them to the registry of
    the new task group, so dlopen() in the program finds the same copy.  In
    the protected build the program is loaded by the kernel libc, which has
    another heap, so its libraries stay on the list of the program.  If the
    bind of a program fails, its libraries are now released.
    
    libelf_removeall() closes the libraries that a task group did not close.
    In a flat build, group_release() does it after the last thread, and then
    gives what the program still uses back to the program, before the
    registry goes with the task information.  In the protected build the
    kernel cannot reach the user registry, so exit() does it.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 binfmt/binfmt_execmodule.c                   |  15 +++
 binfmt/binfmt_loadmodule.c                   |   4 +
 binfmt/elf.c                                 |   9 ++
 include/nuttx/binfmt/binfmt.h                |   1 +
 include/nuttx/lib/elf.h                      | 128 ++++++++++++----------
 include/nuttx/tls.h                          |  52 ++++++++-
 libs/libc/dlfcn/lib_dlclose.c                |   9 +-
 libs/libc/dlfcn/lib_dlopen.c                 |  14 ++-
 libs/libc/elf/elf_bind.c                     |  29 +++--
 libs/libc/elf/elf_depend.c                   |  10 +-
 libs/libc/elf/elf_gethandle.c                |  16 +--
 libs/libc/elf/elf_getsymbol.c                |  19 +---
 libs/libc/elf/elf_insert.c                   |  31 +++---
 libs/libc/elf/elf_registry.c                 | 156 ++++++++-------------------
 libs/libc/elf/elf_remove.c                   |  81 +++++++++-----
 libs/libc/elf/elf_symbols.c                  |   3 +-
 libs/libc/elf/elf_symtab.c                   |  14 ++-
 libs/libc/stdlib/lib_exit.c                  |  11 ++
 sched/group/group_leave.c                    |  25 +++++
 sched/module/CMakeLists.txt                  |   6 +-
 sched/module/Make.defs                       |   4 +-
 sched/module/mod_insmod.c                    |  16 ++-
 sched/module/mod_modhandle.c                 |  12 ++-
 sched/module/mod_modsym.c                    |   9 +-
 sched/module/mod_procfs.c                    |  10 +-
 sched/module/{mod_rmmod.c => mod_registry.c} |  59 +++++++---
 sched/module/mod_rmmod.c                     |  12 ++-
 sched/module/{mod_rmmod.c => module.h}       |  34 +++---
 sched/tls/task_initinfo.c                    |   7 ++
 sched/tls/task_uninitinfo.c                  |   4 +
 30 files changed, 481 insertions(+), 319 deletions(-)

diff --git a/binfmt/binfmt_execmodule.c b/binfmt/binfmt_execmodule.c
index 42b705d5634..5a755711c19 100644
--- a/binfmt/binfmt_execmodule.c
+++ b/binfmt/binfmt_execmodule.c
@@ -190,6 +190,9 @@ int exec_module(FAR struct binary_s *binp,
   char name[PATH_MAX];
 #endif
   FAR void *stackaddr = NULL;
+#if defined(CONFIG_BINFMT_LOADABLE) && defined(CONFIG_BUILD_FLAT)
+  FAR struct list_node *node;
+#endif
   pid_t pid;
   int ret;
 
@@ -394,6 +397,18 @@ int exec_module(FAR struct binary_s *binp,
   tcb->group->tg_bininfo = binp;
 #endif
 
+#if defined(CONFIG_BINFMT_LOADABLE) && defined(CONFIG_BUILD_FLAT)
+  /* The libraries the program needs belong to its task group, where its
+   * own dlopen() finds them.  Only in a flat build: elsewhere the program
+   * has another libc, with its own lock and heap.
+   */
+
+  while ((node = list_remove_head(&binp->libraries)) != NULL)
+    {
+      list_add_tail(&tcb->group->tg_info->ta_modules, node);
+    }
+#endif
+
   /* Then activate the task at the provided priority */
 
   nxtask_activate(tcb);
diff --git a/binfmt/binfmt_loadmodule.c b/binfmt/binfmt_loadmodule.c
index 1d20f2e0ef7..b02fa9ed8d6 100644
--- a/binfmt/binfmt_loadmodule.c
+++ b/binfmt/binfmt_loadmodule.c
@@ -184,6 +184,10 @@ int load_module(FAR struct binary_s *bin, FAR const char 
*filename,
   if (bin && filename)
 #endif
     {
+      /* The libraries the program needs go here */
+
+      list_initialize(&bin->libraries);
+
       /* Set the default priority of the new program. */
 
       ret = load_default_priority(bin);
diff --git a/binfmt/elf.c b/binfmt/elf.c
index bf20ec4127e..72580c95650 100644
--- a/binfmt/elf.c
+++ b/binfmt/elf.c
@@ -141,6 +141,12 @@ static int elf_loadbinary(FAR struct binary_s *binp,
 
   if (loadinfo.ehdr.e_type == ET_REL || loadinfo.gotsize != 0)
     {
+      /* Its DT_NEEDED libraries go on binp->libraries.  The task group
+       * does not exist yet.  In a flat build, exec_module() gives them to
+       * the new task group.
+       */
+
+      loadinfo.registry = &binp->libraries;
       ret = libelf_bind(&binp->mod, &loadinfo, exports, nexports);
       if (ret != 0)
         {
@@ -301,6 +307,9 @@ static int elf_loadbinary(FAR struct binary_s *binp,
 
 errout_with_load:
   libelf_unload(&loadinfo);
+#if CONFIG_LIBC_ELF_MAXDEPEND > 0
+  libelf_undepend(&binp->mod);
+#endif
 errout_with_init:
   libelf_uninitialize(&loadinfo);
   return ret;
diff --git a/include/nuttx/binfmt/binfmt.h b/include/nuttx/binfmt/binfmt.h
index 156dcac9afd..60b42165868 100644
--- a/include/nuttx/binfmt/binfmt.h
+++ b/include/nuttx/binfmt/binfmt.h
@@ -72,6 +72,7 @@ struct binary_s
 #ifdef CONFIG_LIBC_ELF
   struct module_s mod;                 /* Module context */
 #endif
+  struct list_node libraries;          /* The libraries it needs */
 
 #ifdef CONFIG_PIC
   FAR void *picbase;                   /* Position-independent */
diff --git a/include/nuttx/lib/elf.h b/include/nuttx/lib/elf.h
index fbe7269596c..63f78867a2a 100644
--- a/include/nuttx/lib/elf.h
+++ b/include/nuttx/lib/elf.h
@@ -33,6 +33,7 @@
 #include <elf.h>
 
 #include <nuttx/addrenv.h>
+#include <nuttx/list.h>
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -174,7 +175,7 @@ struct fdpic_desc_s;
 
 struct module_s
 {
-  FAR struct module_s *flink;          /* Supports a singly linked list */
+  struct list_node node;               /* Node in the list of its registry */
 #ifdef HAVE_LIBC_ELF_NAMES
   char modname[LIBC_ELF_NAMEMAX];        /* Module name */
 #endif
@@ -307,6 +308,13 @@ struct mod_loadinfo_s
   uint16_t      ndesc;       /* Capacity */
   uint16_t      usedesc;     /* Next free slot */
 
+  /* The registry that the object goes on.  Its DT_NEEDED libraries go on
+   * it too, and its undefined symbols are also looked for in the modules on
+   * it.  NULL for none.
+   */
+
+  FAR struct list_node *registry;
+
   /* Address environment.
    *
    * addrenv - This is the handle created by addrenv_allocate() that can be
@@ -549,38 +557,6 @@ int libelf_read(FAR struct mod_loadinfo_s *loadinfo, FAR 
uint8_t *buffer,
 int libelf_findsection(FAR struct mod_loadinfo_s *loadinfo,
                        FAR const char *sectname);
 
-/****************************************************************************
- * Name: libelf_registry_lock
- *
- * Description:
- *   Get exclusive access to the module registry.
- *
- * Input Parameters:
- *   None
- *
- * Returned Value:
- *   None
- *
- ****************************************************************************/
-
-void libelf_registry_lock(void);
-
-/****************************************************************************
- * Name: libelf_registry_unlock
- *
- * Description:
- *   Relinquish the lock on the module registry
- *
- * Input Parameters:
- *   None
- *
- * Returned Value:
- *   None
- *
- ****************************************************************************/
-
-void libelf_registry_unlock(void);
-
 /****************************************************************************
  * Name: libelf_registry_add
  *
@@ -588,37 +564,38 @@ void libelf_registry_unlock(void);
  *   Add a new entry to the module registry.
  *
  * Input Parameters:
- *   modp - The module data structure to be registered.
+ *   registry - The registry.
+ *   modp     - The module data structure to be registered.
  *
  * Returned Value:
  *   None
  *
  * Assumptions:
- *   The caller holds the lock on the module registry.
+ *   The caller holds the lock of the registry.
  *
  ****************************************************************************/
 
-void libelf_registry_add(FAR struct module_s *modp);
+void libelf_registry_add(FAR struct list_node *registry,
+                         FAR struct module_s *modp);
 
 /****************************************************************************
  * Name: libelf_registry_del
  *
  * Description:
- *   Remove a module entry from the registry
+ *   Remove a module entry from its registry.
  *
  * Input Parameters:
  *   modp - The registry entry to be removed.
  *
  * Returned Value:
- *   Zero (OK) is returned if the registry entry was deleted.  Otherwise,
- *   a negated errno value is returned.
+ *   None
  *
  * Assumptions:
- *   The caller holds the lock on the module registry.
+ *   The caller holds the lock of the registry.
  *
  ****************************************************************************/
 
-int libelf_registry_del(FAR struct module_s *modp);
+void libelf_registry_del(FAR struct module_s *modp);
 
 /****************************************************************************
  * Name: libelf_registry_find
@@ -627,19 +604,21 @@ int libelf_registry_del(FAR struct module_s *modp);
  *   Find an entry in the module registry using the name of the module.
  *
  * Input Parameters:
- *   modname - The name of the module to be found
+ *   registry - The registry.
+ *   modname  - The name of the module to be found
  *
  * Returned Value:
  *   If the registry entry is found, a pointer to the module entry is
  *   returned.  NULL is returned if the entry is not found.
  *
  * Assumptions:
- *   The caller holds the lock on the module registry.
+ *   The caller holds the lock of the registry.
  *
  ****************************************************************************/
 
 #ifdef HAVE_LIBC_ELF_NAMES
-FAR struct module_s *libelf_registry_find(FAR const char *modname);
+FAR struct module_s *libelf_registry_find(FAR struct list_node *registry,
+                                          FAR const char *modname);
 #endif
 
 /****************************************************************************
@@ -651,17 +630,19 @@ FAR struct module_s *libelf_registry_find(FAR const char 
*modname);
  *   the handle is probably a stale pointer.
  *
  * Input Parameters:
- *   modp - The registry entry to be verified.
+ *   registry - The registry.
+ *   modp     - The registry entry to be verified.
  *
  * Returned Value:
  *   Returns OK is the module is valid; -ENOENT otherwise.
  *
  * Assumptions:
- *   The caller holds the lock on the module registry.
+ *   The caller holds the lock of the registry.
  *
  ****************************************************************************/
 
-int libelf_registry_verify(FAR struct module_s *modp);
+int libelf_registry_verify(FAR struct list_node *registry,
+                           FAR struct module_s *modp);
 
 /****************************************************************************
  * Name: libelf_registry_foreach
@@ -671,6 +652,7 @@ int libelf_registry_verify(FAR struct module_s *modp);
  *   not available for use by applications.
  *
  * Input Parameters:
+ *   registry - The registry.
  *   callback - This callback function was be called for each entry in the
  *     registry.
  *   arg - This opaque argument will be passed to the callback function.
@@ -681,11 +663,12 @@ int libelf_registry_verify(FAR struct module_s *modp);
  *   that non-zero value will be returned.
  *
  * Assumptions:
- *   The caller does NOT hold the lock on the module registry.
+ *   The caller holds the lock of the registry.
  *
  ****************************************************************************/
 
-int libelf_registry_foreach(mod_callback_t callback, FAR void *arg);
+int libelf_registry_foreach(FAR struct list_node *registry,
+                            mod_callback_t callback, FAR void *arg);
 
 /****************************************************************************
  * Name: libelf_freesymtab
@@ -748,6 +731,7 @@ void libelf_dumpentrypt(FAR struct mod_loadinfo_s 
*loadinfo);
  *
  * Input Parameters:
  *
+ *   registry - The registry to load into, or to find the module on.
  *   filename - Full path to the module binary to be loaded
  *   modname  - The name that can be used to refer to the module after
  *     it has been loaded.
@@ -758,9 +742,13 @@ void libelf_dumpentrypt(FAR struct mod_loadinfo_s 
*loadinfo);
  *   unable to load the module libelf_insert() will return a NULL handle
  *   and the errno variable will be set appropriately.
  *
+ * Assumptions:
+ *   The caller holds the lock of the registry.
+ *
  ****************************************************************************/
 
-FAR void *libelf_insert(FAR const char *filename, FAR const char *modname);
+FAR void *libelf_insert(FAR struct list_node *registry,
+                        FAR const char *filename, FAR const char *modname);
 
 /****************************************************************************
  * Name: libelf_getsymbol
@@ -790,6 +778,9 @@ FAR void *libelf_insert(FAR const char *filename, FAR const 
char *modname);
  *   NOTE: This means that the address zero can never be a valid return
  *   value.
  *
+ * Assumptions:
+ *   The caller holds the lock of the registry.
+ *
  ****************************************************************************/
 
 FAR const void *libelf_getsymbol(FAR void *handle, FAR const char *name);
@@ -813,15 +804,37 @@ int libelf_uninit(FAR struct module_s *modp);
  *   Remove a previously installed module from memory.
  *
  * Input Parameters:
- *   handle - The module handler previously returned by libelf_insert().
+ *   registry - The registry the module is on.
+ *   handle   - The module handler previously returned by libelf_insert().
  *
  * Returned Value:
  *   Zero (OK) on success.  On any failure, -1 (ERROR) is returned the
  *   errno value is set appropriately.
  *
+ * Assumptions:
+ *   The caller holds the lock of the registry.
+ *
+ ****************************************************************************/
+
+int libelf_remove(FAR struct list_node *registry, FAR void *handle);
+
+/****************************************************************************
+ * Name: libelf_removeall
+ *
+ * Description:
+ *   Give back every reference that dlopen() took in a registry, as at
+ *   process exit.  A module that another module still depends on stays,
+ *   and goes with its last dependent.
+ *
+ * Input Parameters:
+ *   registry - The registry.
+ *
+ * Assumptions:
+ *   The caller holds the lock of the registry.
+ *
  ****************************************************************************/
 
-int libelf_remove(FAR void *handle);
+void libelf_removeall(FAR struct list_node *registry);
 
 /****************************************************************************
  * Name: libelf_modhandle
@@ -832,7 +845,8 @@ int libelf_remove(FAR void *handle);
  *   determine if a module has been loaded or not.
  *
  * Input Parameters:
- *   name   - A pointer to the module name string.
+ *   registry - The registry.
+ *   name     - A pointer to the module name string.
  *
  * Returned Value:
  *   The non-NULL module handle previously returned by libelf_insert() is
@@ -840,12 +854,16 @@ int libelf_remove(FAR void *handle);
  *   libelf_modhandle() will return a NULL handle and the errno variable
  *   will be set appropriately.
  *
+ * Assumptions:
+ *   The caller holds the lock of the registry.
+ *
  ****************************************************************************/
 
 #ifdef HAVE_LIBC_ELF_NAMES
-FAR void *libelf_gethandle(FAR const char *name);
+FAR void *libelf_gethandle(FAR struct list_node *registry,
+                           FAR const char *name);
 #else
-#  define libelf_gethandle(n) NULL
+#  define libelf_gethandle(r, n) NULL
 #endif
 
 /****************************************************************************
diff --git a/include/nuttx/tls.h b/include/nuttx/tls.h
index bd103a73dca..facbf18dee4 100644
--- a/include/nuttx/tls.h
+++ b/include/nuttx/tls.h
@@ -29,6 +29,7 @@
 
 #include <nuttx/config.h>
 
+#include <errno.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -40,13 +41,10 @@
 #include <nuttx/cache.h>
 #include <nuttx/atexit.h>
 #include <nuttx/fs/fs.h>
+#include <nuttx/list.h>
 #include <nuttx/mutex.h>
 #include <nuttx/queue.h>
 
-#ifdef CONFIG_PTHREAD_ATFORK
-#  include <nuttx/list.h>
-#endif
-
 #include <sys/types.h>
 
 /****************************************************************************
@@ -173,6 +171,10 @@ struct task_info_s
   size_t                ta_envpc;   /* Maximum entries of environment array */
   size_t                ta_envc;    /* Number of environment strings        */
 #endif
+#if defined(CONFIG_LIBC_ELF) || defined(CONFIG_BINFMT_LOADABLE)
+  rmutex_t         ta_modlock; /* Protects ta_modules */
+  struct list_node ta_modules; /* Objects this task group loaded */
+#endif
 };
 
 /* struct tls_cleanup_s *****************************************************/
@@ -396,6 +398,48 @@ void tls_destruct(void);
 
 FAR struct task_info_s *task_get_info(void);
 
+#if defined(CONFIG_LIBC_ELF) || defined(CONFIG_BINFMT_LOADABLE)
+/****************************************************************************
+ * Name: task_modules_lock
+ *
+ * Description:
+ *   Lock the list of the objects that the calling task group loaded
+ *   (dlopen() and the libraries its program needs) and return it.  The
+ *   lock is recursive: the constructors of a library run with it held, and
+ *   they can call dlopen().
+ *
+ ****************************************************************************/
+
+static inline FAR struct list_node *task_modules_lock(void)
+{
+  FAR struct task_info_s *info = task_get_info();
+  int ret;
+
+  /* A signal must not make the caller run without the lock */
+
+  do
+    {
+      ret = nxrmutex_lock(&info->ta_modlock);
+    }
+  while (ret == -EINTR);
+
+  return &info->ta_modules;
+}
+
+/****************************************************************************
+ * Name: task_modules_unlock
+ *
+ * Description:
+ *   Unlock the list of the objects that the calling task group loaded.
+ *
+ ****************************************************************************/
+
+static inline void task_modules_unlock(void)
+{
+  nxrmutex_unlock(&task_get_info()->ta_modlock);
+}
+#endif
+
 #if CONFIG_TLS_NCLEANUP > 0
 /****************************************************************************
  * Name: tls_cleanup_push
diff --git a/libs/libc/dlfcn/lib_dlclose.c b/libs/libc/dlfcn/lib_dlclose.c
index 67b96c97bd5..587bdd2b3b5 100644
--- a/libs/libc/dlfcn/lib_dlclose.c
+++ b/libs/libc/dlfcn/lib_dlclose.c
@@ -29,6 +29,7 @@
 #include <dlfcn.h>
 
 #include <nuttx/lib/elf.h>
+#include <nuttx/tls.h>
 
 /****************************************************************************
  * Private Functions
@@ -98,7 +99,13 @@ int dlclose(FAR void *handle)
    * one goes, so closing one of two handles leaves the other usable.
    */
 
-  return libelf_remove(handle);
+  FAR struct list_node *registry;
+  int ret;
+
+  registry = task_modules_lock();
+  ret = libelf_remove(registry, handle);
+  task_modules_unlock();
+  return ret;
 
 #else /* if defined(CONFIG_BUILD_KERNEL) */
   /* The KERNEL build is considerably more complex:  In order to be shared,
diff --git a/libs/libc/dlfcn/lib_dlopen.c b/libs/libc/dlfcn/lib_dlopen.c
index 79ef2270549..c1c25a9515d 100644
--- a/libs/libc/dlfcn/lib_dlopen.c
+++ b/libs/libc/dlfcn/lib_dlopen.c
@@ -34,6 +34,7 @@
 #include <nuttx/envpath.h>
 #include <nuttx/lib/elf.h>
 #include <nuttx/lib/lib.h>
+#include <nuttx/tls.h>
 
 /****************************************************************************
  * Private Functions
@@ -84,7 +85,9 @@
 
 static inline FAR void *dlinsert(FAR const char *filename)
 {
+  FAR struct list_node *registry;
   FAR const char *modname;
+  FAR void *handle;
 
   DEBUGASSERT(filename != NULL);
 
@@ -93,12 +96,15 @@ static inline FAR void *dlinsert(FAR const char *filename)
   modname = strrchr(filename, '/');
   modname = modname != NULL ? modname + 1 : filename;
 
-  /* libelf_insert() returns the module already loaded under this name,
-   * with another reference taken, so a library opened twice is one
-   * instance shared by both callers.
+  /* libelf_insert() returns the module already loaded under this name in
+   * this task group, with another reference taken, so a library opened
+   * twice in a task group is one instance.
    */
 
-  return libelf_insert(filename, modname);
+  registry = task_modules_lock();
+  handle = libelf_insert(registry, filename, modname);
+  task_modules_unlock();
+  return handle;
 }
 #else /* if defined(CONFIG_BUILD_KERNEL) */
 /* The KERNEL build is considerably more complex:  In order to be shared,
diff --git a/libs/libc/elf/elf_bind.c b/libs/libc/elf/elf_bind.c
index f5ba11fff68..18638b63f63 100644
--- a/libs/libc/elf/elf_bind.c
+++ b/libs/libc/elf/elf_bind.c
@@ -135,7 +135,8 @@ struct reldata_s
  * Description:
  *   Load a library a module names in DT_NEEDED.  A bare name is looked for
  *   along LD_LIBRARY_PATH, as dlopen() looks for it, and the module is
- *   registered under its base name, so a library two modules name is loaded
+ *   registered under its base name on the registry of the module that
+ *   needs it, so a library two modules of one task group name is loaded
  *   once and reference counted.
  *
  * Returned Value:
@@ -143,10 +144,17 @@ struct reldata_s
  *
  ****************************************************************************/
 
-static FAR void *libelf_insertneeded(FAR const char *name)
+static FAR void *libelf_insertneeded(FAR struct list_node *registry,
+                                     FAR const char *name)
 {
   FAR const char *modname;
 
+  if (registry == NULL)
+    {
+      berr("ERROR: No registry for needed library %s\n", name);
+      return NULL;
+    }
+
   modname = strrchr(name, '/');
   modname = modname != NULL ? modname + 1 : name;
 
@@ -162,7 +170,7 @@ static FAR void *libelf_insertneeded(FAR const char *name)
         {
           while ((fullpath = envpath_next(env, name)) != NULL)
             {
-              handle = libelf_insert(fullpath, modname);
+              handle = libelf_insert(registry, fullpath, modname);
               lib_free(fullpath);
 
               if (handle != NULL)
@@ -178,7 +186,7 @@ static FAR void *libelf_insertneeded(FAR const char *name)
     }
 #endif
 
-  return libelf_insert(name, modname);
+  return libelf_insert(registry, name, modname);
 }
 #endif
 
@@ -1040,7 +1048,10 @@ static int libelf_relocatedyn(FAR struct module_s *modp,
           return ret;
         }
 
-      handle = libelf_insertneeded((FAR const char *)loadinfo->iobuffer);
+      /* The library goes on the registry of the module that needs it */
+
+      handle = libelf_insertneeded(loadinfo->registry,
+                                   (FAR const char *)loadinfo->iobuffer);
       if (handle == NULL)
         {
           berr("ERROR: Cannot open needed library %s\n",
@@ -1057,20 +1068,16 @@ static int libelf_relocatedyn(FAR struct module_s *modp,
        * reference libelf_insert() took, so libelf_undepend() lets it go.
        */
 
-      libelf_registry_lock();
       ret = libelf_depend(modp, handle);
       if (ret >= 0)
         {
           ((FAR struct module_s *)handle)->nopen--;
         }
-
-      libelf_registry_unlock();
-
-      if (ret < 0)
+      else
         {
           berr("ERROR: Cannot depend on %s: %d\n",
                (FAR char *)loadinfo->iobuffer, ret);
-          libelf_remove(handle);
+          libelf_remove(loadinfo->registry, handle);
           lib_free(sym);
           lib_free(rels);
           lib_free(dyn);
diff --git a/libs/libc/elf/elf_depend.c b/libs/libc/elf/elf_depend.c
index db4f8b18f67..8bcc8d4aa53 100644
--- a/libs/libc/elf/elf_depend.c
+++ b/libs/libc/elf/elf_depend.c
@@ -31,6 +31,7 @@
 #include <errno.h>
 #include <nuttx/debug.h>
 
+#include <nuttx/lib/lib.h>
 #include <nuttx/lib/elf.h>
 
 #if CONFIG_LIBC_ELF_MAXDEPEND > 0
@@ -176,12 +177,15 @@ int libelf_undepend(FAR struct module_s *importer)
           importer->dependencies[i] = NULL;
 
           /* A library loaded for a DT_NEEDED entry is held only by the
-           * modules that depend on it, so the last of them unloads it.
+           * modules that depend on it, so the last of them unloads it.  It
+           * leaves its list here, so no registry is needed.
            */
 
-          if (exporter->dependents == 0 && exporter->nopen == 0)
+          if (exporter->dependents == 0 && exporter->nopen == 0 &&
+              libelf_uninit(exporter) >= 0)
             {
-              libelf_remove(exporter);
+              libelf_registry_del(exporter);
+              lib_free(exporter);
             }
         }
     }
diff --git a/libs/libc/elf/elf_gethandle.c b/libs/libc/elf/elf_gethandle.c
index 74ffa6a61fd..c6ca31fca4f 100644
--- a/libs/libc/elf/elf_gethandle.c
+++ b/libs/libc/elf/elf_gethandle.c
@@ -42,7 +42,8 @@
  *   determine if a module has been loaded or not.
  *
  * Input Parameters:
- *   name   - A pointer to the module name string.
+ *   registry - The registry.
+ *   name     - A pointer to the module name string.
  *
  * Returned Value:
  *   The non-NULL module handle previously returned by libelf_insert() is
@@ -50,30 +51,29 @@
  *   libelf_modhandle() will return a NULL handle and the errno variable
  *   will be set appropriately.
  *
+ * Assumptions:
+ *   The caller holds the lock of the registry.
+ *
  ****************************************************************************/
 
 #ifdef HAVE_LIBC_ELF_NAMES
 
-FAR void *libelf_gethandle(FAR const char *name)
+FAR void *libelf_gethandle(FAR struct list_node *registry,
+                           FAR const char *name)
 {
   FAR struct module_s *modp;
 
   DEBUGASSERT(name != NULL);
 
-  /* Get exclusive access to the module registry */
-
-  libelf_registry_lock();
-
   /* Find the module entry for this name in the registry */
 
-  modp = libelf_registry_find(name);
+  modp = libelf_registry_find(registry, name);
   if (modp == NULL)
     {
       berr("ERROR: Failed to find module %s\n", name);
       set_errno(ENOENT);
     }
 
-  libelf_registry_unlock();
   return modp;
 }
 
diff --git a/libs/libc/elf/elf_getsymbol.c b/libs/libc/elf/elf_getsymbol.c
index ef49c0caa3b..04142a5560c 100644
--- a/libs/libc/elf/elf_getsymbol.c
+++ b/libs/libc/elf/elf_getsymbol.c
@@ -69,18 +69,6 @@ FAR const void *libelf_getsymbol(FAR void *handle, FAR const 
char *name)
   FAR struct module_s *modp = handle;
   FAR const struct symtab_s *symbol;
   int err;
-  int ret;
-
-  /* Verify that the module is in the registry */
-
-  libelf_registry_lock();
-  ret = libelf_registry_verify(modp);
-  if (ret < 0)
-    {
-      berr("ERROR: Failed to verify module: %d\n", ret);
-      err = -ret;
-      goto errout_with_lock;
-    }
 
   /* Does the module have a symbol table? */
 
@@ -88,15 +76,13 @@ FAR const void *libelf_getsymbol(FAR void *handle, FAR 
const char *name)
     {
       berr("ERROR: Module has no symbol table\n");
       err = ENOENT;
-      goto errout_with_lock;
+      goto errout;
     }
 
   /* Search the symbol table for the matching symbol */
 
   symbol = symtab_findbyname(modp->modinfo.exports, name,
                              modp->modinfo.nexports);
-
-  libelf_registry_unlock();
   if (symbol == NULL)
     {
       berr("ERROR: Failed to find symbol in symbol \"%s\" in table\n", name);
@@ -109,8 +95,7 @@ FAR const void *libelf_getsymbol(FAR void *handle, FAR const 
char *name)
   DEBUGASSERT(symbol->sym_value != NULL);
   return symbol->sym_value;
 
-errout_with_lock:
-  libelf_registry_unlock();
+errout:
   set_errno(err);
   return NULL;
 }
diff --git a/libs/libc/elf/elf_insert.c b/libs/libc/elf/elf_insert.c
index bee8f09254c..0c5c0185e03 100644
--- a/libs/libc/elf/elf_insert.c
+++ b/libs/libc/elf/elf_insert.c
@@ -292,6 +292,7 @@ static int libelf_loadsymtab(FAR struct module_s *modp,
  *
  * Input Parameters:
  *
+ *   registry - The registry to load into, or to find the module on.
  *   filename - Full path to the module binary to be loaded
  *   modname  - The name that can be used to refer to the module after
  *     it has been loaded.
@@ -302,9 +303,14 @@ static int libelf_loadsymtab(FAR struct module_s *modp,
  *   unable to load the module libelf_insert() will return a NULL handle
  *   and the errno variable will be set appropriately.
  *
+ * Assumptions:
+ *   The caller holds the lock of the registry.  Constructors run with it
+ *   held, so it must be recursive.
+ *
  ****************************************************************************/
 
-FAR void *libelf_insert(FAR const char *filename, FAR const char *modname)
+FAR void *libelf_insert(FAR struct list_node *registry,
+                        FAR const char *filename, FAR const char *modname)
 {
   FAR const struct symtab_s *exports;
   struct mod_loadinfo_s loadinfo;
@@ -314,32 +320,25 @@ FAR void *libelf_insert(FAR const char *filename, FAR 
const char *modname)
   int ret;
   int i;
 
-  DEBUGASSERT(filename != NULL && modname != NULL);
+  DEBUGASSERT(registry != NULL && filename != NULL && modname != NULL);
   binfo("Loading file: %s\n", filename);
 
-  /* Get exclusive access to the module registry */
-
-  libelf_registry_lock();
-
-  /* Already installed?  Take another reference rather than load a second
-   * copy: there is one instance of a module per name, and every caller
-   * shares it.  The count is kept here so that insmod()/rmmod() and
+  /* Already on this registry?  Take another reference rather than load a
+   * second copy.  The count is kept here so that insmod()/rmmod() and
    * dlopen()/dlclose() get the same behaviour from the same code.
    */
 
 #ifdef HAVE_LIBC_ELF_NAMES
-  modp = libelf_registry_find(modname);
+  modp = libelf_registry_find(registry, modname);
   if (modp != NULL)
     {
       if (modp->nopen == UINT8_MAX)
         {
-          libelf_registry_unlock();
           set_errno(EMFILE);
           return NULL;
         }
 
       modp->nopen++;
-      libelf_registry_unlock();
       return modp;
     }
 #endif
@@ -354,6 +353,10 @@ FAR void *libelf_insert(FAR const char *filename, FAR 
const char *modname)
       goto errout_with_loadinfo;
     }
 
+  /* Its DT_NEEDED libraries go on the same registry */
+
+  loadinfo.registry = registry;
+
   /* Allocate a module registry entry to hold the module data */
 
   modp = lib_zalloc(sizeof(struct module_s));
@@ -456,10 +459,9 @@ FAR void *libelf_insert(FAR const char *filename, FAR 
const char *modname)
 
   /* Add the new module entry to the registry */
 
-  libelf_registry_add(modp);
+  libelf_registry_add(registry, modp);
 
   libelf_uninitialize(&loadinfo);
-  libelf_registry_unlock();
   return modp;
 
 errout_with_load:
@@ -471,7 +473,6 @@ errout_with_registry_entry:
   lib_free(modp);
 errout_with_loadinfo:
   libelf_uninitialize(&loadinfo);
-  libelf_registry_unlock();
   set_errno(-ret);
   return NULL;
 }
diff --git a/libs/libc/elf/elf_registry.c b/libs/libc/elf/elf_registry.c
index a55dd63032f..1612f0faa76 100644
--- a/libs/libc/elf/elf_registry.c
+++ b/libs/libc/elf/elf_registry.c
@@ -28,69 +28,15 @@
 
 #include <string.h>
 #include <assert.h>
-#include <nuttx/debug.h>
 #include <errno.h>
 
-#include <nuttx/mutex.h>
+#include <nuttx/list.h>
 #include <nuttx/lib/elf.h>
 
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-/****************************************************************************
- * Private Types
- ****************************************************************************/
-
-/****************************************************************************
- * Private Data
- ****************************************************************************/
-
-static rmutex_t g_modlock = NXRMUTEX_INITIALIZER;
-static FAR struct module_s *g_mod_registry;
-
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
 
-/****************************************************************************
- * Name: libelf_registry_lock
- *
- * Description:
- *   Get exclusive access to the module registry.
- *
- * Input Parameters:
- *   None
- *
- * Returned Value:
- *   None
- *
- ****************************************************************************/
-
-void libelf_registry_lock(void)
-{
-  nxrmutex_lock(&g_modlock);
-}
-
-/****************************************************************************
- * Name: libelf_registry_unlock
- *
- * Description:
- *   Relinquish the lock on the module registry
- *
- * Input Parameters:
- *   None
- *
- * Returned Value:
- *   None
- *
- ****************************************************************************/
-
-void libelf_registry_unlock(void)
-{
-  nxrmutex_unlock(&g_modlock);
-}
-
 /****************************************************************************
  * Name: libelf_registry_add
  *
@@ -98,67 +44,45 @@ void libelf_registry_unlock(void)
  *   Add a new entry to the module registry.
  *
  * Input Parameters:
- *   modp - The module data structure to be registered.
+ *   registry - The registry.
+ *   modp     - The module data structure to be registered.
  *
  * Returned Value:
  *   None
  *
  * Assumptions:
- *   The caller holds the lock on the module registry.
+ *   The caller holds the lock of the registry.
  *
  ****************************************************************************/
 
-void libelf_registry_add(FAR struct module_s *modp)
+void libelf_registry_add(FAR struct list_node *registry,
+                         FAR struct module_s *modp)
 {
-  DEBUGASSERT(modp);
-  modp->flink = g_mod_registry;
-  g_mod_registry = modp;
+  DEBUGASSERT(registry != NULL && modp != NULL);
+  list_add_head(registry, &modp->node);
 }
 
 /****************************************************************************
  * Name: libelf_registry_del
  *
  * Description:
- *   Remove a module entry from the registry
+ *   Remove a module entry from its registry.
  *
  * Input Parameters:
  *   modp - The registry entry to be removed.
  *
  * Returned Value:
- *   Zero (OK) is returned if the registry entry was deleted.  Otherwise,
- *   a negated errno value is returned.
+ *   None
  *
  * Assumptions:
- *   The caller holds the lock on the module registry.
+ *   The caller holds the lock of the registry.
  *
  ****************************************************************************/
 
-int libelf_registry_del(FAR struct module_s *modp)
+void libelf_registry_del(FAR struct module_s *modp)
 {
-  FAR struct module_s *prev;
-  FAR struct module_s *curr;
-
-  for (prev = NULL, curr = g_mod_registry;
-       curr != NULL && curr != modp;
-       prev = curr, curr = curr->flink);
-
-  if (curr == NULL)
-    {
-      berr("ERROR: Could not find module entry\n");
-      return -ENOENT;
-    }
-
-  if (prev == NULL)
-    {
-      g_mod_registry = modp->flink;
-    }
-  else
-    {
-      prev->flink = modp->flink;
-    }
-
-  modp->flink = NULL;
-  return OK;
+  DEBUGASSERT(modp != NULL);
+  list_delete_init(&modp->node);
 }
 
 /****************************************************************************
@@ -168,28 +92,33 @@ int libelf_registry_del(FAR struct module_s *modp)
  *   Find an entry in the module registry using the name of the module.
  *
  * Input Parameters:
- *   modname - The name of the module to be found
+ *   registry - The registry.
+ *   modname  - The name of the module to be found
  *
  * Returned Value:
  *   If the registry entry is found, a pointer to the module entry is
  *   returned.  NULL is returned if the entry is not found.
  *
  * Assumptions:
- *   The caller holds the lock on the module registry.
+ *   The caller holds the lock of the registry.
  *
  ****************************************************************************/
 
 #ifdef HAVE_LIBC_ELF_NAMES
-FAR struct module_s *libelf_registry_find(FAR const char *modname)
+FAR struct module_s *libelf_registry_find(FAR struct list_node *registry,
+                                          FAR const char *modname)
 {
   FAR struct module_s *modp;
 
-  for (modp = g_mod_registry;
-       modp != NULL &&
-       strncmp(modp->modname, modname, LIBC_ELF_NAMEMAX) != 0;
-       modp = modp->flink);
+  list_for_every_entry(registry, modp, struct module_s, node)
+    {
+      if (strncmp(modp->modname, modname, LIBC_ELF_NAMEMAX) == 0)
+        {
+          return modp;
+        }
+    }
 
-  return modp;
+  return NULL;
 }
 #endif
 
@@ -202,21 +131,23 @@ FAR struct module_s *libelf_registry_find(FAR const char 
*modname)
  *   the handle is probably a stale pointer.
  *
  * Input Parameters:
- *   modp - The registry entry to be verified.
+ *   registry - The registry.
+ *   modp     - The registry entry to be verified.
  *
  * Returned Value:
  *   Returns OK is the module is valid; -ENOENT otherwise.
  *
  * Assumptions:
- *   The caller holds the lock on the module registry.
+ *   The caller holds the lock of the registry.
  *
  ****************************************************************************/
 
-int libelf_registry_verify(FAR struct module_s *modp)
+int libelf_registry_verify(FAR struct list_node *registry,
+                           FAR struct module_s *modp)
 {
   FAR struct module_s *node;
 
-  for (node = g_mod_registry; node != NULL; node = node->flink)
+  list_for_every_entry(registry, node, struct module_s, node)
     {
       if (node == modp)
         {
@@ -234,6 +165,7 @@ int libelf_registry_verify(FAR struct module_s *modp)
  *   Visit each module in the registry
  *
  * Input Parameters:
+ *   registry - The registry, or NULL for none.
  *   callback - This callback function was be called for each entry in the
  *     registry.
  *   arg - This opaque argument will be passed to the callback function.
@@ -243,23 +175,24 @@ int libelf_registry_verify(FAR struct module_s *modp)
  *   function returns a non-zero value, the traversal will be terminated and
  *   that non-zero value will be returned.
  *
+ * Assumptions:
+ *   The caller holds the lock of the registry.
+ *
  ****************************************************************************/
 
-int libelf_registry_foreach(mod_callback_t callback, FAR void *arg)
+int libelf_registry_foreach(FAR struct list_node *registry,
+                            mod_callback_t callback, FAR void *arg)
 {
   FAR struct module_s *modp;
   int ret = OK;
 
-  /* Get exclusive access to the module registry */
-
-  libelf_registry_lock();
-
-  /* Visit each installed module */
-
-  for (modp = g_mod_registry; modp != NULL; modp = modp->flink)
+  if (registry == NULL)
     {
-      /* Perform the callback */
+      return OK;
+    }
 
+  list_for_every_entry(registry, modp, struct module_s, node)
+    {
       ret = callback(modp, arg);
       if (ret != 0)
         {
@@ -267,6 +200,5 @@ int libelf_registry_foreach(mod_callback_t callback, FAR 
void *arg)
         }
     }
 
-  libelf_registry_unlock();
   return ret;
 }
diff --git a/libs/libc/elf/elf_remove.c b/libs/libc/elf/elf_remove.c
index ad00ef99fcb..5623096182d 100644
--- a/libs/libc/elf/elf_remove.c
+++ b/libs/libc/elf/elf_remove.c
@@ -217,32 +217,32 @@ int libelf_uninit(FAR struct module_s *modp)
  *   Remove a previously installed module from memory.
  *
  * Input Parameters:
- *   handle - The module handler previously returned by libelf_insert().
+ *   registry - The registry the module is on.
+ *   handle   - The module handler previously returned by libelf_insert().
  *
  * Returned Value:
  *   Zero (OK) on success.  On any failure, -1 (ERROR) is returned the
  *   errno value is set appropriately.
  *
+ * Assumptions:
+ *   The caller holds the lock of the registry.
+ *
  ****************************************************************************/
 
-int libelf_remove(FAR void *handle)
+int libelf_remove(FAR struct list_node *registry, FAR void *handle)
 {
   FAR struct module_s *modp = (FAR struct module_s *)handle;
   int ret;
 
   DEBUGASSERT(modp != NULL);
 
-  /* Get exclusive access to the module registry */
-
-  libelf_registry_lock();
-
   /* Verify that the module is in the registry */
 
-  ret = libelf_registry_verify(modp);
+  ret = libelf_registry_verify(registry, modp);
   if (ret < 0)
     {
       berr("ERROR: Failed to verify module: %d\n", ret);
-      goto errout_with_lock;
+      goto errout;
     }
 
   /* Give back a reference.  The module goes only when the last one does,
@@ -253,7 +253,6 @@ int libelf_remove(FAR void *handle)
   if (modp->nopen > 1)
     {
       modp->nopen--;
-      libelf_registry_unlock();
       return OK;
     }
 
@@ -266,7 +265,7 @@ int libelf_remove(FAR void *handle)
     {
       berr("ERROR: Module has dependents: %d\n", modp->dependents);
       ret = -EBUSY;
-      goto errout_with_lock;
+      goto errout;
     }
 #endif
 
@@ -277,29 +276,57 @@ int libelf_remove(FAR void *handle)
     {
       berr("ERROR: Failed to uninitialize module %d\n", ret);
       modp->nopen = 1;
-      goto errout_with_lock;
+      goto errout;
     }
 
-  /* Remove the module from the registry */
+  /* Remove the module from the registry and free the registry entry */
 
-  ret = libelf_registry_del(modp);
-  if (ret < 0)
-    {
-      berr("ERROR: Failed to remove the module from the registry: %d\n",
-           ret);
-      goto errout_with_lock;
-    }
+  libelf_registry_del(modp);
+  lib_free(modp);
+  return OK;
 
-  libelf_registry_unlock();
+errout:
+  set_errno(-ret);
+  return ERROR;
+}
 
-  /* And free the registry entry */
+/****************************************************************************
+ * Name: libelf_removeall
+ *
+ * Description:
+ *   Give back every reference that dlopen() took in a registry, as at
+ *   process exit.  A module that another module still depends on stays,
+ *   and goes with its last dependent.
+ *
+ * Input Parameters:
+ *   registry - The registry.
+ *
+ * Assumptions:
+ *   The caller holds the lock of the registry.
+ *
+ ****************************************************************************/
 
-  lib_free(modp);
+void libelf_removeall(FAR struct list_node *registry)
+{
+  FAR struct module_s *modp;
+  FAR struct module_s *next;
 
-  return ret;
+  /* A removal can unload other modules of the list, so start again after
+   * each one.
+   */
 
-errout_with_lock:
-  libelf_registry_unlock();
-  set_errno(-ret);
-  return ERROR;
+  list_for_every_entry_safe(registry, modp, next, struct module_s, node)
+    {
+      if (modp->nopen > 0)
+        {
+          modp->nopen = 0;
+#if CONFIG_LIBC_ELF_MAXDEPEND > 0
+          if (modp->dependents == 0)
+#endif
+            {
+              libelf_remove(registry, modp);
+              next = list_first_entry(registry, struct module_s, node);
+            }
+        }
+    }
 }
diff --git a/libs/libc/elf/elf_symbols.c b/libs/libc/elf/elf_symbols.c
index d81f954ff12..7d3a1e61bbe 100644
--- a/libs/libc/elf/elf_symbols.c
+++ b/libs/libc/elf/elf_symbols.c
@@ -470,7 +470,8 @@ int libelf_symvalue(FAR struct module_s *modp,
           exportinfo.modp   = modp;
           exportinfo.symbol = NULL;
 
-          ret = libelf_registry_foreach(libelf_symcallback,
+          ret = libelf_registry_foreach(loadinfo->registry,
+                                        libelf_symcallback,
                                         (FAR void *)&exportinfo);
           if (ret < 0)
             {
diff --git a/libs/libc/elf/elf_symtab.c b/libs/libc/elf/elf_symtab.c
index ba9dbfb9022..1b85ea16262 100644
--- a/libs/libc/elf/elf_symtab.c
+++ b/libs/libc/elf/elf_symtab.c
@@ -28,6 +28,7 @@
 
 #include <assert.h>
 
+#include <nuttx/mutex.h>
 #include <nuttx/symtab.h>
 #include <nuttx/lib/elf.h>
 
@@ -62,6 +63,7 @@ extern int CONFIG_LIBC_ELF_NSYMBOLS_VAR;
  * Private Data
  ****************************************************************************/
 
+static mutex_t g_libelf_symlock = NXMUTEX_INITIALIZER;
 static FAR const struct symtab_s *g_libelf_symtab;
 static int g_libelf_nsymbols;
 
@@ -89,9 +91,7 @@ void libelf_getsymtab(FAR const struct symtab_s **symtab, FAR 
int *nsymbols)
 {
   DEBUGASSERT(symtab != NULL && nsymbols != NULL);
 
-  /* Borrow the registry lock to assure atomic access */
-
-  libelf_registry_lock();
+  nxmutex_lock(&g_libelf_symlock);
 #ifdef CONFIG_LIBC_ELF_HAVE_SYMTAB
   if (g_libelf_symtab == NULL)
     {
@@ -102,7 +102,7 @@ void libelf_getsymtab(FAR const struct symtab_s **symtab, 
FAR int *nsymbols)
 
   *symtab   = g_libelf_symtab;
   *nsymbols = g_libelf_nsymbols;
-  libelf_registry_unlock();
+  nxmutex_unlock(&g_libelf_symlock);
 }
 
 /****************************************************************************
@@ -122,10 +122,8 @@ void libelf_getsymtab(FAR const struct symtab_s **symtab, 
FAR int *nsymbols)
 
 void libelf_setsymtab(FAR const struct symtab_s *symtab, int nsymbols)
 {
-  /* Borrow the registry lock to assure atomic access */
-
-  libelf_registry_lock();
+  nxmutex_lock(&g_libelf_symlock);
   g_libelf_symtab   = symtab;
   g_libelf_nsymbols = nsymbols;
-  libelf_registry_unlock();
+  nxmutex_unlock(&g_libelf_symlock);
 }
diff --git a/libs/libc/stdlib/lib_exit.c b/libs/libc/stdlib/lib_exit.c
index af579946534..460bee9f828 100644
--- a/libs/libc/stdlib/lib_exit.c
+++ b/libs/libc/stdlib/lib_exit.c
@@ -35,6 +35,7 @@
 
 #include <nuttx/tls.h>
 #include <nuttx/pthread.h>
+#include <nuttx/lib/elf.h>
 
 #if defined(CONFIG_BUILD_FLAT) || !defined(__KERNEL__)
 
@@ -108,6 +109,16 @@ void exit(int status)
 
   atexit_call_exitfuncs(status, false);
 
+#if defined(CONFIG_LIBC_DLFCN) && defined(CONFIG_BUILD_PROTECTED)
+  /* Close what dlopen() left open.  The kernel cannot reach this registry.
+   * A flat build does it in the kernel, after the last thread.  A kernel
+   * build has no registry in user space.
+   */
+
+  libelf_removeall(task_modules_lock());
+  task_modules_unlock();
+#endif
+
 #if defined(CONFIG_TLS_TASK_NELEM) && CONFIG_TLS_TASK_NELEM > 0
   task_tls_destruct();
 #endif
diff --git a/sched/group/group_leave.c b/sched/group/group_leave.c
index 80a8f1c7880..404a33c82f1 100644
--- a/sched/group/group_leave.c
+++ b/sched/group/group_leave.c
@@ -34,6 +34,7 @@
 #include <nuttx/irq.h>
 #include <nuttx/fs/fs.h>
 #include <nuttx/kmalloc.h>
+#include <nuttx/lib/elf.h>
 #include <nuttx/net/net.h>
 #include <nuttx/sched.h>
 #include <nuttx/spinlock.h>
@@ -80,6 +81,30 @@ static inline void group_release(FAR struct task_group_s 
*group)
 
   nxrmutex_destroy(&group->tg_mutex);
 
+#if defined(CONFIG_LIBC_ELF) && defined(CONFIG_BUILD_FLAT)
+  /* Close what dlopen() left open.  No thread of the group runs now, so
+   * the lock is not needed.
+   */
+
+  libelf_removeall(&group->tg_info->ta_modules);
+
+#  ifdef CONFIG_BINFMT_LOADABLE
+  /* The rest is used by the program.  Give it back to the program before
+   * the list goes with the task information: binfmt_exit() unloads it.
+   */
+
+  if (group->tg_bininfo != NULL)
+    {
+      FAR struct list_node *node;
+
+      while ((node = list_remove_head(&group->tg_info->ta_modules)) != NULL)
+        {
+          list_add_tail(&group->tg_bininfo->libraries, node);
+        }
+    }
+#  endif
+#endif
+
   task_uninit_info(group);
 
 #if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS)
diff --git a/sched/module/CMakeLists.txt b/sched/module/CMakeLists.txt
index 4acb7e486bb..d575d0de5c4 100644
--- a/sched/module/CMakeLists.txt
+++ b/sched/module/CMakeLists.txt
@@ -21,9 +21,11 @@
 # 
##############################################################################
 
 if(CONFIG_MODULE)
-  set(SRCS mod_insmod.c mod_rmmod.c mod_modsym.c mod_modhandle.c)
+  set(SRCS mod_insmod.c mod_rmmod.c mod_modsym.c mod_modhandle.c 
mod_registry.c)
 
-  if(CONFIG_FS_PROCFS AND NOT CONFIG_FS_PROCFS_EXCLUDE_MODULE)
+  if(NOT CONFIG_DISABLE_MOUNTPOINT
+     AND CONFIG_FS_PROCFS
+     AND NOT CONFIG_FS_PROCFS_EXCLUDE_MODULE)
     list(APPEND SRCS mod_procfs.c)
   endif()
 
diff --git a/sched/module/Make.defs b/sched/module/Make.defs
index 3c385ce6824..70c7197ba97 100644
--- a/sched/module/Make.defs
+++ b/sched/module/Make.defs
@@ -24,15 +24,17 @@ ifeq ($(CONFIG_MODULE),y)
 
 # OS module interfaces
 
-CSRCS += mod_insmod.c mod_rmmod.c mod_modsym.c mod_modhandle.c
+CSRCS += mod_insmod.c mod_rmmod.c mod_modsym.c mod_modhandle.c mod_registry.c
 
 # procfs support
 
+ifneq ($(CONFIG_DISABLE_MOUNTPOINT),y)
 ifeq ($(CONFIG_FS_PROCFS),y)
 ifneq ($(CONFIG_FS_PROCFS_EXCLUDE_MODULE),y)
 CSRCS += mod_procfs.c
 endif
 endif
+endif
 
 # Hook the module subdirectory into the build
 
diff --git a/sched/module/mod_insmod.c b/sched/module/mod_insmod.c
index 28a9e0c0169..2b99985147f 100644
--- a/sched/module/mod_insmod.c
+++ b/sched/module/mod_insmod.c
@@ -31,7 +31,7 @@
 #include <nuttx/module.h>
 #include <nuttx/lib/elf.h>
 
-#ifdef CONFIG_MODULE
+#include "module/module.h"
 
 /****************************************************************************
  * Public Functions
@@ -65,18 +65,24 @@
 
 FAR void *insmod(FAR const char *filename, FAR const char *modname)
 {
+  FAR struct list_node *registry;
+  FAR void *handle;
+
+  registry = mod_registry_lock();
+
   /* A duplicate name is an error here, where it has always been.
    * libelf_insert() would take a reference instead, which is what
    * dlopen() wants and insmod() does not.
    */
 
-  if (libelf_gethandle(modname) != NULL)
+  if (libelf_gethandle(registry, modname) != NULL)
     {
+      mod_registry_unlock();
       set_errno(EEXIST);
       return NULL;
     }
 
-  return libelf_insert(filename, modname);
+  handle = libelf_insert(registry, filename, modname);
+  mod_registry_unlock();
+  return handle;
 }
-
-#endif /* CONFIG_MODULE */
diff --git a/sched/module/mod_modhandle.c b/sched/module/mod_modhandle.c
index 10806a8af7d..5fdea41846c 100644
--- a/sched/module/mod_modhandle.c
+++ b/sched/module/mod_modhandle.c
@@ -29,7 +29,7 @@
 #include <nuttx/module.h>
 #include <nuttx/lib/elf.h>
 
-#ifdef CONFIG_MODULE
+#include "module/module.h"
 
 /****************************************************************************
  * Public Functions
@@ -55,7 +55,11 @@
 
 FAR void *modhandle(FAR const char *name)
 {
-  return libelf_gethandle(name);
-}
+  FAR struct list_node *registry;
+  FAR void *handle;
 
-#endif /* CONFIG_MODULE */
+  registry = mod_registry_lock();
+  handle = libelf_gethandle(registry, name);
+  mod_registry_unlock();
+  return handle;
+}
diff --git a/sched/module/mod_modsym.c b/sched/module/mod_modsym.c
index a6eb0cea2e4..72241441e86 100644
--- a/sched/module/mod_modsym.c
+++ b/sched/module/mod_modsym.c
@@ -29,6 +29,8 @@
 #include <nuttx/module.h>
 #include <nuttx/lib/elf.h>
 
+#include "module/module.h"
+
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -64,5 +66,10 @@
 
 FAR const void *modsym(FAR void *handle, FAR const char *name)
 {
-  return libelf_getsymbol(handle, name);
+  FAR const void *symbol;
+
+  mod_registry_lock();
+  symbol = libelf_getsymbol(handle, name);
+  mod_registry_unlock();
+  return symbol;
 }
diff --git a/sched/module/mod_procfs.c b/sched/module/mod_procfs.c
index 0e59509f72b..8850c51e2f9 100644
--- a/sched/module/mod_procfs.c
+++ b/sched/module/mod_procfs.c
@@ -42,8 +42,7 @@
 #include <nuttx/fs/fs.h>
 #include <nuttx/fs/procfs.h>
 
-#if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) && \
-    !defined(CONFIG_FS_PROCFS_EXCLUDE_MODULE)
+#include "module/module.h"
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -239,7 +238,9 @@ static ssize_t modprocfs_read(FAR struct file *filep, FAR 
char *buffer,
   priv->buflen    = buflen;
   priv->offset    = filep->f_pos;
 
-  ret = libelf_registry_foreach(modprocfs_callback, priv);
+  ret = libelf_registry_foreach(mod_registry_lock(),
+                                modprocfs_callback, priv);
+  mod_registry_unlock();
   if (ret >= 0)
     {
       filep->f_pos += priv->totalsize;
@@ -306,6 +307,3 @@ static int modprocfs_stat(FAR const char *relpath, FAR 
struct stat *buf)
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
-
-#endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_PROCFS &&
-        * !CONFIG_FS_PROCFS_EXCLUDE_MODULE */
diff --git a/sched/module/mod_rmmod.c b/sched/module/mod_registry.c
similarity index 55%
copy from sched/module/mod_rmmod.c
copy to sched/module/mod_registry.c
index 0d80eb4ad42..2f931cc1450 100644
--- a/sched/module/mod_rmmod.c
+++ b/sched/module/mod_registry.c
@@ -1,5 +1,5 @@
 /****************************************************************************
- * sched/module/mod_rmmod.c
+ * sched/module/mod_registry.c
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -26,33 +26,62 @@
 
 #include <nuttx/config.h>
 
-#include <nuttx/module.h>
-#include <nuttx/lib/elf.h>
+#include <errno.h>
 
-#ifdef CONFIG_MODULE
+#include <nuttx/list.h>
+#include <nuttx/mutex.h>
+
+#include "module/module.h"
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* The kernel modules that insmod() loads.  What dlopen() loads is on the
+ * registry of the task group.
+ */
+
+static rmutex_t g_mod_lock = NXRMUTEX_INITIALIZER;
+static struct list_node g_mod_registry = LIST_INITIAL_VALUE(g_mod_registry);
 
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: rmmod
+ * Name: mod_registry_lock
  *
  * Description:
- *   Remove a previously installed module from memory.
+ *   Lock the registry of the kernel modules that insmod() loads, and
+ *   return it.  The lock is recursive: the constructors of a module run
+ *   with it held.
  *
- * Input Parameters:
- *   handle - The module handler previously returned by insmod().
+ ****************************************************************************/
+
+FAR struct list_node *mod_registry_lock(void)
+{
+  int ret;
+
+  /* A signal must not make the caller run without the lock */
+
+  do
+    {
+      ret = nxrmutex_lock(&g_mod_lock);
+    }
+  while (ret == -EINTR);
+
+  return &g_mod_registry;
+}
+
+/****************************************************************************
+ * Name: mod_registry_unlock
  *
- * Returned Value:
- *   Zero (OK) on success.  On any failure, -1 (ERROR) is returned the
- *   errno value is set appropriately.
+ * Description:
+ *   Unlock the registry of the kernel modules.
  *
  ****************************************************************************/
 
-int rmmod(FAR void *handle)
+void mod_registry_unlock(void)
 {
-  return libelf_remove(handle);
+  nxrmutex_unlock(&g_mod_lock);
 }
-
-#endif /* CONFIG_MODULE */
diff --git a/sched/module/mod_rmmod.c b/sched/module/mod_rmmod.c
index 0d80eb4ad42..3806f2a689b 100644
--- a/sched/module/mod_rmmod.c
+++ b/sched/module/mod_rmmod.c
@@ -29,7 +29,7 @@
 #include <nuttx/module.h>
 #include <nuttx/lib/elf.h>
 
-#ifdef CONFIG_MODULE
+#include "module/module.h"
 
 /****************************************************************************
  * Public Functions
@@ -52,7 +52,11 @@
 
 int rmmod(FAR void *handle)
 {
-  return libelf_remove(handle);
-}
+  FAR struct list_node *registry;
+  int ret;
 
-#endif /* CONFIG_MODULE */
+  registry = mod_registry_lock();
+  ret = libelf_remove(registry, handle);
+  mod_registry_unlock();
+  return ret;
+}
diff --git a/sched/module/mod_rmmod.c b/sched/module/module.h
similarity index 71%
copy from sched/module/mod_rmmod.c
copy to sched/module/module.h
index 0d80eb4ad42..f2878fca6d9 100644
--- a/sched/module/mod_rmmod.c
+++ b/sched/module/module.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * sched/module/mod_rmmod.c
+ * sched/module/module.h
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -20,39 +20,43 @@
  *
  ****************************************************************************/
 
+#ifndef __SCHED_MODULE_MODULE_H
+#define __SCHED_MODULE_MODULE_H
+
 /****************************************************************************
  * Included Files
  ****************************************************************************/
 
 #include <nuttx/config.h>
 
-#include <nuttx/module.h>
-#include <nuttx/lib/elf.h>
+#include <nuttx/list.h>
 
 #ifdef CONFIG_MODULE
 
 /****************************************************************************
- * Public Functions
+ * Public Function Prototypes
  ****************************************************************************/
 
 /****************************************************************************
- * Name: rmmod
+ * Name: mod_registry_lock
  *
  * Description:
- *   Remove a previously installed module from memory.
+ *   Lock the registry of the kernel modules that insmod() loads, and
+ *   return it.
  *
- * Input Parameters:
- *   handle - The module handler previously returned by insmod().
+ ****************************************************************************/
+
+FAR struct list_node *mod_registry_lock(void);
+
+/****************************************************************************
+ * Name: mod_registry_unlock
  *
- * Returned Value:
- *   Zero (OK) on success.  On any failure, -1 (ERROR) is returned the
- *   errno value is set appropriately.
+ * Description:
+ *   Unlock the registry of the kernel modules.
  *
  ****************************************************************************/
 
-int rmmod(FAR void *handle)
-{
-  return libelf_remove(handle);
-}
+void mod_registry_unlock(void);
 
 #endif /* CONFIG_MODULE */
+#endif /* __SCHED_MODULE_MODULE_H */
diff --git a/sched/tls/task_initinfo.c b/sched/tls/task_initinfo.c
index e268454608f..992964c2b94 100644
--- a/sched/tls/task_initinfo.c
+++ b/sched/tls/task_initinfo.c
@@ -132,6 +132,13 @@ int task_init_info(FAR struct task_group_s *group)
   list_initialize(&info->ta_atfork);
 #endif
 
+#if defined(CONFIG_LIBC_ELF) || defined(CONFIG_BINFMT_LOADABLE)
+  /* The ELF objects that the task group loads with dlopen() */
+
+  nxrmutex_init(&info->ta_modlock);
+  list_initialize(&info->ta_modules);
+#endif
+
 #ifdef CONFIG_FILE_STREAM
   /* Initialize file streams for the task group */
 
diff --git a/sched/tls/task_uninitinfo.c b/sched/tls/task_uninitinfo.c
index c6ff0c79c54..6212cce8551 100644
--- a/sched/tls/task_uninitinfo.c
+++ b/sched/tls/task_uninitinfo.c
@@ -150,6 +150,10 @@ void task_uninit_info(FAR struct task_group_s *group)
 
   env_release(info);
 
+#if defined(CONFIG_LIBC_ELF) || defined(CONFIG_BINFMT_LOADABLE)
+  nxrmutex_destroy(&info->ta_modlock);
+#endif
+
   nxrmutex_destroy(&info->ta_lock);
 #ifdef CONFIG_MM_KERNEL_HEAP
   group_free(group, info);

Reply via email to