(_dl_close): Optimize a bit by optimizing out the nsearchlist variable.
[kopensolaris-gnu/glibc.git] / elf / dl-close.c
1 /* Close a shared object opened by `_dl_open'.
2    Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
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.  */
19
20 #include <assert.h>
21 #include <dlfcn.h>
22 #include <libintl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <bits/libc-lock.h>
26 #include <ldsodefs.h>
27 #include <sys/types.h>
28 #include <sys/mman.h>
29
30 #include <stdio-common/_itoa.h>
31
32
33 /* Type of the constructor functions.  */
34 typedef void (*fini_t) (void);
35
36
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
42 void
43 internal_function
44 _dl_close (void *_map)
45 {
46   struct reldep_list
47   {
48     struct link_map **rellist;
49     unsigned int nrellist;
50     struct reldep_list *next;
51   } *reldeps = NULL;
52   struct link_map **list;
53   struct link_map *map = _map;
54   unsigned int i;
55   unsigned int *new_opencount;
56
57   /* First see whether we can remove the object at all.  */
58   if (map->l_flags_1 & DF_1_NODELETE)
59     /* Nope.  Do nothing.  */
60     return;
61
62   if (__builtin_expect (map->l_opencount, 1) == 0)
63     _dl_signal_error (0, map->l_name, N_("shared object not open"));
64
65   /* Acquire the lock.  */
66   __libc_lock_lock (_dl_load_lock);
67
68   /* Decrement the reference count.  */
69   if (map->l_opencount > 1 || map->l_type != lt_loaded)
70     {
71       /* There are still references to this object.  Do nothing more.  */
72       if (__builtin_expect (_dl_debug_files, 0))
73         {
74           char buf[20];
75
76           buf[sizeof buf - 1] = '\0';
77
78           _dl_debug_message (1, "\nclosing file=", map->l_name,
79                              "; opencount == ",
80                              _itoa_word (map->l_opencount,
81                                          buf + sizeof buf - 1, 10, 0),
82                              "\n", NULL);
83         }
84
85       /* One decrement the object itself, not the dependencies.  */
86       --map->l_opencount;
87
88       __libc_lock_unlock (_dl_load_lock);
89       return;
90     }
91
92   list = map->l_initfini;
93
94   /* Compute the new l_opencount values.  */
95   new_opencount = (unsigned int *) alloca (map->l_searchlist.r_nlist
96                                            * sizeof (unsigned int));
97   for (i = 0; list[i] != NULL; ++i)
98     {
99       list[i]->l_idx = i;
100       new_opencount[i] = list[i]->l_opencount;
101     }
102   --new_opencount[0];
103   for (i = 1; list[i] != NULL; ++i)
104     if (! (list[i]->l_flags_1 & DF_1_NODELETE)
105         /* Decrement counter.  */
106         && --new_opencount[i] == 0
107         /* Test whether this object was also loaded directly.  */
108         && list[i]->l_searchlist.r_list != NULL)
109       {
110         /* In this case we have the decrement all the dependencies of
111            this object.  They are all in MAP's dependency list.  */
112         unsigned int j;
113         struct link_map **dep_list = list[i]->l_searchlist.r_list;
114
115         for (j = 1; j < list[i]->l_searchlist.r_nlist; ++j)
116           if (! (dep_list[j]->l_flags_1 & DF_1_NODELETE))
117             {
118               assert (dep_list[j]->l_idx < map->l_searchlist.r_nlist);
119               --new_opencount[dep_list[j]->l_idx];
120             }
121       }
122   assert (new_opencount[0] == 0);
123
124   /* Call all termination functions at once.  */
125   for (i = 0; list[i] != NULL; ++i)
126     {
127       struct link_map *imap = list[i];
128       if (new_opencount[i] == 0 && imap->l_type == lt_loaded
129           && (imap->l_info[DT_FINI] || imap->l_info[DT_FINI_ARRAY])
130           && ! (imap->l_flags_1 & DF_1_NODELETE)
131           /* Skip any half-cooked objects that were never initialized.  */
132           && imap->l_init_called)
133         {
134           /* When debugging print a message first.  */
135           if (__builtin_expect (_dl_debug_impcalls, 0))
136             _dl_debug_message (1, "\ncalling fini: ", imap->l_name,
137                                "\n\n", NULL);
138
139           /* Call its termination function.  */
140           if (imap->l_info[DT_FINI_ARRAY] != NULL)
141             {
142               ElfW(Addr) *array =
143                 (ElfW(Addr) *) (imap->l_addr
144                                 + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
145               unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
146                                  / sizeof (ElfW(Addr)));
147               unsigned int cnt;
148
149               for (cnt = 0; cnt < sz; ++cnt)
150                 ((fini_t) (imap->l_addr + array[cnt])) ();
151             }
152
153           /* Next try the old-style destructor.  */
154           if (imap->l_info[DT_FINI] != NULL)
155             (*(void (*) (void)) ((void *) imap->l_addr
156                                  + imap->l_info[DT_FINI]->d_un.d_ptr)) ();
157         }
158
159       /* Store the new l_opencount value.  */
160       imap->l_opencount = new_opencount[i];
161       /* Just a sanity check.  */
162       assert (imap->l_type == lt_loaded || imap->l_opencount > 0);
163     }
164
165   /* Notify the debugger we are about to remove some loaded objects.  */
166   _r_debug.r_state = RT_DELETE;
167   _dl_debug_state ();
168
169   /* Check each element of the search list to see if all references to
170      it are gone.  */
171   for (i = 0; list[i] != NULL; ++i)
172     {
173       struct link_map *imap = list[i];
174       if (imap->l_opencount == 0 && imap->l_type == lt_loaded)
175         {
176           struct libname_list *lnp;
177
178           /* That was the last reference, and this was a dlopen-loaded
179              object.  We can unmap it.  */
180           if (__builtin_expect (imap->l_global, 0))
181             {
182               /* This object is in the global scope list.  Remove it.  */
183               int cnt = _dl_main_searchlist->r_nlist;
184
185               do
186                 --cnt;
187               while (_dl_main_searchlist->r_list[cnt] != imap);
188
189               /* The object was already correctly registered.  */
190               while (++cnt < _dl_main_searchlist->r_nlist)
191                 _dl_main_searchlist->r_list[cnt - 1]
192                   = _dl_main_searchlist->r_list[cnt];
193
194               --_dl_main_searchlist->r_nlist;
195             }
196
197           /* We can unmap all the maps at once.  We determined the
198              start address and length when we loaded the object and
199              the `munmap' call does the rest.  */
200           DL_UNMAP (imap);
201
202           /* Finally, unlink the data structure and free it.  */
203 #ifdef SHARED
204           /* We will unlink the first object only if this is a statically
205              linked program.  */
206           assert (imap->l_prev != NULL);
207           imap->l_prev->l_next = imap->l_next;
208 #else
209           if (imap->l_prev != NULL)
210             imap->l_prev->l_next = imap->l_next;
211           else
212             _dl_loaded = imap->l_next;
213 #endif
214           --_dl_nloaded;
215           if (imap->l_next)
216             imap->l_next->l_prev = imap->l_prev;
217
218           if (imap->l_versions != NULL)
219             free (imap->l_versions);
220           if (imap->l_origin != NULL && imap->l_origin != (char *) -1)
221             free ((char *) imap->l_origin);
222
223           /* If the object has relocation dependencies save this
224              information for latter.  */
225           if (__builtin_expect (imap->l_reldeps != NULL, 0))
226             {
227               struct reldep_list *newrel;
228
229               newrel = (struct reldep_list *) alloca (sizeof (*reldeps));
230               newrel->rellist = map->l_reldeps;
231               newrel->nrellist = map->l_reldepsact;
232               newrel->next = reldeps;
233
234               reldeps = newrel;
235             }
236
237           /* This name always is allocated.  */
238           free (imap->l_name);
239           /* Remove the list with all the names of the shared object.  */
240           lnp = imap->l_libname;
241           do
242             {
243               struct libname_list *this = lnp;
244               lnp = lnp->next;
245               free (this);
246             }
247           while (lnp != NULL);
248
249           /* Remove the searchlists.  */
250           if (imap != map)
251               free (imap->l_initfini);
252
253           if (imap->l_phdr_allocated)
254             free ((void *) imap->l_phdr);
255
256           if (imap->l_rpath_dirs.dirs != (void *) -1)
257             free (imap->l_rpath_dirs.dirs);
258           if (imap->l_runpath_dirs.dirs != (void *) -1)
259             free (imap->l_runpath_dirs.dirs);
260
261           free (imap);
262         }
263     }
264
265   /* Notify the debugger those objects are finalized and gone.  */
266   _r_debug.r_state = RT_CONSISTENT;
267   _dl_debug_state ();
268
269   /* Now we can perhaps also remove the modules for which we had
270      dependencies because of symbol lookup.  */
271   while (__builtin_expect (reldeps != NULL, 0))
272     {
273       while (reldeps->nrellist-- > 0)
274         _dl_close (reldeps->rellist[reldeps->nrellist]);
275
276       free (reldeps->rellist);
277
278       reldeps = reldeps->next;
279     }
280
281   free (list);
282
283   /* Release the lock.  */
284   __libc_lock_unlock (_dl_load_lock);
285 }
286
287
288 static void
289 free_mem (void)
290 {
291   if (__builtin_expect (_dl_global_scope_alloc, 0) != 0
292       && _dl_main_searchlist->r_nlist == _dl_initial_searchlist.r_nlist)
293     {
294       /* All object dynamically loaded by the program are unloaded.  Free
295          the memory allocated for the global scope variable.  */
296       struct link_map **old = _dl_main_searchlist->r_list;
297
298       /* Put the old map in.  */
299       _dl_main_searchlist->r_list = _dl_initial_searchlist.r_list;
300       /* Signal that the original map is used.  */
301       _dl_global_scope_alloc = 0;
302
303       /* Now free the old map.  */
304       free (old);
305     }
306 }
307 text_set_element (__libc_subfreeres, free_mem);