blob: b051e22d5e2aa6ef6b4d5a49e309c57d88819520 [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
40int __atexit_invalid = 1;
41struct atexit *__atexit;
42
43/*
Dmitriy Ivanov6b566912014-04-29 08:41:29 -070044 * TODO: Read this before upstreaming:
45 *
46 * As of Apr 2014 there is a bug regaring function type detection logic in
47 * Free/Open/NetBSD implementations of __cxa_finalize().
48 *
49 * What it is about:
50 * First of all there are two kind of atexit handlers:
51 * 1) void handler(void) - this is the regular type
52 * available for to user via atexit(.) function call.
53 *
54 * 2) void internal_handler(void*) - this is the type
55 * __cxa_atexit() function expects. This handler is used
56 * by C++ compiler to register static destructor calls.
57 * Note that calling this function as the handler of type (1)
58 * results in incorrect this pointer in static d-tors.
59 *
60 * What is wrong with BSD implementations:
61 *
62 * They use dso argument to identify the handler type. The problem
63 * with it is dso is also used to identify the handlers associated
64 * with particular dynamic library and allow __cxa_finalize to call correct
65 * set of functions on dlclose(). And it cannot identify both.
66 *
67 * What is correct way to identify function type?
68 *
69 * Consider this:
70 * 1. __cxa_finalize and __cxa_atexit are part of libc and do not have access to hidden
71 * &__dso_handle.
72 * 2. __cxa_atexit has only 3 arguments: function pointer, function argument, dso.
73 * none of them can be reliably used to pass information about handler type.
74 * 3. following http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor (3.3.5.3 - B)
75 * translation of user atexit -> __cxa_atexit(f, NULL, NULL) results in crashes
76 * on exit() after dlclose() of a library with an atexit() call.
77 *
78 * One way to resolve this is to always call second form of handler, which will
79 * result in storing unused argument in register/stack depending on architecture
80 * and should not present any problems.
81 *
82 * Another way is to make them dso-local in one way or the other.
83 */
84
85/*
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080086 * Function pointers are stored in a linked list of pages. The list
87 * is initially empty, and pages are allocated on demand. The first
88 * function pointer in the first allocated page (the last one in
Elliott Hughes61e699a2013-06-12 14:05:46 -070089 * the linked list) was reserved for the cleanup function.
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080090 *
91 * Outside the following functions, all pages are mprotect()'ed
92 * to prevent unintentional/malicious corruption.
93 */
94
95/*
96 * Register a function to be performed at exit or when a shared object
97 * with the given dso handle is unloaded dynamically. Also used as
98 * the backend for atexit(). For more info on this API, see:
99 *
100 * http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor
101 */
102int
103__cxa_atexit(void (*func)(void *), void *arg, void *dso)
104{
105 struct atexit *p = __atexit;
106 struct atexit_fn *fnp;
107 int pgsize = getpagesize();
108 int ret = -1;
109
110 if (pgsize < (int)sizeof(*p))
111 return (-1);
112 _ATEXIT_LOCK();
113 p = __atexit;
114 if (p != NULL) {
115 if (p->ind + 1 >= p->max)
116 p = NULL;
117 else if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
118 goto unlock;
119 }
120 if (p == NULL) {
121 p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
122 MAP_ANON | MAP_PRIVATE, -1, 0);
123 if (p == MAP_FAILED)
124 goto unlock;
125 if (__atexit == NULL) {
126 memset(&p->fns[0], 0, sizeof(p->fns[0]));
127 p->ind = 1;
128 } else
129 p->ind = 0;
130 p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
131 sizeof(p->fns[0]);
132 p->next = __atexit;
133 __atexit = p;
134 if (__atexit_invalid)
135 __atexit_invalid = 0;
136 }
137 fnp = &p->fns[p->ind++];
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700138 fnp->cxa_func = func;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800139 fnp->fn_arg = arg;
140 fnp->fn_dso = dso;
141 if (mprotect(p, pgsize, PROT_READ))
142 goto unlock;
143 ret = 0;
144unlock:
145 _ATEXIT_UNLOCK();
146 return (ret);
147}
148
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800149/*
150 * Call all handlers registered with __cxa_atexit() for the shared
151 * object owning 'dso'.
152 * Note: if 'dso' is NULL, then all remaining handlers are called.
153 */
154void
155__cxa_finalize(void *dso)
156{
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700157 struct atexit *p, *q, *original_atexit;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800158 struct atexit_fn fn;
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700159 int n, pgsize = getpagesize(), original_ind;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800160 static int call_depth;
161
162 if (__atexit_invalid)
163 return;
Srinavasa Nagaraju2270dfa2012-02-28 12:08:22 +0900164 _ATEXIT_LOCK();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800165 call_depth++;
166
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700167 p = original_atexit = __atexit;
168 n = original_ind = p != NULL ? p->ind : 0;
169 while (p != NULL) {
170 if (p->fns[n].cxa_func != NULL /* not called */
171 && (dso == NULL || dso == p->fns[n].fn_dso)) { /* correct DSO */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800172 /*
173 * Mark handler as having been already called to avoid
174 * dupes and loops, then call the appropriate function.
175 */
176 fn = p->fns[n];
177 if (mprotect(p, pgsize, PROT_READ | PROT_WRITE) == 0) {
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700178 p->fns[n].cxa_func = NULL;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800179 mprotect(p, pgsize, PROT_READ);
180 }
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700181
Srinavasa Nagaraju2270dfa2012-02-28 12:08:22 +0900182 _ATEXIT_UNLOCK();
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700183 (*fn.cxa_func)(fn.fn_arg);
Srinavasa Nagaraju2270dfa2012-02-28 12:08:22 +0900184 _ATEXIT_LOCK();
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700185 // check for new atexit handlers
186 if ((__atexit->ind != original_ind) || (__atexit != original_atexit)) {
187 // need to restart now to preserve correct
188 // call order - LIFO
189 p = original_atexit = __atexit;
190 n = original_ind = p->ind;
191 continue;
192 }
193 }
194 if (n == 0) {
195 p = p->next;
196 n = p != NULL ? p->ind : 0;
197 } else {
198 --n;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800199 }
200 }
201
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700202 --call_depth;
203
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800204 /*
205 * If called via exit(), unmap the pages since we have now run
206 * all the handlers. We defer this until calldepth == 0 so that
207 * we don't unmap things prematurely if called recursively.
208 */
Dmitriy Ivanov6b566912014-04-29 08:41:29 -0700209 if (dso == NULL && call_depth == 0) {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800210 for (p = __atexit; p != NULL; ) {
211 q = p;
212 p = p->next;
213 munmap(q, pgsize);
214 }
215 __atexit = NULL;
216 }
Srinavasa Nagaraju2270dfa2012-02-28 12:08:22 +0900217 _ATEXIT_UNLOCK();
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800218}