]>
Commit | Line | Data |
---|---|---|
26b4d766 | 1 | /* Close a shared object opened by `_dl_open'. |
aff4519d | 2 | Copyright (C) 1996-2002, 2003 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 | |
1f0c4a10 RM |
39 | remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, |
40 | bool should_be_there) | |
fc093be1 UD |
41 | { |
42 | if (idx - disp >= listp->len) | |
43 | { | |
1f0c4a10 RM |
44 | if (listp->next == NULL) |
45 | { | |
46 | /* The index is not actually valid in the slotinfo list, | |
8265947d RM |
47 | because this object was closed before it was fully set |
48 | up due to some error. */ | |
1f0c4a10 RM |
49 | assert (! should_be_there); |
50 | } | |
51 | else | |
52 | { | |
53 | if (remove_slotinfo (idx, listp->next, disp + listp->len, | |
54 | should_be_there)) | |
55 | return true; | |
fc093be1 | 56 | |
1f0c4a10 RM |
57 | /* No non-empty entry. Search from the end of this element's |
58 | slotinfo array. */ | |
59 | idx = disp + listp->len; | |
60 | } | |
fc093be1 UD |
61 | } |
62 | else | |
63 | { | |
64 | struct link_map *old_map = listp->slotinfo[idx - disp].map; | |
fc093be1 | 65 | |
2430d57a RM |
66 | /* The entry might still be in its unused state if we are closing an |
67 | object that wasn't fully set up. */ | |
68 | if (__builtin_expect (old_map != NULL, 1)) | |
69 | { | |
70 | assert (old_map->l_tls_modid == idx); | |
71 | ||
72 | /* Mark the entry as unused. */ | |
73 | listp->slotinfo[idx - disp].gen = GL(dl_tls_generation) + 1; | |
74 | listp->slotinfo[idx - disp].map = NULL; | |
75 | } | |
fc093be1 UD |
76 | |
77 | /* If this is not the last currently used entry no need to look | |
78 | further. */ | |
2430d57a | 79 | if (idx != GL(dl_tls_max_dtv_idx)) |
fc093be1 | 80 | return true; |
fc093be1 UD |
81 | } |
82 | ||
83 | while (idx - disp > disp == 0 ? 1 + GL(dl_tls_static_nelem) : 0) | |
84 | { | |
85 | --idx; | |
86 | ||
87 | if (listp->slotinfo[idx - disp].map != NULL) | |
88 | { | |
89 | /* Found a new last used index. */ | |
90 | GL(dl_tls_max_dtv_idx) = idx; | |
91 | return true; | |
92 | } | |
93 | } | |
94 | ||
95 | /* No non-entry in this list element. */ | |
96 | return false; | |
97 | } | |
98 | #endif | |
99 | ||
100 | ||
ba79d61b | 101 | void |
d0fc4041 | 102 | internal_function |
94e365c6 | 103 | _dl_close (void *_map) |
ba79d61b | 104 | { |
4b4fcf99 UD |
105 | struct reldep_list |
106 | { | |
107 | struct link_map **rellist; | |
108 | unsigned int nrellist; | |
556224ab | 109 | unsigned int nhandled; |
4b4fcf99 | 110 | struct reldep_list *next; |
556224ab | 111 | bool handled[0]; |
4b4fcf99 | 112 | } *reldeps = NULL; |
ba79d61b | 113 | struct link_map **list; |
94e365c6 | 114 | struct link_map *map = _map; |
ba79d61b | 115 | unsigned int i; |
42c4f32a | 116 | unsigned int *new_opencount; |
a04586d8 UD |
117 | #ifdef USE_TLS |
118 | bool any_tls = false; | |
119 | #endif | |
ba79d61b | 120 | |
bf8b3e74 | 121 | /* First see whether we can remove the object at all. */ |
c4bb124a UD |
122 | if (__builtin_expect (map->l_flags_1 & DF_1_NODELETE, 0) |
123 | && map->l_init_called) | |
bf8b3e74 UD |
124 | /* Nope. Do nothing. */ |
125 | return; | |
126 | ||
c41c89d3 | 127 | if (__builtin_expect (map->l_opencount, 1) == 0) |
407fe3bb | 128 | _dl_signal_error (0, map->l_name, NULL, N_("shared object not open")); |
ba79d61b | 129 | |
26b4d766 | 130 | /* Acquire the lock. */ |
d3c9f895 | 131 | __rtld_lock_lock_recursive (GL(dl_load_lock)); |
26b4d766 | 132 | |
ba79d61b | 133 | /* Decrement the reference count. */ |
a709dd43 | 134 | if (map->l_opencount > 1 || map->l_type != lt_loaded) |
26b4d766 UD |
135 | { |
136 | /* There are still references to this object. Do nothing more. */ | |
d6b5d570 | 137 | if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_FILES, 0)) |
c4bb124a UD |
138 | _dl_debug_printf ("\nclosing file=%s; opencount == %u\n", |
139 | map->l_name, map->l_opencount); | |
b35e21f4 | 140 | |
aff4519d | 141 | /* Decrement the object's reference counter, not the dependencies'. */ |
42c4f32a | 142 | --map->l_opencount; |
8699e7b1 | 143 | |
d3c9f895 | 144 | __rtld_lock_unlock_recursive (GL(dl_load_lock)); |
26b4d766 UD |
145 | return; |
146 | } | |
ba79d61b | 147 | |
2e93b4a4 | 148 | list = map->l_initfini; |
42c4f32a UD |
149 | |
150 | /* Compute the new l_opencount values. */ | |
c4bb124a UD |
151 | i = map->l_searchlist.r_nlist; |
152 | if (__builtin_expect (i == 0, 0)) | |
153 | /* This can happen if we handle relocation dependencies for an | |
154 | object which wasn't loaded directly. */ | |
155 | for (i = 1; list[i] != NULL; ++i) | |
156 | ; | |
157 | ||
556224ab | 158 | unsigned int nopencount = i; |
c4bb124a UD |
159 | new_opencount = (unsigned int *) alloca (i * sizeof (unsigned int)); |
160 | ||
7e036a01 | 161 | for (i = 0; list[i] != NULL; ++i) |
42c4f32a | 162 | { |
2e93b4a4 UD |
163 | list[i]->l_idx = i; |
164 | new_opencount[i] = list[i]->l_opencount; | |
42c4f32a UD |
165 | } |
166 | --new_opencount[0]; | |
7e036a01 | 167 | for (i = 1; list[i] != NULL; ++i) |
aff4519d | 168 | if ((list[i]->l_flags_1 & DF_1_NODELETE) == 0 |
42c4f32a | 169 | /* Decrement counter. */ |
556224ab | 170 | && --new_opencount[i] == 0) |
42c4f32a | 171 | { |
556224ab UD |
172 | void mark_removed (struct link_map *remmap) |
173 | { | |
174 | /* Test whether this object was also loaded directly. */ | |
175 | if (remmap->l_searchlist.r_list != NULL) | |
176 | { | |
177 | /* In this case we have to decrement all the dependencies of | |
178 | this object. They are all in MAP's dependency list. */ | |
179 | unsigned int j; | |
180 | struct link_map **dep_list = remmap->l_searchlist.r_list; | |
181 | ||
182 | for (j = 1; j < remmap->l_searchlist.r_nlist; ++j) | |
183 | if (! (dep_list[j]->l_flags_1 & DF_1_NODELETE) | |
184 | || ! dep_list[j]->l_init_called) | |
185 | { | |
186 | assert (dep_list[j]->l_idx < map->l_searchlist.r_nlist); | |
187 | if (--new_opencount[dep_list[j]->l_idx] == 0) | |
aff4519d UD |
188 | { |
189 | assert (dep_list[j]->l_type == lt_loaded); | |
190 | mark_removed (dep_list[j]); | |
191 | } | |
556224ab UD |
192 | } |
193 | } | |
194 | ||
195 | if (remmap->l_reldeps != NULL) | |
196 | { | |
197 | unsigned int j; | |
198 | for (j = 0; j < remmap->l_reldepsact; ++j) | |
199 | { | |
200 | /* Find out whether this object is in our list. */ | |
201 | if (remmap->l_reldeps[j]->l_idx < nopencount | |
202 | && (list[remmap->l_reldeps[j]->l_idx] | |
203 | == remmap->l_reldeps[j])) | |
204 | /* Yes, it is. */ | |
205 | if (--new_opencount[remmap->l_reldeps[j]->l_idx] == 0) | |
aff4519d UD |
206 | { |
207 | /* This one is now gone, too. */ | |
208 | assert (remmap->l_reldeps[j]->l_type == lt_loaded); | |
209 | mark_removed (remmap->l_reldeps[j]); | |
210 | } | |
556224ab UD |
211 | } |
212 | } | |
213 | } | |
214 | ||
215 | mark_removed (list[i]); | |
42c4f32a UD |
216 | } |
217 | assert (new_opencount[0] == 0); | |
218 | ||
a709dd43 | 219 | /* Call all termination functions at once. */ |
7e036a01 | 220 | for (i = 0; list[i] != NULL; ++i) |
a709dd43 | 221 | { |
2e93b4a4 | 222 | struct link_map *imap = list[i]; |
42c4f32a | 223 | if (new_opencount[i] == 0 && imap->l_type == lt_loaded |
aff4519d | 224 | && (imap->l_flags_1 & DF_1_NODELETE) == 0) |
a709dd43 | 225 | { |
b48abe3c | 226 | /* When debugging print a message first. */ |
d6b5d570 | 227 | if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) |
35fc382a | 228 | _dl_debug_printf ("\ncalling fini: %s\n\n", imap->l_name); |
dacc8ffa | 229 | |
aff4519d UD |
230 | /* Call its termination function. Do not do it for |
231 | half-cooked objects. */ | |
232 | if (imap->l_init_called) | |
dacc8ffa | 233 | { |
aff4519d UD |
234 | if (imap->l_info[DT_FINI_ARRAY] != NULL) |
235 | { | |
236 | ElfW(Addr) *array = | |
237 | (ElfW(Addr) *) (imap->l_addr | |
238 | + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr); | |
239 | unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val | |
240 | / sizeof (ElfW(Addr))); | |
aff4519d | 241 | |
62f29da7 UD |
242 | while (sz-- > 0) |
243 | ((fini_t) array[sz]) (); | |
aff4519d UD |
244 | } |
245 | ||
246 | /* Next try the old-style destructor. */ | |
247 | if (imap->l_info[DT_FINI] != NULL) | |
248 | (*(void (*) (void)) DL_DT_FINI_ADDRESS | |
249 | (imap, ((void *) imap->l_addr | |
250 | + imap->l_info[DT_FINI]->d_un.d_ptr))) (); | |
dacc8ffa UD |
251 | } |
252 | ||
aff4519d UD |
253 | /* This object must not be used anymore. We must remove the |
254 | reference from the scope. */ | |
255 | unsigned int j; | |
256 | struct link_map **searchlist = map->l_searchlist.r_list; | |
257 | unsigned int nsearchlist = map->l_searchlist.r_nlist; | |
258 | ||
259 | #ifndef NDEBUG | |
260 | bool found = false; | |
261 | #endif | |
262 | for (j = 0; j < nsearchlist; ++j) | |
263 | if (imap == searchlist[j]) | |
264 | { | |
265 | /* This is the object to remove. Copy all the | |
266 | following ones. */ | |
267 | while (++j < nsearchlist) | |
268 | searchlist[j - 1] = searchlist[j]; | |
269 | ||
270 | searchlist[j - 1] = NULL; | |
271 | ||
272 | --map->l_searchlist.r_nlist; | |
273 | ||
274 | #ifndef NDEBUG | |
275 | found = true; | |
276 | #endif | |
277 | break; | |
278 | } | |
279 | assert (found); | |
a709dd43 | 280 | } |
aff4519d UD |
281 | else if (new_opencount[i] != 0 && imap->l_type == lt_loaded |
282 | && imap->l_searchlist.r_list == NULL | |
283 | && imap->l_initfini != NULL) | |
5a21d307 | 284 | { |
aff4519d UD |
285 | /* The object is still used. But the object we are |
286 | unloading right now is responsible for loading it. If | |
287 | the current object does not have it's own scope yet we | |
288 | have to create one. This has to be done before running | |
289 | the finalizers. | |
290 | ||
291 | To do this count the number of dependencies. */ | |
292 | unsigned int cnt; | |
293 | for (cnt = 1; imap->l_initfini[cnt] != NULL; ++cnt) | |
294 | if (imap->l_initfini[cnt]->l_idx >= i | |
295 | && imap->l_initfini[cnt]->l_idx < nopencount) | |
296 | ++new_opencount[imap->l_initfini[cnt]->l_idx]; | |
297 | else | |
298 | ++imap->l_initfini[cnt]->l_opencount; | |
299 | ||
300 | /* We simply reuse the l_initfini list. */ | |
301 | imap->l_searchlist.r_list = &imap->l_initfini[cnt + 1]; | |
302 | imap->l_searchlist.r_nlist = cnt; | |
303 | ||
304 | for (cnt = 0; imap->l_scope[cnt] != NULL; ++cnt) | |
1376cd8c | 305 | if (imap->l_scope[cnt] == &map->l_searchlist) |
5a21d307 | 306 | { |
aff4519d | 307 | imap->l_scope[cnt] = &imap->l_searchlist; |
5a21d307 UD |
308 | break; |
309 | } | |
5a21d307 | 310 | } |
42c4f32a UD |
311 | |
312 | /* Store the new l_opencount value. */ | |
313 | imap->l_opencount = new_opencount[i]; | |
aff4519d | 314 | |
42c4f32a UD |
315 | /* Just a sanity check. */ |
316 | assert (imap->l_type == lt_loaded || imap->l_opencount > 0); | |
a709dd43 UD |
317 | } |
318 | ||
4d6acc61 RM |
319 | /* Notify the debugger we are about to remove some loaded objects. */ |
320 | _r_debug.r_state = RT_DELETE; | |
321 | _dl_debug_state (); | |
322 | ||
c877418f RM |
323 | #ifdef USE_TLS |
324 | size_t tls_free_start, tls_free_end; | |
325 | tls_free_start = tls_free_end = GL(dl_tls_static_used); | |
326 | #endif | |
327 | ||
ba79d61b RM |
328 | /* Check each element of the search list to see if all references to |
329 | it are gone. */ | |
7e036a01 | 330 | for (i = 0; list[i] != NULL; ++i) |
ba79d61b | 331 | { |
af69217f UD |
332 | struct link_map *imap = list[i]; |
333 | if (imap->l_opencount == 0 && imap->l_type == lt_loaded) | |
ba79d61b | 334 | { |
a8a1269d UD |
335 | struct libname_list *lnp; |
336 | ||
ba79d61b RM |
337 | /* That was the last reference, and this was a dlopen-loaded |
338 | object. We can unmap it. */ | |
c41c89d3 | 339 | if (__builtin_expect (imap->l_global, 0)) |
ba79d61b RM |
340 | { |
341 | /* This object is in the global scope list. Remove it. */ | |
d6b5d570 | 342 | unsigned int cnt = GL(dl_main_searchlist)->r_nlist; |
be935610 | 343 | |
ba79d61b | 344 | do |
50b65db1 | 345 | --cnt; |
d6b5d570 | 346 | while (GL(dl_main_searchlist)->r_list[cnt] != imap); |
482eec0d | 347 | |
50b65db1 | 348 | /* The object was already correctly registered. */ |
d6b5d570 UD |
349 | while (++cnt < GL(dl_main_searchlist)->r_nlist) |
350 | GL(dl_main_searchlist)->r_list[cnt - 1] | |
351 | = GL(dl_main_searchlist)->r_list[cnt]; | |
50b65db1 | 352 | |
d6b5d570 | 353 | --GL(dl_main_searchlist)->r_nlist; |
ba79d61b RM |
354 | } |
355 | ||
a04586d8 | 356 | #ifdef USE_TLS |
1f0c4a10 | 357 | /* Remove the object from the dtv slotinfo array if it uses TLS. */ |
a04586d8 UD |
358 | if (__builtin_expect (imap->l_tls_blocksize > 0, 0)) |
359 | { | |
a04586d8 | 360 | any_tls = true; |
bb4cb252 | 361 | |
fc093be1 | 362 | if (! remove_slotinfo (imap->l_tls_modid, |
1f0c4a10 RM |
363 | GL(dl_tls_dtv_slotinfo_list), 0, |
364 | imap->l_init_called)) | |
fc093be1 UD |
365 | /* All dynamically loaded modules with TLS are unloaded. */ |
366 | GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem); | |
c877418f RM |
367 | |
368 | if (imap->l_tls_offset != 0) | |
369 | { | |
370 | /* Collect a contiguous chunk built from the objects in | |
371 | this search list, going in either direction. When the | |
372 | whole chunk is at the end of the used area then we can | |
373 | reclaim it. */ | |
374 | if (imap->l_tls_offset == tls_free_end) | |
375 | /* Extend the contiguous chunk being reclaimed. */ | |
376 | tls_free_end += imap->l_tls_blocksize; | |
377 | else if (imap->l_tls_offset + imap->l_tls_blocksize | |
378 | == tls_free_start) | |
379 | /* Extend the chunk backwards. */ | |
380 | tls_free_start = imap->l_tls_offset; | |
381 | else | |
382 | { | |
383 | /* This isn't contiguous with the last chunk freed. | |
384 | One of them will be leaked. */ | |
385 | if (tls_free_end == GL(dl_tls_static_used)) | |
386 | GL(dl_tls_static_used) = tls_free_start; | |
387 | tls_free_start = imap->l_tls_offset; | |
388 | tls_free_end = tls_free_start + imap->l_tls_blocksize; | |
389 | } | |
390 | } | |
a04586d8 UD |
391 | } |
392 | #endif | |
393 | ||
a8a1269d | 394 | /* We can unmap all the maps at once. We determined the |
4ce636da UD |
395 | start address and length when we loaded the object and |
396 | the `munmap' call does the rest. */ | |
09bf6406 | 397 | DL_UNMAP (imap); |
22bc7978 | 398 | |
ba79d61b | 399 | /* Finally, unlink the data structure and free it. */ |
b5567b2a | 400 | #ifdef SHARED |
7afab53d UD |
401 | /* We will unlink the first object only if this is a statically |
402 | linked program. */ | |
403 | assert (imap->l_prev != NULL); | |
6a805a0b | 404 | imap->l_prev->l_next = imap->l_next; |
7afab53d UD |
405 | #else |
406 | if (imap->l_prev != NULL) | |
af69217f | 407 | imap->l_prev->l_next = imap->l_next; |
7afab53d | 408 | else |
d6b5d570 | 409 | GL(dl_loaded) = imap->l_next; |
7afab53d | 410 | #endif |
d6b5d570 | 411 | --GL(dl_nloaded); |
af69217f UD |
412 | if (imap->l_next) |
413 | imap->l_next->l_prev = imap->l_prev; | |
a8a1269d | 414 | |
556224ab UD |
415 | free (imap->l_versions); |
416 | if (imap->l_origin != (char *) -1) | |
a8a1269d UD |
417 | free ((char *) imap->l_origin); |
418 | ||
4b4fcf99 UD |
419 | /* If the object has relocation dependencies save this |
420 | information for latter. */ | |
421 | if (__builtin_expect (imap->l_reldeps != NULL, 0)) | |
422 | { | |
423 | struct reldep_list *newrel; | |
424 | ||
556224ab UD |
425 | newrel = (struct reldep_list *) alloca (sizeof (*reldeps) |
426 | + (imap->l_reldepsact | |
427 | * sizeof (bool))); | |
1826d793 UD |
428 | newrel->rellist = imap->l_reldeps; |
429 | newrel->nrellist = imap->l_reldepsact; | |
4b4fcf99 UD |
430 | newrel->next = reldeps; |
431 | ||
556224ab UD |
432 | newrel->nhandled = imap->l_reldepsact; |
433 | unsigned int j; | |
434 | for (j = 0; j < imap->l_reldepsact; ++j) | |
435 | { | |
436 | /* Find out whether this object is in our list. */ | |
437 | if (imap->l_reldeps[j]->l_idx < nopencount | |
438 | && list[imap->l_reldeps[j]->l_idx] == imap->l_reldeps[j]) | |
439 | /* Yes, it is. */ | |
440 | newrel->handled[j] = true; | |
441 | else | |
442 | newrel->handled[j] = false; | |
443 | } | |
444 | ||
4b4fcf99 UD |
445 | reldeps = newrel; |
446 | } | |
447 | ||
4ce636da | 448 | /* This name always is allocated. */ |
a8a1269d | 449 | free (imap->l_name); |
4ce636da | 450 | /* Remove the list with all the names of the shared object. */ |
a8a1269d UD |
451 | lnp = imap->l_libname; |
452 | do | |
453 | { | |
76156ea1 | 454 | struct libname_list *this = lnp; |
a8a1269d | 455 | lnp = lnp->next; |
11810621 UD |
456 | if (!this->dont_free) |
457 | free (this); | |
a8a1269d UD |
458 | } |
459 | while (lnp != NULL); | |
a8a1269d | 460 | |
4ce636da | 461 | /* Remove the searchlists. */ |
dacc8ffa | 462 | if (imap != map) |
556224ab | 463 | free (imap->l_initfini); |
4ce636da | 464 | |
5a21d307 UD |
465 | /* Remove the scope array if we allocated it. */ |
466 | if (imap->l_scope != imap->l_scope_mem) | |
467 | free (imap->l_scope); | |
468 | ||
7bcaca43 | 469 | if (imap->l_phdr_allocated) |
15925412 | 470 | free ((void *) imap->l_phdr); |
7bcaca43 | 471 | |
f55727ca UD |
472 | if (imap->l_rpath_dirs.dirs != (void *) -1) |
473 | free (imap->l_rpath_dirs.dirs); | |
474 | if (imap->l_runpath_dirs.dirs != (void *) -1) | |
475 | free (imap->l_runpath_dirs.dirs); | |
476 | ||
af69217f | 477 | free (imap); |
ba79d61b RM |
478 | } |
479 | } | |
480 | ||
a04586d8 | 481 | #ifdef USE_TLS |
c877418f | 482 | /* If we removed any object which uses TLS bump the generation counter. */ |
bb4cb252 | 483 | if (any_tls) |
c877418f RM |
484 | { |
485 | if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) | |
486 | __libc_fatal (_("TLS generation counter wrapped! Please send report with the 'glibcbug' script.")); | |
487 | ||
488 | if (tls_free_end == GL(dl_tls_static_used)) | |
489 | GL(dl_tls_static_used) = tls_free_start; | |
490 | } | |
a04586d8 UD |
491 | #endif |
492 | ||
e3e5f672 UD |
493 | /* Notify the debugger those objects are finalized and gone. */ |
494 | _r_debug.r_state = RT_CONSISTENT; | |
495 | _dl_debug_state (); | |
496 | ||
cf197e41 UD |
497 | /* Now we can perhaps also remove the modules for which we had |
498 | dependencies because of symbol lookup. */ | |
4b4fcf99 | 499 | while (__builtin_expect (reldeps != NULL, 0)) |
cf197e41 | 500 | { |
4b4fcf99 | 501 | while (reldeps->nrellist-- > 0) |
556224ab UD |
502 | /* Some of the relocation dependencies might be on the |
503 | dependency list of the object we are closing right now. | |
504 | They were already handled. Do not close them again. */ | |
505 | if (reldeps->nrellist < reldeps->nhandled | |
506 | && ! reldeps->handled[reldeps->nrellist]) | |
507 | _dl_close (reldeps->rellist[reldeps->nrellist]); | |
4b4fcf99 UD |
508 | |
509 | free (reldeps->rellist); | |
cf197e41 | 510 | |
4b4fcf99 | 511 | reldeps = reldeps->next; |
cf197e41 UD |
512 | } |
513 | ||
ba79d61b | 514 | free (list); |
4d6acc61 | 515 | |
e3e5f672 | 516 | /* Release the lock. */ |
d3c9f895 | 517 | __rtld_lock_unlock_recursive (GL(dl_load_lock)); |
e3e5f672 | 518 | } |
cc7e7a26 | 519 | libc_hidden_def (_dl_close) |
e3e5f672 UD |
520 | |
521 | ||
100e184f | 522 | #ifdef USE_TLS |
a04586d8 | 523 | static bool |
216455bc | 524 | free_slotinfo (struct dtv_slotinfo_list **elemp) |
a04586d8 UD |
525 | { |
526 | size_t cnt; | |
527 | ||
216455bc | 528 | if (*elemp == NULL) |
69f0c4d8 UD |
529 | /* Nothing here, all is removed (or there never was anything). */ |
530 | return true; | |
531 | ||
216455bc | 532 | if (!free_slotinfo (&(*elemp)->next)) |
a04586d8 UD |
533 | /* We cannot free the entry. */ |
534 | return false; | |
535 | ||
216455bc | 536 | /* That cleared our next pointer for us. */ |
a04586d8 | 537 | |
216455bc RM |
538 | for (cnt = 0; cnt < (*elemp)->len; ++cnt) |
539 | if ((*elemp)->slotinfo[cnt].map != NULL) | |
a04586d8 UD |
540 | /* Still used. */ |
541 | return false; | |
542 | ||
543 | /* We can remove the list element. */ | |
216455bc RM |
544 | free (*elemp); |
545 | *elemp = NULL; | |
a04586d8 UD |
546 | |
547 | return true; | |
548 | } | |
100e184f | 549 | #endif |
a04586d8 UD |
550 | |
551 | ||
c877418f | 552 | libc_freeres_fn (free_mem) |
e3e5f672 | 553 | { |
d6b5d570 UD |
554 | if (__builtin_expect (GL(dl_global_scope_alloc), 0) != 0 |
555 | && GL(dl_main_searchlist)->r_nlist == GL(dl_initial_searchlist).r_nlist) | |
d3556ac9 UD |
556 | { |
557 | /* All object dynamically loaded by the program are unloaded. Free | |
558 | the memory allocated for the global scope variable. */ | |
d6b5d570 | 559 | struct link_map **old = GL(dl_main_searchlist)->r_list; |
d3556ac9 UD |
560 | |
561 | /* Put the old map in. */ | |
d6b5d570 | 562 | GL(dl_main_searchlist)->r_list = GL(dl_initial_searchlist).r_list; |
d3556ac9 | 563 | /* Signal that the original map is used. */ |
d6b5d570 | 564 | GL(dl_global_scope_alloc) = 0; |
d3556ac9 UD |
565 | |
566 | /* Now free the old map. */ | |
567 | free (old); | |
568 | } | |
a04586d8 UD |
569 | |
570 | #ifdef USE_TLS | |
2d148689 RM |
571 | if (USE___THREAD || GL(dl_tls_dtv_slotinfo_list) != NULL) |
572 | { | |
573 | /* Free the memory allocated for the dtv slotinfo array. We can do | |
216455bc RM |
574 | this only if all modules which used this memory are unloaded. */ |
575 | # ifdef SHARED | |
576 | if (GL(dl_initial_dtv) == NULL) | |
577 | /* There was no initial TLS setup, it was set up later when | |
578 | it used the normal malloc. */ | |
579 | free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)); | |
580 | # endif | |
581 | /* The first element of the list does not have to be deallocated. | |
582 | It was allocated in the dynamic linker (i.e., with a different | |
583 | malloc), and in the static library it's in .bss space. */ | |
584 | free_slotinfo (&GL(dl_tls_dtv_slotinfo_list)->next); | |
2d148689 | 585 | } |
a04586d8 | 586 | #endif |
ba79d61b | 587 | } |