| xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Support for reading /etc/ld.so.cache files written by Linux ldconfig. | 
|  | 2 | Copyright (C) 1996-2016 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 | <http://www.gnu.org/licenses/>.  */ | 
|  | 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> | 
|  | 25 | #include <stdint.h> | 
|  | 26 | #include <_itoa.h> | 
|  | 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);		      \ | 
|  | 68 | if (__glibc_unlikely (cmpres == 0))				      \ | 
|  | 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 |  | 
|  | 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. | 
|  | 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 * | 
|  | 183 | internal_function | 
|  | 184 | _dl_load_cache_lookup (const char *name) | 
|  | 185 | { | 
|  | 186 | int left, right, middle; | 
|  | 187 | int cmpres; | 
|  | 188 | const char *cache_data; | 
|  | 189 | uint32_t cache_data_size; | 
|  | 190 | const char *best; | 
|  | 191 |  | 
|  | 192 | /* Print a message if the loading of libs is traced.  */ | 
|  | 193 | if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) | 
|  | 194 | _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE); | 
|  | 195 |  | 
|  | 196 | if (cache == NULL) | 
|  | 197 | { | 
|  | 198 | /* Read the contents of the file.  */ | 
|  | 199 | void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize, | 
|  | 200 | PROT_READ); | 
|  | 201 |  | 
|  | 202 | /* We can handle three different cache file formats here: | 
|  | 203 | - the old libc5/glibc2.0/2.1 format | 
|  | 204 | - the old format with the new format in it | 
|  | 205 | - only the new format | 
|  | 206 | The following checks if the cache contains any of these formats.  */ | 
|  | 207 | if (file != MAP_FAILED && cachesize > sizeof *cache | 
|  | 208 | && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0) | 
|  | 209 | { | 
|  | 210 | size_t offset; | 
|  | 211 | /* Looks ok.  */ | 
|  | 212 | cache = file; | 
|  | 213 |  | 
|  | 214 | /* Check for new version.  */ | 
|  | 215 | offset = ALIGN_CACHE (sizeof (struct cache_file) | 
|  | 216 | + cache->nlibs * sizeof (struct file_entry)); | 
|  | 217 |  | 
|  | 218 | cache_new = (struct cache_file_new *) ((void *) cache + offset); | 
|  | 219 | if (cachesize < (offset + sizeof (struct cache_file_new)) | 
|  | 220 | || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW, | 
|  | 221 | sizeof CACHEMAGIC_VERSION_NEW - 1) != 0) | 
|  | 222 | cache_new = (void *) -1; | 
|  | 223 | } | 
|  | 224 | else if (file != MAP_FAILED && cachesize > sizeof *cache_new | 
|  | 225 | && memcmp (file, CACHEMAGIC_VERSION_NEW, | 
|  | 226 | sizeof CACHEMAGIC_VERSION_NEW - 1) == 0) | 
|  | 227 | { | 
|  | 228 | cache_new = file; | 
|  | 229 | cache = file; | 
|  | 230 | } | 
|  | 231 | else | 
|  | 232 | { | 
|  | 233 | if (file != MAP_FAILED) | 
|  | 234 | __munmap (file, cachesize); | 
|  | 235 | cache = (void *) -1; | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | assert (cache != NULL); | 
|  | 239 | } | 
|  | 240 |  | 
|  | 241 | if (cache == (void *) -1) | 
|  | 242 | /* Previously looked for the cache file and didn't find it.  */ | 
|  | 243 | return NULL; | 
|  | 244 |  | 
|  | 245 | best = NULL; | 
|  | 246 |  | 
|  | 247 | if (cache_new != (void *) -1) | 
|  | 248 | { | 
|  | 249 | uint64_t platform; | 
|  | 250 |  | 
|  | 251 | /* This is where the strings start.  */ | 
|  | 252 | cache_data = (const char *) cache_new; | 
|  | 253 |  | 
|  | 254 | /* Now we can compute how large the string table is.  */ | 
|  | 255 | cache_data_size = (const char *) cache + cachesize - cache_data; | 
|  | 256 |  | 
|  | 257 | platform = _dl_string_platform (GLRO(dl_platform)); | 
|  | 258 | if (platform != (uint64_t) -1) | 
|  | 259 | platform = 1ULL << platform; | 
|  | 260 |  | 
|  | 261 | #define _DL_HWCAP_TLS_MASK (1LL << 63) | 
|  | 262 | uint64_t hwcap_exclude = ~((GLRO(dl_hwcap) & GLRO(dl_hwcap_mask)) | 
|  | 263 | | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK); | 
|  | 264 |  | 
|  | 265 | /* Only accept hwcap if it's for the right platform.  */ | 
|  | 266 | #define HWCAP_CHECK \ | 
|  | 267 | if (lib->hwcap & hwcap_exclude)					      \ | 
|  | 268 | continue;							      \ | 
|  | 269 | if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion))	      \ | 
|  | 270 | continue;							      \ | 
|  | 271 | if (_DL_PLATFORMS_COUNT						      \ | 
|  | 272 | && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0			      \ | 
|  | 273 | && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform)		      \ | 
|  | 274 | continue | 
|  | 275 | SEARCH_CACHE (cache_new); | 
|  | 276 | } | 
|  | 277 | else | 
|  | 278 | { | 
|  | 279 | /* This is where the strings start.  */ | 
|  | 280 | cache_data = (const char *) &cache->libs[cache->nlibs]; | 
|  | 281 |  | 
|  | 282 | /* Now we can compute how large the string table is.  */ | 
|  | 283 | cache_data_size = (const char *) cache + cachesize - cache_data; | 
|  | 284 |  | 
|  | 285 | #undef HWCAP_CHECK | 
|  | 286 | #define HWCAP_CHECK do {} while (0) | 
|  | 287 | SEARCH_CACHE (cache); | 
|  | 288 | } | 
|  | 289 |  | 
|  | 290 | /* Print our result if wanted.  */ | 
|  | 291 | if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0) | 
|  | 292 | && best != NULL) | 
|  | 293 | _dl_debug_printf ("  trying file=%s\n", best); | 
|  | 294 |  | 
|  | 295 | if (best == NULL) | 
|  | 296 | return NULL; | 
|  | 297 |  | 
|  | 298 | /* The double copy is *required* since malloc may be interposed | 
|  | 299 | and call dlopen itself whose completion would unmap the data | 
|  | 300 | we are accessing. Therefore we must make the copy of the | 
|  | 301 | mapping data without using malloc.  */ | 
|  | 302 | char *temp; | 
|  | 303 | temp = alloca (strlen (best) + 1); | 
|  | 304 | strcpy (temp, best); | 
|  | 305 | return strdup (temp); | 
|  | 306 | } | 
|  | 307 |  | 
|  | 308 | #ifndef MAP_COPY | 
|  | 309 | /* If the system does not support MAP_COPY we cannot leave the file open | 
|  | 310 | all the time since this would create problems when the file is replaced. | 
|  | 311 | Therefore we provide this function to close the file and open it again | 
|  | 312 | once needed.  */ | 
|  | 313 | void | 
|  | 314 | _dl_unload_cache (void) | 
|  | 315 | { | 
|  | 316 | if (cache != NULL && cache != (struct cache_file *) -1) | 
|  | 317 | { | 
|  | 318 | __munmap (cache, cachesize); | 
|  | 319 | cache = NULL; | 
|  | 320 | } | 
|  | 321 | } | 
|  | 322 | #endif |