]>
Commit | Line | Data |
---|---|---|
26b4d766 | 1 | /* Close a shared object opened by `_dl_open'. |
dacc8ffa | 2 | Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. |
afd4eb37 UD |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Library General Public License as | |
7 | published by the Free Software Foundation; either version 2 of the | |
8 | License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Library General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Library General Public | |
16 | License along with the GNU C Library; see the file COPYING.LIB. If not, | |
17 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
18 | Boston, MA 02111-1307, USA. */ | |
ba79d61b | 19 | |
7afab53d | 20 | #include <assert.h> |
ba79d61b | 21 | #include <dlfcn.h> |
8e17ea58 | 22 | #include <libintl.h> |
ba79d61b | 23 | #include <stdlib.h> |
8d6468d0 | 24 | #include <string.h> |
a853022c | 25 | #include <bits/libc-lock.h> |
b8445829 | 26 | #include <ldsodefs.h> |
ba79d61b RM |
27 | #include <sys/types.h> |
28 | #include <sys/mman.h> | |
29 | ||
b35e21f4 UD |
30 | #include <stdio-common/_itoa.h> |
31 | ||
ba79d61b | 32 | |
dacc8ffa UD |
33 | /* Type of the constructor functions. */ |
34 | typedef void (*fini_t) (void); | |
35 | ||
36 | ||
26b4d766 UD |
37 | /* During the program run we must not modify the global data of |
38 | loaded shared object simultanously in two threads. Therefore we | |
39 | protect `dlopen' and `dlclose' in dlclose.c. */ | |
40 | __libc_lock_define (extern, _dl_load_lock) | |
41 | ||
ba79d61b | 42 | void |
d0fc4041 | 43 | internal_function |
94e365c6 | 44 | _dl_close (void *_map) |
ba79d61b RM |
45 | { |
46 | struct link_map **list; | |
cf197e41 | 47 | struct link_map **rellist; |
94e365c6 | 48 | struct link_map *map = _map; |
cf197e41 UD |
49 | unsigned int nsearchlist; |
50 | unsigned int nrellist; | |
ba79d61b | 51 | unsigned int i; |
42c4f32a | 52 | unsigned int *new_opencount; |
ba79d61b | 53 | |
bf8b3e74 UD |
54 | /* First see whether we can remove the object at all. */ |
55 | if (map->l_flags_1 & DF_1_NODELETE) | |
56 | /* Nope. Do nothing. */ | |
57 | return; | |
58 | ||
c41c89d3 | 59 | if (__builtin_expect (map->l_opencount, 1) == 0) |
8e17ea58 | 60 | _dl_signal_error (0, map->l_name, N_("shared object not open")); |
ba79d61b | 61 | |
26b4d766 UD |
62 | /* Acquire the lock. */ |
63 | __libc_lock_lock (_dl_load_lock); | |
64 | ||
ba79d61b | 65 | /* Decrement the reference count. */ |
a709dd43 | 66 | if (map->l_opencount > 1 || map->l_type != lt_loaded) |
26b4d766 UD |
67 | { |
68 | /* There are still references to this object. Do nothing more. */ | |
b35e21f4 UD |
69 | if (__builtin_expect (_dl_debug_files, 0)) |
70 | { | |
71 | char buf[20]; | |
72 | ||
73 | buf[sizeof buf - 1] = '\0'; | |
74 | ||
75 | _dl_debug_message (1, "\nclosing file=", map->l_name, | |
76 | "; opencount == ", | |
77 | _itoa_word (map->l_opencount, | |
78 | buf + sizeof buf - 1, 10, 0), | |
79 | "\n", NULL); | |
80 | } | |
81 | ||
42c4f32a UD |
82 | /* One decrement the object itself, not the dependencies. */ |
83 | --map->l_opencount; | |
8699e7b1 | 84 | |
26b4d766 UD |
85 | __libc_lock_unlock (_dl_load_lock); |
86 | return; | |
87 | } | |
ba79d61b | 88 | |
42c4f32a UD |
89 | list = map->l_searchlist.r_list; |
90 | nsearchlist = map->l_searchlist.r_nlist; | |
91 | ||
92 | /* Compute the new l_opencount values. */ | |
93 | new_opencount = (unsigned int *) alloca (nsearchlist | |
94 | * sizeof (unsigned int)); | |
95 | for (i = 0; i < nsearchlist; ++i) | |
96 | { | |
8edd9172 UD |
97 | map->l_initfini[i]->l_idx = i; |
98 | new_opencount[i] = map->l_initfini[i]->l_opencount; | |
42c4f32a UD |
99 | } |
100 | --new_opencount[0]; | |
101 | for (i = 1; i < nsearchlist; ++i) | |
8edd9172 | 102 | if (! (map->l_initfini[i]->l_flags_1 & DF_1_NODELETE) |
42c4f32a UD |
103 | /* Decrement counter. */ |
104 | && --new_opencount[i] == 0 | |
105 | /* Test whether this object was also loaded directly. */ | |
8edd9172 | 106 | && map->l_initfini[i]->l_searchlist.r_list != NULL) |
42c4f32a UD |
107 | { |
108 | /* In this case we have the decrement all the dependencies of | |
109 | this object. They are all in MAP's dependency list. */ | |
110 | unsigned int j; | |
8edd9172 | 111 | struct link_map **dep_list = map->l_initfini[i]->l_searchlist.r_list; |
42c4f32a | 112 | |
8edd9172 | 113 | for (j = 1; j < map->l_initfini[i]->l_searchlist.r_nlist; ++j) |
42c4f32a UD |
114 | if (! (dep_list[j]->l_flags_1 & DF_1_NODELETE)) |
115 | { | |
116 | assert (dep_list[j]->l_idx < nsearchlist); | |
117 | --new_opencount[dep_list[j]->l_idx]; | |
118 | } | |
119 | } | |
120 | assert (new_opencount[0] == 0); | |
121 | ||
cf197e41 UD |
122 | rellist = map->l_reldeps; |
123 | nrellist = map->l_reldepsact; | |
124 | ||
a709dd43 | 125 | /* Call all termination functions at once. */ |
6075607b | 126 | for (i = 0; i < nsearchlist; ++i) |
a709dd43 | 127 | { |
dacc8ffa | 128 | struct link_map *imap = map->l_initfini[i]; |
42c4f32a | 129 | if (new_opencount[i] == 0 && imap->l_type == lt_loaded |
dacc8ffa | 130 | && (imap->l_info[DT_FINI] || imap->l_info[DT_FINI_ARRAY]) |
172b90bb | 131 | && ! (imap->l_flags_1 & DF_1_NODELETE) |
7a68c94a UD |
132 | /* Skip any half-cooked objects that were never initialized. */ |
133 | && imap->l_init_called) | |
a709dd43 | 134 | { |
b48abe3c | 135 | /* When debugging print a message first. */ |
466a0ec9 | 136 | if (__builtin_expect (_dl_debug_impcalls, 0)) |
b48abe3c UD |
137 | _dl_debug_message (1, "\ncalling fini: ", imap->l_name, |
138 | "\n\n", NULL); | |
dacc8ffa | 139 | |
b48abe3c | 140 | /* Call its termination function. */ |
dacc8ffa UD |
141 | if (imap->l_info[DT_FINI_ARRAY] != NULL) |
142 | { | |
143 | ElfW(Addr) *array = | |
144 | (ElfW(Addr) *) (imap->l_addr | |
145 | + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr); | |
146 | unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val | |
147 | / sizeof (ElfW(Addr))); | |
148 | unsigned int cnt; | |
149 | ||
150 | for (cnt = 0; cnt < sz; ++cnt) | |
151 | ((fini_t) (imap->l_addr + array[cnt])) (); | |
152 | } | |
153 | ||
154 | /* Next try the old-style destructor. */ | |
155 | if (imap->l_info[DT_FINI] != NULL) | |
156 | (*(void (*) (void)) ((void *) imap->l_addr | |
157 | + imap->l_info[DT_FINI]->d_un.d_ptr)) (); | |
a709dd43 | 158 | } |
42c4f32a UD |
159 | |
160 | /* Store the new l_opencount value. */ | |
161 | imap->l_opencount = new_opencount[i]; | |
162 | /* Just a sanity check. */ | |
163 | assert (imap->l_type == lt_loaded || imap->l_opencount > 0); | |
a709dd43 UD |
164 | } |
165 | ||
4d6acc61 RM |
166 | /* Notify the debugger we are about to remove some loaded objects. */ |
167 | _r_debug.r_state = RT_DELETE; | |
168 | _dl_debug_state (); | |
169 | ||
ba79d61b RM |
170 | /* Check each element of the search list to see if all references to |
171 | it are gone. */ | |
6075607b | 172 | for (i = 0; i < nsearchlist; ++i) |
ba79d61b | 173 | { |
af69217f UD |
174 | struct link_map *imap = list[i]; |
175 | if (imap->l_opencount == 0 && imap->l_type == lt_loaded) | |
ba79d61b | 176 | { |
a8a1269d UD |
177 | struct libname_list *lnp; |
178 | ||
ba79d61b RM |
179 | /* That was the last reference, and this was a dlopen-loaded |
180 | object. We can unmap it. */ | |
c41c89d3 | 181 | if (__builtin_expect (imap->l_global, 0)) |
ba79d61b RM |
182 | { |
183 | /* This object is in the global scope list. Remove it. */ | |
82df2969 | 184 | int cnt = _dl_main_searchlist->r_nlist; |
be935610 | 185 | |
ba79d61b | 186 | do |
50b65db1 | 187 | --cnt; |
be935610 | 188 | while (_dl_main_searchlist->r_list[cnt] != imap); |
482eec0d | 189 | |
50b65db1 UD |
190 | /* The object was already correctly registered. */ |
191 | while (++cnt < _dl_main_searchlist->r_nlist) | |
192 | _dl_main_searchlist->r_list[cnt - 1] | |
193 | = _dl_main_searchlist->r_list[cnt]; | |
194 | ||
195 | --_dl_main_searchlist->r_nlist; | |
ba79d61b RM |
196 | } |
197 | ||
a8a1269d | 198 | /* We can unmap all the maps at once. We determined the |
4ce636da UD |
199 | start address and length when we loaded the object and |
200 | the `munmap' call does the rest. */ | |
09bf6406 | 201 | DL_UNMAP (imap); |
22bc7978 | 202 | |
ba79d61b | 203 | /* Finally, unlink the data structure and free it. */ |
b5567b2a | 204 | #ifdef SHARED |
7afab53d UD |
205 | /* We will unlink the first object only if this is a statically |
206 | linked program. */ | |
207 | assert (imap->l_prev != NULL); | |
6a805a0b | 208 | imap->l_prev->l_next = imap->l_next; |
7afab53d UD |
209 | #else |
210 | if (imap->l_prev != NULL) | |
af69217f | 211 | imap->l_prev->l_next = imap->l_next; |
7afab53d UD |
212 | else |
213 | _dl_loaded = imap->l_next; | |
214 | #endif | |
1ebba33e | 215 | --_dl_nloaded; |
af69217f UD |
216 | if (imap->l_next) |
217 | imap->l_next->l_prev = imap->l_prev; | |
a8a1269d UD |
218 | |
219 | if (imap->l_versions != NULL) | |
220 | free (imap->l_versions); | |
1c3a6f19 | 221 | if (imap->l_origin != NULL && imap->l_origin != (char *) -1) |
a8a1269d UD |
222 | free ((char *) imap->l_origin); |
223 | ||
4ce636da | 224 | /* This name always is allocated. */ |
a8a1269d | 225 | free (imap->l_name); |
4ce636da | 226 | /* Remove the list with all the names of the shared object. */ |
a8a1269d UD |
227 | lnp = imap->l_libname; |
228 | do | |
229 | { | |
76156ea1 | 230 | struct libname_list *this = lnp; |
a8a1269d | 231 | lnp = lnp->next; |
76156ea1 | 232 | free (this); |
a8a1269d UD |
233 | } |
234 | while (lnp != NULL); | |
a8a1269d | 235 | |
4ce636da | 236 | /* Remove the searchlists. */ |
dacc8ffa | 237 | if (imap != map) |
4ce636da | 238 | { |
07a3d63e UD |
239 | if (imap->l_searchlist.r_list != NULL) |
240 | free (imap->l_searchlist.r_list); | |
8edd9172 | 241 | else |
07a3d63e | 242 | free (imap->l_initfini); |
4ce636da | 243 | } |
4ce636da | 244 | |
7bcaca43 | 245 | if (imap->l_phdr_allocated) |
15925412 | 246 | free ((void *) imap->l_phdr); |
7bcaca43 | 247 | |
f55727ca UD |
248 | if (imap->l_rpath_dirs.dirs != (void *) -1) |
249 | free (imap->l_rpath_dirs.dirs); | |
250 | if (imap->l_runpath_dirs.dirs != (void *) -1) | |
251 | free (imap->l_runpath_dirs.dirs); | |
252 | ||
af69217f | 253 | free (imap); |
ba79d61b RM |
254 | } |
255 | } | |
256 | ||
cf197e41 UD |
257 | /* Now we can perhaps also remove the modules for which we had |
258 | dependencies because of symbol lookup. */ | |
c41c89d3 | 259 | if (__builtin_expect (rellist != NULL, 0)) |
cf197e41 UD |
260 | { |
261 | while (nrellist-- > 0) | |
262 | _dl_close (rellist[nrellist]); | |
263 | ||
264 | free (rellist); | |
265 | } | |
266 | ||
ba79d61b | 267 | free (list); |
4d6acc61 | 268 | |
c41c89d3 | 269 | if (__builtin_expect (_dl_global_scope_alloc, 0) != 0 |
d3556ac9 UD |
270 | && _dl_main_searchlist->r_nlist == _dl_initial_searchlist.r_nlist) |
271 | { | |
272 | /* All object dynamically loaded by the program are unloaded. Free | |
273 | the memory allocated for the global scope variable. */ | |
274 | struct link_map **old = _dl_main_searchlist->r_list; | |
275 | ||
276 | /* Put the old map in. */ | |
277 | _dl_main_searchlist->r_list = _dl_initial_searchlist.r_list; | |
278 | /* Signal that the original map is used. */ | |
279 | _dl_global_scope_alloc = 0; | |
280 | ||
281 | /* Now free the old map. */ | |
282 | free (old); | |
283 | } | |
284 | ||
4d6acc61 RM |
285 | /* Notify the debugger those objects are finalized and gone. */ |
286 | _r_debug.r_state = RT_CONSISTENT; | |
287 | _dl_debug_state (); | |
26b4d766 UD |
288 | |
289 | /* Release the lock. */ | |
290 | __libc_lock_unlock (_dl_load_lock); | |
ba79d61b | 291 | } |