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

source code of glibc/elf/cache.c