]>
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> |
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 | ||
30 | ||
dacc8ffa UD |
31 | /* Type of the constructor functions. */ |
32 | typedef void (*fini_t) (void); | |
33 | ||
34 | ||
ba79d61b | 35 | void |
d0fc4041 | 36 | internal_function |
94e365c6 | 37 | _dl_close (void *_map) |
ba79d61b | 38 | { |
4b4fcf99 UD |
39 | struct reldep_list |
40 | { | |
41 | struct link_map **rellist; | |
42 | unsigned int nrellist; | |
43 | struct reldep_list *next; | |
44 | } *reldeps = NULL; | |
ba79d61b | 45 | struct link_map **list; |
94e365c6 | 46 | struct link_map *map = _map; |
ba79d61b | 47 | unsigned int i; |
42c4f32a | 48 | unsigned int *new_opencount; |
ba79d61b | 49 | |
bf8b3e74 | 50 | /* First see whether we can remove the object at all. */ |
c4bb124a UD |
51 | if (__builtin_expect (map->l_flags_1 & DF_1_NODELETE, 0) |
52 | && map->l_init_called) | |
bf8b3e74 UD |
53 | /* Nope. Do nothing. */ |
54 | return; | |
55 | ||
c41c89d3 | 56 | if (__builtin_expect (map->l_opencount, 1) == 0) |
407fe3bb | 57 | _dl_signal_error (0, map->l_name, NULL, N_("shared object not open")); |
ba79d61b | 58 | |
26b4d766 | 59 | /* Acquire the lock. */ |
c12aa801 | 60 | __libc_lock_lock_recursive (_dl_load_lock); |
26b4d766 | 61 | |
ba79d61b | 62 | /* Decrement the reference count. */ |
a709dd43 | 63 | if (map->l_opencount > 1 || map->l_type != lt_loaded) |
26b4d766 UD |
64 | { |
65 | /* There are still references to this object. Do nothing more. */ | |
d6b5d570 | 66 | if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_FILES, 0)) |
c4bb124a UD |
67 | _dl_debug_printf ("\nclosing file=%s; opencount == %u\n", |
68 | map->l_name, map->l_opencount); | |
b35e21f4 | 69 | |
42c4f32a UD |
70 | /* One decrement the object itself, not the dependencies. */ |
71 | --map->l_opencount; | |
8699e7b1 | 72 | |
c12aa801 | 73 | __libc_lock_unlock_recursive (_dl_load_lock); |
26b4d766 UD |
74 | return; |
75 | } | |
ba79d61b | 76 | |
2e93b4a4 | 77 | list = map->l_initfini; |
42c4f32a UD |
78 | |
79 | /* Compute the new l_opencount values. */ | |
c4bb124a UD |
80 | i = map->l_searchlist.r_nlist; |
81 | if (__builtin_expect (i == 0, 0)) | |
82 | /* This can happen if we handle relocation dependencies for an | |
83 | object which wasn't loaded directly. */ | |
84 | for (i = 1; list[i] != NULL; ++i) | |
85 | ; | |
86 | ||
87 | new_opencount = (unsigned int *) alloca (i * sizeof (unsigned int)); | |
88 | ||
7e036a01 | 89 | for (i = 0; list[i] != NULL; ++i) |
42c4f32a | 90 | { |
2e93b4a4 UD |
91 | list[i]->l_idx = i; |
92 | new_opencount[i] = list[i]->l_opencount; | |
42c4f32a UD |
93 | } |
94 | --new_opencount[0]; | |
7e036a01 | 95 | for (i = 1; list[i] != NULL; ++i) |
c77a4478 | 96 | if ((! (list[i]->l_flags_1 & DF_1_NODELETE) || ! list[i]->l_init_called) |
42c4f32a UD |
97 | /* Decrement counter. */ |
98 | && --new_opencount[i] == 0 | |
99 | /* Test whether this object was also loaded directly. */ | |
2e93b4a4 | 100 | && list[i]->l_searchlist.r_list != NULL) |
42c4f32a UD |
101 | { |
102 | /* In this case we have the decrement all the dependencies of | |
103 | this object. They are all in MAP's dependency list. */ | |
104 | unsigned int j; | |
2e93b4a4 | 105 | struct link_map **dep_list = list[i]->l_searchlist.r_list; |
42c4f32a | 106 | |
2e93b4a4 | 107 | for (j = 1; j < list[i]->l_searchlist.r_nlist; ++j) |
c77a4478 UD |
108 | if (! (dep_list[j]->l_flags_1 & DF_1_NODELETE) |
109 | || ! dep_list[j]->l_init_called) | |
42c4f32a | 110 | { |
7e036a01 | 111 | assert (dep_list[j]->l_idx < map->l_searchlist.r_nlist); |
42c4f32a UD |
112 | --new_opencount[dep_list[j]->l_idx]; |
113 | } | |
114 | } | |
115 | assert (new_opencount[0] == 0); | |
116 | ||
a709dd43 | 117 | /* Call all termination functions at once. */ |
7e036a01 | 118 | for (i = 0; list[i] != NULL; ++i) |
a709dd43 | 119 | { |
2e93b4a4 | 120 | struct link_map *imap = list[i]; |
42c4f32a | 121 | if (new_opencount[i] == 0 && imap->l_type == lt_loaded |
dacc8ffa | 122 | && (imap->l_info[DT_FINI] || imap->l_info[DT_FINI_ARRAY]) |
c77a4478 | 123 | && (! (imap->l_flags_1 & DF_1_NODELETE) || ! imap->l_init_called) |
7a68c94a UD |
124 | /* Skip any half-cooked objects that were never initialized. */ |
125 | && imap->l_init_called) | |
a709dd43 | 126 | { |
b48abe3c | 127 | /* When debugging print a message first. */ |
d6b5d570 | 128 | if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) |
35fc382a | 129 | _dl_debug_printf ("\ncalling fini: %s\n\n", imap->l_name); |
dacc8ffa | 130 | |
b48abe3c | 131 | /* Call its termination function. */ |
dacc8ffa UD |
132 | if (imap->l_info[DT_FINI_ARRAY] != NULL) |
133 | { | |
134 | ElfW(Addr) *array = | |
135 | (ElfW(Addr) *) (imap->l_addr | |
136 | + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr); | |
137 | unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val | |
138 | / sizeof (ElfW(Addr))); | |
139 | unsigned int cnt; | |
140 | ||
141 | for (cnt = 0; cnt < sz; ++cnt) | |
142 | ((fini_t) (imap->l_addr + array[cnt])) (); | |
143 | } | |
144 | ||
145 | /* Next try the old-style destructor. */ | |
146 | if (imap->l_info[DT_FINI] != NULL) | |
40306912 UD |
147 | (*(void (*) (void)) DL_DT_FINI_ADDRESS |
148 | (imap, (void *) imap->l_addr | |
149 | + imap->l_info[DT_FINI]->d_un.d_ptr)) (); | |
a709dd43 | 150 | } |
5a21d307 UD |
151 | else if (new_opencount[i] != 0 && imap->l_type == lt_loaded) |
152 | { | |
153 | /* The object is still used. But the object we are unloading | |
154 | right now is responsible for loading it and therefore we | |
155 | have the search list of the current object in its scope. | |
156 | Remove it. */ | |
157 | struct r_scope_elem **runp = imap->l_scope; | |
158 | ||
159 | while (*runp != NULL) | |
160 | if (*runp == &map->l_searchlist) | |
161 | { | |
162 | /* Copy all later elements. */ | |
163 | while ((runp[0] = runp[1]) != NULL) | |
164 | ++runp; | |
165 | break; | |
166 | } | |
167 | else | |
168 | ++runp; | |
169 | } | |
42c4f32a UD |
170 | |
171 | /* Store the new l_opencount value. */ | |
172 | imap->l_opencount = new_opencount[i]; | |
173 | /* Just a sanity check. */ | |
174 | assert (imap->l_type == lt_loaded || imap->l_opencount > 0); | |
a709dd43 UD |
175 | } |
176 | ||
4d6acc61 RM |
177 | /* Notify the debugger we are about to remove some loaded objects. */ |
178 | _r_debug.r_state = RT_DELETE; | |
179 | _dl_debug_state (); | |
180 | ||
ba79d61b RM |
181 | /* Check each element of the search list to see if all references to |
182 | it are gone. */ | |
7e036a01 | 183 | for (i = 0; list[i] != NULL; ++i) |
ba79d61b | 184 | { |
af69217f UD |
185 | struct link_map *imap = list[i]; |
186 | if (imap->l_opencount == 0 && imap->l_type == lt_loaded) | |
ba79d61b | 187 | { |
a8a1269d UD |
188 | struct libname_list *lnp; |
189 | ||
ba79d61b RM |
190 | /* That was the last reference, and this was a dlopen-loaded |
191 | object. We can unmap it. */ | |
c41c89d3 | 192 | if (__builtin_expect (imap->l_global, 0)) |
ba79d61b RM |
193 | { |
194 | /* This object is in the global scope list. Remove it. */ | |
d6b5d570 | 195 | unsigned int cnt = GL(dl_main_searchlist)->r_nlist; |
be935610 | 196 | |
ba79d61b | 197 | do |
50b65db1 | 198 | --cnt; |
d6b5d570 | 199 | while (GL(dl_main_searchlist)->r_list[cnt] != imap); |
482eec0d | 200 | |
50b65db1 | 201 | /* The object was already correctly registered. */ |
d6b5d570 UD |
202 | while (++cnt < GL(dl_main_searchlist)->r_nlist) |
203 | GL(dl_main_searchlist)->r_list[cnt - 1] | |
204 | = GL(dl_main_searchlist)->r_list[cnt]; | |
50b65db1 | 205 | |
d6b5d570 | 206 | --GL(dl_main_searchlist)->r_nlist; |
ba79d61b RM |
207 | } |
208 | ||
a8a1269d | 209 | /* We can unmap all the maps at once. We determined the |
4ce636da UD |
210 | start address and length when we loaded the object and |
211 | the `munmap' call does the rest. */ | |
09bf6406 | 212 | DL_UNMAP (imap); |
22bc7978 | 213 | |
ba79d61b | 214 | /* Finally, unlink the data structure and free it. */ |
b5567b2a | 215 | #ifdef SHARED |
7afab53d UD |
216 | /* We will unlink the first object only if this is a statically |
217 | linked program. */ | |
218 | assert (imap->l_prev != NULL); | |
6a805a0b | 219 | imap->l_prev->l_next = imap->l_next; |
7afab53d UD |
220 | #else |
221 | if (imap->l_prev != NULL) | |
af69217f | 222 | imap->l_prev->l_next = imap->l_next; |
7afab53d | 223 | else |
d6b5d570 | 224 | GL(dl_loaded) = imap->l_next; |
7afab53d | 225 | #endif |
d6b5d570 | 226 | --GL(dl_nloaded); |
af69217f UD |
227 | if (imap->l_next) |
228 | imap->l_next->l_prev = imap->l_prev; | |
a8a1269d UD |
229 | |
230 | if (imap->l_versions != NULL) | |
231 | free (imap->l_versions); | |
1c3a6f19 | 232 | if (imap->l_origin != NULL && imap->l_origin != (char *) -1) |
a8a1269d UD |
233 | free ((char *) imap->l_origin); |
234 | ||
4b4fcf99 UD |
235 | /* If the object has relocation dependencies save this |
236 | information for latter. */ | |
237 | if (__builtin_expect (imap->l_reldeps != NULL, 0)) | |
238 | { | |
239 | struct reldep_list *newrel; | |
240 | ||
241 | newrel = (struct reldep_list *) alloca (sizeof (*reldeps)); | |
1826d793 UD |
242 | newrel->rellist = imap->l_reldeps; |
243 | newrel->nrellist = imap->l_reldepsact; | |
4b4fcf99 UD |
244 | newrel->next = reldeps; |
245 | ||
246 | reldeps = newrel; | |
247 | } | |
248 | ||
4ce636da | 249 | /* This name always is allocated. */ |
a8a1269d | 250 | free (imap->l_name); |
4ce636da | 251 | /* Remove the list with all the names of the shared object. */ |
a8a1269d UD |
252 | lnp = imap->l_libname; |
253 | do | |
254 | { | |
76156ea1 | 255 | struct libname_list *this = lnp; |
a8a1269d | 256 | lnp = lnp->next; |
11810621 UD |
257 | if (!this->dont_free) |
258 | free (this); | |
a8a1269d UD |
259 | } |
260 | while (lnp != NULL); | |
a8a1269d | 261 | |
4ce636da | 262 | /* Remove the searchlists. */ |
dacc8ffa | 263 | if (imap != map) |
2e93b4a4 | 264 | free (imap->l_initfini); |
4ce636da | 265 | |
5a21d307 UD |
266 | /* Remove the scope array if we allocated it. */ |
267 | if (imap->l_scope != imap->l_scope_mem) | |
268 | free (imap->l_scope); | |
269 | ||
7bcaca43 | 270 | if (imap->l_phdr_allocated) |
15925412 | 271 | free ((void *) imap->l_phdr); |
7bcaca43 | 272 | |
f55727ca UD |
273 | if (imap->l_rpath_dirs.dirs != (void *) -1) |
274 | free (imap->l_rpath_dirs.dirs); | |
275 | if (imap->l_runpath_dirs.dirs != (void *) -1) | |
276 | free (imap->l_runpath_dirs.dirs); | |
277 | ||
af69217f | 278 | free (imap); |
ba79d61b RM |
279 | } |
280 | } | |
281 | ||
e3e5f672 UD |
282 | /* Notify the debugger those objects are finalized and gone. */ |
283 | _r_debug.r_state = RT_CONSISTENT; | |
284 | _dl_debug_state (); | |
285 | ||
cf197e41 UD |
286 | /* Now we can perhaps also remove the modules for which we had |
287 | dependencies because of symbol lookup. */ | |
4b4fcf99 | 288 | while (__builtin_expect (reldeps != NULL, 0)) |
cf197e41 | 289 | { |
4b4fcf99 UD |
290 | while (reldeps->nrellist-- > 0) |
291 | _dl_close (reldeps->rellist[reldeps->nrellist]); | |
292 | ||
293 | free (reldeps->rellist); | |
cf197e41 | 294 | |
4b4fcf99 | 295 | reldeps = reldeps->next; |
cf197e41 UD |
296 | } |
297 | ||
ba79d61b | 298 | free (list); |
4d6acc61 | 299 | |
e3e5f672 | 300 | /* Release the lock. */ |
c12aa801 | 301 | __libc_lock_unlock_recursive (_dl_load_lock); |
e3e5f672 UD |
302 | } |
303 | ||
304 | ||
305 | static void | |
306 | free_mem (void) | |
307 | { | |
d6b5d570 UD |
308 | if (__builtin_expect (GL(dl_global_scope_alloc), 0) != 0 |
309 | && GL(dl_main_searchlist)->r_nlist == GL(dl_initial_searchlist).r_nlist) | |
d3556ac9 UD |
310 | { |
311 | /* All object dynamically loaded by the program are unloaded. Free | |
312 | the memory allocated for the global scope variable. */ | |
d6b5d570 | 313 | struct link_map **old = GL(dl_main_searchlist)->r_list; |
d3556ac9 UD |
314 | |
315 | /* Put the old map in. */ | |
d6b5d570 | 316 | GL(dl_main_searchlist)->r_list = GL(dl_initial_searchlist).r_list; |
d3556ac9 | 317 | /* Signal that the original map is used. */ |
d6b5d570 | 318 | GL(dl_global_scope_alloc) = 0; |
d3556ac9 UD |
319 | |
320 | /* Now free the old map. */ | |
321 | free (old); | |
322 | } | |
ba79d61b | 323 | } |
e3e5f672 | 324 | text_set_element (__libc_subfreeres, free_mem); |