Hello!

I have found exact cause while atexit(3) from dlopen(3) crashes later under NetBSD (tested all major releases since 8.3) while it works under Linux, OpenBSD and FreeBSD.

It is because NetBSD's atexit(3) on https://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/stdlib/atexit.c?rev=1.35;content-type=text%2Fx-cvsweb-markup calls __cxa_atexit_internal with dso=NULL (last argument):

int
atexit(void (*func)(void))
{

        return (__cxa_atexit_internal((void (*)(void *))func, NULL, NULL));
}

When later __cxa_finalize is run on dlclose(3) it will not call its callback because condition (dso == ah->ah_dso) will be always false ( ah->ah_dso == NULL which is != dso) - that was set in atexit(3) call. Therefore DSO callback will be called on global exit (when DSO is no longer mapped to memory) causing SIGSEGV.

I looked into OpenBSD 7.8's /usr/src/lib/csu/crtbeginS.c that does something like:

int
atexit(void (*fn)(void))
{
        return (__cxa_atexit((void (*)(void *))fn, NULL, &__dso_handle));
}
asm(".hidden atexit");

which properly passes implicit __dso_handle making this case to work.

So now it is up to you to decide what to do:

a) implement similar mechanism as other BSDs and Linux to make atexit(3) work properly from dlopen(3)ed DSOs

b) replace all dlopen(3)ed DSOs atexit(3) calls with destructor attribute

Below is patch for RVP's example that does later (works as expected):

diff -up atexit-example/libfoo.c dtor-example/libfoo.c
--- atexit-example/libfoo.c     2025-12-18 17:33:15.952473568 +0100
+++ dtor-example/libfoo.c       2025-12-20 10:22:29.112508712 +0100
@@ -4,6 +4,7 @@
 #include <stdlib.h>

 static void
+__attribute__((__destructor__))
 die(void)
 {
        printf("%s: atexit handler\n", __func__);
@@ -12,6 +13,6 @@ die(void)
 void
 foo(void)
 {
-       atexit(die);
+       //atexit(die);
        printf("%s: atexit handler die() @ %p\n", __func__, die);
 }


If you plan to stick to later solution I strongly suggest to update manual page for atexit(3) to clearly state that NetBSD does not support calling atexit(3) from dlopen(3)ed DSO and suggest using __attribute__((__destructor__)) for such case.

Once decision is made I can create PR - category depending on solution: if it will be xsrc component or lib.

Regards
  --Henryk Paluch


On 12/19/25 07:36, Martin Husemann wrote:
On Fri, Dec 19, 2025 at 06:52:04AM +0100, Henryk Paluch wrote:
These places seem to permit atexit(3) in DSOs:

1. https://gnats.netbsd.org/18379

That is not talking about atexit(3).

3. https://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/stdlib/atexit.c

And this is something else. The DSO has not been unloaded before the
exit happens - this case clearly needs to work.

Martin

Reply via email to