blob: a6970dd5d69d38530e5dbb6dcbcdd9dfd1321f7d [file] [log] [blame]
Dmitriy Ivanov53c3c272014-07-11 12:59:16 -07001/* $OpenBSD: atexit.c,v 1.20 2014/07/11 09:51:37 kettenis 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"
Dmitriy Ivanovea295f62014-11-20 20:47:02 -080038#include "private/thread_private.h"
Dmitriy Ivanov53c3c272014-07-11 12:59:16 -070039
Dmitriy Ivanov9cf6fc32015-06-04 14:33:18 -070040struct atexit {
41 struct atexit *next; /* next in list */
42 int ind; /* next index in this table */
43 int max; /* max entries >= ATEXIT_SIZE */
44 struct atexit_fn {
45 void (*fn_ptr)(void *);
46 void *fn_arg; /* argument for CXA callback */
47 void *fn_dso; /* shared module handle */
48 } fns[1]; /* the table itself */
49};
50
51static struct atexit *__atexit;
Dmitriy Ivanov53c3c272014-07-11 12:59:16 -070052static int restartloop;
53
Dmitriy Ivanovea295f62014-11-20 20:47:02 -080054/* BEGIN android-changed: __unregister_atfork is used by __cxa_finalize */
55extern void __unregister_atfork(void* dso);
56/* END android-changed */
57
Dmitriy Ivanov53c3c272014-07-11 12:59:16 -070058/*
59 * Function pointers are stored in a linked list of pages. The list
60 * is initially empty, and pages are allocated on demand. The first
61 * function pointer in the first allocated page (the last one in
62 * the linked list) is reserved for the cleanup function.
63 *
64 * Outside the following functions, all pages are mprotect()'ed
65 * to prevent unintentional/malicious corruption.
66 */
67
68/*
69 * Register a function to be performed at exit or when a shared object
70 * with the given dso handle is unloaded dynamically. Also used as
71 * the backend for atexit(). For more info on this API, see:
72 *
73 * http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor
74 */
75int
76__cxa_atexit(void (*func)(void *), void *arg, void *dso)
77{
78 struct atexit *p = __atexit;
79 struct atexit_fn *fnp;
Dmitriy Ivanovea295f62014-11-20 20:47:02 -080080 size_t pgsize = getpagesize();
Dmitriy Ivanov53c3c272014-07-11 12:59:16 -070081 int ret = -1;
82
83 if (pgsize < sizeof(*p))
84 return (-1);
85 _ATEXIT_LOCK();
86 p = __atexit;
87 if (p != NULL) {
88 if (p->ind + 1 >= p->max)
89 p = NULL;
90 else if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
91 goto unlock;
92 }
93 if (p == NULL) {
94 p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
95 MAP_ANON | MAP_PRIVATE, -1, 0);
96 if (p == MAP_FAILED)
97 goto unlock;
98 if (__atexit == NULL) {
99 memset(&p->fns[0], 0, sizeof(p->fns[0]));
100 p->ind = 1;
101 } else
102 p->ind = 0;
103 p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
104 sizeof(p->fns[0]);
105 p->next = __atexit;
106 __atexit = p;
107 }
108 fnp = &p->fns[p->ind++];
109 fnp->fn_ptr = func;
110 fnp->fn_arg = arg;
111 fnp->fn_dso = dso;
112 if (mprotect(p, pgsize, PROT_READ))
113 goto unlock;
114 restartloop = 1;
115 ret = 0;
116unlock:
117 _ATEXIT_UNLOCK();
118 return (ret);
119}
120
121/*
122 * Call all handlers registered with __cxa_atexit() for the shared
123 * object owning 'dso'.
124 * Note: if 'dso' is NULL, then all remaining handlers are called.
125 */
126void
127__cxa_finalize(void *dso)
128{
129 struct atexit *p, *q;
130 struct atexit_fn fn;
131 int n, pgsize = getpagesize();
132 static int call_depth;
133
134 _ATEXIT_LOCK();
135 call_depth++;
136
137restart:
138 restartloop = 0;
139 for (p = __atexit; p != NULL; p = p->next) {
140 for (n = p->ind; --n >= 0;) {
141 if (p->fns[n].fn_ptr == NULL)
142 continue; /* already called */
143 if (dso != NULL && dso != p->fns[n].fn_dso)
144 continue; /* wrong DSO */
145
146 /*
147 * Mark handler as having been already called to avoid
148 * dupes and loops, then call the appropriate function.
149 */
150 fn = p->fns[n];
151 if (mprotect(p, pgsize, PROT_READ | PROT_WRITE) == 0) {
152 p->fns[n].fn_ptr = NULL;
153 mprotect(p, pgsize, PROT_READ);
154 }
155 _ATEXIT_UNLOCK();
156 (*fn.fn_ptr)(fn.fn_arg);
157 _ATEXIT_LOCK();
158 if (restartloop)
159 goto restart;
160 }
161 }
162
163 call_depth--;
164
165 /*
166 * If called via exit(), unmap the pages since we have now run
167 * all the handlers. We defer this until calldepth == 0 so that
168 * we don't unmap things prematurely if called recursively.
169 */
170 if (dso == NULL && call_depth == 0) {
171 for (p = __atexit; p != NULL; ) {
172 q = p;
173 p = p->next;
174 munmap(q, pgsize);
175 }
176 __atexit = NULL;
177 }
178 _ATEXIT_UNLOCK();
Dmitriy Ivanovea295f62014-11-20 20:47:02 -0800179
180 /* BEGIN android-changed: call __unregister_atfork if dso is not null */
181 if (dso != NULL) {
182 __unregister_atfork(dso);
183 }
184 /* END android-changed */
Dmitriy Ivanov53c3c272014-07-11 12:59:16 -0700185}
186
187/*
188 * Register the cleanup function
189 */
190void
191__atexit_register_cleanup(void (*func)(void))
192{
193 struct atexit *p;
Dmitriy Ivanovea295f62014-11-20 20:47:02 -0800194 size_t pgsize = getpagesize();
Dmitriy Ivanov53c3c272014-07-11 12:59:16 -0700195
196 if (pgsize < sizeof(*p))
197 return;
198 _ATEXIT_LOCK();
199 p = __atexit;
200 while (p != NULL && p->next != NULL)
201 p = p->next;
202 if (p == NULL) {
203 p = mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
204 MAP_ANON | MAP_PRIVATE, -1, 0);
205 if (p == MAP_FAILED)
206 goto unlock;
207 p->ind = 1;
208 p->max = (pgsize - ((char *)&p->fns[0] - (char *)p)) /
209 sizeof(p->fns[0]);
210 p->next = NULL;
211 __atexit = p;
212 } else {
213 if (mprotect(p, pgsize, PROT_READ | PROT_WRITE))
214 goto unlock;
215 }
216 p->fns[0].fn_ptr = (void (*)(void *))func;
217 p->fns[0].fn_arg = NULL;
218 p->fns[0].fn_dso = NULL;
219 mprotect(p, pgsize, PROT_READ);
220 restartloop = 1;
221unlock:
222 _ATEXIT_UNLOCK();
223}