]>
Commit | Line | Data |
---|---|---|
26b4d766 | 1 | /* Close a shared object opened by `_dl_open'. |
d6b5d570 | 2 | Copyright (C) 1996-2001, 2002 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 | |
41bdb6e2 AJ |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
afd4eb37 UD |
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 | |
41bdb6e2 | 13 | Lesser General Public License for more details. |
afd4eb37 | 14 | |
41bdb6e2 AJ |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, write to the Free | |
17 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
18 | 02111-1307 USA. */ | |
ba79d61b | 19 | |
7afab53d | 20 | #include <assert.h> |
ba79d61b | 21 | #include <dlfcn.h> |
8e17ea58 | 22 | #include <libintl.h> |
b209e34a | 23 | #include <stdio.h> |
ba79d61b | 24 | #include <stdlib.h> |
8d6468d0 | 25 | #include <string.h> |
a853022c | 26 | #include <bits/libc-lock.h> |
b8445829 | 27 | #include <ldsodefs.h> |
ba79d61b RM |
28 | #include <sys/types.h> |
29 | #include <sys/mman.h> | |
30 | ||
31 | ||
dacc8ffa UD |
32 | /* Type of the constructor functions. */ |
33 | typedef void (*fini_t) (void); | |
34 | ||
35 | ||
fc093be1 UD |
36 | #ifdef USE_TLS |
37 | /* Returns true we an non-empty was found. */ | |
38 | static bool | |
39 | remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp) | |
40 | { | |
41 | if (idx - disp >= listp->len) | |
42 | { | |
43 | /* There must be a next entry. Otherwise would the index be wrong. */ | |
44 | assert (listp->next != NULL); | |
45 | ||
46 | if (remove_slotinfo (idx, listp->next, disp + listp->len)) | |
47 | return true; | |
48 | ||
49 | /* No non-empty entry. Search from the end of this elements | |
50 | slotinfo array. */ | |
51 | idx = disp + listp->len; | |
52 | } | |
53 | else | |
54 | { | |
55 | struct link_map *old_map = listp->slotinfo[idx - disp].map; | |
56 | assert (old_map != NULL); | |
57 | ||
58 | /* Mark the entry as unused. */ | |
59 | listp->slotinfo[idx - disp].gen = GL(dl_tls_generation) + 1; | |
60 | listp->slotinfo[idx - disp].map = NULL; | |
61 | ||
62 | /* If this is not the last currently used entry no need to look | |
63 | further. */ | |
64 | if (old_map->l_tls_modid != GL(dl_tls_max_dtv_idx)) | |
65 | return true; | |
66 | ||
67 | assert (old_map->l_tls_modid == GL(dl_tls_max_dtv_idx)); | |
68 | } | |
69 | ||
70 | while (idx - disp > disp == 0 ? 1 + GL(dl_tls_static_nelem) : 0) | |
71 | { | |
72 | --idx; | |
73 | ||
74 | if (listp->slotinfo[idx - disp].map != NULL) | |
75 | { | |
76 | /* Found a new last used index. */ | |
77 | GL(dl_tls_max_dtv_idx) = idx; | |
78 | return true; | |
79 | } | |
80 | } | |
81 | ||
82 | /* No non-entry in this list element. */ | |
83 | return false; | |
84 | } | |
85 | #endif | |
86 | ||
87 | ||
ba79d61b | 88 | void |
d0fc4041 | 89 | internal_function |
94e365c6 | 90 | _dl_close (void *_map) |
ba79d61b | 91 | { |
4b4fcf99 UD |
92 | struct reldep_list |
93 | { | |
94 | struct link_map **rellist; | |
95 | unsigned int nrellist; | |
96 | struct reldep_list *next; | |
97 | } *reldeps = NULL; | |
ba79d61b | 98 | struct link_map **list; |
94e365c6 | 99 | struct link_map *map = _map; |
ba79d61b | 100 | unsigned int i; |
42c4f32a | 101 | unsigned int *new_opencount; |
a04586d8 UD |
102 | #ifdef USE_TLS |
103 | bool any_tls = false; | |
104 | #endif | |
ba79d61b | 105 | |
bf8b3e74 | 106 | /* First see whether we can remove the object at all. */ |
c4bb124a UD |
107 | if (__builtin_expect (map->l_flags_1 & DF_1_NODELETE, 0) |
108 | && map->l_init_called) | |
bf8b3e74 UD |
109 | /* Nope. Do nothing. */ |
110 | return; | |
111 | ||
c41c89d3 | 112 | if (__builtin_expect (map->l_opencount, 1) == 0) |
407fe3bb | 113 | _dl_signal_error (0, map->l_name, NULL, N_("shared object not open")); |
ba79d61b | 114 | |
26b4d766 | 115 | /* Acquire the lock. */ |
5688da55 | 116 | __libc_lock_lock_recursive (GL(dl_load_lock)); |
26b4d766 | 117 | |
ba79d61b | 118 | /* Decrement the reference count. */ |
a709dd43 | 119 | if (map->l_opencount > 1 || map->l_type != lt_loaded) |
26b4d766 UD |
120 | { |
121 | /* There are still references to this object. Do nothing more. */ | |
d6b5d570 | 122 | if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_FILES, 0)) |
c4bb124a UD |
123 | _dl_debug_printf ("\nclosing file=%s; opencount == %u\n", |
124 | map->l_name, map->l_opencount); | |
b35e21f4 | 125 | |
42c4f32a UD |
126 | /* One decrement the object itself, not the dependencies. */ |
127 | --map->l_opencount; | |
8699e7b1 | 128 | |
5688da55 | 129 | __libc_lock_unlock_recursive (GL(dl_load_lock)); |
26b4d766 UD |
130 | return; |
131 | } | |
ba79d61b | 132 | |
2e93b4a4 | 133 | list = map->l_initfini; |
42c4f32a UD |
134 | |
135 | /* Compute the new l_opencount values. */ | |
c4bb124a UD |
136 | i = map->l_searchlist.r_nlist; |
137 | if (__builtin_expect (i == 0, 0)) | |
138 | /* This can happen if we handle relocation dependencies for an | |
139 | object which wasn't loaded directly. */ | |
140 | for (i = 1; list[i] != NULL; ++i) | |
141 | ; | |
142 | ||
143 | new_opencount = (unsigned int *) alloca (i * sizeof (unsigned int)); | |
144 | ||
7e036a01 | 145 | for (i = 0; list[i] != NULL; ++i) |
42c4f32a | 146 | { |
2e93b4a4 UD |
147 | list[i]->l_idx = i; |
148 | new_opencount[i] = list[i]->l_opencount; | |
42c4f32a UD |
149 | } |
150 | --new_opencount[0]; | |
7e036a01 | 151 | for (i = 1; list[i] != NULL; ++i) |
c77a4478 | 152 | if ((! (list[i]->l_flags_1 & DF_1_NODELETE) || ! list[i]->l_init_called) |
42c4f32a UD |
153 | /* Decrement counter. */ |
154 | && --new_opencount[i] == 0 | |
155 | /* Test whether this object was also loaded directly. */ | |
2e93b4a4 | 156 | && list[i]->l_searchlist.r_list != NULL) |
42c4f32a UD |
157 | { |
158 | /* In this case we have the decrement all the dependencies of | |
159 | this object. They are all in MAP's dependency list. */ | |
160 | unsigned int j; | |
2e93b4a4 | 161 | struct link_map **dep_list = list[i]->l_searchlist.r_list; |
42c4f32a | 162 | |
2e93b4a4 | 163 | for (j = 1; j < list[i]->l_searchlist.r_nlist; ++j) |
c77a4478 UD |
164 | if (! (dep_list[j]->l_flags_1 & DF_1_NODELETE) |
165 | || ! dep_list[j]->l_init_called) | |
42c4f32a | 166 | { |
7e036a01 | 167 | assert (dep_list[j]->l_idx < map->l_searchlist.r_nlist); |
42c4f32a UD |
168 | --new_opencount[dep_list[j]->l_idx]; |
169 | } | |
170 | } | |
171 | assert (new_opencount[0] == 0); | |
172 | ||
a709dd43 | 173 | /* Call all termination functions at once. */ |
7e036a01 | 174 | for (i = 0; list[i] != NULL; ++i) |
a709dd43 | 175 | { |
2e93b4a4 | 176 | struct link_map *imap = list[i]; |
42c4f32a | 177 | if (new_opencount[i] == 0 && imap->l_type == lt_loaded |
dacc8ffa | 178 | && (imap->l_info[DT_FINI] || imap->l_info[DT_FINI_ARRAY]) |
c77a4478 | 179 | && (! (imap->l_flags_1 & DF_1_NODELETE) || ! imap->l_init_called) |
7a68c94a UD |
180 | /* Skip any half-cooked objects that were never initialized. */ |
181 | && imap->l_init_called) | |
a709dd43 | 182 | { |
b48abe3c | 183 | /* When debugging print a message first. */ |
d6b5d570 | 184 | if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) |
35fc382a | 185 | _dl_debug_printf ("\ncalling fini: %s\n\n", imap->l_name); |
dacc8ffa | 186 | |
b48abe3c | 187 | /* Call its termination function. */ |
dacc8ffa UD |
188 | if (imap->l_info[DT_FINI_ARRAY] != NULL) |
189 | { | |
190 | ElfW(Addr) *array = | |
191 | (ElfW(Addr) *) (imap->l_addr | |
192 | + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr); | |
193 | unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val | |
194 | / sizeof (ElfW(Addr))); | |
195 | unsigned int cnt; | |
196 | ||
197 | for (cnt = 0; cnt < sz; ++cnt) | |
198 | ((fini_t) (imap->l_addr + array[cnt])) (); | |
199 | } | |
200 | ||
201 | /* Next try the old-style destructor. */ | |
202 | if (imap->l_info[DT_FINI] != NULL) | |
40306912 UD |
203 | (*(void (*) (void)) DL_DT_FINI_ADDRESS |
204 | (imap, (void *) imap->l_addr | |
205 | + imap->l_info[DT_FINI]->d_un.d_ptr)) (); | |
a709dd43 | 206 | } |
5a21d307 UD |
207 | else if (new_opencount[i] != 0 && imap->l_type == lt_loaded) |
208 | { | |
209 | /* The object is still used. But the object we are unloading | |
210 | right now is responsible for loading it and therefore we | |
211 | have the search list of the current object in its scope. | |
212 | Remove it. */ | |
213 | struct r_scope_elem **runp = imap->l_scope; | |
214 | ||
215 | while (*runp != NULL) | |
216 | if (*runp == &map->l_searchlist) | |
217 | { | |
218 | /* Copy all later elements. */ | |
219 | while ((runp[0] = runp[1]) != NULL) | |
220 | ++runp; | |
221 | break; | |
222 | } | |
223 | else | |
224 | ++runp; | |
225 | } | |
42c4f32a UD |
226 | |
227 | /* Store the new l_opencount value. */ | |
228 | imap->l_opencount = new_opencount[i]; | |
229 | /* Just a sanity check. */ | |
230 | assert (imap->l_type == lt_loaded || imap->l_opencount > 0); | |
a709dd43 UD |
231 | } |
232 | ||
4d6acc61 RM |
233 | /* Notify the debugger we are about to remove some loaded objects. */ |
234 | _r_debug.r_state = RT_DELETE; | |
235 | _dl_debug_state (); | |
236 | ||
ba79d61b RM |
237 | /* Check each element of the search list to see if all references to |
238 | it are gone. */ | |
7e036a01 | 239 | for (i = 0; list[i] != NULL; ++i) |
ba79d61b | 240 | { |
af69217f UD |
241 | struct link_map *imap = list[i]; |
242 | if (imap->l_opencount == 0 && imap->l_type == lt_loaded) | |
ba79d61b | 243 | { |
a8a1269d UD |
244 | struct libname_list *lnp; |
245 | ||
ba79d61b RM |
246 | /* That was the last reference, and this was a dlopen-loaded |
247 | object. We can unmap it. */ | |
c41c89d3 | 248 | if (__builtin_expect (imap->l_global, 0)) |
ba79d61b RM |
249 | { |
250 | /* This object is in the global scope list. Remove it. */ | |
d6b5d570 | 251 | unsigned int cnt = GL(dl_main_searchlist)->r_nlist; |
be935610 | 252 | |
ba79d61b | 253 | do |
50b65db1 | 254 | --cnt; |
d6b5d570 | 255 | while (GL(dl_main_searchlist)->r_list[cnt] != imap); |
482eec0d | 256 | |
50b65db1 | 257 | /* The object was already correctly registered. */ |
d6b5d570 UD |
258 | while (++cnt < GL(dl_main_searchlist)->r_nlist) |
259 | GL(dl_main_searchlist)->r_list[cnt - 1] | |
260 | = GL(dl_main_searchlist)->r_list[cnt]; | |
50b65db1 | 261 | |
d6b5d570 | 262 | --GL(dl_main_searchlist)->r_nlist; |
ba79d61b RM |
263 | } |
264 | ||
a04586d8 UD |
265 | #ifdef USE_TLS |
266 | /* Remove the object from the dtv slotinfo array if it uses | |
267 | TLS. */ | |
268 | if (__builtin_expect (imap->l_tls_blocksize > 0, 0)) | |
269 | { | |
a04586d8 | 270 | any_tls = true; |
bb4cb252 | 271 | |
fc093be1 UD |
272 | if (! remove_slotinfo (imap->l_tls_modid, |
273 | GL(dl_tls_dtv_slotinfo_list), 0)) | |
274 | /* All dynamically loaded modules with TLS are unloaded. */ | |
275 | GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem); | |
a04586d8 UD |
276 | } |
277 | #endif | |
278 | ||
a8a1269d | 279 | /* We can unmap all the maps at once. We determined the |
4ce636da UD |
280 | start address and length when we loaded the object and |
281 | the `munmap' call does the rest. */ | |
09bf6406 | 282 | DL_UNMAP (imap); |
22bc7978 | 283 | |
ba79d61b | 284 | /* Finally, unlink the data structure and free it. */ |
b5567b2a | 285 | #ifdef SHARED |
7afab53d UD |
286 | /* We will unlink the first object only if this is a statically |
287 | linked program. */ | |
288 | assert (imap->l_prev != NULL); | |
6a805a0b | 289 | imap->l_prev->l_next = imap->l_next; |
7afab53d UD |
290 | #else |
291 | if (imap->l_prev != NULL) | |
af69217f | 292 | imap->l_prev->l_next = imap->l_next; |
7afab53d | 293 | else |
d6b5d570 | 294 | GL(dl_loaded) = imap->l_next; |
7afab53d | 295 | #endif |
d6b5d570 | 296 | --GL(dl_nloaded); |
af69217f UD |
297 | if (imap->l_next) |
298 | imap->l_next->l_prev = imap->l_prev; | |
a8a1269d UD |
299 | |
300 | if (imap->l_versions != NULL) | |
301 | free (imap->l_versions); | |
1c3a6f19 | 302 | if (imap->l_origin != NULL && imap->l_origin != (char *) -1) |
a8a1269d UD |
303 | free ((char *) imap->l_origin); |
304 | ||
4b4fcf99 UD |
305 | /* If the object has relocation dependencies save this |
306 | information for latter. */ | |
307 | if (__builtin_expect (imap->l_reldeps != NULL, 0)) | |
308 | { | |
309 | struct reldep_list *newrel; | |
310 | ||
311 | newrel = (struct reldep_list *) alloca (sizeof (*reldeps)); | |
1826d793 UD |
312 | newrel->rellist = imap->l_reldeps; |
313 | newrel->nrellist = imap->l_reldepsact; | |
4b4fcf99 UD |
314 | newrel->next = reldeps; |
315 | ||
316 | reldeps = newrel; | |
317 | } | |
318 | ||
4ce636da | 319 | /* This name always is allocated. */ |
a8a1269d | 320 | free (imap->l_name); |
4ce636da | 321 | /* Remove the list with all the names of the shared object. */ |
a8a1269d UD |
322 | lnp = imap->l_libname; |
323 | do | |
324 | { | |
76156ea1 | 325 | struct libname_list *this = lnp; |
a8a1269d | 326 | lnp = lnp->next; |
11810621 UD |
327 | if (!this->dont_free) |
328 | free (this); | |
a8a1269d UD |
329 | } |
330 | while (lnp != NULL); | |
a8a1269d | 331 | |
4ce636da | 332 | /* Remove the searchlists. */ |
dacc8ffa | 333 | if (imap != map) |
2e93b4a4 | 334 | free (imap->l_initfini); |
4ce636da | 335 | |
5a21d307 UD |
336 | /* Remove the scope array if we allocated it. */ |
337 | if (imap->l_scope != imap->l_scope_mem) | |
338 | free (imap->l_scope); | |
339 | ||
7bcaca43 | 340 | if (imap->l_phdr_allocated) |
15925412 | 341 | free ((void *) imap->l_phdr); |
7bcaca43 | 342 | |
f55727ca UD |
343 | if (imap->l_rpath_dirs.dirs != (void *) -1) |
344 | free (imap->l_rpath_dirs.dirs); | |
345 | if (imap->l_runpath_dirs.dirs != (void *) -1) | |
346 | free (imap->l_runpath_dirs.dirs); | |
347 | ||
af69217f | 348 | free (imap); |
ba79d61b RM |
349 | } |
350 | } | |
351 | ||
a04586d8 UD |
352 | #ifdef USE_TLS |
353 | /* If we removed any object which uses TLS bumnp the generation | |
354 | counter. */ | |
bb4cb252 | 355 | if (any_tls) |
b209e34a | 356 | if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) |
a585ba22 | 357 | __libc_fatal (_("TLS generation counter wrapped! Please send report with the 'glibcbug' script.")); |
a04586d8 UD |
358 | #endif |
359 | ||
e3e5f672 UD |
360 | /* Notify the debugger those objects are finalized and gone. */ |
361 | _r_debug.r_state = RT_CONSISTENT; | |
362 | _dl_debug_state (); | |
363 | ||
cf197e41 UD |
364 | /* Now we can perhaps also remove the modules for which we had |
365 | dependencies because of symbol lookup. */ | |
4b4fcf99 | 366 | while (__builtin_expect (reldeps != NULL, 0)) |
cf197e41 | 367 | { |
4b4fcf99 UD |
368 | while (reldeps->nrellist-- > 0) |
369 | _dl_close (reldeps->rellist[reldeps->nrellist]); | |
370 | ||
371 | free (reldeps->rellist); | |
cf197e41 | 372 | |
4b4fcf99 | 373 | reldeps = reldeps->next; |
cf197e41 UD |
374 | } |
375 | ||
ba79d61b | 376 | free (list); |
4d6acc61 | 377 | |
e3e5f672 | 378 | /* Release the lock. */ |
5688da55 | 379 | __libc_lock_unlock_recursive (GL(dl_load_lock)); |
e3e5f672 | 380 | } |
cc7e7a26 | 381 | libc_hidden_def (_dl_close) |
e3e5f672 UD |
382 | |
383 | ||
100e184f | 384 | #ifdef USE_TLS |
a04586d8 UD |
385 | static bool |
386 | free_slotinfo (struct dtv_slotinfo_list *elemp) | |
387 | { | |
388 | size_t cnt; | |
389 | ||
69f0c4d8 UD |
390 | if (elemp == NULL) |
391 | /* Nothing here, all is removed (or there never was anything). */ | |
392 | return true; | |
393 | ||
394 | if (!free_slotinfo (elemp->next)) | |
a04586d8 UD |
395 | /* We cannot free the entry. */ |
396 | return false; | |
397 | ||
398 | /* The least we could do is remove next element (if there was any). */ | |
399 | elemp->next = NULL; | |
400 | ||
401 | for (cnt = 0; cnt < elemp->len; ++cnt) | |
402 | if (elemp->slotinfo[cnt].map != NULL) | |
403 | /* Still used. */ | |
404 | return false; | |
405 | ||
406 | /* We can remove the list element. */ | |
407 | free (elemp); | |
408 | ||
409 | return true; | |
410 | } | |
100e184f | 411 | #endif |
a04586d8 UD |
412 | |
413 | ||
e3e5f672 UD |
414 | static void |
415 | free_mem (void) | |
416 | { | |
d6b5d570 UD |
417 | if (__builtin_expect (GL(dl_global_scope_alloc), 0) != 0 |
418 | && GL(dl_main_searchlist)->r_nlist == GL(dl_initial_searchlist).r_nlist) | |
d3556ac9 UD |
419 | { |
420 | /* All object dynamically loaded by the program are unloaded. Free | |
421 | the memory allocated for the global scope variable. */ | |
d6b5d570 | 422 | struct link_map **old = GL(dl_main_searchlist)->r_list; |
d3556ac9 UD |
423 | |
424 | /* Put the old map in. */ | |
d6b5d570 | 425 | GL(dl_main_searchlist)->r_list = GL(dl_initial_searchlist).r_list; |
d3556ac9 | 426 | /* Signal that the original map is used. */ |
d6b5d570 | 427 | GL(dl_global_scope_alloc) = 0; |
d3556ac9 UD |
428 | |
429 | /* Now free the old map. */ | |
430 | free (old); | |
431 | } | |
a04586d8 UD |
432 | |
433 | #ifdef USE_TLS | |
434 | /* Free the memory allocated for the dtv slotinfo array. We can do | |
435 | this only if all modules which used this memory are unloaded. | |
436 | Also, the first element of the list does not have to be | |
437 | deallocated. It was allocated in the dynamic linker (i.e., with | |
438 | a different malloc). */ | |
439 | if (free_slotinfo (GL(dl_tls_dtv_slotinfo_list)->next)) | |
440 | GL(dl_tls_dtv_slotinfo_list)->next = NULL; | |
441 | #endif | |
ba79d61b | 442 | } |
e3e5f672 | 443 | text_set_element (__libc_subfreeres, free_mem); |