exec() of an FDPIC module now works. The loader already places such an
object and binds it; what was missing is everything binfmt has to carry
across from the load to the running task.
The task needs the module's data base in its PIC base register. binfmt
builds a D-Space for any object with a GOT, taking the base from the .got
section address; an FDPIC object names it in DT_PLTGOT instead, which the
loader has already translated, so the two are the same idea reached by
different routes and both are what up_initial_state() installs.
Constructors are not binfmt's business. A module carries its own crt0,
which walks .init_array on the task that runs the module and then calls
main, so they run in the module's own context and with its own data base.
For a module that arrives through dlopen(), libelf_insert() walks the array
instead, and it enters each entry through fdpic_invoke() because a
descriptor resolved on the calling task carries the wrong base.
The read-only segment of a module that executes in place is held by a
filesystem pin. The load takes it, and the module owns it from the point
where nothing can fail any more; it is given back when the task that runs
the module exits. The pin is held through a reference to the file rather
than a descriptor, because the descriptor belongs to the task that called
the loader and the release happens on another one.
libelf_remove() and libelf_uninit() give back what an FDPIC module holds:
the pin, and the writable segment, while the read-only one is media rather
than an allocation and must not be freed.
Built for mps3-an547:picostest with CONFIG_FDPIC both ways.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
libelf_uninit() only called libelf_freesymtab() when the module had an
uninitializer. But the exported symbol table is built by
libelf_insertsymtab() for every loaded module, and nothing in the tree
sets modinfo.uninitializer anymore: modules have registered their
teardown through .fini_array since a9cb28cd23. The condition is
therefore always false and every rmmod()/dlclose() leaks the exports
array together with the strdup-ed symbol names.
Call libelf_freesymtab() unconditionally, and clear the exports
pointers next to it instead of under a vestigial procfs guard that
dates back to the removed module initializer field.
Assisted-by: OpenAI Codex
Signed-off-by: yushuailong <yyyusl@qq.com>
libelf_remove() takes the module out of the registry but never frees
the registry entry, so every successful rmmod()/dlclose() leaks
sizeof(struct module_s), the name included. The lib_free() call was
dropped by e9550783d3 when the removal path was reworked.
Free the entry after the registry lock is released.
Assisted-by: OpenAI Codex
Signed-off-by: yushuailong <yyyusl@qq.com>
dlopen() of a library that is already loaded fails. libelf_insert()
rejects a name that is already in the module registry with EEXIST, and
dlinsert() passes that straight out, so the second caller gets NULL.
POSIX says dlopen() shall return a handle to the object, and there is no
way today for two modules to hold the same library at once -- which is
what a shared library is for.
So dlopen() now takes another reference on a library that is already
there, and dlclose() only tears it down when the last handle goes. The
count lives in the dlfcn layer rather than in libelf_insert() so that
insmod keeps its own behaviour: a second insmod of the same name still
fails with EEXIST, which is right for a kernel module.
The module name is what makes any of this possible, and a PROTECTED build
did not have one. Names were defined for CONFIG_BUILD_FLAT or the kernel
side of a split build, on the reasoning that only the kernel needed them,
which predates dlopen() being usable from user space. Without a name the
user-space copy of libelf cannot recognise a second open of a library,
cannot count opens, and cannot make dlclose() mean anything -- two
dlopen()s there produce two independent copies of the library and lose
track of the first. Names are therefore defined wherever CONFIG_LIBC_DLFCN
is, which costs NAME_MAX per loaded module in that configuration.
The path no longer has to be copied either. The module name is the
basename of the file and libelf_insert() takes it as a const string, so
dlinsert() finds it with strrchr() instead of handing a writable
duplicate of the whole path to basename().
BUILD_KERNEL is deliberately untouched. dlopen() returns NULL there
unconditionally: dlinsert() is a stub, because sharing a library between
processes with separate address spaces needs the text in a shared region
and the data per process at a matching virtual address, which is a
different problem from this one.
Built for mps3-an547:picostest with and without CONFIG_LIBC_DLFCN, and
for stm32f4discovery:kostest, a PROTECTED configuration, with it enabled.
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
debug.h is a NuttX-specific, non-POSIX header. Placing it in the
top-level include/ directory creates naming conflicts with external
projects that define their own debug.h.
This commit moves the canonical header to include/nuttx/debug.h,
following the NuttX convention for non-POSIX/non-standard headers,
and updates all in-tree references.
A backward-compatibility shim is left at include/debug.h that
emits a deprecation #warning and re-includes <nuttx/debug.h>,
allowing out-of-tree code to continue building while migrating.
Signed-off-by: Piyush Patle <piyushpatle228@gmail.com>
Renaming "modlib" to "libelf" is more in line with the implementation content,
which makes it easier for individual developers to understand the capabilities of this module.
CONFIG_LIBC_MODLIB -> CONFIG_LIBC_ELF
Signed-off-by: chao an <anchao.archer@bytedance.com>
2025-04-11 09:43:22 +08:00
Renamed from libs/libc/modlib/modlib_remove.c (Browse further)