]>
Commit | Line | Data |
---|---|---|
6973fc01 | 1 | /* Handle loading/unloading of shared object for transformation. |
915a6c51 | 2 | Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005 |
7c11c4a1 | 3 | Free Software Foundation, Inc. |
6973fc01 UD |
4 | This file is part of the GNU C Library. |
5 | Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. | |
6 | ||
7 | The GNU C Library is free software; you can redistribute it and/or | |
41bdb6e2 AJ |
8 | modify it under the terms of the GNU Lesser General Public |
9 | License as published by the Free Software Foundation; either | |
10 | version 2.1 of the License, or (at your option) any later version. | |
6973fc01 UD |
11 | |
12 | The GNU C Library is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
41bdb6e2 | 15 | Lesser General Public License for more details. |
6973fc01 | 16 | |
41bdb6e2 AJ |
17 | You should have received a copy of the GNU Lesser General Public |
18 | License along with the GNU C Library; if not, write to the Free | |
19 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
20 | 02111-1307 USA. */ | |
6973fc01 | 21 | |
add40772 | 22 | #include <assert.h> |
6973fc01 | 23 | #include <dlfcn.h> |
6973fc01 | 24 | #include <inttypes.h> |
6973fc01 UD |
25 | #include <search.h> |
26 | #include <stdlib.h> | |
27 | #include <string.h> | |
28 | #include <bits/libc-lock.h> | |
29 | #include <sys/param.h> | |
30 | ||
e62c19f1 | 31 | #include <gconv_int.h> |
915a6c51 | 32 | #include <sysdep.h> |
e62c19f1 | 33 | |
76a2102b UD |
34 | |
35 | #ifdef DEBUG | |
36 | /* For debugging purposes. */ | |
37 | static void print_all (void); | |
38 | #endif | |
39 | ||
40 | ||
6973fc01 UD |
41 | /* This is a tuning parameter. If a transformation module is not used |
42 | anymore it gets not immediately unloaded. Instead we wait a certain | |
43 | number of load attempts for further modules. If none of the | |
44 | subsequent load attempts name the same object it finally gets unloaded. | |
45 | Otherwise it is still available which hopefully is the frequent case. | |
46 | The following number is the number of unloading attempts we wait | |
47 | before unloading. */ | |
48 | #define TRIES_BEFORE_UNLOAD 2 | |
49 | ||
6973fc01 UD |
50 | /* Array of loaded objects. This is shared by all threads so we have |
51 | to use semaphores to access it. */ | |
52 | static void *loaded; | |
6973fc01 | 53 | |
6973fc01 UD |
54 | /* Comparison function for searching `loaded_object' tree. */ |
55 | static int | |
56 | known_compare (const void *p1, const void *p2) | |
57 | { | |
d64b6ad0 UD |
58 | const struct __gconv_loaded_object *s1 = |
59 | (const struct __gconv_loaded_object *) p1; | |
60 | const struct __gconv_loaded_object *s2 = | |
61 | (const struct __gconv_loaded_object *) p2; | |
6973fc01 | 62 | |
76a2102b | 63 | return strcmp (s1->name, s2->name); |
6973fc01 UD |
64 | } |
65 | ||
6973fc01 UD |
66 | /* Open the gconv database if necessary. A non-negative return value |
67 | means success. */ | |
d64b6ad0 | 68 | struct __gconv_loaded_object * |
e62c19f1 | 69 | internal_function |
6973fc01 UD |
70 | __gconv_find_shlib (const char *name) |
71 | { | |
d64b6ad0 | 72 | struct __gconv_loaded_object *found; |
7a68c94a | 73 | void *keyp; |
6973fc01 UD |
74 | |
75 | /* Search the tree of shared objects previously requested. Data in | |
76 | the tree are `loaded_object' structures, whose first member is a | |
77 | `const char *', the lookup key. The search returns a pointer to | |
78 | the tree node structure; the first member of the is a pointer to | |
79 | our structure (i.e. what will be a `loaded_object'); since the | |
80 | first member of that is the lookup key string, &FCT_NAME is close | |
81 | enough to a pointer to our structure to use as a lookup key that | |
82 | will be passed to `known_compare' (above). */ | |
83 | ||
7a68c94a UD |
84 | keyp = __tfind (&name, &loaded, known_compare); |
85 | if (keyp == NULL) | |
6973fc01 UD |
86 | { |
87 | /* This name was not known before. */ | |
0db59742 UD |
88 | size_t namelen = strlen (name) + 1; |
89 | ||
90 | found = malloc (sizeof (struct __gconv_loaded_object) + namelen); | |
6973fc01 UD |
91 | if (found != NULL) |
92 | { | |
93 | /* Point the tree node at this new structure. */ | |
0db59742 | 94 | found->name = (char *) memcpy (found + 1, name, namelen); |
6973fc01 UD |
95 | found->counter = -TRIES_BEFORE_UNLOAD - 1; |
96 | found->handle = NULL; | |
97 | ||
365afefc UD |
98 | if (__builtin_expect (__tsearch (found, &loaded, known_compare) |
99 | == NULL, 0)) | |
6973fc01 UD |
100 | { |
101 | /* Something went wrong while inserting the entry. */ | |
102 | free (found); | |
103 | found = NULL; | |
104 | } | |
105 | } | |
106 | } | |
7a68c94a | 107 | else |
d64b6ad0 | 108 | found = *(struct __gconv_loaded_object **) keyp; |
6973fc01 UD |
109 | |
110 | /* Try to load the shared object if the usage count is 0. This | |
111 | implies that if the shared object is not loadable, the handle is | |
112 | NULL and the usage count > 0. */ | |
113 | if (found != NULL) | |
114 | { | |
115 | if (found->counter < -TRIES_BEFORE_UNLOAD) | |
116 | { | |
fc5f4a97 | 117 | assert (found->handle == NULL); |
b3fc5f84 UD |
118 | found->handle = __libc_dlopen (found->name); |
119 | if (found->handle != NULL) | |
0d9f6793 | 120 | { |
b3fc5f84 | 121 | found->fct = __libc_dlsym (found->handle, "gconv"); |
0d9f6793 UD |
122 | if (found->fct == NULL) |
123 | { | |
124 | /* Argh, no conversion function. There is something | |
125 | wrong here. */ | |
126 | __gconv_release_shlib (found); | |
127 | found = NULL; | |
128 | } | |
129 | else | |
130 | { | |
b3fc5f84 UD |
131 | found->init_fct = __libc_dlsym (found->handle, "gconv_init"); |
132 | found->end_fct = __libc_dlsym (found->handle, "gconv_end"); | |
0d9f6793 | 133 | |
915a6c51 UD |
134 | #ifdef PTR_MANGLE |
135 | PTR_MANGLE (found->fct); | |
136 | if (found->init_fct != NULL) | |
137 | PTR_MANGLE (found->init_fct); | |
138 | if (found->end_fct != NULL) | |
139 | PTR_MANGLE (found->end_fct); | |
140 | #endif | |
141 | ||
0d9f6793 UD |
142 | /* We have succeeded in loading the shared object. */ |
143 | found->counter = 1; | |
144 | } | |
145 | } | |
146 | else | |
147 | /* Error while loading the shared object. */ | |
148 | found = NULL; | |
6973fc01 UD |
149 | } |
150 | else if (found->handle != NULL) | |
151 | found->counter = MAX (found->counter + 1, 1); | |
6973fc01 UD |
152 | } |
153 | ||
0d9f6793 | 154 | return found; |
6973fc01 UD |
155 | } |
156 | ||
157 | ||
158 | /* This is very ugly but the tsearch functions provide no way to pass | |
159 | information to the walker function. So we use a global variable. | |
160 | It is MT safe since we use a lock. */ | |
d64b6ad0 | 161 | static struct __gconv_loaded_object *release_handle; |
6973fc01 UD |
162 | |
163 | static void | |
17427edd | 164 | do_release_shlib (void *nodep, VISIT value, int level) |
6973fc01 | 165 | { |
d64b6ad0 | 166 | struct __gconv_loaded_object *obj = *(struct __gconv_loaded_object **) nodep; |
6973fc01 UD |
167 | |
168 | if (value != preorder && value != leaf) | |
169 | return; | |
170 | ||
0d9f6793 | 171 | if (obj == release_handle) |
add40772 UD |
172 | { |
173 | /* This is the object we want to unload. Now decrement the | |
174 | reference counter. */ | |
175 | assert (obj->counter > 0); | |
176 | --obj->counter; | |
177 | } | |
fc5f4a97 UD |
178 | else if (obj->counter <= 0 && obj->counter >= -TRIES_BEFORE_UNLOAD |
179 | && --obj->counter < -TRIES_BEFORE_UNLOAD && obj->handle != NULL) | |
6973fc01 | 180 | { |
fc5f4a97 UD |
181 | /* Unload the shared object. */ |
182 | __libc_dlclose (obj->handle); | |
183 | obj->handle = NULL; | |
6973fc01 UD |
184 | } |
185 | } | |
186 | ||
187 | ||
188 | /* Notify system that a shared object is not longer needed. */ | |
b79f74cd | 189 | void |
e62c19f1 | 190 | internal_function |
d64b6ad0 | 191 | __gconv_release_shlib (struct __gconv_loaded_object *handle) |
6973fc01 | 192 | { |
6973fc01 UD |
193 | /* Urgh, this is ugly but we have no other possibility. */ |
194 | release_handle = handle; | |
195 | ||
196 | /* Process all entries. Please note that we also visit entries | |
197 | with release counts <= 0. This way we can finally unload them | |
198 | if necessary. */ | |
17427edd | 199 | __twalk (loaded, (__action_fn_t) do_release_shlib); |
6973fc01 | 200 | } |
e6df9a56 UD |
201 | |
202 | ||
203 | /* We run this if we debug the memory allocation. */ | |
7c11c4a1 | 204 | static void __libc_freeres_fn_section |
74454183 | 205 | do_release_all (void *nodep) |
e6df9a56 | 206 | { |
d64b6ad0 | 207 | struct __gconv_loaded_object *obj = (struct __gconv_loaded_object *) nodep; |
e6df9a56 | 208 | |
b3fc5f84 | 209 | /* Unload the shared object. */ |
74454183 | 210 | if (obj->handle != NULL) |
9a0a9895 | 211 | __libc_dlclose (obj->handle); |
e6df9a56 UD |
212 | |
213 | free (obj); | |
214 | } | |
215 | ||
c877418f | 216 | libc_freeres_fn (free_mem) |
e6df9a56 | 217 | { |
74454183 | 218 | __tdestroy (loaded, do_release_all); |
f6e50e66 | 219 | loaded = NULL; |
e6df9a56 | 220 | } |
76a2102b UD |
221 | |
222 | ||
223 | #ifdef DEBUG | |
224 | static void | |
225 | do_print (const void *nodep, VISIT value, int level) | |
226 | { | |
227 | struct __gconv_loaded_object *obj = *(struct __gconv_loaded_object **) nodep; | |
228 | ||
229 | printf ("%10s: \"%s\", %d\n", | |
230 | value == leaf ? "leaf" : | |
231 | value == preorder ? "preorder" : | |
232 | value == postorder ? "postorder" : "endorder", | |
233 | obj->name, obj->counter); | |
234 | } | |
235 | ||
236 | static void | |
237 | print_all (void) | |
238 | { | |
239 | __twalk (loaded, do_print); | |
240 | } | |
241 | #endif |