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

source code of glibc/elf/cache.c