]>
Commit | Line | Data |
---|---|---|
6d7e8eda | 1 | /* Copyright (C) 1999-2023 Free Software Foundation, Inc. |
591e1ffb | 2 | This file is part of the GNU C Library. |
591e1ffb | 3 | |
43bc8ac6 | 4 | This program is free software; you can redistribute it and/or modify |
2e2efe65 RM |
5 | it under the terms of the GNU General Public License as published |
6 | by the Free Software Foundation; version 2 of the License, or | |
7 | (at your option) any later version. | |
591e1ffb | 8 | |
43bc8ac6 | 9 | This program is distributed in the hope that it will be useful, |
591e1ffb | 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
43bc8ac6 UD |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU General Public License for more details. | |
591e1ffb | 13 | |
43bc8ac6 | 14 | You should have received a copy of the GNU General Public License |
5a82c748 | 15 | along with this program; if not, see <https://www.gnu.org/licenses/>. */ |
591e1ffb | 16 | |
dfb3f101 | 17 | #include <assert.h> |
591e1ffb UD |
18 | #include <errno.h> |
19 | #include <error.h> | |
20 | #include <dirent.h> | |
68162753 | 21 | #include <inttypes.h> |
27d9ffda | 22 | #include <libgen.h> |
591e1ffb UD |
23 | #include <libintl.h> |
24 | #include <stdio.h> | |
25 | #include <stdlib.h> | |
26 | #include <string.h> | |
27 | #include <unistd.h> | |
e054f494 | 28 | #include <stdint.h> |
591e1ffb UD |
29 | #include <sys/fcntl.h> |
30 | #include <sys/mman.h> | |
880433de | 31 | #include <sys/param.h> |
591e1ffb UD |
32 | #include <sys/stat.h> |
33 | #include <sys/types.h> | |
34 | ||
8f480b4b RM |
35 | #include <ldconfig.h> |
36 | #include <dl-cache.h> | |
dfb3f101 | 37 | #include <version.h> |
73b6e50a FW |
38 | #include <stringtable.h> |
39 | ||
40 | /* Used to store library names, paths, and other strings. */ | |
41 | static struct stringtable strings; | |
591e1ffb | 42 | |
b44ac4f4 FW |
43 | /* Keeping track of "glibc-hwcaps" subdirectories. During cache |
44 | construction, a linear search by name is performed to deduplicate | |
45 | entries. */ | |
46 | struct glibc_hwcaps_subdirectory | |
47 | { | |
48 | struct glibc_hwcaps_subdirectory *next; | |
49 | ||
50 | /* Interned string with the subdirectory name. */ | |
51 | struct stringtable_entry *name; | |
52 | ||
53 | /* Array index in the cache_extension_tag_glibc_hwcaps section in | |
54 | the stored cached file. This is computed after all the | |
55 | subdirectories have been processed, so that subdirectory names in | |
56 | the extension section can be sorted. */ | |
57 | uint32_t section_index; | |
58 | ||
59 | /* True if the subdirectory is actually used for anything. */ | |
60 | bool used; | |
61 | }; | |
62 | ||
63 | const char * | |
64 | glibc_hwcaps_subdirectory_name (const struct glibc_hwcaps_subdirectory *dir) | |
65 | { | |
66 | return dir->name->string; | |
67 | } | |
68 | ||
69 | /* Linked list of known hwcaps subdirecty names. */ | |
70 | static struct glibc_hwcaps_subdirectory *hwcaps; | |
71 | ||
72 | struct glibc_hwcaps_subdirectory * | |
73 | new_glibc_hwcaps_subdirectory (const char *name) | |
74 | { | |
75 | struct stringtable_entry *name_interned = stringtable_add (&strings, name); | |
76 | for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) | |
77 | if (p->name == name_interned) | |
78 | return p; | |
79 | struct glibc_hwcaps_subdirectory *p = xmalloc (sizeof (*p)); | |
80 | p->next = hwcaps; | |
81 | p->name = name_interned; | |
82 | p->section_index = 0; | |
83 | p->used = false; | |
84 | hwcaps = p; | |
85 | return p; | |
86 | } | |
87 | ||
88 | /* Helper for sorting struct glibc_hwcaps_subdirectory elements by | |
89 | name. */ | |
90 | static int | |
91 | assign_glibc_hwcaps_indices_compare (const void *l, const void *r) | |
92 | { | |
93 | const struct glibc_hwcaps_subdirectory *left | |
94 | = *(struct glibc_hwcaps_subdirectory **)l; | |
95 | const struct glibc_hwcaps_subdirectory *right | |
96 | = *(struct glibc_hwcaps_subdirectory **)r; | |
97 | return strcmp (glibc_hwcaps_subdirectory_name (left), | |
98 | glibc_hwcaps_subdirectory_name (right)); | |
99 | } | |
100 | ||
101 | /* Count the number of hwcaps subdirectories which are actually | |
102 | used. */ | |
103 | static size_t | |
104 | glibc_hwcaps_count (void) | |
105 | { | |
106 | size_t count = 0; | |
107 | for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) | |
108 | if (p->used) | |
109 | ++count; | |
110 | return count; | |
111 | } | |
112 | ||
113 | /* Compute the section_index fields for all */ | |
114 | static void | |
115 | assign_glibc_hwcaps_indices (void) | |
116 | { | |
117 | /* Convert the linked list into an array, so that we can use qsort. | |
118 | Only copy the subdirectories which are actually used. */ | |
119 | size_t count = glibc_hwcaps_count (); | |
120 | struct glibc_hwcaps_subdirectory **array | |
121 | = xmalloc (sizeof (*array) * count); | |
122 | { | |
123 | size_t i = 0; | |
124 | for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) | |
125 | if (p->used) | |
126 | { | |
127 | array[i] = p; | |
128 | ++i; | |
129 | } | |
130 | assert (i == count); | |
131 | } | |
132 | ||
133 | qsort (array, count, sizeof (*array), assign_glibc_hwcaps_indices_compare); | |
134 | ||
135 | /* Assign the array indices. */ | |
136 | for (size_t i = 0; i < count; ++i) | |
137 | array[i]->section_index = i; | |
138 | ||
139 | free (array); | |
140 | } | |
141 | ||
591e1ffb UD |
142 | struct cache_entry |
143 | { | |
73b6e50a FW |
144 | struct stringtable_entry *lib; /* Library name. */ |
145 | struct stringtable_entry *path; /* Path to find library. */ | |
45eca4d1 | 146 | int flags; /* Flags to indicate kind of library. */ |
efbbd9c3 | 147 | unsigned int isa_level; /* Required ISA level. */ |
b44ac4f4 | 148 | |
cfbf883d | 149 | /* glibc-hwcaps subdirectory. */ |
b44ac4f4 FW |
150 | struct glibc_hwcaps_subdirectory *hwcaps; |
151 | ||
45eca4d1 | 152 | struct cache_entry *next; /* Next entry in list. */ |
591e1ffb UD |
153 | }; |
154 | ||
591e1ffb UD |
155 | /* List of all cache entries. */ |
156 | static struct cache_entry *entries; | |
157 | ||
8ee87859 | 158 | /* libc4, ELF and libc5 are unsupported. */ |
591e1ffb UD |
159 | static const char *flag_descr[] = |
160 | { "libc4", "ELF", "libc5", "libc6"}; | |
161 | ||
591e1ffb UD |
162 | /* Print a single entry. */ |
163 | static void | |
c628c229 AZ |
164 | print_entry (const char *lib, int flag, uint64_t hwcap, |
165 | const char *hwcap_string, const char *key) | |
591e1ffb UD |
166 | { |
167 | printf ("\t%s (", lib); | |
0b7219cc | 168 | switch (flag & FLAG_TYPE_MASK) |
591e1ffb | 169 | { |
591e1ffb | 170 | case FLAG_ELF_LIBC6: |
a986484f | 171 | fputs (flag_descr[flag & FLAG_TYPE_MASK], stdout); |
591e1ffb UD |
172 | break; |
173 | default: | |
8ee87859 | 174 | fputs (_("unknown or unsupported flag"), stdout); |
591e1ffb UD |
175 | break; |
176 | } | |
177 | switch (flag & FLAG_REQUIRED_MASK) | |
178 | { | |
591e1ffb UD |
179 | case FLAG_SPARC_LIB64: |
180 | fputs (",64bit", stdout); | |
48c53070 | 181 | break; |
95582174 UD |
182 | case FLAG_IA64_LIB64: |
183 | fputs (",IA-64", stdout); | |
fdedb42e | 184 | break; |
fdedb42e AJ |
185 | case FLAG_X8664_LIB64: |
186 | fputs (",x86-64", stdout); | |
187 | break; | |
96c0d65d | 188 | case FLAG_S390_LIB64: |
27d9ffda | 189 | fputs (",64bit", stdout); |
48c53070 RM |
190 | break; |
191 | case FLAG_POWERPC_LIB64: | |
27d9ffda | 192 | fputs (",64bit", stdout); |
48c53070 | 193 | break; |
b5bac573 | 194 | case FLAG_MIPS64_LIBN32: |
27d9ffda | 195 | fputs (",N32", stdout); |
b5bac573 AO |
196 | break; |
197 | case FLAG_MIPS64_LIBN64: | |
27d9ffda | 198 | fputs (",64bit", stdout); |
f1a77b01 L |
199 | break; |
200 | case FLAG_X8664_LIBX32: | |
201 | fputs (",x32", stdout); | |
202 | break; | |
6665d4a2 SM |
203 | case FLAG_ARM_LIBHF: |
204 | fputs (",hard-float", stdout); | |
205 | break; | |
1f51ee92 SM |
206 | case FLAG_AARCH64_LIB64: |
207 | fputs (",AArch64", stdout); | |
208 | break; | |
b39949d2 CD |
209 | /* Uses the ARM soft-float ABI. */ |
210 | case FLAG_ARM_LIBSF: | |
211 | fputs (",soft-float", stdout); | |
212 | break; | |
9c21573c MR |
213 | case FLAG_MIPS_LIB32_NAN2008: |
214 | fputs (",nan2008", stdout); | |
215 | break; | |
216 | case FLAG_MIPS64_LIBN32_NAN2008: | |
217 | fputs (",N32,nan2008", stdout); | |
218 | break; | |
219 | case FLAG_MIPS64_LIBN64_NAN2008: | |
220 | fputs (",64bit,nan2008", stdout); | |
221 | break; | |
fb58aac5 PD |
222 | case FLAG_RISCV_FLOAT_ABI_SOFT: |
223 | fputs (",soft-float", stdout); | |
224 | break; | |
225 | case FLAG_RISCV_FLOAT_ABI_DOUBLE: | |
226 | fputs (",double-float", stdout); | |
227 | break; | |
591e1ffb UD |
228 | case 0: |
229 | break; | |
230 | default: | |
45eca4d1 | 231 | printf (",%d", flag & FLAG_REQUIRED_MASK); |
591e1ffb UD |
232 | break; |
233 | } | |
b44ac4f4 FW |
234 | if (hwcap_string != NULL) |
235 | printf (", hwcap: \"%s\"", hwcap_string); | |
236 | else if (hwcap != 0) | |
ab1d521d | 237 | printf (", hwcap: %#.16" PRIx64, hwcap); |
591e1ffb UD |
238 | printf (") => %s\n", key); |
239 | } | |
240 | ||
b44ac4f4 FW |
241 | /* Returns the string with the name of the glibcs-hwcaps subdirectory |
242 | associated with ENTRY->hwcap. file_base must be the base address | |
243 | for string table indices. */ | |
244 | static const char * | |
245 | glibc_hwcaps_string (struct cache_extension_all_loaded *ext, | |
246 | const void *file_base, size_t file_size, | |
247 | struct file_entry_new *entry) | |
248 | { | |
249 | const uint32_t *hwcaps_array | |
250 | = ext->sections[cache_extension_tag_glibc_hwcaps].base; | |
251 | if (dl_cache_hwcap_extension (entry) && hwcaps_array != NULL) | |
252 | { | |
253 | uint32_t index = (uint32_t) entry->hwcap; | |
254 | if (index < ext->sections[cache_extension_tag_glibc_hwcaps].size / 4) | |
255 | { | |
256 | uint32_t string_table_index = hwcaps_array[index]; | |
257 | if (string_table_index < file_size) | |
258 | return file_base + string_table_index; | |
259 | } | |
260 | } | |
261 | return NULL; | |
262 | } | |
263 | ||
84ba719b FW |
264 | /* Print an error and exit if the new-file cache is internally |
265 | inconsistent. */ | |
266 | static void | |
267 | check_new_cache (struct cache_file_new *cache) | |
268 | { | |
269 | if (! cache_file_new_matches_endian (cache)) | |
270 | error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n")); | |
271 | } | |
591e1ffb | 272 | |
b44ac4f4 | 273 | /* Print the extension information in *EXT. */ |
dfb3f101 FW |
274 | static void |
275 | print_extensions (struct cache_extension_all_loaded *ext) | |
276 | { | |
277 | if (ext->sections[cache_extension_tag_generator].base != NULL) | |
278 | { | |
279 | fputs (_("Cache generated by: "), stdout); | |
280 | fwrite (ext->sections[cache_extension_tag_generator].base, 1, | |
281 | ext->sections[cache_extension_tag_generator].size, stdout); | |
282 | putchar ('\n'); | |
283 | } | |
284 | } | |
285 | ||
45eca4d1 UD |
286 | /* Print the whole cache file, if a file contains the new cache format |
287 | hidden in the old one, print the contents of the new format. */ | |
591e1ffb UD |
288 | void |
289 | print_cache (const char *cache_name) | |
290 | { | |
27d9ffda | 291 | int fd = open (cache_name, O_RDONLY); |
591e1ffb UD |
292 | if (fd < 0) |
293 | error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name); | |
294 | ||
9fe6f636 AZ |
295 | struct stat st; |
296 | if (fstat (fd, &st) < 0 | |
591e1ffb UD |
297 | /* No need to map the file if it is empty. */ |
298 | || st.st_size == 0) | |
299 | { | |
300 | close (fd); | |
301 | return; | |
302 | } | |
25ee87d6 | 303 | |
27d9ffda UD |
304 | struct cache_file *cache |
305 | = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); | |
591e1ffb UD |
306 | if (cache == MAP_FAILED) |
307 | error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n")); | |
591e1ffb | 308 | |
27d9ffda | 309 | size_t cache_size = st.st_size; |
45eca4d1 UD |
310 | if (cache_size < sizeof (struct cache_file)) |
311 | error (EXIT_FAILURE, 0, _("File is not a cache file.\n")); | |
312 | ||
27d9ffda UD |
313 | struct cache_file_new *cache_new = NULL; |
314 | const char *cache_data; | |
315 | int format = 0; | |
316 | ||
45eca4d1 UD |
317 | if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1)) |
318 | { | |
319 | /* This can only be the new format without the old one. */ | |
320 | cache_new = (struct cache_file_new *) cache; | |
321 | ||
322 | if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1) | |
323 | || memcmp (cache_new->version, CACHE_VERSION, | |
324 | sizeof CACHE_VERSION - 1)) | |
325 | error (EXIT_FAILURE, 0, _("File is not a cache file.\n")); | |
84ba719b | 326 | check_new_cache (cache_new); |
45eca4d1 UD |
327 | format = 1; |
328 | /* This is where the strings start. */ | |
e25054c4 | 329 | cache_data = (const char *) cache_new; |
45eca4d1 UD |
330 | } |
331 | else | |
332 | { | |
2954daf0 AS |
333 | /* Check for corruption, avoiding overflow. */ |
334 | if ((cache_size - sizeof (struct cache_file)) / sizeof (struct file_entry) | |
335 | < cache->nlibs) | |
336 | error (EXIT_FAILURE, 0, _("File is not a cache file.\n")); | |
337 | ||
e25054c4 | 338 | size_t offset = ALIGN_CACHE (sizeof (struct cache_file) |
a986484f UD |
339 | + (cache->nlibs |
340 | * sizeof (struct file_entry))); | |
45eca4d1 UD |
341 | /* This is where the strings start. */ |
342 | cache_data = (const char *) &cache->libs[cache->nlibs]; | |
343 | ||
344 | /* Check for a new cache embedded in the old format. */ | |
a04549c1 JM |
345 | if (cache_size |
346 | > (offset + sizeof (struct cache_file_new))) | |
45eca4d1 | 347 | { |
e25054c4 AJ |
348 | |
349 | cache_new = (struct cache_file_new *) ((void *)cache + offset); | |
591e1ffb | 350 | |
a986484f UD |
351 | if (memcmp (cache_new->magic, CACHEMAGIC_NEW, |
352 | sizeof CACHEMAGIC_NEW - 1) == 0 | |
353 | && memcmp (cache_new->version, CACHE_VERSION, | |
354 | sizeof CACHE_VERSION - 1) == 0) | |
e25054c4 | 355 | { |
84ba719b | 356 | check_new_cache (cache_new); |
e25054c4 AJ |
357 | cache_data = (const char *) cache_new; |
358 | format = 1; | |
359 | } | |
45eca4d1 UD |
360 | } |
361 | } | |
25ee87d6 | 362 | |
45eca4d1 UD |
363 | if (format == 0) |
364 | { | |
365 | printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name); | |
591e1ffb | 366 | |
45eca4d1 | 367 | /* Print everything. */ |
27d9ffda | 368 | for (unsigned int i = 0; i < cache->nlibs; i++) |
45eca4d1 | 369 | print_entry (cache_data + cache->libs[i].key, |
c628c229 | 370 | cache->libs[i].flags, 0, NULL, |
45eca4d1 UD |
371 | cache_data + cache->libs[i].value); |
372 | } | |
373 | else if (format == 1) | |
374 | { | |
dfb3f101 FW |
375 | struct cache_extension_all_loaded ext; |
376 | if (!cache_extension_load (cache_new, cache, cache_size, &ext)) | |
377 | error (EXIT_FAILURE, 0, | |
378 | _("Malformed extension data in cache file %s\n"), cache_name); | |
379 | ||
a986484f UD |
380 | printf (_("%d libs found in cache `%s'\n"), |
381 | cache_new->nlibs, cache_name); | |
45eca4d1 UD |
382 | |
383 | /* Print everything. */ | |
27d9ffda | 384 | for (unsigned int i = 0; i < cache_new->nlibs; i++) |
b44ac4f4 FW |
385 | { |
386 | const char *hwcaps_string | |
387 | = glibc_hwcaps_string (&ext, cache, cache_size, | |
388 | &cache_new->libs[i]); | |
389 | print_entry (cache_data + cache_new->libs[i].key, | |
390 | cache_new->libs[i].flags, | |
b44ac4f4 FW |
391 | cache_new->libs[i].hwcap, hwcaps_string, |
392 | cache_data + cache_new->libs[i].value); | |
393 | } | |
dfb3f101 | 394 | print_extensions (&ext); |
45eca4d1 | 395 | } |
591e1ffb UD |
396 | /* Cleanup. */ |
397 | munmap (cache, cache_size); | |
398 | close (fd); | |
399 | } | |
400 | ||
401 | /* Initialize cache data structures. */ | |
402 | void | |
403 | init_cache (void) | |
404 | { | |
405 | entries = NULL; | |
406 | } | |
407 | ||
27d9ffda UD |
408 | static int |
409 | compare (const struct cache_entry *e1, const struct cache_entry *e2) | |
591e1ffb | 410 | { |
591e1ffb | 411 | /* We need to swap entries here to get the correct sort order. */ |
73b6e50a | 412 | int res = _dl_cache_libcmp (e2->lib->string, e1->lib->string); |
591e1ffb UD |
413 | if (res == 0) |
414 | { | |
415 | if (e1->flags < e2->flags) | |
416 | return 1; | |
417 | else if (e1->flags > e2->flags) | |
418 | return -1; | |
b44ac4f4 FW |
419 | /* Keep the glibc-hwcaps extension entries before the regular |
420 | entries, and sort them by their names. search_cache in | |
421 | dl-cache.c stops searching once the first non-extension entry | |
422 | is found, so the extension entries need to come first. */ | |
423 | else if (e1->hwcaps != NULL && e2->hwcaps == NULL) | |
424 | return -1; | |
425 | else if (e1->hwcaps == NULL && e2->hwcaps != NULL) | |
426 | return 1; | |
427 | else if (e1->hwcaps != NULL && e2->hwcaps != NULL) | |
428 | { | |
429 | res = strcmp (glibc_hwcaps_subdirectory_name (e1->hwcaps), | |
430 | glibc_hwcaps_subdirectory_name (e2->hwcaps)); | |
431 | if (res != 0) | |
432 | return res; | |
433 | } | |
591e1ffb UD |
434 | } |
435 | return res; | |
436 | } | |
437 | ||
dfb3f101 FW |
438 | /* Size of the cache extension directory. All tags are assumed to be |
439 | present. */ | |
440 | enum | |
441 | { | |
442 | cache_extension_size = (offsetof (struct cache_extension, sections) | |
443 | + (cache_extension_count | |
444 | * sizeof (struct cache_extension_section))) | |
445 | }; | |
446 | ||
b44ac4f4 FW |
447 | /* Write the cache extensions to FD. The string table is shifted by |
448 | STRING_TABLE_OFFSET. The extension directory is assumed to be | |
449 | located at CACHE_EXTENSION_OFFSET. assign_glibc_hwcaps_indices | |
450 | must have been called. */ | |
dfb3f101 | 451 | static void |
b44ac4f4 FW |
452 | write_extensions (int fd, uint32_t str_offset, |
453 | uint32_t cache_extension_offset) | |
dfb3f101 FW |
454 | { |
455 | assert ((cache_extension_offset % 4) == 0); | |
456 | ||
b44ac4f4 FW |
457 | /* The length and contents of the glibc-hwcaps section. */ |
458 | uint32_t hwcaps_count = glibc_hwcaps_count (); | |
459 | uint32_t hwcaps_offset = cache_extension_offset + cache_extension_size; | |
460 | uint32_t hwcaps_size = hwcaps_count * sizeof (uint32_t); | |
461 | uint32_t *hwcaps_array = xmalloc (hwcaps_size); | |
462 | for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) | |
463 | if (p->used) | |
464 | hwcaps_array[p->section_index] = str_offset + p->name->offset; | |
465 | ||
466 | /* This is the offset of the generator string. */ | |
467 | uint32_t generator_offset = hwcaps_offset; | |
468 | if (hwcaps_count == 0) | |
469 | /* There is no section for the hwcaps subdirectories. */ | |
470 | generator_offset -= sizeof (struct cache_extension_section); | |
471 | else | |
472 | /* The string table indices for the hwcaps subdirectories shift | |
473 | the generator string backwards. */ | |
474 | generator_offset += hwcaps_size; | |
475 | ||
dfb3f101 FW |
476 | struct cache_extension *ext = xmalloc (cache_extension_size); |
477 | ext->magic = cache_extension_magic; | |
dfb3f101 | 478 | |
b44ac4f4 FW |
479 | /* Extension index current being filled. */ |
480 | size_t xid = 0; | |
dfb3f101 FW |
481 | |
482 | const char *generator | |
483 | = "ldconfig " PKGVERSION RELEASE " release version " VERSION; | |
b44ac4f4 FW |
484 | ext->sections[xid].tag = cache_extension_tag_generator; |
485 | ext->sections[xid].flags = 0; | |
486 | ext->sections[xid].offset = generator_offset; | |
487 | ext->sections[xid].size = strlen (generator); | |
488 | ||
489 | if (hwcaps_count > 0) | |
490 | { | |
491 | ++xid; | |
492 | ext->sections[xid].tag = cache_extension_tag_glibc_hwcaps; | |
493 | ext->sections[xid].flags = 0; | |
494 | ext->sections[xid].offset = hwcaps_offset; | |
495 | ext->sections[xid].size = hwcaps_size; | |
496 | } | |
dfb3f101 | 497 | |
b44ac4f4 FW |
498 | ++xid; |
499 | ext->count = xid; | |
500 | assert (xid <= cache_extension_count); | |
501 | ||
502 | size_t ext_size = (offsetof (struct cache_extension, sections) | |
503 | + xid * sizeof (struct cache_extension_section)); | |
504 | if (write (fd, ext, ext_size) != ext_size | |
505 | || write (fd, hwcaps_array, hwcaps_size) != hwcaps_size | |
dfb3f101 FW |
506 | || write (fd, generator, strlen (generator)) != strlen (generator)) |
507 | error (EXIT_FAILURE, errno, _("Writing of cache extension data failed")); | |
508 | ||
a85cdcdb | 509 | free (hwcaps_array); |
dfb3f101 FW |
510 | free (ext); |
511 | } | |
512 | ||
efbbd9c3 L |
513 | /* Compute the hwcap value from ENTRY. */ |
514 | static inline uint64_t | |
515 | compute_hwcap_value (struct cache_entry *entry) | |
516 | { | |
517 | if (entry->isa_level > DL_CACHE_HWCAP_ISA_LEVEL_MASK) | |
518 | error (EXIT_FAILURE, 0, _("%s: ISA level is too high (%d > %d)"), | |
519 | entry->path->string, entry->isa_level, | |
520 | DL_CACHE_HWCAP_ISA_LEVEL_MASK); | |
521 | return (DL_CACHE_HWCAP_EXTENSION | |
522 | | (((uint64_t) entry->isa_level) << 32) | |
523 | | entry->hwcaps->section_index); | |
524 | } | |
525 | ||
591e1ffb UD |
526 | /* Save the contents of the cache. */ |
527 | void | |
528 | save_cache (const char *cache_name) | |
529 | { | |
591e1ffb UD |
530 | /* The cache entries are sorted already, save them in this order. */ |
531 | ||
b44ac4f4 FW |
532 | assign_glibc_hwcaps_indices (); |
533 | ||
27d9ffda UD |
534 | struct cache_entry *entry; |
535 | /* Number of cache entries. */ | |
536 | int cache_entry_count = 0; | |
73b6e50a FW |
537 | /* The old format doesn't contain hwcap entries and doesn't contain |
538 | libraries in subdirectories with hwcaps entries. Count therefore | |
cfbf883d | 539 | all entries. */ |
27d9ffda UD |
540 | int cache_entry_old_count = 0; |
541 | ||
591e1ffb UD |
542 | for (entry = entries; entry != NULL; entry = entry->next) |
543 | { | |
591e1ffb | 544 | ++cache_entry_count; |
cfbf883d | 545 | ++cache_entry_old_count; |
591e1ffb | 546 | } |
25ee87d6 | 547 | |
73b6e50a FW |
548 | struct stringtable_finalized strings_finalized; |
549 | stringtable_finalize (&strings, &strings_finalized); | |
550 | ||
591e1ffb | 551 | /* Create the on disk cache structure. */ |
27d9ffda UD |
552 | struct cache_file *file_entries = NULL; |
553 | size_t file_entries_size = 0; | |
591e1ffb | 554 | |
db07fae8 | 555 | if (opt_format != opt_format_new) |
45eca4d1 | 556 | { |
062df960 UD |
557 | /* struct cache_file_new is 64-bit aligned on some arches while |
558 | only 32-bit aligned on other arches. Duplicate last old | |
559 | cache entry so that new cache in ld.so.cache can be used by | |
560 | both. */ | |
db07fae8 | 561 | if (opt_format != opt_format_old) |
062df960 UD |
562 | cache_entry_old_count = (cache_entry_old_count + 1) & ~1; |
563 | ||
45eca4d1 UD |
564 | /* And the list of all entries in the old format. */ |
565 | file_entries_size = sizeof (struct cache_file) | |
566 | + cache_entry_old_count * sizeof (struct file_entry); | |
27d9ffda | 567 | file_entries = xmalloc (file_entries_size); |
45eca4d1 UD |
568 | |
569 | /* Fill in the header. */ | |
27d9ffda | 570 | memset (file_entries, '\0', sizeof (struct cache_file)); |
45eca4d1 | 571 | memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1); |
591e1ffb | 572 | |
45eca4d1 UD |
573 | file_entries->nlibs = cache_entry_old_count; |
574 | } | |
25ee87d6 | 575 | |
27d9ffda UD |
576 | struct cache_file_new *file_entries_new = NULL; |
577 | size_t file_entries_new_size = 0; | |
578 | ||
db07fae8 | 579 | if (opt_format != opt_format_old) |
45eca4d1 UD |
580 | { |
581 | /* And the list of all entries in the new format. */ | |
582 | file_entries_new_size = sizeof (struct cache_file_new) | |
583 | + cache_entry_count * sizeof (struct file_entry_new); | |
27d9ffda | 584 | file_entries_new = xmalloc (file_entries_new_size); |
45eca4d1 UD |
585 | |
586 | /* Fill in the header. */ | |
27d9ffda | 587 | memset (file_entries_new, '\0', sizeof (struct cache_file_new)); |
a986484f UD |
588 | memcpy (file_entries_new->magic, CACHEMAGIC_NEW, |
589 | sizeof CACHEMAGIC_NEW - 1); | |
590 | memcpy (file_entries_new->version, CACHE_VERSION, | |
591 | sizeof CACHE_VERSION - 1); | |
45eca4d1 UD |
592 | |
593 | file_entries_new->nlibs = cache_entry_count; | |
73b6e50a | 594 | file_entries_new->len_strings = strings_finalized.size; |
84ba719b | 595 | file_entries_new->flags = cache_file_new_flags_endian_current; |
45eca4d1 | 596 | } |
95582174 | 597 | |
27d9ffda UD |
598 | /* Pad for alignment of cache_file_new. */ |
599 | size_t pad = ALIGN_CACHE (file_entries_size) - file_entries_size; | |
95582174 | 600 | |
45eca4d1 UD |
601 | /* If we have both formats, we hide the new format in the strings |
602 | table, we have to adjust all string indices for this so that | |
603 | old libc5/glibc 2 dynamic linkers just ignore them. */ | |
27d9ffda | 604 | unsigned int str_offset; |
db07fae8 | 605 | if (opt_format != opt_format_old) |
45eca4d1 UD |
606 | str_offset = file_entries_new_size; |
607 | else | |
608 | str_offset = 0; | |
591e1ffb | 609 | |
27d9ffda | 610 | /* An array for all strings. */ |
27d9ffda UD |
611 | int idx_old; |
612 | int idx_new; | |
613 | ||
45eca4d1 UD |
614 | for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL; |
615 | entry = entry->next, ++idx_new) | |
591e1ffb | 616 | { |
cfbf883d | 617 | if (opt_format != opt_format_new) |
45eca4d1 UD |
618 | { |
619 | file_entries->libs[idx_old].flags = entry->flags; | |
620 | /* XXX: Actually we can optimize here and remove duplicates. */ | |
e25054c4 | 621 | file_entries->libs[idx_old].key = str_offset + pad; |
73b6e50a FW |
622 | file_entries->libs[idx_new].key = str_offset + entry->lib->offset; |
623 | file_entries->libs[idx_new].value | |
624 | = str_offset + entry->path->offset; | |
45eca4d1 | 625 | } |
db07fae8 | 626 | if (opt_format != opt_format_old) |
45eca4d1 UD |
627 | { |
628 | /* We could subtract file_entries_new_size from str_offset - | |
629 | not doing so makes the code easier, the string table | |
ded5b9b7 | 630 | always begins at the beginning of the new cache |
45eca4d1 UD |
631 | struct. */ |
632 | file_entries_new->libs[idx_new].flags = entry->flags; | |
c628c229 | 633 | file_entries_new->libs[idx_new].osversion_unused = 0; |
b44ac4f4 | 634 | if (entry->hwcaps == NULL) |
cfbf883d | 635 | file_entries_new->libs[idx_new].hwcap = 0; |
b44ac4f4 FW |
636 | else |
637 | file_entries_new->libs[idx_new].hwcap | |
efbbd9c3 | 638 | = compute_hwcap_value (entry); |
73b6e50a FW |
639 | file_entries_new->libs[idx_new].key |
640 | = str_offset + entry->lib->offset; | |
641 | file_entries_new->libs[idx_new].value | |
642 | = str_offset + entry->path->offset; | |
45eca4d1 | 643 | } |
27d9ffda | 644 | |
cfbf883d | 645 | ++idx_old; |
591e1ffb | 646 | } |
591e1ffb | 647 | |
062df960 | 648 | /* Duplicate last old cache entry if needed. */ |
db07fae8 | 649 | if (opt_format != opt_format_new |
062df960 UD |
650 | && idx_old < cache_entry_old_count) |
651 | file_entries->libs[idx_old] = file_entries->libs[idx_old - 1]; | |
652 | ||
dfb3f101 FW |
653 | /* Compute the location of the extension directory. This |
654 | implementation puts the directory after the string table. The | |
655 | size computation matches the write calls below. The extension | |
656 | directory does not exist with format 0, so the value does not | |
657 | matter. */ | |
658 | uint32_t extension_offset = 0; | |
659 | if (opt_format != opt_format_new) | |
660 | extension_offset += file_entries_size; | |
661 | if (opt_format != opt_format_old) | |
662 | { | |
663 | if (opt_format != opt_format_new) | |
664 | extension_offset += pad; | |
665 | extension_offset += file_entries_new_size; | |
666 | } | |
73b6e50a | 667 | extension_offset += strings_finalized.size; |
dfb3f101 FW |
668 | extension_offset = roundup (extension_offset, 4); /* Provide alignment. */ |
669 | if (opt_format != opt_format_old) | |
670 | file_entries_new->extension_offset = extension_offset; | |
671 | ||
591e1ffb UD |
672 | /* Write out the cache. */ |
673 | ||
674 | /* Write cache first to a temporary file and rename it later. */ | |
27d9ffda | 675 | char *temp_name = xmalloc (strlen (cache_name) + 2); |
591e1ffb | 676 | sprintf (temp_name, "%s~", cache_name); |
591e1ffb UD |
677 | |
678 | /* Create file. */ | |
27d9ffda UD |
679 | int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, |
680 | S_IRUSR|S_IWUSR); | |
591e1ffb UD |
681 | if (fd < 0) |
682 | error (EXIT_FAILURE, errno, _("Can't create temporary cache file %s"), | |
683 | temp_name); | |
684 | ||
685 | /* Write contents. */ | |
db07fae8 | 686 | if (opt_format != opt_format_new) |
45eca4d1 | 687 | { |
a986484f | 688 | if (write (fd, file_entries, file_entries_size) |
0292b0dd | 689 | != (ssize_t) file_entries_size) |
45eca4d1 UD |
690 | error (EXIT_FAILURE, errno, _("Writing of cache data failed")); |
691 | } | |
db07fae8 | 692 | if (opt_format != opt_format_old) |
45eca4d1 | 693 | { |
e25054c4 | 694 | /* Align cache. */ |
db07fae8 | 695 | if (opt_format != opt_format_new) |
e25054c4 | 696 | { |
a986484f | 697 | char zero[pad]; |
0292b0dd UD |
698 | memset (zero, '\0', pad); |
699 | if (write (fd, zero, pad) != (ssize_t) pad) | |
e25054c4 AJ |
700 | error (EXIT_FAILURE, errno, _("Writing of cache data failed")); |
701 | } | |
45eca4d1 | 702 | if (write (fd, file_entries_new, file_entries_new_size) |
0292b0dd | 703 | != (ssize_t) file_entries_new_size) |
45eca4d1 UD |
704 | error (EXIT_FAILURE, errno, _("Writing of cache data failed")); |
705 | } | |
591e1ffb | 706 | |
73b6e50a FW |
707 | if (write (fd, strings_finalized.strings, strings_finalized.size) |
708 | != (ssize_t) strings_finalized.size) | |
11bf311e | 709 | error (EXIT_FAILURE, errno, _("Writing of cache data failed")); |
591e1ffb | 710 | |
dfb3f101 FW |
711 | if (opt_format != opt_format_old) |
712 | { | |
713 | /* Align file position to 4. */ | |
5e89ed42 YY |
714 | __attribute__ ((unused)) off64_t old_offset |
715 | = lseek64 (fd, extension_offset, SEEK_SET); | |
dfb3f101 | 716 | assert ((unsigned long long int) (extension_offset - old_offset) < 4); |
b44ac4f4 | 717 | write_extensions (fd, str_offset, extension_offset); |
dfb3f101 FW |
718 | } |
719 | ||
25ee87d6 | 720 | /* Make sure user can always read cache file */ |
ba9fcb3f | 721 | if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR)) |
25ee87d6 | 722 | error (EXIT_FAILURE, errno, |
ba9fcb3f UD |
723 | _("Changing access rights of %s to %#o failed"), temp_name, |
724 | S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR); | |
25ee87d6 | 725 | |
999a6dab FW |
726 | /* Make sure that data is written to disk. */ |
727 | if (fsync (fd) != 0 || close (fd) != 0) | |
728 | error (EXIT_FAILURE, errno, _("Writing of cache data failed")); | |
729 | ||
591e1ffb UD |
730 | /* Move temporary to its final location. */ |
731 | if (rename (temp_name, cache_name)) | |
732 | error (EXIT_FAILURE, errno, _("Renaming of %s to %s failed"), temp_name, | |
733 | cache_name); | |
25ee87d6 | 734 | |
591e1ffb | 735 | /* Free all allocated memory. */ |
0292b0dd | 736 | free (file_entries_new); |
591e1ffb | 737 | free (file_entries); |
73b6e50a | 738 | free (strings_finalized.strings); |
a85cdcdb | 739 | free (temp_name); |
591e1ffb UD |
740 | |
741 | while (entries) | |
742 | { | |
743 | entry = entries; | |
591e1ffb UD |
744 | entries = entries->next; |
745 | free (entry); | |
746 | } | |
747 | } | |
748 | ||
45eca4d1 | 749 | |
591e1ffb UD |
750 | /* Add one library to the cache. */ |
751 | void | |
b44ac4f4 | 752 | add_to_cache (const char *path, const char *filename, const char *soname, |
4a709411 | 753 | int flags, unsigned int isa_level, |
b44ac4f4 | 754 | struct glibc_hwcaps_subdirectory *hwcaps) |
591e1ffb | 755 | { |
73b6e50a FW |
756 | struct cache_entry *new_entry = xmalloc (sizeof (*new_entry)); |
757 | ||
758 | struct stringtable_entry *path_interned; | |
759 | { | |
760 | char *p; | |
b44ac4f4 | 761 | if (asprintf (&p, "%s/%s", path, filename) < 0) |
73b6e50a FW |
762 | error (EXIT_FAILURE, errno, _("Could not create library path")); |
763 | path_interned = stringtable_add (&strings, p); | |
764 | free (p); | |
765 | } | |
766 | ||
b44ac4f4 | 767 | new_entry->lib = stringtable_add (&strings, soname); |
73b6e50a | 768 | new_entry->path = path_interned; |
591e1ffb | 769 | new_entry->flags = flags; |
efbbd9c3 | 770 | new_entry->isa_level = isa_level; |
b44ac4f4 | 771 | new_entry->hwcaps = hwcaps; |
45eca4d1 | 772 | |
b44ac4f4 | 773 | if (hwcaps != NULL) |
4a709411 | 774 | hwcaps->used = true; |
591e1ffb UD |
775 | |
776 | /* Keep the list sorted - search for right place to insert. */ | |
27d9ffda UD |
777 | struct cache_entry *ptr = entries; |
778 | struct cache_entry *prev = entries; | |
591e1ffb UD |
779 | while (ptr != NULL) |
780 | { | |
781 | if (compare (ptr, new_entry) > 0) | |
782 | break; | |
783 | prev = ptr; | |
784 | ptr = ptr->next; | |
785 | } | |
786 | /* Is this the first entry? */ | |
787 | if (ptr == entries) | |
788 | { | |
789 | new_entry->next = entries; | |
790 | entries = new_entry; | |
791 | } | |
792 | else | |
793 | { | |
794 | new_entry->next = prev->next; | |
795 | prev->next = new_entry; | |
796 | } | |
797 | } | |
27d9ffda UD |
798 | |
799 | ||
800 | /* Auxiliary cache. */ | |
801 | ||
802 | struct aux_cache_entry_id | |
803 | { | |
804 | uint64_t ino; | |
805 | uint64_t ctime; | |
806 | uint64_t size; | |
807 | uint64_t dev; | |
808 | }; | |
809 | ||
810 | struct aux_cache_entry | |
811 | { | |
812 | struct aux_cache_entry_id id; | |
813 | int flags; | |
efbbd9c3 | 814 | unsigned int isa_level; |
27d9ffda UD |
815 | int used; |
816 | char *soname; | |
817 | struct aux_cache_entry *next; | |
818 | }; | |
819 | ||
820 | #define AUX_CACHEMAGIC "glibc-ld.so.auxcache-1.0" | |
821 | ||
822 | struct aux_cache_file_entry | |
823 | { | |
824 | struct aux_cache_entry_id id; /* Unique id of entry. */ | |
825 | int32_t flags; /* This is 1 for an ELF library. */ | |
826 | uint32_t soname; /* String table indice. */ | |
efbbd9c3 | 827 | uint32_t isa_level; /* Required ISA level. */ |
27d9ffda UD |
828 | }; |
829 | ||
830 | /* ldconfig maintains an auxiliary cache file that allows | |
831 | only reading those libraries that have changed since the last iteration. | |
832 | For this for each library some information is cached in the auxiliary | |
833 | cache. */ | |
834 | struct aux_cache_file | |
835 | { | |
836 | char magic[sizeof AUX_CACHEMAGIC - 1]; | |
837 | uint32_t nlibs; /* Number of entries. */ | |
838 | uint32_t len_strings; /* Size of string table. */ | |
839 | struct aux_cache_file_entry libs[0]; /* Entries describing libraries. */ | |
840 | /* After this the string table of size len_strings is found. */ | |
841 | }; | |
842 | ||
3c87d79d | 843 | static const unsigned int primes[] = |
27d9ffda UD |
844 | { |
845 | 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, | |
846 | 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, | |
847 | 67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647 | |
848 | }; | |
849 | ||
850 | static size_t aux_hash_size; | |
851 | static struct aux_cache_entry **aux_hash; | |
852 | ||
853 | /* Simplistic hash function for aux_cache_entry_id. */ | |
854 | static unsigned int | |
855 | aux_cache_entry_id_hash (struct aux_cache_entry_id *id) | |
856 | { | |
857 | uint64_t ret = ((id->ino * 11 + id->ctime) * 11 + id->size) * 11 + id->dev; | |
858 | return ret ^ (ret >> 32); | |
859 | } | |
860 | ||
861 | static size_t nextprime (size_t x) | |
862 | { | |
863 | for (unsigned int i = 0; i < sizeof (primes) / sizeof (primes[0]); ++i) | |
864 | if (primes[i] >= x) | |
865 | return primes[i]; | |
866 | return x; | |
867 | } | |
868 | ||
869 | void | |
870 | init_aux_cache (void) | |
871 | { | |
872 | aux_hash_size = primes[3]; | |
873 | aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *)); | |
874 | } | |
875 | ||
876 | int | |
c628c229 | 877 | search_aux_cache (struct stat *stat_buf, int *flags, unsigned int *isa_level, |
efbbd9c3 | 878 | char **soname) |
27d9ffda UD |
879 | { |
880 | struct aux_cache_entry_id id; | |
881 | id.ino = (uint64_t) stat_buf->st_ino; | |
882 | id.ctime = (uint64_t) stat_buf->st_ctime; | |
883 | id.size = (uint64_t) stat_buf->st_size; | |
884 | id.dev = (uint64_t) stat_buf->st_dev; | |
885 | ||
886 | unsigned int hash = aux_cache_entry_id_hash (&id); | |
887 | struct aux_cache_entry *entry; | |
888 | for (entry = aux_hash[hash % aux_hash_size]; entry; entry = entry->next) | |
889 | if (id.ino == entry->id.ino | |
890 | && id.ctime == entry->id.ctime | |
891 | && id.size == entry->id.size | |
892 | && id.dev == entry->id.dev) | |
893 | { | |
894 | *flags = entry->flags; | |
efbbd9c3 | 895 | *isa_level = entry->isa_level; |
27d9ffda UD |
896 | if (entry->soname != NULL) |
897 | *soname = xstrdup (entry->soname); | |
898 | else | |
899 | *soname = NULL; | |
900 | entry->used = 1; | |
901 | return 1; | |
902 | } | |
903 | ||
904 | return 0; | |
905 | } | |
906 | ||
907 | static void | |
908 | insert_to_aux_cache (struct aux_cache_entry_id *id, int flags, | |
c628c229 | 909 | unsigned int isa_level, const char *soname, int used) |
27d9ffda UD |
910 | { |
911 | size_t hash = aux_cache_entry_id_hash (id) % aux_hash_size; | |
912 | struct aux_cache_entry *entry; | |
913 | for (entry = aux_hash[hash]; entry; entry = entry->next) | |
914 | if (id->ino == entry->id.ino | |
915 | && id->ctime == entry->id.ctime | |
916 | && id->size == entry->id.size | |
917 | && id->dev == entry->id.dev) | |
918 | abort (); | |
919 | ||
920 | size_t len = soname ? strlen (soname) + 1 : 0; | |
921 | entry = xmalloc (sizeof (struct aux_cache_entry) + len); | |
922 | entry->id = *id; | |
923 | entry->flags = flags; | |
efbbd9c3 | 924 | entry->isa_level = isa_level; |
27d9ffda UD |
925 | entry->used = used; |
926 | if (soname != NULL) | |
927 | entry->soname = memcpy ((char *) (entry + 1), soname, len); | |
928 | else | |
929 | entry->soname = NULL; | |
930 | entry->next = aux_hash[hash]; | |
931 | aux_hash[hash] = entry; | |
932 | } | |
933 | ||
934 | void | |
c628c229 | 935 | add_to_aux_cache (struct stat *stat_buf, int flags, unsigned int isa_level, |
efbbd9c3 | 936 | const char *soname) |
27d9ffda UD |
937 | { |
938 | struct aux_cache_entry_id id; | |
939 | id.ino = (uint64_t) stat_buf->st_ino; | |
940 | id.ctime = (uint64_t) stat_buf->st_ctime; | |
941 | id.size = (uint64_t) stat_buf->st_size; | |
942 | id.dev = (uint64_t) stat_buf->st_dev; | |
c628c229 | 943 | insert_to_aux_cache (&id, flags, isa_level, soname, 1); |
27d9ffda UD |
944 | } |
945 | ||
946 | /* Load auxiliary cache to search for unchanged entries. */ | |
947 | void | |
948 | load_aux_cache (const char *aux_cache_name) | |
949 | { | |
950 | int fd = open (aux_cache_name, O_RDONLY); | |
951 | if (fd < 0) | |
952 | { | |
953 | init_aux_cache (); | |
954 | return; | |
955 | } | |
956 | ||
9fe6f636 AZ |
957 | struct stat st; |
958 | if (fstat (fd, &st) < 0 || st.st_size < sizeof (struct aux_cache_file)) | |
27d9ffda UD |
959 | { |
960 | close (fd); | |
961 | init_aux_cache (); | |
962 | return; | |
963 | } | |
964 | ||
965 | size_t aux_cache_size = st.st_size; | |
966 | struct aux_cache_file *aux_cache | |
967 | = mmap (NULL, aux_cache_size, PROT_READ, MAP_PRIVATE, fd, 0); | |
968 | if (aux_cache == MAP_FAILED | |
969 | || aux_cache_size < sizeof (struct aux_cache_file) | |
970 | || memcmp (aux_cache->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1) | |
c4f50205 JM |
971 | || aux_cache_size != (sizeof (struct aux_cache_file) |
972 | + aux_cache->nlibs * sizeof (struct aux_cache_file_entry) | |
34a5a146 | 973 | + aux_cache->len_strings)) |
27d9ffda | 974 | { |
a85cdcdb SP |
975 | if (aux_cache != MAP_FAILED) |
976 | munmap (aux_cache, aux_cache_size); | |
977 | ||
27d9ffda UD |
978 | close (fd); |
979 | init_aux_cache (); | |
980 | return; | |
981 | } | |
982 | ||
983 | aux_hash_size = nextprime (aux_cache->nlibs); | |
984 | aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *)); | |
985 | ||
986 | const char *aux_cache_data | |
987 | = (const char *) &aux_cache->libs[aux_cache->nlibs]; | |
988 | for (unsigned int i = 0; i < aux_cache->nlibs; ++i) | |
989 | insert_to_aux_cache (&aux_cache->libs[i].id, | |
990 | aux_cache->libs[i].flags, | |
efbbd9c3 | 991 | aux_cache->libs[i].isa_level, |
27d9ffda UD |
992 | aux_cache->libs[i].soname == 0 |
993 | ? NULL : aux_cache_data + aux_cache->libs[i].soname, | |
994 | 0); | |
995 | ||
996 | munmap (aux_cache, aux_cache_size); | |
997 | close (fd); | |
998 | } | |
999 | ||
1000 | /* Save the contents of the auxiliary cache. */ | |
1001 | void | |
1002 | save_aux_cache (const char *aux_cache_name) | |
1003 | { | |
1004 | /* Count the length of all sonames. We start with empty string. */ | |
1005 | size_t total_strlen = 1; | |
1006 | /* Number of cache entries. */ | |
1007 | int cache_entry_count = 0; | |
1008 | ||
1009 | for (size_t i = 0; i < aux_hash_size; ++i) | |
1010 | for (struct aux_cache_entry *entry = aux_hash[i]; | |
1011 | entry != NULL; entry = entry->next) | |
1012 | if (entry->used) | |
1013 | { | |
1014 | ++cache_entry_count; | |
1015 | if (entry->soname != NULL) | |
1016 | total_strlen += strlen (entry->soname) + 1; | |
1017 | } | |
1018 | ||
1019 | /* Auxiliary cache. */ | |
1020 | size_t file_entries_size | |
1021 | = sizeof (struct aux_cache_file) | |
1022 | + cache_entry_count * sizeof (struct aux_cache_file_entry); | |
1023 | struct aux_cache_file *file_entries | |
1024 | = xmalloc (file_entries_size + total_strlen); | |
1025 | ||
1026 | /* Fill in the header of the auxiliary cache. */ | |
1027 | memset (file_entries, '\0', sizeof (struct aux_cache_file)); | |
1028 | memcpy (file_entries->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1); | |
1029 | ||
1030 | file_entries->nlibs = cache_entry_count; | |
1031 | file_entries->len_strings = total_strlen; | |
1032 | ||
1033 | /* Initial String offset for auxiliary cache is always after the | |
1034 | special empty string. */ | |
1035 | unsigned int str_offset = 1; | |
1036 | ||
1037 | /* An array for all strings. */ | |
1038 | char *str = (char *) file_entries + file_entries_size; | |
1039 | *str++ = '\0'; | |
1040 | ||
1041 | size_t idx = 0; | |
1042 | for (size_t i = 0; i < aux_hash_size; ++i) | |
1043 | for (struct aux_cache_entry *entry = aux_hash[i]; | |
1044 | entry != NULL; entry = entry->next) | |
1045 | if (entry->used) | |
1046 | { | |
1047 | file_entries->libs[idx].id = entry->id; | |
1048 | file_entries->libs[idx].flags = entry->flags; | |
1049 | if (entry->soname == NULL) | |
1050 | file_entries->libs[idx].soname = 0; | |
1051 | else | |
1052 | { | |
1053 | file_entries->libs[idx].soname = str_offset; | |
1054 | ||
1055 | size_t len = strlen (entry->soname) + 1; | |
1056 | str = mempcpy (str, entry->soname, len); | |
1057 | str_offset += len; | |
1058 | } | |
efbbd9c3 | 1059 | file_entries->libs[idx++].isa_level = entry->isa_level; |
27d9ffda UD |
1060 | } |
1061 | ||
1062 | /* Write out auxiliary cache file. */ | |
1063 | /* Write auxiliary cache first to a temporary file and rename it later. */ | |
1064 | ||
1065 | char *temp_name = xmalloc (strlen (aux_cache_name) + 2); | |
1066 | sprintf (temp_name, "%s~", aux_cache_name); | |
1067 | ||
1068 | /* Check that directory exists and create if needed. */ | |
1069 | char *dir = strdupa (aux_cache_name); | |
1070 | dir = dirname (dir); | |
1071 | ||
9fe6f636 AZ |
1072 | struct stat st; |
1073 | if (stat (dir, &st) < 0) | |
27d9ffda UD |
1074 | { |
1075 | if (mkdir (dir, 0700) < 0) | |
1076 | goto out_fail; | |
1077 | } | |
1078 | ||
1079 | /* Create file. */ | |
1080 | int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, | |
1081 | S_IRUSR|S_IWUSR); | |
1082 | if (fd < 0) | |
1083 | goto out_fail; | |
1084 | ||
a85cdcdb SP |
1085 | bool fail = ((write (fd, file_entries, file_entries_size + total_strlen) |
1086 | != (ssize_t) (file_entries_size + total_strlen)) | |
1087 | || fdatasync (fd) != 0); | |
1088 | ||
1089 | fail |= close (fd) != 0; | |
1090 | ||
1091 | if (fail) | |
27d9ffda UD |
1092 | { |
1093 | unlink (temp_name); | |
1094 | goto out_fail; | |
1095 | } | |
1096 | ||
1097 | /* Move temporary to its final location. */ | |
1098 | if (rename (temp_name, aux_cache_name)) | |
1099 | unlink (temp_name); | |
1100 | ||
1101 | out_fail: | |
1102 | /* Free allocated memory. */ | |
78d6dd72 | 1103 | free (temp_name); |
27d9ffda UD |
1104 | free (file_entries); |
1105 | } |