summaryrefslogtreecommitdiff
path: root/libc/stdlib/atexit.c
blob: d5f83ee8e57cdc93ad44c4a1e9278aca74461b55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/* Copyright (C) 1995,1996 Robert de Bath <rdebath@cix.compulink.co.uk>
 * This file is part of the Linux-8086 C library and is distributed
 * under the GNU Library General Public License.
 */

/*
 * Dec 2000          Manuel Novoa III
 *
 *   Made atexit handling conform to standards... i.e. no args.
 *   Removed on_exit since it did not match gnu libc definition.
 *   Combined atexit and __do_exit into one object file.
 *
 * Feb 2001          Manuel Novoa III
 *
 *   Reworked file after addition of __uClibc_main.
 *   Changed name of __do_exit to atexit_handler.
 *   Changed name of __cleanup to __uClibc_cleanup.
 *   Moved declaration of __uClibc_cleanup to __uClibc_main
 *      where it is initialized with (possibly weak alias)
 *      _stdio_term.
 *
 * Jul 2001          Steve Thayer
 * 
 *   Added an on_exit implementation (that now matches gnu libc definition.)
 *   Pulled atexit_handler out of the atexit object since it is now required by
 *   on_exit as well.  Renamed it to __exit_handler.
 *   Fixed a problem where exit functions stop getting called if one of
 *   them calls exit().
 *   As a side effect of these changes, abort() no longer calls the exit
 *   functions (it now matches the gnu libc definition).
 *
 * August 2002    Erik Andersen
 *   Added locking so atexit and friends can be thread safe
 *
 */

#define _GNU_SOURCE
#include <features.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>


#ifdef __UCLIBC_HAS_THREADS__
#include <pthread.h>
extern pthread_mutex_t mylock;
# define LOCK	__pthread_mutex_lock(&mylock)
# define UNLOCK	__pthread_mutex_unlock(&mylock);
#else
# define LOCK
# define UNLOCK
#endif


typedef void (*aefuncp) (void);         /* atexit function pointer */
typedef void (*oefuncp) (int, void *);  /* on_exit function pointer */
typedef enum {
	ef_atexit,
	ef_on_exit
} ef_type; /* exit function types */

/* this is in the L_exit object */
extern void (*__exit_cleanup) (int);

/* these are in the L___do_exit object */
extern int __exit_slots;
extern int __exit_count;
extern void __exit_handler(int);
struct exit_function {
	ef_type type;	/* ef_atexit or ef_on_exit */
	union {
		aefuncp atexit;
		struct {
			oefuncp func;
			void *arg;
		} on_exit;
	} funcs;
};
#ifdef __UCLIBC_DYNAMIC_ATEXIT__
extern struct exit_function *__exit_function_table;
#else
extern struct exit_function __exit_function_table[__UCLIBC_MAX_ATEXIT];
#endif

#ifdef L_atexit
	/*
 * register a function to be called at normal program termination
 * (the registered function takes no arguments)
	 */
int atexit(aefuncp func)
{
    struct exit_function *efp;

    LOCK;
    if (func) {
#ifdef __UCLIBC_DYNAMIC_ATEXIT__
	/* If we are out of function table slots, make some more */
	if (__exit_slots < __exit_count+1) {
	    efp=realloc(__exit_function_table, 
					(__exit_slots+20)*sizeof(struct exit_function));
	    if (efp==NULL) {
		UNLOCK;
		__set_errno(ENOMEM);
		return -1;
	    }
		__exit_function_table = efp;
	    __exit_slots+=20;
	}
#else
	if (__exit_count >= __UCLIBC_MAX_ATEXIT) {
	    UNLOCK;
	    __set_errno(ENOMEM);
	    return -1;
	}
#endif
	__exit_cleanup = __exit_handler; /* enable cleanup */
	efp = &__exit_function_table[__exit_count++];
	efp->type = ef_atexit;
	efp->funcs.atexit = func;
    }
    UNLOCK;
    return 0;
}
#endif

#ifdef L_on_exit
/*
 * register a function to be called at normal program termination
 * the registered function takes two arguments:
 *     status - the exit status that was passed to the exit() function
 *     arg - generic argument
 */
int on_exit(oefuncp func, void *arg)
{
    struct exit_function *efp;

    LOCK;
    if (func) {
#ifdef __UCLIBC_DYNAMIC_ATEXIT__
	/* If we are out of function table slots, make some more */
	if (__exit_slots < __exit_count+1) {
	    efp=realloc(__exit_function_table, 
					(__exit_slots+20)*sizeof(struct exit_function));
	    if (efp==NULL) {
		UNLOCK;
		__set_errno(ENOMEM);
		return -1;
	    }
		__exit_function_table=efp;
	    __exit_slots+=20;
	}
#else
	if (__exit_count >= __UCLIBC_MAX_ATEXIT) {
	    UNLOCK;
	    __set_errno(ENOMEM);
	    return -1;
	}
#endif

	__exit_cleanup = __exit_handler; /* enable cleanup */
	efp = &__exit_function_table[__exit_count++];
	efp->type = ef_on_exit;
	efp->funcs.on_exit.func = func;
	efp->funcs.on_exit.arg = arg;
    }
    UNLOCK;
    return 0;
}
#endif

#ifdef L___exit_handler
int __exit_count = 0; /* Number of registered exit functions */
#ifdef __UCLIBC_DYNAMIC_ATEXIT__
struct exit_function *__exit_function_table = NULL;
int __exit_slots = 0; /* Size of __exit_function_table */
#else
struct exit_function __exit_function_table[__UCLIBC_MAX_ATEXIT];
#endif


/*
 * Handle the work of executing the registered exit functions
 * This is called while we are locked, so no additional locking
 * is needed...
 */
void __exit_handler(int status)
{
	struct exit_function *efp;

	/* In reverse order */
	while ( __exit_count ) {
		efp = &__exit_function_table[--__exit_count];
		switch (efp->type) {
		case ef_on_exit:
			if (efp->funcs.on_exit.func) {
				(efp->funcs.on_exit.func) (status, efp->funcs.on_exit.arg);
			}
			break;
		case ef_atexit:
			if (efp->funcs.atexit) {
				(efp->funcs.atexit) ();
			}
			break;
		}
	}
#ifdef __UCLIBC_DYNAMIC_ATEXIT__
	/* Free up memory used by the __exit_function_table structure */ 
	if (__exit_function_table)
	    free(__exit_function_table);
#endif
}
#endif

#ifdef L_exit
extern void weak_function _stdio_term(void);
void (*__exit_cleanup) (int) = 0;
#ifdef __UCLIBC_HAS_THREADS__
pthread_mutex_t mylock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
#endif

#ifdef __UCLIBC_CTOR_DTOR__
extern void (*__app_fini)(void);
#endif

extern void (*__rtld_fini)(void);
/*
 * Normal program termination
 */
void exit(int rv)
{
	/* Perform exit-specific cleanup (atexit and on_exit) */
	LOCK;
	if (__exit_cleanup) {
		__exit_cleanup(rv);
	}
	UNLOCK;

#ifdef __UCLIBC_CTOR_DTOR__
	if (__app_fini != NULL)
		(__app_fini)();
#endif
#ifdef _DL_DO_FINI_IN_LIBC
/* arches that has moved their ldso FINI handling should #define _DL_DO_FINI_IN_LIBC */
	if (__rtld_fini != NULL)
		(__rtld_fini)();
#endif

    /* If we are using stdio, try to shut it down.  At the very least,
	 * this will attempt to commit all buffered writes.  It may also
	 * unbuffer all writable files, or close them outright.
	 * Check the stdio routines for details. */
	if (_stdio_term) 
	    _stdio_term();

	_exit(rv);
}
#endif