]>
Commit | Line | Data |
---|---|---|
b6ab06ce | 1 | /* Support for reading /etc/ld.so.cache files written by Linux ldconfig. |
688903eb | 2 | Copyright (C) 1996-2018 Free Software Foundation, Inc. |
b6ab06ce 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 | |
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 | |
59ba27a6 PE |
16 | License along with the GNU C Library; if not, see |
17 | <http://www.gnu.org/licenses/>. */ | |
b6ab06ce UD |
18 | |
19 | #include <assert.h> | |
20 | #include <unistd.h> | |
21 | #include <ldsodefs.h> | |
22 | #include <sys/mman.h> | |
23 | #include <dl-cache.h> | |
24 | #include <dl-procinfo.h> | |
e054f494 | 25 | #include <stdint.h> |
eb96ffb0 | 26 | #include <_itoa.h> |
ff08fc59 | 27 | #include <dl-hwcaps.h> |
b6ab06ce UD |
28 | |
29 | #ifndef _DL_PLATFORMS_COUNT | |
30 | # define _DL_PLATFORMS_COUNT 0 | |
31 | #endif | |
32 | ||
33 | /* This is the starting address and the size of the mmap()ed file. */ | |
34 | static struct cache_file *cache; | |
35 | static struct cache_file_new *cache_new; | |
36 | static size_t cachesize; | |
37 | ||
38 | /* 1 if cache_data + PTR points into the cache. */ | |
39 | #define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size) | |
40 | ||
41 | #define SEARCH_CACHE(cache) \ | |
42 | /* We use binary search since the table is sorted in the cache file. \ | |
43 | The first matching entry in the table is returned. \ | |
44 | It is important to use the same algorithm as used while generating \ | |
45 | the cache file. */ \ | |
46 | do \ | |
47 | { \ | |
48 | left = 0; \ | |
49 | right = cache->nlibs - 1; \ | |
50 | \ | |
51 | while (left <= right) \ | |
52 | { \ | |
53 | __typeof__ (cache->libs[0].key) key; \ | |
54 | \ | |
55 | middle = (left + right) / 2; \ | |
56 | \ | |
57 | key = cache->libs[middle].key; \ | |
58 | \ | |
59 | /* Make sure string table indices are not bogus before using \ | |
60 | them. */ \ | |
61 | if (! _dl_cache_verify_ptr (key)) \ | |
62 | { \ | |
63 | cmpres = 1; \ | |
64 | break; \ | |
65 | } \ | |
66 | \ | |
67 | /* Actually compare the entry with the key. */ \ | |
68 | cmpres = _dl_cache_libcmp (name, cache_data + key); \ | |
a1ffb40e | 69 | if (__glibc_unlikely (cmpres == 0)) \ |
b6ab06ce UD |
70 | { \ |
71 | /* Found it. LEFT now marks the last entry for which we \ | |
72 | know the name is correct. */ \ | |
73 | left = middle; \ | |
74 | \ | |
75 | /* There might be entries with this name before the one we \ | |
76 | found. So we have to find the beginning. */ \ | |
77 | while (middle > 0) \ | |
78 | { \ | |
79 | __typeof__ (cache->libs[0].key) key; \ | |
80 | \ | |
81 | key = cache->libs[middle - 1].key; \ | |
82 | /* Make sure string table indices are not bogus before \ | |
83 | using them. */ \ | |
84 | if (! _dl_cache_verify_ptr (key) \ | |
85 | /* Actually compare the entry. */ \ | |
86 | || _dl_cache_libcmp (name, cache_data + key) != 0) \ | |
87 | break; \ | |
88 | --middle; \ | |
89 | } \ | |
90 | \ | |
91 | do \ | |
92 | { \ | |
93 | int flags; \ | |
94 | __typeof__ (cache->libs[0]) *lib = &cache->libs[middle]; \ | |
95 | \ | |
96 | /* Only perform the name test if necessary. */ \ | |
97 | if (middle > left \ | |
98 | /* We haven't seen this string so far. Test whether the \ | |
99 | index is ok and whether the name matches. Otherwise \ | |
100 | we are done. */ \ | |
101 | && (! _dl_cache_verify_ptr (lib->key) \ | |
102 | || (_dl_cache_libcmp (name, cache_data + lib->key) \ | |
103 | != 0))) \ | |
104 | break; \ | |
105 | \ | |
106 | flags = lib->flags; \ | |
107 | if (_dl_cache_check_flags (flags) \ | |
108 | && _dl_cache_verify_ptr (lib->value)) \ | |
109 | { \ | |
110 | if (best == NULL || flags == GLRO(dl_correct_cache_id)) \ | |
111 | { \ | |
112 | HWCAP_CHECK; \ | |
113 | best = cache_data + lib->value; \ | |
114 | \ | |
115 | if (flags == GLRO(dl_correct_cache_id)) \ | |
116 | /* We've found an exact match for the shared \ | |
117 | object and no general `ELF' release. Stop \ | |
118 | searching. */ \ | |
119 | break; \ | |
120 | } \ | |
121 | } \ | |
122 | } \ | |
123 | while (++middle <= right); \ | |
124 | break; \ | |
125 | } \ | |
126 | \ | |
127 | if (cmpres < 0) \ | |
128 | left = middle + 1; \ | |
129 | else \ | |
130 | right = middle - 1; \ | |
131 | } \ | |
132 | } \ | |
133 | while (0) | |
134 | ||
135 | ||
136 | int | |
b6ab06ce UD |
137 | _dl_cache_libcmp (const char *p1, const char *p2) |
138 | { | |
139 | while (*p1 != '\0') | |
140 | { | |
141 | if (*p1 >= '0' && *p1 <= '9') | |
142 | { | |
143 | if (*p2 >= '0' && *p2 <= '9') | |
144 | { | |
145 | /* Must compare this numerically. */ | |
146 | int val1; | |
147 | int val2; | |
148 | ||
149 | val1 = *p1++ - '0'; | |
150 | val2 = *p2++ - '0'; | |
151 | while (*p1 >= '0' && *p1 <= '9') | |
152 | val1 = val1 * 10 + *p1++ - '0'; | |
153 | while (*p2 >= '0' && *p2 <= '9') | |
154 | val2 = val2 * 10 + *p2++ - '0'; | |
155 | if (val1 != val2) | |
156 | return val1 - val2; | |
157 | } | |
158 | else | |
159 | return 1; | |
160 | } | |
161 | else if (*p2 >= '0' && *p2 <= '9') | |
162 | return -1; | |
163 | else if (*p1 != *p2) | |
164 | return *p1 - *p2; | |
165 | else | |
166 | { | |
167 | ++p1; | |
168 | ++p2; | |
169 | } | |
170 | } | |
171 | return *p1 - *p2; | |
172 | } | |
173 | ||
174 | ||
b2d3c3be CD |
175 | /* Look up NAME in ld.so.cache and return the file name stored there, or null |
176 | if none is found. The cache is loaded if it was not already. If loading | |
ccdb048d CD |
177 | the cache previously failed there will be no more attempts to load it. |
178 | The caller is responsible for freeing the returned string. The ld.so.cache | |
179 | may be unmapped at any time by a completing recursive dlopen and | |
180 | this function must take care that it does not return references to | |
181 | any data in the mapping. */ | |
182 | char * | |
b6ab06ce UD |
183 | _dl_load_cache_lookup (const char *name) |
184 | { | |
185 | int left, right, middle; | |
186 | int cmpres; | |
187 | const char *cache_data; | |
188 | uint32_t cache_data_size; | |
189 | const char *best; | |
190 | ||
191 | /* Print a message if the loading of libs is traced. */ | |
a1ffb40e | 192 | if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) |
b6ab06ce UD |
193 | _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE); |
194 | ||
195 | if (cache == NULL) | |
196 | { | |
197 | /* Read the contents of the file. */ | |
198 | void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize, | |
199 | PROT_READ); | |
200 | ||
201 | /* We can handle three different cache file formats here: | |
202 | - the old libc5/glibc2.0/2.1 format | |
203 | - the old format with the new format in it | |
204 | - only the new format | |
205 | The following checks if the cache contains any of these formats. */ | |
206 | if (file != MAP_FAILED && cachesize > sizeof *cache | |
207 | && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0) | |
208 | { | |
209 | size_t offset; | |
210 | /* Looks ok. */ | |
211 | cache = file; | |
212 | ||
213 | /* Check for new version. */ | |
214 | offset = ALIGN_CACHE (sizeof (struct cache_file) | |
215 | + cache->nlibs * sizeof (struct file_entry)); | |
216 | ||
217 | cache_new = (struct cache_file_new *) ((void *) cache + offset); | |
218 | if (cachesize < (offset + sizeof (struct cache_file_new)) | |
219 | || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW, | |
220 | sizeof CACHEMAGIC_VERSION_NEW - 1) != 0) | |
221 | cache_new = (void *) -1; | |
222 | } | |
223 | else if (file != MAP_FAILED && cachesize > sizeof *cache_new | |
224 | && memcmp (file, CACHEMAGIC_VERSION_NEW, | |
225 | sizeof CACHEMAGIC_VERSION_NEW - 1) == 0) | |
226 | { | |
227 | cache_new = file; | |
228 | cache = file; | |
229 | } | |
230 | else | |
231 | { | |
232 | if (file != MAP_FAILED) | |
233 | __munmap (file, cachesize); | |
234 | cache = (void *) -1; | |
235 | } | |
236 | ||
237 | assert (cache != NULL); | |
238 | } | |
239 | ||
240 | if (cache == (void *) -1) | |
241 | /* Previously looked for the cache file and didn't find it. */ | |
242 | return NULL; | |
243 | ||
244 | best = NULL; | |
245 | ||
246 | if (cache_new != (void *) -1) | |
247 | { | |
248 | uint64_t platform; | |
249 | ||
250 | /* This is where the strings start. */ | |
251 | cache_data = (const char *) cache_new; | |
252 | ||
253 | /* Now we can compute how large the string table is. */ | |
254 | cache_data_size = (const char *) cache + cachesize - cache_data; | |
255 | ||
256 | platform = _dl_string_platform (GLRO(dl_platform)); | |
257 | if (platform != (uint64_t) -1) | |
258 | platform = 1ULL << platform; | |
259 | ||
ff08fc59 SP |
260 | uint64_t hwcap_mask = GET_HWCAP_MASK(); |
261 | ||
11bf311e | 262 | #define _DL_HWCAP_TLS_MASK (1LL << 63) |
ff08fc59 | 263 | uint64_t hwcap_exclude = ~((GLRO(dl_hwcap) & hwcap_mask) |
7bfa311f RM |
264 | | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK); |
265 | ||
266 | /* Only accept hwcap if it's for the right platform. */ | |
b6ab06ce | 267 | #define HWCAP_CHECK \ |
7bfa311f RM |
268 | if (lib->hwcap & hwcap_exclude) \ |
269 | continue; \ | |
b6ab06ce UD |
270 | if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion)) \ |
271 | continue; \ | |
33315ced | 272 | if (_DL_PLATFORMS_COUNT \ |
b6ab06ce UD |
273 | && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0 \ |
274 | && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform) \ | |
b6ab06ce UD |
275 | continue |
276 | SEARCH_CACHE (cache_new); | |
277 | } | |
278 | else | |
279 | { | |
280 | /* This is where the strings start. */ | |
281 | cache_data = (const char *) &cache->libs[cache->nlibs]; | |
282 | ||
283 | /* Now we can compute how large the string table is. */ | |
284 | cache_data_size = (const char *) cache + cachesize - cache_data; | |
285 | ||
286 | #undef HWCAP_CHECK | |
287 | #define HWCAP_CHECK do {} while (0) | |
288 | SEARCH_CACHE (cache); | |
289 | } | |
290 | ||
291 | /* Print our result if wanted. */ | |
292 | if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0) | |
293 | && best != NULL) | |
294 | _dl_debug_printf (" trying file=%s\n", best); | |
295 | ||
ccdb048d CD |
296 | if (best == NULL) |
297 | return NULL; | |
298 | ||
299 | /* The double copy is *required* since malloc may be interposed | |
300 | and call dlopen itself whose completion would unmap the data | |
301 | we are accessing. Therefore we must make the copy of the | |
302 | mapping data without using malloc. */ | |
303 | char *temp; | |
304 | temp = alloca (strlen (best) + 1); | |
305 | strcpy (temp, best); | |
ae65d4f3 | 306 | return __strdup (temp); |
b6ab06ce UD |
307 | } |
308 | ||
309 | #ifndef MAP_COPY | |
310 | /* If the system does not support MAP_COPY we cannot leave the file open | |
311 | all the time since this would create problems when the file is replaced. | |
312 | Therefore we provide this function to close the file and open it again | |
313 | once needed. */ | |
314 | void | |
315 | _dl_unload_cache (void) | |
316 | { | |
317 | if (cache != NULL && cache != (struct cache_file *) -1) | |
318 | { | |
319 | __munmap (cache, cachesize); | |
320 | cache = NULL; | |
321 | } | |
322 | } | |
323 | #endif |