]>
Commit | Line | Data |
---|---|---|
1 | /* Handle loading/unloading of shared object for transformation. | |
2 | Copyright (C) 1997-2024 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 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. | |
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 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library; if not, see | |
17 | <https://www.gnu.org/licenses/>. */ | |
18 | ||
19 | #include <assert.h> | |
20 | #include <dlfcn.h> | |
21 | #include <inttypes.h> | |
22 | #include <search.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
25 | #include <libc-lock.h> | |
26 | #include <sys/param.h> | |
27 | ||
28 | #include <gconv_int.h> | |
29 | #include <pointer_guard.h> | |
30 | ||
31 | ||
32 | #ifdef DEBUG | |
33 | /* For debugging purposes. */ | |
34 | static void print_all (void); | |
35 | #endif | |
36 | ||
37 | ||
38 | /* This is a tuning parameter. If a transformation module is not used | |
39 | anymore it gets not immediately unloaded. Instead we wait a certain | |
40 | number of load attempts for further modules. If none of the | |
41 | subsequent load attempts name the same object it finally gets unloaded. | |
42 | Otherwise it is still available which hopefully is the frequent case. | |
43 | The following number is the number of unloading attempts we wait | |
44 | before unloading. */ | |
45 | #define TRIES_BEFORE_UNLOAD 2 | |
46 | ||
47 | /* Array of loaded objects. This is shared by all threads so we have | |
48 | to use semaphores to access it. */ | |
49 | static void *loaded; | |
50 | ||
51 | /* Comparison function for searching `loaded_object' tree. */ | |
52 | static int | |
53 | known_compare (const void *p1, const void *p2) | |
54 | { | |
55 | const struct __gconv_loaded_object *s1 = | |
56 | (const struct __gconv_loaded_object *) p1; | |
57 | const struct __gconv_loaded_object *s2 = | |
58 | (const struct __gconv_loaded_object *) p2; | |
59 | ||
60 | return strcmp (s1->name, s2->name); | |
61 | } | |
62 | ||
63 | /* Open the gconv database if necessary. A non-negative return value | |
64 | means success. */ | |
65 | struct __gconv_loaded_object * | |
66 | __gconv_find_shlib (const char *name) | |
67 | { | |
68 | struct __gconv_loaded_object *found; | |
69 | void *keyp; | |
70 | ||
71 | /* Search the tree of shared objects previously requested. Data in | |
72 | the tree are `loaded_object' structures, whose first member is a | |
73 | `const char *', the lookup key. The search returns a pointer to | |
74 | the tree node structure; the first member of the is a pointer to | |
75 | our structure (i.e. what will be a `loaded_object'); since the | |
76 | first member of that is the lookup key string, &FCT_NAME is close | |
77 | enough to a pointer to our structure to use as a lookup key that | |
78 | will be passed to `known_compare' (above). */ | |
79 | ||
80 | keyp = __tfind (&name, &loaded, known_compare); | |
81 | if (keyp == NULL) | |
82 | { | |
83 | /* This name was not known before. */ | |
84 | size_t namelen = strlen (name) + 1; | |
85 | ||
86 | found = malloc (sizeof (struct __gconv_loaded_object) + namelen); | |
87 | if (found != NULL) | |
88 | { | |
89 | /* Point the tree node at this new structure. */ | |
90 | found->name = (char *) memcpy (found + 1, name, namelen); | |
91 | found->counter = -TRIES_BEFORE_UNLOAD - 1; | |
92 | found->handle = NULL; | |
93 | ||
94 | if (__builtin_expect (__tsearch (found, &loaded, known_compare) | |
95 | == NULL, 0)) | |
96 | { | |
97 | /* Something went wrong while inserting the entry. */ | |
98 | free (found); | |
99 | found = NULL; | |
100 | } | |
101 | } | |
102 | } | |
103 | else | |
104 | found = *(struct __gconv_loaded_object **) keyp; | |
105 | ||
106 | /* Try to load the shared object if the usage count is 0. This | |
107 | implies that if the shared object is not loadable, the handle is | |
108 | NULL and the usage count > 0. */ | |
109 | if (found != NULL) | |
110 | { | |
111 | if (found->counter < -TRIES_BEFORE_UNLOAD) | |
112 | { | |
113 | assert (found->handle == NULL); | |
114 | found->handle = __libc_dlopen (found->name); | |
115 | if (found->handle != NULL) | |
116 | { | |
117 | found->fct = __libc_dlsym (found->handle, "gconv"); | |
118 | if (found->fct == NULL) | |
119 | { | |
120 | /* Argh, no conversion function. There is something | |
121 | wrong here. */ | |
122 | __gconv_release_shlib (found); | |
123 | found = NULL; | |
124 | } | |
125 | else | |
126 | { | |
127 | found->init_fct = __libc_dlsym (found->handle, "gconv_init"); | |
128 | found->end_fct = __libc_dlsym (found->handle, "gconv_end"); | |
129 | ||
130 | PTR_MANGLE (found->fct); | |
131 | PTR_MANGLE (found->init_fct); | |
132 | PTR_MANGLE (found->end_fct); | |
133 | ||
134 | /* We have succeeded in loading the shared object. */ | |
135 | found->counter = 1; | |
136 | } | |
137 | } | |
138 | else | |
139 | /* Error while loading the shared object. */ | |
140 | found = NULL; | |
141 | } | |
142 | else if (found->handle != NULL) | |
143 | found->counter = MAX (found->counter + 1, 1); | |
144 | } | |
145 | ||
146 | return found; | |
147 | } | |
148 | ||
149 | static void | |
150 | do_release_shlib (const void *nodep, VISIT value, void *closure) | |
151 | { | |
152 | struct __gconv_loaded_object *release_handle = closure; | |
153 | struct __gconv_loaded_object *obj = *(struct __gconv_loaded_object **) nodep; | |
154 | ||
155 | if (value != preorder && value != leaf) | |
156 | return; | |
157 | ||
158 | if (obj == release_handle) | |
159 | { | |
160 | /* This is the object we want to unload. Now decrement the | |
161 | reference counter. */ | |
162 | assert (obj->counter > 0); | |
163 | --obj->counter; | |
164 | } | |
165 | else if (obj->counter <= 0 && obj->counter >= -TRIES_BEFORE_UNLOAD | |
166 | && --obj->counter < -TRIES_BEFORE_UNLOAD && obj->handle != NULL) | |
167 | { | |
168 | /* Unload the shared object. */ | |
169 | __libc_dlclose (obj->handle); | |
170 | obj->handle = NULL; | |
171 | } | |
172 | } | |
173 | ||
174 | ||
175 | /* Notify system that a shared object is not longer needed. */ | |
176 | void | |
177 | __gconv_release_shlib (struct __gconv_loaded_object *handle) | |
178 | { | |
179 | /* Process all entries. Please note that we also visit entries | |
180 | with release counts <= 0. This way we can finally unload them | |
181 | if necessary. */ | |
182 | __twalk_r (loaded, do_release_shlib, handle); | |
183 | } | |
184 | ||
185 | ||
186 | /* We run this if we debug the memory allocation. */ | |
187 | static void | |
188 | do_release_all (void *nodep) | |
189 | { | |
190 | struct __gconv_loaded_object *obj = (struct __gconv_loaded_object *) nodep; | |
191 | ||
192 | /* Unload the shared object. */ | |
193 | if (obj->handle != NULL) | |
194 | __libc_dlclose (obj->handle); | |
195 | ||
196 | free (obj); | |
197 | } | |
198 | ||
199 | void | |
200 | __gconv_dl_freemem (void) | |
201 | { | |
202 | __tdestroy (loaded, do_release_all); | |
203 | loaded = NULL; | |
204 | } | |
205 | ||
206 | ||
207 | #ifdef DEBUG | |
208 | ||
209 | #include <stdio.h> | |
210 | ||
211 | static void | |
212 | do_print (const void *nodep, VISIT value, int level) | |
213 | { | |
214 | struct __gconv_loaded_object *obj = *(struct __gconv_loaded_object **) nodep; | |
215 | ||
216 | printf ("%10s: \"%s\", %d\n", | |
217 | value == leaf ? "leaf" | |
218 | : value == preorder ? "preorder" | |
219 | : value == postorder ? "postorder" : "endorder", | |
220 | obj->name, obj->counter); | |
221 | } | |
222 | ||
223 | static void __attribute__ ((used)) | |
224 | print_all (void) | |
225 | { | |
226 | __twalk (loaded, do_print); | |
227 | } | |
228 | #endif |