1 | /* |
---|
2 | * Common routine to implement atexit-like functionality. |
---|
3 | * |
---|
4 | * This is also the key function to be configured as lite exit, a size-reduced |
---|
5 | * implementation of exit that doesn't invoke clean-up functions such as _fini |
---|
6 | * or global destructors. |
---|
7 | * |
---|
8 | * Default (without lite exit) call graph is like: |
---|
9 | * _start -> atexit -> __register_exitproc |
---|
10 | * _start -> __libc_init_array -> __cxa_atexit -> __register_exitproc |
---|
11 | * on_exit -> __register_exitproc |
---|
12 | * _start -> exit -> __call_exitprocs |
---|
13 | * |
---|
14 | * Here an -> means arrow tail invokes arrow head. All invocations here |
---|
15 | * are non-weak reference in current newlib/libgloss. |
---|
16 | * |
---|
17 | * Lite exit makes some of above calls as weak reference, so that size expansive |
---|
18 | * functions __register_exitproc and __call_exitprocs may not be linked. These |
---|
19 | * calls are: |
---|
20 | * _start w-> atexit |
---|
21 | * __cxa_atexit w-> __register_exitproc |
---|
22 | * exit w-> __call_exitprocs |
---|
23 | * |
---|
24 | * Lite exit also makes sure that __call_exitprocs will be referenced as non-weak |
---|
25 | * whenever __register_exitproc is referenced as non-weak. |
---|
26 | * |
---|
27 | * Thus with lite exit libs, a program not explicitly calling atexit or on_exit |
---|
28 | * will escape from the burden of cleaning up code. A program with atexit or on_exit |
---|
29 | * will work consistently to normal libs. |
---|
30 | * |
---|
31 | * Lite exit is enabled with --enable-lite-exit, and is controlled with macro |
---|
32 | * _LITE_EXIT. |
---|
33 | */ |
---|
34 | |
---|
35 | #include <stddef.h> |
---|
36 | #include <stdlib.h> |
---|
37 | #include <reent.h> |
---|
38 | #include <sys/lock.h> |
---|
39 | #include "atexit.h" |
---|
40 | |
---|
41 | /* Make this a weak reference to avoid pulling in malloc. */ |
---|
42 | void * malloc(size_t) _ATTRIBUTE((__weak__)); |
---|
43 | |
---|
44 | #ifdef _LITE_EXIT |
---|
45 | /* As __call_exitprocs is weak reference in lite exit, make a |
---|
46 | non-weak reference to it here. */ |
---|
47 | const void * __atexit_dummy = &__call_exitprocs; |
---|
48 | #endif |
---|
49 | |
---|
50 | #ifndef __SINGLE_THREAD__ |
---|
51 | extern _LOCK_RECURSIVE_T __atexit_recursive_mutex; |
---|
52 | #endif |
---|
53 | |
---|
54 | #ifdef _REENT_GLOBAL_ATEXIT |
---|
55 | static struct _atexit _global_atexit0 = _ATEXIT_INIT; |
---|
56 | # define _GLOBAL_ATEXIT0 (&_global_atexit0) |
---|
57 | #else |
---|
58 | # define _GLOBAL_ATEXIT0 (&_GLOBAL_REENT->_atexit0) |
---|
59 | #endif |
---|
60 | |
---|
61 | /* |
---|
62 | * Register a function to be performed at exit or on shared library unload. |
---|
63 | */ |
---|
64 | |
---|
65 | int |
---|
66 | __register_exitproc (int type, |
---|
67 | void (*fn) (void), |
---|
68 | void *arg, |
---|
69 | void *d) |
---|
70 | { |
---|
71 | struct _on_exit_args * args; |
---|
72 | register struct _atexit *p; |
---|
73 | |
---|
74 | #ifndef __SINGLE_THREAD__ |
---|
75 | __lock_acquire_recursive(__atexit_recursive_mutex); |
---|
76 | #endif |
---|
77 | |
---|
78 | p = _GLOBAL_ATEXIT; |
---|
79 | if (p == NULL) |
---|
80 | { |
---|
81 | _GLOBAL_ATEXIT = p = _GLOBAL_ATEXIT0; |
---|
82 | #ifdef _REENT_SMALL |
---|
83 | extern struct _on_exit_args * const __on_exit_args _ATTRIBUTE ((weak)); |
---|
84 | if (&__on_exit_args != NULL) |
---|
85 | p->_on_exit_args_ptr = __on_exit_args; |
---|
86 | #endif /* def _REENT_SMALL */ |
---|
87 | } |
---|
88 | if (p->_ind >= _ATEXIT_SIZE) |
---|
89 | { |
---|
90 | #ifndef _ATEXIT_DYNAMIC_ALLOC |
---|
91 | #ifndef __SINGLE_THREAD__ |
---|
92 | __lock_release_recursive(__atexit_recursive_mutex); |
---|
93 | #endif |
---|
94 | return -1; |
---|
95 | #else |
---|
96 | /* Don't dynamically allocate the atexit array if malloc is not |
---|
97 | available. */ |
---|
98 | if (!malloc) |
---|
99 | { |
---|
100 | #ifndef __SINGLE_THREAD__ |
---|
101 | __lock_release_recursive(__atexit_recursive_mutex); |
---|
102 | #endif |
---|
103 | return -1; |
---|
104 | } |
---|
105 | |
---|
106 | p = (struct _atexit *) malloc (sizeof *p); |
---|
107 | if (p == NULL) |
---|
108 | { |
---|
109 | #ifndef __SINGLE_THREAD__ |
---|
110 | __lock_release_recursive(__atexit_recursive_mutex); |
---|
111 | #endif |
---|
112 | return -1; |
---|
113 | } |
---|
114 | p->_ind = 0; |
---|
115 | p->_next = _GLOBAL_ATEXIT; |
---|
116 | _GLOBAL_ATEXIT = p; |
---|
117 | #ifndef _REENT_SMALL |
---|
118 | p->_on_exit_args._fntypes = 0; |
---|
119 | p->_on_exit_args._is_cxa = 0; |
---|
120 | #else |
---|
121 | p->_on_exit_args_ptr = NULL; |
---|
122 | #endif |
---|
123 | #endif |
---|
124 | } |
---|
125 | |
---|
126 | if (type != __et_atexit) |
---|
127 | { |
---|
128 | #ifdef _REENT_SMALL |
---|
129 | args = p->_on_exit_args_ptr; |
---|
130 | if (args == NULL) |
---|
131 | { |
---|
132 | #ifndef _ATEXIT_DYNAMIC_ALLOC |
---|
133 | #ifndef __SINGLE_THREAD__ |
---|
134 | __lock_release_recursive(__atexit_recursive_mutex); |
---|
135 | #endif |
---|
136 | return -1; |
---|
137 | #else |
---|
138 | if (malloc) |
---|
139 | args = malloc (sizeof * p->_on_exit_args_ptr); |
---|
140 | |
---|
141 | if (args == NULL) |
---|
142 | { |
---|
143 | #ifndef __SINGLE_THREAD__ |
---|
144 | __lock_release(__atexit_recursive_mutex); |
---|
145 | #endif |
---|
146 | return -1; |
---|
147 | } |
---|
148 | args->_fntypes = 0; |
---|
149 | args->_is_cxa = 0; |
---|
150 | p->_on_exit_args_ptr = args; |
---|
151 | #endif |
---|
152 | } |
---|
153 | #else |
---|
154 | args = &p->_on_exit_args; |
---|
155 | #endif |
---|
156 | args->_fnargs[p->_ind] = arg; |
---|
157 | args->_fntypes |= (1 << p->_ind); |
---|
158 | args->_dso_handle[p->_ind] = d; |
---|
159 | if (type == __et_cxa) |
---|
160 | args->_is_cxa |= (1 << p->_ind); |
---|
161 | } |
---|
162 | p->_fns[p->_ind++] = fn; |
---|
163 | #ifndef __SINGLE_THREAD__ |
---|
164 | __lock_release_recursive(__atexit_recursive_mutex); |
---|
165 | #endif |
---|
166 | return 0; |
---|
167 | } |
---|