2024-01-01 18:12:26 +00:00
|
|
|
/* Copyright (C) 1999-2024 Free Software Foundation, Inc.
|
1999-12-27 21:50:29 +00:00
|
|
|
This file is part of the GNU C Library.
|
|
|
|
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
2001-07-06 04:58:11 +00:00
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) any later version.
|
1999-12-27 21:50:29 +00:00
|
|
|
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2001-07-06 04:58:11 +00:00
|
|
|
Lesser General Public License for more details.
|
1999-12-27 21:50:29 +00:00
|
|
|
|
2001-07-06 04:58:11 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2012-02-09 23:18:22 +00:00
|
|
|
License along with the GNU C Library; if not, see
|
Prefer https to http for gnu.org and fsf.org URLs
Also, change sources.redhat.com to sourceware.org.
This patch was automatically generated by running the following shell
script, which uses GNU sed, and which avoids modifying files imported
from upstream:
sed -ri '
s,(http|ftp)(://(.*\.)?(gnu|fsf|sourceware)\.org($|[^.]|\.[^a-z])),https\2,g
s,(http|ftp)(://(.*\.)?)sources\.redhat\.com($|[^.]|\.[^a-z]),https\2sourceware.org\4,g
' \
$(find $(git ls-files) -prune -type f \
! -name '*.po' \
! -name 'ChangeLog*' \
! -path COPYING ! -path COPYING.LIB \
! -path manual/fdl-1.3.texi ! -path manual/lgpl-2.1.texi \
! -path manual/texinfo.tex ! -path scripts/config.guess \
! -path scripts/config.sub ! -path scripts/install-sh \
! -path scripts/mkinstalldirs ! -path scripts/move-if-change \
! -path INSTALL ! -path locale/programs/charmap-kw.h \
! -path po/libc.pot ! -path sysdeps/gnu/errlist.c \
! '(' -name configure \
-execdir test -f configure.ac -o -f configure.in ';' ')' \
! '(' -name preconfigure \
-execdir test -f preconfigure.ac ';' ')' \
-print)
and then by running 'make dist-prepare' to regenerate files built
from the altered files, and then executing the following to cleanup:
chmod a+x sysdeps/unix/sysv/linux/riscv/configure
# Omit irrelevant whitespace and comment-only changes,
# perhaps from a slightly-different Autoconf version.
git checkout -f \
sysdeps/csky/configure \
sysdeps/hppa/configure \
sysdeps/riscv/configure \
sysdeps/unix/sysv/linux/csky/configure
# Omit changes that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/powerpc/powerpc64/ppc-mcount.S: trailing lines
git checkout -f \
sysdeps/powerpc/powerpc64/ppc-mcount.S \
sysdeps/unix/sysv/linux/s390/s390-64/syscall.S
# Omit change that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S: last line does not end in newline
git checkout -f sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S
2019-09-07 05:40:42 +00:00
|
|
|
<https://www.gnu.org/licenses/>. */
|
1999-12-27 21:50:29 +00:00
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "exit.h"
|
2021-03-24 20:27:34 +00:00
|
|
|
#include <register-atfork.h>
|
2022-10-18 15:00:07 +00:00
|
|
|
#include <pointer_guard.h>
|
2013-05-01 15:46:34 +00:00
|
|
|
#include <stdint.h>
|
1999-12-27 21:50:29 +00:00
|
|
|
|
|
|
|
/* If D is non-NULL, call all functions registered with `__cxa_atexit'
|
2003-04-19 18:26:10 +00:00
|
|
|
with the same dso handle. Otherwise, if D is NULL, call all of the
|
2024-06-03 17:04:58 +00:00
|
|
|
registered handlers.
|
|
|
|
|
|
|
|
A __cxa_finalize function is declared in the libstdc++ <cxxabi.h>
|
|
|
|
header, and the libstdc++ implementation calls this function. GCC
|
|
|
|
calls the glibc variant directly from its CRT files, from an ELF
|
|
|
|
destructor. this call always passes a non-null D argument. In the
|
|
|
|
current implementation, the GCC-provided __cxa_finalize call is
|
|
|
|
responsible for removing the registered __cxa_atexit (C++)
|
|
|
|
destructors of an object that is undergoing dlclose. Note that
|
|
|
|
this is specific to dlclose. During process termination, glibc
|
|
|
|
invokes the __run_exit_handlers, which calls registered
|
|
|
|
__cxa_atexit (C++) destructors in reverse registration order,
|
|
|
|
across all objects. The subsequent GCC-provided __cxa_finalize
|
|
|
|
calls (which are ordered according to ELF object dependencies, not
|
|
|
|
__cxa_atexit call order, and group destructor calls per object
|
|
|
|
during dlclose) do not result in further destructor invocations. */
|
1999-12-27 21:50:29 +00:00
|
|
|
void
|
|
|
|
__cxa_finalize (void *d)
|
|
|
|
{
|
|
|
|
struct exit_function_list *funcs;
|
|
|
|
|
2017-09-20 16:31:48 +00:00
|
|
|
__libc_lock_lock (__exit_funcs_lock);
|
|
|
|
|
2006-07-26 07:27:20 +00:00
|
|
|
restart:
|
1999-12-27 21:50:29 +00:00
|
|
|
for (funcs = __exit_funcs; funcs; funcs = funcs->next)
|
|
|
|
{
|
|
|
|
struct exit_function *f;
|
|
|
|
|
|
|
|
for (f = &funcs->fns[funcs->idx - 1]; f >= &funcs->fns[0]; --f)
|
2017-09-20 16:31:48 +00:00
|
|
|
if ((d == NULL || d == f->func.cxa.dso_handle) && f->flavor == ef_cxa)
|
|
|
|
{
|
|
|
|
const uint64_t check = __new_exitfn_called;
|
|
|
|
void (*cxafn) (void *arg, int status) = f->func.cxa.fn;
|
|
|
|
void *cxaarg = f->func.cxa.arg;
|
|
|
|
|
|
|
|
/* We don't want to run this cleanup more than once. The Itanium
|
|
|
|
C++ ABI requires that multiple calls to __cxa_finalize not
|
|
|
|
result in calling termination functions more than once. One
|
|
|
|
potential scenario where that could happen is with a concurrent
|
|
|
|
dlclose and exit, where the running dlclose must at some point
|
|
|
|
release the list lock, an exiting thread may acquire it, and
|
|
|
|
without setting flavor to ef_free, might re-run this destructor
|
|
|
|
which could result in undefined behaviour. Therefore we must
|
|
|
|
set flavor to ef_free to avoid calling this destructor again.
|
|
|
|
Note that the concurrent exit must also take the dynamic loader
|
|
|
|
lock (for library finalizer processing) and therefore will
|
|
|
|
block while dlclose completes the processing of any in-progress
|
|
|
|
exit functions. Lastly, once we release the list lock for the
|
|
|
|
entry marked ef_free, we must not read from that entry again
|
|
|
|
since it may have been reused by the time we take the list lock
|
|
|
|
again. Lastly the detection of new registered exit functions is
|
|
|
|
based on a monotonically incrementing counter, and there is an
|
|
|
|
ABA if between the unlock to run the exit function and the
|
|
|
|
re-lock after completion the user registers 2^64 exit functions,
|
|
|
|
the implementation will not detect this and continue without
|
|
|
|
executing any more functions.
|
2006-07-26 06:44:46 +00:00
|
|
|
|
2017-09-20 16:31:48 +00:00
|
|
|
One minor issue remains: A registered exit function that is in
|
|
|
|
progress by a call to dlclose() may not completely finish before
|
|
|
|
the next registered exit function is run. This may, according to
|
|
|
|
some readings of POSIX violate the requirement that functions
|
|
|
|
run in effective LIFO order. This should probably be fixed in a
|
|
|
|
future implementation to ensure the functions do not run in
|
|
|
|
parallel. */
|
|
|
|
f->flavor = ef_free;
|
2006-07-26 07:27:20 +00:00
|
|
|
|
2017-09-20 16:31:48 +00:00
|
|
|
PTR_DEMANGLE (cxafn);
|
2022-10-18 15:00:07 +00:00
|
|
|
|
2017-09-20 16:31:48 +00:00
|
|
|
/* Unlock the list while we call a foreign function. */
|
|
|
|
__libc_lock_unlock (__exit_funcs_lock);
|
|
|
|
cxafn (cxaarg, 0);
|
|
|
|
__libc_lock_lock (__exit_funcs_lock);
|
2006-07-26 07:27:20 +00:00
|
|
|
|
2017-09-20 16:31:48 +00:00
|
|
|
/* It is possible that that last exit function registered
|
|
|
|
more exit functions. Start the loop over. */
|
|
|
|
if (__glibc_unlikely (check != __new_exitfn_called))
|
|
|
|
goto restart;
|
|
|
|
}
|
1999-12-27 21:50:29 +00:00
|
|
|
}
|
2002-10-01 00:05:23 +00:00
|
|
|
|
2009-03-08 19:53:12 +00:00
|
|
|
/* Also remove the quick_exit handlers, but do not call them. */
|
|
|
|
for (funcs = __quick_exit_funcs; funcs; funcs = funcs->next)
|
|
|
|
{
|
|
|
|
struct exit_function *f;
|
|
|
|
|
|
|
|
for (f = &funcs->fns[funcs->idx - 1]; f >= &funcs->fns[0]; --f)
|
|
|
|
if (d == NULL || d == f->func.cxa.dso_handle)
|
|
|
|
f->flavor = ef_free;
|
|
|
|
}
|
|
|
|
|
2003-04-19 18:26:10 +00:00
|
|
|
/* Remove the registered fork handlers. We do not have to
|
|
|
|
unregister anything if the program is going to terminate anyway. */
|
|
|
|
if (d != NULL)
|
|
|
|
UNREGISTER_ATFORK (d);
|
2017-09-20 16:31:48 +00:00
|
|
|
__libc_lock_unlock (__exit_funcs_lock);
|
1999-12-27 21:50:29 +00:00
|
|
|
}
|