]>
Commit | Line | Data |
---|---|---|
b6ab06ce | 1 | /* Support for reading /etc/ld.so.cache files written by Linux ldconfig. |
d4697bc9 | 2 | Copyright (C) 1996-2014 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> |
b6ab06ce UD |
27 | |
28 | #ifndef _DL_PLATFORMS_COUNT | |
29 | # define _DL_PLATFORMS_COUNT 0 | |
30 | #endif | |
31 | ||
32 | /* This is the starting address and the size of the mmap()ed file. */ | |
33 | static struct cache_file *cache; | |
34 | static struct cache_file_new *cache_new; | |
35 | static size_t cachesize; | |
36 | ||
37 | /* 1 if cache_data + PTR points into the cache. */ | |
38 | #define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size) | |
39 | ||
40 | #define SEARCH_CACHE(cache) \ | |
41 | /* We use binary search since the table is sorted in the cache file. \ | |
42 | The first matching entry in the table is returned. \ | |
43 | It is important to use the same algorithm as used while generating \ | |
44 | the cache file. */ \ | |
45 | do \ | |
46 | { \ | |
47 | left = 0; \ | |
48 | right = cache->nlibs - 1; \ | |
49 | \ | |
50 | while (left <= right) \ | |
51 | { \ | |
52 | __typeof__ (cache->libs[0].key) key; \ | |
53 | \ | |
54 | middle = (left + right) / 2; \ | |
55 | \ | |
56 | key = cache->libs[middle].key; \ | |
57 | \ | |
58 | /* Make sure string table indices are not bogus before using \ | |
59 | them. */ \ | |
60 | if (! _dl_cache_verify_ptr (key)) \ | |
61 | { \ | |
62 | cmpres = 1; \ | |
63 | break; \ | |
64 | } \ | |
65 | \ | |
66 | /* Actually compare the entry with the key. */ \ | |
67 | cmpres = _dl_cache_libcmp (name, cache_data + key); \ | |
a1ffb40e | 68 | if (__glibc_unlikely (cmpres == 0)) \ |
b6ab06ce UD |
69 | { \ |
70 | /* Found it. LEFT now marks the last entry for which we \ | |
71 | know the name is correct. */ \ | |
72 | left = middle; \ | |
73 | \ | |
74 | /* There might be entries with this name before the one we \ | |
75 | found. So we have to find the beginning. */ \ | |
76 | while (middle > 0) \ | |
77 | { \ | |
78 | __typeof__ (cache->libs[0].key) key; \ | |
79 | \ | |
80 | key = cache->libs[middle - 1].key; \ | |
81 | /* Make sure string table indices are not bogus before \ | |
82 | using them. */ \ | |
83 | if (! _dl_cache_verify_ptr (key) \ | |
84 | /* Actually compare the entry. */ \ | |
85 | || _dl_cache_libcmp (name, cache_data + key) != 0) \ | |
86 | break; \ | |
87 | --middle; \ | |
88 | } \ | |
89 | \ | |
90 | do \ | |
91 | { \ | |
92 | int flags; \ | |
93 | __typeof__ (cache->libs[0]) *lib = &cache->libs[middle]; \ | |
94 | \ | |
95 | /* Only perform the name test if necessary. */ \ | |
96 | if (middle > left \ | |
97 | /* We haven't seen this string so far. Test whether the \ | |
98 | index is ok and whether the name matches. Otherwise \ | |
99 | we are done. */ \ | |
100 | && (! _dl_cache_verify_ptr (lib->key) \ | |
101 | || (_dl_cache_libcmp (name, cache_data + lib->key) \ | |
102 | != 0))) \ | |
103 | break; \ | |
104 | \ | |
105 | flags = lib->flags; \ | |
106 | if (_dl_cache_check_flags (flags) \ | |
107 | && _dl_cache_verify_ptr (lib->value)) \ | |
108 | { \ | |
109 | if (best == NULL || flags == GLRO(dl_correct_cache_id)) \ | |
110 | { \ | |
111 | HWCAP_CHECK; \ | |
112 | best = cache_data + lib->value; \ | |
113 | \ | |
114 | if (flags == GLRO(dl_correct_cache_id)) \ | |
115 | /* We've found an exact match for the shared \ | |
116 | object and no general `ELF' release. Stop \ | |
117 | searching. */ \ | |
118 | break; \ | |
119 | } \ | |
120 | } \ | |
121 | } \ | |
122 | while (++middle <= right); \ | |
123 | break; \ | |
124 | } \ | |
125 | \ | |
126 | if (cmpres < 0) \ | |
127 | left = middle + 1; \ | |
128 | else \ | |
129 | right = middle - 1; \ | |
130 | } \ | |
131 | } \ | |
132 | while (0) | |
133 | ||
134 | ||
135 | int | |
136 | internal_function | |
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 | |
177 | the cache previously failed there will be no more attempts to load it. */ | |
b6ab06ce UD |
178 | |
179 | const char * | |
180 | internal_function | |
181 | _dl_load_cache_lookup (const char *name) | |
182 | { | |
183 | int left, right, middle; | |
184 | int cmpres; | |
185 | const char *cache_data; | |
186 | uint32_t cache_data_size; | |
187 | const char *best; | |
188 | ||
189 | /* Print a message if the loading of libs is traced. */ | |
a1ffb40e | 190 | if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) |
b6ab06ce UD |
191 | _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE); |
192 | ||
193 | if (cache == NULL) | |
194 | { | |
195 | /* Read the contents of the file. */ | |
196 | void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize, | |
197 | PROT_READ); | |
198 | ||
199 | /* We can handle three different cache file formats here: | |
200 | - the old libc5/glibc2.0/2.1 format | |
201 | - the old format with the new format in it | |
202 | - only the new format | |
203 | The following checks if the cache contains any of these formats. */ | |
204 | if (file != MAP_FAILED && cachesize > sizeof *cache | |
205 | && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0) | |
206 | { | |
207 | size_t offset; | |
208 | /* Looks ok. */ | |
209 | cache = file; | |
210 | ||
211 | /* Check for new version. */ | |
212 | offset = ALIGN_CACHE (sizeof (struct cache_file) | |
213 | + cache->nlibs * sizeof (struct file_entry)); | |
214 | ||
215 | cache_new = (struct cache_file_new *) ((void *) cache + offset); | |
216 | if (cachesize < (offset + sizeof (struct cache_file_new)) | |
217 | || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW, | |
218 | sizeof CACHEMAGIC_VERSION_NEW - 1) != 0) | |
219 | cache_new = (void *) -1; | |
220 | } | |
221 | else if (file != MAP_FAILED && cachesize > sizeof *cache_new | |
222 | && memcmp (file, CACHEMAGIC_VERSION_NEW, | |
223 | sizeof CACHEMAGIC_VERSION_NEW - 1) == 0) | |
224 | { | |
225 | cache_new = file; | |
226 | cache = file; | |
227 | } | |
228 | else | |
229 | { | |
230 | if (file != MAP_FAILED) | |
231 | __munmap (file, cachesize); | |
232 | cache = (void *) -1; | |
233 | } | |
234 | ||
235 | assert (cache != NULL); | |
236 | } | |
237 | ||
238 | if (cache == (void *) -1) | |
239 | /* Previously looked for the cache file and didn't find it. */ | |
240 | return NULL; | |
241 | ||
242 | best = NULL; | |
243 | ||
244 | if (cache_new != (void *) -1) | |
245 | { | |
246 | uint64_t platform; | |
247 | ||
248 | /* This is where the strings start. */ | |
249 | cache_data = (const char *) cache_new; | |
250 | ||
251 | /* Now we can compute how large the string table is. */ | |
252 | cache_data_size = (const char *) cache + cachesize - cache_data; | |
253 | ||
254 | platform = _dl_string_platform (GLRO(dl_platform)); | |
255 | if (platform != (uint64_t) -1) | |
256 | platform = 1ULL << platform; | |
257 | ||
11bf311e | 258 | #define _DL_HWCAP_TLS_MASK (1LL << 63) |
7bfa311f RM |
259 | uint64_t hwcap_exclude = ~((GLRO(dl_hwcap) & GLRO(dl_hwcap_mask)) |
260 | | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK); | |
261 | ||
262 | /* Only accept hwcap if it's for the right platform. */ | |
b6ab06ce | 263 | #define HWCAP_CHECK \ |
7bfa311f RM |
264 | if (lib->hwcap & hwcap_exclude) \ |
265 | continue; \ | |
b6ab06ce UD |
266 | if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion)) \ |
267 | continue; \ | |
33315ced | 268 | if (_DL_PLATFORMS_COUNT \ |
b6ab06ce UD |
269 | && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0 \ |
270 | && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform) \ | |
b6ab06ce UD |
271 | continue |
272 | SEARCH_CACHE (cache_new); | |
273 | } | |
274 | else | |
275 | { | |
276 | /* This is where the strings start. */ | |
277 | cache_data = (const char *) &cache->libs[cache->nlibs]; | |
278 | ||
279 | /* Now we can compute how large the string table is. */ | |
280 | cache_data_size = (const char *) cache + cachesize - cache_data; | |
281 | ||
282 | #undef HWCAP_CHECK | |
283 | #define HWCAP_CHECK do {} while (0) | |
284 | SEARCH_CACHE (cache); | |
285 | } | |
286 | ||
287 | /* Print our result if wanted. */ | |
288 | if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0) | |
289 | && best != NULL) | |
290 | _dl_debug_printf (" trying file=%s\n", best); | |
291 | ||
292 | return best; | |
293 | } | |
294 | ||
295 | #ifndef MAP_COPY | |
296 | /* If the system does not support MAP_COPY we cannot leave the file open | |
297 | all the time since this would create problems when the file is replaced. | |
298 | Therefore we provide this function to close the file and open it again | |
299 | once needed. */ | |
300 | void | |
301 | _dl_unload_cache (void) | |
302 | { | |
303 | if (cache != NULL && cache != (struct cache_file *) -1) | |
304 | { | |
305 | __munmap (cache, cachesize); | |
306 | cache = NULL; | |
307 | } | |
308 | } | |
309 | #endif |