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 58e227a8a56436981fb959065b5d9d9142617b34
Author: Marco Casaroli <[email protected]>
AuthorDate: Mon Aug 3 13:34:50 2026 +0200

    Documentation: Document dlopen() reference counting.
    
    Describe the shared library open semantics in the FLAT and PROTECTED
    builds: dlopen() of a library that is already loaded returns a handle
    to it and takes an additional reference, and the library is unloaded
    only when the last handle is closed.  Note the consequences that follow
    from having a single instance: libraries are matched by basename, data
    is shared by all users, and constructors and destructors run once.
    
    Contrast this with insmod(), which still rejects a duplicate module
    name, and note that dlopen() is not implemented in the KERNEL build.
    
    Assisted-by: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: Marco Casaroli <[email protected]>
---
 .../kernel_modules_vs_shared_libraries.rst         | 57 +++++++++++++++++++---
 1 file changed, 50 insertions(+), 7 deletions(-)

diff --git 
a/Documentation/implementation/kernel_modules_vs_shared_libraries.rst 
b/Documentation/implementation/kernel_modules_vs_shared_libraries.rst
index 0334393d99a..33c1d3e5f16 100644
--- a/Documentation/implementation/kernel_modules_vs_shared_libraries.rst
+++ b/Documentation/implementation/kernel_modules_vs_shared_libraries.rst
@@ -67,12 +67,15 @@ of a shared library in that environment.
 In this case kernel modules really only differ from shared
 libraries in their usage semantics:
 
-For the FLAT build, I have added the standard ``include/dllfcn.h``
-and have implemented the FLAT shared library support as a thin wrapper
-around the kernel module support:
-
-* ``dlopen()`` maps to ``insmod()``.
-* ``dlclose()`` maps to ``rmmod()``.
+For the FLAT build, the standard ``include/dlfcn.h`` interfaces are
+implemented as a thin wrapper around the same module library that the kernel
+module support uses:
+
+* ``dlopen()`` loads the library, or takes an additional reference on it if
+  it is already loaded, and returns a handle to it. See
+  `Opening a Library More Than Once`_.
+* ``dlclose()`` releases one reference. The library is unloaded, as
+  ``rmmod()`` would, only when the last handle is closed.
 * ``dlsym()`` maps to ``modsym()``.
 * ``dlerror()`` is only a stub at the present time.
 
@@ -101,6 +104,43 @@ The shared library functions no longer call the kernel 
module logic but rather
 implement their one top-level management logic using the lower-level routines
 in the module library.
 
+The user space copy of the module library keeps the name of each loaded
+library whenever ``CONFIG_LIBC_DLFCN`` is enabled, since the name is the only
+way to tell that a library is already loaded. This costs ``NAME_MAX`` bytes
+per loaded library, but it makes ``dlopen()`` behave exactly as it does in the
+FLAT build.
+
+
+Opening a Library More Than Once
+================================
+
+In the FLAT and PROTECTED builds, ``dlopen()`` of a library that is already
+loaded does not load a second copy and does not fail. It returns a handle to
+the library that is already loaded and takes an additional reference on it.
+Each successful ``dlopen()`` must be matched by a ``dlclose()``; the library
+is unloaded only when the last handle is closed. Up to 255 handles may be
+outstanding on one library; beyond that ``dlopen()`` fails with ``EMFILE``.
+
+Some consequences worth keeping in mind:
+
+* A library is identified by the *basename* of the path passed to
+  ``dlopen()``. Two files with the same basename in different directories
+  are treated as the same library, and the second ``dlopen()`` will return
+  the first one.
+* There is only one instance of the library's ``.data`` and ``.bss``. Global
+  and static data are shared by every user of the library, and by every task
+  group in the system.
+* Constructors in ``.init_array`` run once, when the library is first loaded,
+  and destructors in ``.fini_array`` run once, when the last handle is
+  closed. They do not run per ``dlopen()``/``dlclose()`` pair.
+* Symbols obtained with ``dlsym()`` remain valid until the last handle is
+  closed, not until the caller's own handle is closed.
+
+Kernel modules deliberately behave differently: ``insmod()`` fails with
+``EEXIST`` if a module of that name is already installed, and ``rmmod()``
+removes it immediately. A kernel module is a singleton and is not reference
+counted.
+
 
 Better FLAT and PROTECTED Mode Shared Libraries
 ===============================================
@@ -112,7 +152,7 @@ for each NuttX task group.
 A task group is the moral equivalent of a Unix process.
 That is how a shared library would have to work in uClinux, for example.
 But that would be a substantial effort! For example, since each
-``.bss``/``.data`` would lie at a different physical addres,
+``.bss``/``.data`` would lie at a different physical address,
 the ``.text`` section logic would need support
 Position-Independent-Data (PID).
 Embedded PID support, however, is pretty much broken on all current GCC
@@ -174,3 +214,6 @@ are loaded into memory before the programs ``main()`` logic 
is called.
 .. note:: There is not yet any shared library support in the KERNEL build mode.
           This would be quite a large effort and not on the plan of record
           at the present time.
+          ``dlopen()`` always fails and returns ``NULL`` in the KERNEL build,
+          so none of the reference counting behaviour described above applies
+          there.

Reply via email to