On Sat, 20 Dec 2025, RVP wrote:
Shared libs. should use __cxa_atexit() with a non-NULL `dso' parameter.
This works on FreeBSD, OpenBSD, Linux and even OpenIndiana. (Anything which
supports C++ would be my bet.)
-RVP
---START---
==> dl-atexit.c <==
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
static void
diemain(void)
{
printf("%s: atexit handler\n", __func__);
}
int
main(void)
{
extern const void* const __dso_handle;
printf("__dso_handle = %p\n", __dso_handle);
atexit(diemain);
void *h = dlopen("libfoo.so", RTLD_LOCAL | RTLD_LAZY);
if (h == NULL) {
fprintf(stderr, "dlopen(): %s\n", dlerror());
return 1;
}
void (*foo)(void) = dlsym(h, "foo");
if (foo == NULL) {
fprintf(stderr, "dlsym(foo): %s\n", dlerror());
return 1;
}
printf("%s: calling foo()\n", __func__);
foo();
printf("%s: calling dlclose()\n", __func__);
dlclose(h);
printf("%s: exiting...\n", __func__);
return 0;
}
==> libfoo.c <==
#include <stdio.h>
#include <stdlib.h>
extern int __cxa_atexit(void (*)(void*), void*, const void*);
extern const void* const __dso_handle;
static void
// die(void)
die(void* v)
{
printf("%s: atexit handler\n", __func__);
}
void
foo(void)
{
printf("__dso_handle = %p\n", __dso_handle);
// atexit(die);
__cxa_atexit(die, NULL, __dso_handle);
printf("%s: atexit handler die() @ %p\n", __func__, die);
}
==> Makefile <==
LDFLAGS= -fuse-linker-plugin -Wl,--enable-new-dtags,-z,relro,-z,now
dl-atexit: dl-atexit.c libfoo.so
cc -pie -fPIE -o dl-atexit dl-atexit.c -Wl,-rpath=$$(pwd) ${LDFLAGS}
libfoo.so: libfoo.c
cc -shared -DPIC -fPIC -o libfoo.so libfoo.c ${LDFLAGS}
run: dl-atexit
./dl-atexit
clean:
rm -f dl-atexit *.so *.core
---END---