fgetws implementation.
[kopensolaris-gnu/glibc.git] / elf / dl-close.c
1 /* Close a shared object opened by `_dl_open'.
2    Copyright (C) 1996, 1997, 1998, 1999 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 <stdlib.h>
23 #include <string.h>
24 #include <bits/libc-lock.h>
25 #include <elf/ldsodefs.h>
26 #include <sys/types.h>
27 #include <sys/mman.h>
28
29
30 /* During the program run we must not modify the global data of
31    loaded shared object simultanously in two threads.  Therefore we
32    protect `dlopen' and `dlclose' in dlclose.c.  */
33 __libc_lock_define (extern, _dl_load_lock)
34
35 #define LOSE(s) _dl_signal_error (0, map->l_name, s)
36
37 void
38 internal_function
39 _dl_close (struct link_map *map)
40 {
41   struct link_map **list;
42   unsigned nsearchlist;
43   unsigned int i;
44
45   if (map->l_opencount == 0)
46     LOSE ("shared object not open");
47
48   /* Acquire the lock.  */
49   __libc_lock_lock (_dl_load_lock);
50
51   /* Decrement the reference count.  */
52   if (map->l_opencount > 1 || map->l_type != lt_loaded)
53     {
54       /* There are still references to this object.  Do nothing more.  */
55       --map->l_opencount;
56       __libc_lock_unlock (_dl_load_lock);
57       return;
58     }
59
60   list = map->l_searchlist.r_list;
61   nsearchlist = map->l_searchlist.r_nlist;
62
63   /* Call all termination functions at once.  */
64   for (i = 0; i < nsearchlist; ++i)
65     {
66       struct link_map *imap = list[i];
67       if (imap->l_opencount == 1 && imap->l_type == lt_loaded
68           && imap->l_info[DT_FINI]
69           /* Skip any half-cooked objects that were never initialized.  */
70           && imap->l_init_called)
71         {
72           /* When debugging print a message first.  */
73           if (_dl_debug_impcalls)
74             _dl_debug_message (1, "\ncalling fini: ", imap->l_name,
75                                "\n\n", NULL);
76           /* Call its termination function.  */
77           (*(void (*) (void)) ((void *) imap->l_addr
78                                + imap->l_info[DT_FINI]->d_un.d_ptr)) ();
79         }
80     }
81
82   /* Notify the debugger we are about to remove some loaded objects.  */
83   _r_debug.r_state = RT_DELETE;
84   _dl_debug_state ();
85
86   /* The search list contains a counted reference to each object it
87      points to, the 0th elt being MAP itself.  Decrement the reference
88      counts on all the objects MAP depends on.  */
89   for (i = 0; i < nsearchlist; ++i)
90     --list[i]->l_opencount;
91
92   /* Check each element of the search list to see if all references to
93      it are gone.  */
94   for (i = 0; i < nsearchlist; ++i)
95     {
96       struct link_map *imap = list[i];
97       if (imap->l_opencount == 0 && imap->l_type == lt_loaded)
98         {
99           struct libname_list *lnp;
100
101           /* That was the last reference, and this was a dlopen-loaded
102              object.  We can unmap it.  */
103           if (imap->l_global)
104             {
105               /* This object is in the global scope list.  Remove it.  */
106               int cnt = _dl_main_searchlist->r_nlist;
107
108               do
109                 --cnt;
110               while (_dl_main_searchlist->r_list[cnt] != imap);
111
112               /* The object was already correctly registered.  */
113               while (++cnt < _dl_main_searchlist->r_nlist)
114                 _dl_main_searchlist->r_list[cnt - 1]
115                   = _dl_main_searchlist->r_list[cnt];
116
117               --_dl_main_searchlist->r_nlist;
118             }
119
120           /* We can unmap all the maps at once.  We determined the
121              start address and length when we loaded the object and
122              the `munmap' call does the rest.  */
123           __munmap ((void *) imap->l_map_start,
124                     imap->l_map_end - imap->l_map_start);
125
126           /* Finally, unlink the data structure and free it.  */
127 #ifdef PIC
128           /* We will unlink the first object only if this is a statically
129              linked program.  */
130           assert (imap->l_prev != NULL);
131           imap->l_prev->l_next = imap->l_next;
132 #else
133           if (imap->l_prev != NULL)
134             imap->l_prev->l_next = imap->l_next;
135           else
136             _dl_loaded = imap->l_next;
137 #endif
138           if (imap->l_next)
139             imap->l_next->l_prev = imap->l_prev;
140
141           if (imap->l_versions != NULL)
142             free (imap->l_versions);
143           if (imap->l_origin != NULL && imap->l_origin != (char *) -1)
144             free ((char *) imap->l_origin);
145
146           /* This name always is allocated.  */
147           free (imap->l_name);
148           /* Remove the list with all the names of the shared object.  */
149           lnp = imap->l_libname;
150           do
151             {
152               struct libname_list *this = lnp;
153               lnp = lnp->next;
154               free (this);
155             }
156           while (lnp != NULL);
157
158           /* Remove the searchlists.  */
159           if (imap->l_searchlist.r_duplist != imap->l_searchlist.r_list)
160             {
161               /* If a r_list exists there always also is a r_duplist.  */
162               assert (imap->l_searchlist.r_list != NULL);
163               free (imap->l_searchlist.r_duplist);
164             }
165           if (imap != map && imap->l_searchlist.r_list != NULL)
166             free (imap->l_searchlist.r_list);
167
168           if (imap->l_phdr_allocated)
169             free ((void *) imap->l_phdr);
170
171           free (imap);
172         }
173     }
174
175   free (list);
176
177   if (_dl_global_scope_alloc != 0
178       && _dl_main_searchlist->r_nlist == _dl_initial_searchlist.r_nlist)
179     {
180       /* All object dynamically loaded by the program are unloaded.  Free
181          the memory allocated for the global scope variable.  */
182       struct link_map **old = _dl_main_searchlist->r_list;
183
184       /* Put the old map in.  */
185       _dl_main_searchlist->r_list = _dl_initial_searchlist.r_list;
186       /* Signal that the original map is used.  */
187       _dl_global_scope_alloc = 0;
188
189       /* Now free the old map.  */
190       free (old);
191     }
192
193   /* Notify the debugger those objects are finalized and gone.  */
194   _r_debug.r_state = RT_CONSISTENT;
195   _dl_debug_state ();
196
197   /* Release the lock.  */
198   __libc_lock_unlock (_dl_load_lock);
199 }