blob: 05f2faa528b71e776d44029e659906c45b34e011 [file] [log] [blame]
The Android Open Source Project1dc9e472009-03-03 19:28:35 -08001/* $OpenBSD: atexit.c,v 1.14 2007/09/05 20:47:47 chl Exp $ */
2/*
3 * Copyright (c) 2002 Daniel Hartmeier
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials provided
15 * with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 *
30 */
31
32#include <sys/types.h>
33#include <sys/mman.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37#include "atexit.h"
Elliott Hugheseb847bc2013-10-09 15:50:50 -070038#include "private/thread_private.h"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080039
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080040struct atexit *__atexit;
41
42/*
Dmitriy Ivanov6b566912014-04-29 08:41:29 -070043 * TODO: Read this before upstreaming:
44 *
45 * As of Apr 2014 there is a bug regaring function type detection logic in
46 * Free/Open/NetBSD implementations of __cxa_finalize().
47 *
48 * What it is about:
49 * First of all there are two kind of atexit handlers:
50 * 1) void handler(void) - this is the regular type
51 * available for to user via atexit(.) function call.
52 *
53 * 2) void internal_handler(void*) - this is the type
54 * __cxa_atexit() function expects. This handler is used
55 * by C++ compiler to register static destructor calls.
56 * Note that calling this function as the handler of type (1)
57 * results in incorrect this pointer in static d-tors.
58 *
59 * What is wrong with BSD implementations:
60 *
61 * They use dso argument to identify the handler type. The problem
62 * with it is dso is also used to identify the handlers associated
63 * with particular dynamic library and allow __cxa_finalize to call correct
64 * set of functions on dlclose(). And it cannot identify both.
65 *
66 * What is correct way to identify function type?
67 *
68 * Consider this:
69 * 1. __cxa_finalize and __cxa_atexit are part of libc and do not have access to hidden
70 * &__dso_handle.
71 * 2. __cxa_atexit has only 3 arguments: function pointer, function argument, dso.
72 * none of them can be reliably used to pass information about handler type.
73 * 3. following http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor (3.3.5.3 - B)
74 * translation of user atexit -> __cxa_atexit(f, NULL, NULL) results in crashes
75 * on exit() after dlclose() of a library with an atexit() call.
76 *
77 * One way to resolve this is to always call second form of handler, which will
78 * result in storing unused argument in register/stack depending on architecture
79 * and should not present any problems.
80 *
81 * Another way is to make them dso-local in one way or the other.
82 */
83
84/*
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080085 * Function pointers are stored in a linked list of pages. The list
86 * is initially empty, and pages are allocated on demand. The first
87 * function pointer in the first allocated page (the last one in
Elliott Hughes61e699a2013-06-12 14:05:46 -070088 * the linked list) was reserved for the cleanup function.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080089 *
90 * Outside the following functions, all pages are mprotect()'ed
91 * to prevent unintentional/malicious corruption.
92 */
93
94/*
95 * Register a function to be performed at exit or when a shared object
96 * with the given dso handle is unloaded dynamically. Also used as
97 * the backend for atexit(). For more info on this API, see:
98 *
99 * http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor
100 */
101int
102__cxa_atexit(void (*func)(void *), void *arg, void *dso)
103{
104 struct atexit *p = __atexit;
105 struct atexit_fn *fnp;
Dmitriy Ivanov623b0d02014-05-14 23:11:05 -0700106 size_t pgsize = sysconf(_SC_PAGESIZE);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800107 int ret = -1;
108
Dmitriy Ivanov623b0d02014-05-14 23:11:05 -0700109 if (pgsize < sizeof(*p))
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800110 return (-1);
111 _ATEXIT_LOCK();
112 p = __atexit;
113 if (p != NULL) {
114 if (p->ind + 1 >= p->max)
115 p = NULL;
116 else if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
117 goto unlock;
118 }
119 if (p == NULL) {
120 p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
121 MAP_ANON | MAP_PRIVATE, -1, 0);
122 if (p == MAP_FAILED)
123 goto unlock;
124 if (__atexit == NULL) {
125 memset(&p->fns[0], 0, sizeof(p->fns[0]));
126 p->ind = 1;
127 } else
128 p->ind = 0;
129 p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
130 sizeof(p->fns[0]);
131 p->next = __atexit;
132 __atexit = p;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800133 }
134 fnp = &p->fns[p->ind++];
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700135 fnp->cxa_func = func;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800136 fnp->fn_arg = arg;
137 fnp->fn_dso = dso;
138 if (mprotect(p, pgsize, PROT_READ))
139 goto unlock;
140 ret = 0;
141unlock:
142 _ATEXIT_UNLOCK();
143 return (ret);
144}
145
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800146/*
147 * Call all handlers registered with __cxa_atexit() for the shared
148 * object owning 'dso'.
149 * Note: if 'dso' is NULL, then all remaining handlers are called.
150 */
151void
152__cxa_finalize(void *dso)
153{
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700154 struct atexit *p, *q, *original_atexit;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800155 struct atexit_fn fn;
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700156 int n, pgsize = getpagesize(), original_ind;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800157 static int call_depth;
158
Srinavasa Nagaraju2270dfa2012-02-28 12:08:22 +0900159 _ATEXIT_LOCK();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800160 call_depth++;
161
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700162 p = original_atexit = __atexit;
163 n = original_ind = p != NULL ? p->ind : 0;
164 while (p != NULL) {
165 if (p->fns[n].cxa_func != NULL /* not called */
166 && (dso == NULL || dso == p->fns[n].fn_dso)) { /* correct DSO */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800167 /*
168 * Mark handler as having been already called to avoid
169 * dupes and loops, then call the appropriate function.
170 */
171 fn = p->fns[n];
172 if (mprotect(p, pgsize, PROT_READ | PROT_WRITE) == 0) {
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700173 p->fns[n].cxa_func = NULL;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800174 mprotect(p, pgsize, PROT_READ);
175 }
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700176
Srinavasa Nagaraju2270dfa2012-02-28 12:08:22 +0900177 _ATEXIT_UNLOCK();
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700178 (*fn.cxa_func)(fn.fn_arg);
Srinavasa Nagaraju2270dfa2012-02-28 12:08:22 +0900179 _ATEXIT_LOCK();
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700180 // check for new atexit handlers
181 if ((__atexit->ind != original_ind) || (__atexit != original_atexit)) {
182 // need to restart now to preserve correct
183 // call order - LIFO
184 p = original_atexit = __atexit;
185 n = original_ind = p->ind;
186 continue;
187 }
188 }
189 if (n == 0) {
190 p = p->next;
191 n = p != NULL ? p->ind : 0;
192 } else {
193 --n;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800194 }
195 }
196
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700197 --call_depth;
198
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800199 /*
200 * If called via exit(), unmap the pages since we have now run
201 * all the handlers. We defer this until calldepth == 0 so that
202 * we don't unmap things prematurely if called recursively.
203 */
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700204 if (dso == NULL && call_depth == 0) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800205 for (p = __atexit; p != NULL; ) {
206 q = p;
207 p = p->next;
208 munmap(q, pgsize);
209 }
210 __atexit = NULL;
211 }
Srinavasa Nagaraju2270dfa2012-02-28 12:08:22 +0900212 _ATEXIT_UNLOCK();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800213}
Dmitriy Ivanov623b0d02014-05-14 23:11:05 -0700214
215/*
216 * Register the cleanup function
217 */
218void
219__atexit_register_cleanup(void (*func)(void))
220{
221 struct atexit *p;
222 size_t pgsize = sysconf(_SC_PAGESIZE);
223
224 if (pgsize < sizeof(*p))
225 return;
226 _ATEXIT_LOCK();
227 p = __atexit;
228 while (p != NULL && p->next != NULL)
229 p = p->next;
230 if (p == NULL) {
231 p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
232 MAP_ANON | MAP_PRIVATE, -1, 0);
233 if (p == MAP_FAILED)
234 goto unlock;
235 p->ind = 1;
236 p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
237 sizeof(p->fns[0]);
238 p->next = NULL;
239 __atexit = p;
240 } else {
241 if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
242 goto unlock;
243 }
244 p->fns[0].cxa_func = (void (*)(void*))func;
245 p->fns[0].fn_arg = NULL;
246 p->fns[0].fn_dso = NULL;
247 mprotect(p, pgsize, PROT_READ);
248unlock:
249 _ATEXIT_UNLOCK();
250}
251