head	1.1;
access;
symbols
	netbsd-11:1.1.0.2;
locks; strict;
comment	@ * @;


1.1
date	2026.07.16.02.50.11;	author riastradh;	state Exp;
branches
	1.1.2.1;
next	;
commitid	F6yXJ1EABPgYaPNG;

1.1.2.1
date	2026.07.16.02.50.11;	author martin;	state dead;
branches;
next	1.1.2.2;
commitid	AcgcFayxvmwSxhOG;

1.1.2.2
date	2026.07.19.16.17.13;	author martin;	state Exp;
branches;
next	;
commitid	AcgcFayxvmwSxhOG;


desc
@@


1.1
log
@t_dlclose_thread: Test recursive dlopen/dlclose too.

Simple test first:

- h_helper_recurdso dlopens h_helper_dso2
- h_helper_dso2 needs h_helper_dso1

When dlopening h_helper_recurdso, the initialization order must be:

ENTER h_helper_recurdso
-> h_helper_dso1
-> h_helper_dso2
LEAVE h_helper_recurdso

For the more complex test, we have the relations:

- h_helper_recurdso2 needs h_helper_recurdso
- h_helper_recurdso dlopens h_helper_dso2
- h_helper_dso2 needs h_helper_dso1

(And the finalization order must be in reverse.)

When dlopening h_helper_recurdso2, the initialization order,
therefore, must be:

ENTER h_helper_recurdso
-> h_helper_dso1
-> h_helper_dso2
LEAVE h_helper_recurdso
h_helper_recurdso2

PR lib/59751: dlclose is not MT-safe depending on the libraries
unloaded

(Really, this is about a followup bug in the fix for that issue,
which broke recursive dlopen/dlclose.)
@
text
@/*	$NetBSD$	*/

/*-
 * Copyright (c) 2026 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__RCSID("$NetBSD$");

#include <dlfcn.h>
#include <stdbool.h>

static void *dso2_handle;

bool recurdso_initialized;
bool recurdso_finalized;

__attribute__((__constructor__))
static void
initrecurdso(void)
{

	dso2_handle = dlopen("libh_helper_dso2.so", RTLD_LAZY | RTLD_LOCAL);
	recurdso_initialized = true;
}

__attribute__((__destructor__))
static void
finirecurdso(void)
{

	if (dso2_handle)
		dlclose(dso2_handle);
	recurdso_finalized = true;
}
@


1.1.2.1
log
@file h_helper_recurdso.c was added on branch netbsd-11 on 2026-07-19 16:17:13 +0000
@
text
@d1 57
@


1.1.2.2
log
@Pull up following revision(s) (requested by riastradh in ticket #386):

	tests/libexec/ld.elf_so/helper_recurdso2/Makefile: revision 1.1
	distrib/sets/lists/tests/shl.mi: revision 1.18
	tests/libexec/ld.elf_so/t_dlclose_thread.c: revision 1.4
	tests/libexec/ld.elf_so/t_dlclose_thread.c: revision 1.5
	tests/libexec/ld.elf_so/t_dlclose_thread.c: revision 1.6
	libexec/ld.elf_so/rtld.c: revision 1.227
	libexec/ld.elf_so/xmalloc.c: revision 1.26
	tests/libexec/ld.elf_so/helper_recurdso/Makefile: revision 1.1
	tests/libexec/ld.elf_so/Makefile: revision 1.36
	libexec/ld.elf_so/rtld.h: revision 1.157
	tests/libexec/ld.elf_so/helper_recurdso/h_helper_recurdso.c: revision 1.1
	tests/libexec/ld.elf_so/helper_recurdso2/h_helper_recurdso2.c: revision 1.1
	distrib/sets/lists/debug/shl.mi: revision 1.410
	libexec/ld.elf_so/reloc.c: revision 1.122

t_dlclose_thread: Test recursive dlopen/dlclose too.

Simple test first:

- h_helper_recurdso dlopens h_helper_dso2
- h_helper_dso2 needs h_helper_dso1

When dlopening h_helper_recurdso, the initialization order must be:
ENTER h_helper_recurdso
-> h_helper_dso1
-> h_helper_dso2
LEAVE h_helper_recurdso

For the more complex test, we have the relations:
- h_helper_recurdso2 needs h_helper_recurdso
- h_helper_recurdso dlopens h_helper_dso2
- h_helper_dso2 needs h_helper_dso1
(And the finalization order must be in reverse.)

When dlopening h_helper_recurdso2, the initialization order,
therefore, must be:
ENTER h_helper_recurdso
-> h_helper_dso1
-> h_helper_dso2
LEAVE h_helper_recurdso
h_helper_recurdso2

PR lib/59751: dlclose is not MT-safe depending on the libraries
unloaded

(Really, this is about a followup bug in the fix for that issue,
which broke recursive dlopen/dlclose.)


t_dlclose_thread: Make timeouts manifest more predictably.

And use SIGABRT to get stack traces.

PR lib/59751: dlclose is not MT-safe depending on the libraries
unloaded


ld.elf_so: Fix more races with recursive threaded dlopen/dlclose.
1. When loading an object, keep track of a finer-grained state for
   relocation so that a concurrent _rtld_relocate_objects doesn't
   relocate an object too early (e.g., while waiting in
   _rtld_load_needed_objects for an object that is concurrently being
   dlclosed to finish before we can map it afresh) leading to
   spurious symbol resolution failures:
   - OBJRELOC_NONE (0): object has been created but the rtld state
     isn't enough to attempt relocation yet (either not all DT_NEEDED
     entries have been resolved, or the dldags lists have not yet
     been populated)
   - OBJRELOC_READY (1): object has been created and rtld state is
     ready to relocate (all DT_NEEDED entries have been resolved and
     the dldags lists have been populated), but object has not yet
     been relocated.
   - OBJRELOC_DONE (2): object has been relocated successfully.
   - OBJRELOC_FAILED (3): relocation failed, e.g. because of symbol
     resolution failure.
   Allowed transitions:
   - NONE->READY
   - READY->DONE
   - READY->FAILED
2. When calling dlopen/dlclose from an object's constructor or
   destructor, exclude that object and any object that needs it from
   the topological ordering of objects for constructor/destructor calls
   inside dlopen/dlclose -- we can't re-enter the same object's
   constructor or destructor, and before the object's constructor or
   destructor has returned (which can't happen until the recursive
   dlopen or dlclose has returned), it would be wrong to invoke the
   constructor or destructor of any object that needs it.
3. When dlclosing an object, mark any of its dagmembers as busy
   dlclosing so a concurrent GC loop at the end of
   _rtld_unload_object doesn't free them while we sleep in init/fini
   functions before we can remove the DAG from the members' dldags.
   Later, at the end of this call to _rtld_unload_object, we will
   take care of them.
   This requires changing the boolean dlclosing state to an integer
   reference count.  `int' is big enough because each thread can hold
   at most one reference to each object, so the number of threads is
   an upper bound on the reference count.

Unfortunately, there is still a bug lurking: every now and then,


t_dlclose_thread:dlclose_thread_recursive2 trips one of the abort
branches in libh_helper_recurdso2.so, e.g. because the destructor for
libh_helper_recurdso.so ran first.  But the frequency of these
failures has gone down dramatically, from around 19/20 to about 1/20
trials.  Simply serializing dlopen/dlclose calls in an application is
likely to be an effective workaround.  And these changes fix issues
in single-threaded recursive dlopen/dlclose even if the multithreaded
case has a remaining low-probability race.

PR lib/59751: dlclose is not MT-safe depending on the libraries
unloaded


ld.elf_so: Fix ASSERT macro in xmalloc.c.

1. Use #p, not "p", in the expansion of #define ASSERT(p), for C as of
   this millennium.
2. Consistently make ASSERT(...) a single expression.

Prompted by enabling MALLOC_DEBUG to track down issues in:

PR lib/59751: dlclose is not MT-safe depending on the libraries
unloaded
@
text
@a0 57
/*	$NetBSD: h_helper_recurdso.c,v 1.1 2026/07/16 02:50:11 riastradh Exp $	*/

/*-
 * Copyright (c) 2026 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__RCSID("$NetBSD: h_helper_recurdso.c,v 1.1 2026/07/16 02:50:11 riastradh Exp $");

#include <dlfcn.h>
#include <stdbool.h>

static void *dso2_handle;

bool recurdso_initialized;
bool recurdso_finalized;

__attribute__((__constructor__))
static void
initrecurdso(void)
{

	dso2_handle = dlopen("libh_helper_dso2.so", RTLD_LAZY | RTLD_LOCAL);
	recurdso_initialized = true;
}

__attribute__((__destructor__))
static void
finirecurdso(void)
{

	if (dso2_handle)
		dlclose(dso2_handle);
	recurdso_finalized = true;
}
@


