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.
