Browse Source

[libc][libdl] Fix dlopen() failing to find already-loaded modules

Fix name mismatch between dlmodule_find() and _dlmodule_set_name() that
caused dlopen() to always reload modules instead of reusing them:

1. In dlopen(): Extract module name from full path before calling
   dlmodule_find(), matching the stripped name stored by _dlmodule_set_name().
   This allows dlopen() to properly find and reuse already-loaded modules.

2. In _dlmodule_set_name(): Fix bug where extension detection searched
   the entire path instead of just the filename portion. For paths like
   "/mnt/v1.2/app.so", it would incorrectly find "." in "v1.2" directory
   name instead of the ".so" extension.

Fixes: Module reloading on every dlopen() call, memory leaks from
duplicate module instances, and dlclose() cleanup issues.

Co-authored-by: BernardXiong <1241087+BernardXiong@users.noreply.github.com>
copilot-swe-agent[bot] 18 hours ago
parent
commit
8a99de1747
2 changed files with 64 additions and 11 deletions
  1. 16 4
      components/libc/posix/libdl/dlmodule.c
  2. 48 7
      components/libc/posix/libdl/dlopen.c

+ 16 - 4
components/libc/posix/libdl/dlmodule.c

@@ -44,17 +44,29 @@ static void _dlmodule_set_name(struct rt_dlmodule *module, const char *path)
     ptr   = first = (char *)path;
     end   = path + rt_strlen(path);
 
+    /* find the start of filename (after last '/') */
     while (*ptr != '\0')
     {
         if (*ptr == '/')
             first = ptr + 1;
-        if (*ptr == '.')
-            end = ptr - 1;
+        ptr++;
+    }
 
-        ptr ++;
+    /* find extension in filename portion only (after last '/') */
+    ptr = first;
+    while (*ptr != '\0')
+    {
+        if (*ptr == '.')
+            end = ptr;
+        ptr++;
     }
 
-    size = end - first + 1;
+    size = end - first;
+    if (size <= 0)
+    {
+        /* no extension found, use entire filename */
+        size = rt_strlen(first);
+    }
     if (size >= RT_NAME_MAX) size = RT_NAME_MAX - 1;
 
     rt_strncpy(object->name, first, size);

+ 48 - 7
components/libc/posix/libdl/dlopen.c

@@ -33,7 +33,8 @@ void* dlopen(const char *filename, int flags)
 {
     struct rt_dlmodule *module;
     char *fullpath;
-    const char*def_path = MODULE_ROOT_DIR;
+    const char *def_path = MODULE_ROOT_DIR;
+    char module_name[RT_NAME_MAX];
 
     /* check parameters */
     RT_ASSERT(filename != RT_NULL);
@@ -48,15 +49,55 @@ void* dlopen(const char *filename, int flags)
     }
     else
     {
-        fullpath = (char*)filename; /* absolute path, use it directly */
+        fullpath = (char *)filename; /* absolute path, use it directly */
+    }
+
+    /* Extract module name from path (strip directory and extension)
+     * This matches the logic in _dlmodule_set_name() so that dlmodule_find()
+     * can properly locate already-loaded modules by their stored name.
+     */
+    {
+        const char *first, *end, *ptr;
+        int size;
+
+        ptr = first = fullpath;
+        end = fullpath + strlen(fullpath);
+
+        while (*ptr != '\0')
+        {
+            if (*ptr == '/')
+                first = ptr + 1;
+            ptr++;
+        }
+
+        /* find extension in filename portion only (after last '/') */
+        ptr = first;
+        while (*ptr != '\0')
+        {
+            if (*ptr == '.')
+                end = ptr;
+            ptr++;
+        }
+
+        size = end - first;
+        if (size <= 0)
+        {
+            /* no extension found, use entire filename */
+            size = strlen(first);
+        }
+        if (size >= RT_NAME_MAX)
+            size = RT_NAME_MAX - 1;
+
+        rt_strncpy(module_name, first, size);
+        module_name[size] = '\0';
     }
 
     rt_enter_critical();
 
-    /* find in module list */
-    module = dlmodule_find(fullpath);
+    /* find in module list using the stripped module name */
+    module = dlmodule_find(module_name);
 
-    if(module != RT_NULL)
+    if (module != RT_NULL)
     {
         rt_exit_critical();
         module->nref++;
@@ -67,11 +108,11 @@ void* dlopen(const char *filename, int flags)
         module = dlmodule_load(fullpath);
     }
 
-    if(fullpath != filename)
+    if (fullpath != filename)
     {
         rt_free(fullpath);
     }
 
-    return (void*)module;
+    return (void *)module;
 }
 RTM_EXPORT(dlopen);