1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1990, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Chris Torek.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "namespace.h"
36#include <errno.h>
37#include <link.h>
38#include <stddef.h>
39#include <stdlib.h>
40#include <unistd.h>
41#include <pthread.h>
42#include "atexit.h"
43#include "un-namespace.h"
44#include "block_abi.h"
45
46#include "libc_private.h"
47
48/**
49 * The _Block_copy() function is provided by the block runtime.
50 */
51__attribute__((weak)) void*
52_Block_copy(void*);
53
54#define	ATEXIT_FN_EMPTY	0
55#define	ATEXIT_FN_STD	1
56#define	ATEXIT_FN_CXA	2
57
58static pthread_mutex_t atexit_mutex = PTHREAD_MUTEX_INITIALIZER;
59
60#define _MUTEX_LOCK(x)		if (__isthreaded) _pthread_mutex_lock(x)
61#define _MUTEX_UNLOCK(x)	if (__isthreaded) _pthread_mutex_unlock(x)
62#define _MUTEX_DESTROY(x)	if (__isthreaded) _pthread_mutex_destroy(x)
63
64struct atexit {
65	struct atexit *next;			/* next in list */
66	int ind;				/* next index in this table */
67	struct atexit_fn {
68		int fn_type;			/* ATEXIT_? from above */
69		union {
70			void (*std_func)(void);
71			void (*cxa_func)(void *);
72		} fn_ptr;			/* function pointer */
73		void *fn_arg;			/* argument for CXA callback */
74		void *fn_dso;			/* shared module handle */
75	} fns[ATEXIT_SIZE];			/* the table itself */
76};
77
78static struct atexit *__atexit;		/* points to head of LIFO stack */
79typedef DECLARE_BLOCK(void, atexit_block, void);
80
81int atexit_b(atexit_block);
82int __cxa_atexit(void (*)(void *), void *, void *);
83
84/*
85 * Register the function described by 'fptr' to be called at application
86 * exit or owning shared object unload time. This is a helper function
87 * for atexit and __cxa_atexit.
88 */
89static int
90atexit_register(struct atexit_fn *fptr)
91{
92	static struct atexit __atexit0;	/* one guaranteed table */
93	struct atexit *p;
94
95	_MUTEX_LOCK(&atexit_mutex);
96	if ((p = __atexit) == NULL)
97		__atexit = p = &__atexit0;
98	else while (p->ind >= ATEXIT_SIZE) {
99		struct atexit *old__atexit;
100		old__atexit = __atexit;
101	        _MUTEX_UNLOCK(&atexit_mutex);
102		if ((p = (struct atexit *)malloc(sizeof(*p))) == NULL)
103			return (-1);
104		_MUTEX_LOCK(&atexit_mutex);
105		if (old__atexit != __atexit) {
106			/* Lost race, retry operation */
107			_MUTEX_UNLOCK(&atexit_mutex);
108			free(p);
109			_MUTEX_LOCK(&atexit_mutex);
110			p = __atexit;
111			continue;
112		}
113		p->ind = 0;
114		p->next = __atexit;
115		__atexit = p;
116	}
117	p->fns[p->ind++] = *fptr;
118	_MUTEX_UNLOCK(&atexit_mutex);
119	return 0;
120}
121
122/*
123 * Register a function to be performed at exit.
124 */
125int
126atexit(void (*func)(void))
127{
128	struct atexit_fn fn;
129	int error;
130
131	fn.fn_type = ATEXIT_FN_STD;
132	fn.fn_ptr.std_func = func;
133	fn.fn_arg = NULL;
134	fn.fn_dso = NULL;
135
136	error = atexit_register(&fn);
137	return (error);
138}
139__weak_reference(atexit, __libc_atexit);
140
141/**
142 * Register a block to be performed at exit.
143 */
144int
145atexit_b(atexit_block func)
146{
147	struct atexit_fn fn;
148	int error;
149	if (_Block_copy == 0) {
150		errno = ENOSYS;
151		return -1;
152	}
153	func = _Block_copy(func);
154
155	// Blocks are not C++ destructors, but they have the same signature (a
156	// single void* parameter), so we can pretend that they are.
157	fn.fn_type = ATEXIT_FN_CXA;
158	fn.fn_ptr.cxa_func = (void(*)(void*))GET_BLOCK_FUNCTION(func);
159	fn.fn_arg = func;
160	fn.fn_dso = NULL;
161
162	error = atexit_register(&fn);
163	return (error);
164}
165
166/*
167 * Register a function to be performed at exit or when an shared object
168 * with given dso handle is unloaded dynamically.
169 */
170int
171__cxa_atexit(void (*func)(void *), void *arg, void *dso)
172{
173	struct atexit_fn fn;
174	int error;
175
176	fn.fn_type = ATEXIT_FN_CXA;
177	fn.fn_ptr.cxa_func = func;
178	fn.fn_arg = arg;
179	fn.fn_dso = dso;
180
181	error = atexit_register(&fn);
182	return (error);
183}
184
185#pragma weak __pthread_cxa_finalize
186void __pthread_cxa_finalize(const struct dl_phdr_info *);
187
188static int global_exit;
189
190/*
191 * Call all handlers registered with __cxa_atexit for the shared
192 * object owning 'dso'.  Note: if 'dso' is NULL, then all remaining
193 * handlers are called.
194 */
195void
196__cxa_finalize(void *dso)
197{
198	struct dl_phdr_info phdr_info;
199	struct atexit *p;
200	struct atexit_fn fn;
201	int n, has_phdr;
202
203	if (dso != NULL) {
204		has_phdr = _rtld_addr_phdr(dso, &phdr_info);
205	} else {
206		has_phdr = 0;
207		global_exit = 1;
208	}
209
210	_MUTEX_LOCK(&atexit_mutex);
211	for (p = __atexit; p; p = p->next) {
212		for (n = p->ind; --n >= 0;) {
213			if (p->fns[n].fn_type == ATEXIT_FN_EMPTY)
214				continue; /* already been called */
215			fn = p->fns[n];
216			if (dso != NULL && dso != fn.fn_dso) {
217				/* wrong DSO ? */
218				if (!has_phdr || global_exit ||
219				    !__elf_phdr_match_addr(&phdr_info,
220				    fn.fn_ptr.cxa_func))
221					continue;
222			}
223			/*
224			  Mark entry to indicate that this particular handler
225			  has already been called.
226			*/
227			p->fns[n].fn_type = ATEXIT_FN_EMPTY;
228		        _MUTEX_UNLOCK(&atexit_mutex);
229
230			/* Call the function of correct type. */
231			if (fn.fn_type == ATEXIT_FN_CXA)
232				fn.fn_ptr.cxa_func(fn.fn_arg);
233			else if (fn.fn_type == ATEXIT_FN_STD)
234				fn.fn_ptr.std_func();
235			_MUTEX_LOCK(&atexit_mutex);
236		}
237	}
238	_MUTEX_UNLOCK(&atexit_mutex);
239	if (dso == NULL)
240		_MUTEX_DESTROY(&atexit_mutex);
241
242	if (has_phdr && !global_exit && &__pthread_cxa_finalize != NULL)
243		__pthread_cxa_finalize(&phdr_info);
244}
245