Mention IFUNC enhancements to testsuite in NEWS.
[platform/upstream/glibc.git] / elf / cache.c
1 /* Copyright (C) 1999-2012 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Andreas Jaeger <aj@suse.de>, 1999.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published
7    by the Free Software Foundation; version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, see <http://www.gnu.org/licenses/>.  */
17
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 <sys/fcntl.h>
29 #include <sys/mman.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32
33 #include <ldconfig.h>
34 #include <dl-cache.h>
35
36 struct cache_entry
37 {
38   char *lib;                    /* Library name.  */
39   char *path;                   /* Path to find library.  */
40   int flags;                    /* Flags to indicate kind of library.  */
41   unsigned int osversion;       /* Required OS version.  */
42   uint64_t hwcap;               /* Important hardware capabilities.  */
43   int bits_hwcap;               /* Number of bits set in hwcap.  */
44   struct cache_entry *next;     /* Next entry in list.  */
45 };
46
47 /* List of all cache entries.  */
48 static struct cache_entry *entries;
49
50 static const char *flag_descr[] =
51 { "libc4", "ELF", "libc5", "libc6"};
52
53 /* Print a single entry.  */
54 static void
55 print_entry (const char *lib, int flag, unsigned int osversion,
56              uint64_t hwcap, const char *key)
57 {
58   printf ("\t%s (", lib);
59   switch (flag & FLAG_TYPE_MASK)
60     {
61     case FLAG_LIBC4:
62     case FLAG_ELF:
63     case FLAG_ELF_LIBC5:
64     case FLAG_ELF_LIBC6:
65       fputs (flag_descr[flag & FLAG_TYPE_MASK], stdout);
66       break;
67     default:
68       fputs (_("unknown"), stdout);
69       break;
70     }
71   switch (flag & FLAG_REQUIRED_MASK)
72     {
73     case FLAG_SPARC_LIB64:
74       fputs (",64bit", stdout);
75       break;
76     case FLAG_IA64_LIB64:
77       fputs (",IA-64", stdout);
78       break;
79     case FLAG_X8664_LIB64:
80       fputs (",x86-64", stdout);
81       break;
82     case FLAG_S390_LIB64:
83       fputs (",64bit", stdout);
84       break;
85     case FLAG_POWERPC_LIB64:
86       fputs (",64bit", stdout);
87       break;
88     case FLAG_MIPS64_LIBN32:
89       fputs (",N32", stdout);
90       break;
91     case FLAG_MIPS64_LIBN64:
92       fputs (",64bit", stdout);
93       break;
94     case FLAG_X8664_LIBX32:
95       fputs (",x32", stdout);
96       break;
97     case FLAG_ARM_LIBHF:
98       fputs (",hard-float", stdout);
99       break;
100     case FLAG_AARCH64_LIB64:
101       fputs (",AArch64", stdout);
102       break;
103     case 0:
104       break;
105     default:
106       printf (",%d", flag & FLAG_REQUIRED_MASK);
107       break;
108     }
109   if (hwcap != 0)
110     printf (", hwcap: %#.16" PRIx64, hwcap);
111   if (osversion != 0)
112     {
113       static const char *const abi_tag_os[] =
114       {
115         [0] = "Linux",
116         [1] = "Hurd",
117         [2] = "Solaris",
118         [3] = "FreeBSD",
119         [4] = "kNetBSD",
120         [5] = "Syllable",
121         [6] = N_("Unknown OS")
122       };
123 #define MAXTAG (sizeof abi_tag_os / sizeof abi_tag_os[0] - 1)
124       unsigned int os = osversion >> 24;
125
126       printf (_(", OS ABI: %s %d.%d.%d"),
127               _(abi_tag_os[os > MAXTAG ? MAXTAG : os]),
128               (osversion >> 16) & 0xff,
129               (osversion >> 8) & 0xff,
130               osversion & 0xff);
131     }
132   printf (") => %s\n", key);
133 }
134
135
136 /* Print the whole cache file, if a file contains the new cache format
137    hidden in the old one, print the contents of the new format.  */
138 void
139 print_cache (const char *cache_name)
140 {
141   int fd = open (cache_name, O_RDONLY);
142   if (fd < 0)
143     error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name);
144
145   struct stat64 st;
146   if (fstat64 (fd, &st) < 0
147       /* No need to map the file if it is empty.  */
148       || st.st_size == 0)
149     {
150       close (fd);
151       return;
152     }
153
154   struct cache_file *cache
155     = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
156   if (cache == MAP_FAILED)
157     error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n"));
158
159   size_t cache_size = st.st_size;
160   if (cache_size < sizeof (struct cache_file))
161     error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
162
163   struct cache_file_new *cache_new = NULL;
164   const char *cache_data;
165   int format = 0;
166
167   if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
168     {
169       /* This can only be the new format without the old one.  */
170       cache_new = (struct cache_file_new *) cache;
171
172       if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
173           || memcmp (cache_new->version, CACHE_VERSION,
174                       sizeof CACHE_VERSION - 1))
175         error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
176       format = 1;
177       /* This is where the strings start.  */
178       cache_data = (const char *) cache_new;
179     }
180   else
181     {
182       size_t offset = ALIGN_CACHE (sizeof (struct cache_file)
183                                    + (cache->nlibs
184                                       * sizeof (struct file_entry)));
185       /* This is where the strings start.  */
186       cache_data = (const char *) &cache->libs[cache->nlibs];
187
188       /* Check for a new cache embedded in the old format.  */
189       if (cache_size >
190           (offset + sizeof (struct cache_file_new)))
191         {
192
193           cache_new = (struct cache_file_new *) ((void *)cache + offset);
194
195           if (memcmp (cache_new->magic, CACHEMAGIC_NEW,
196                       sizeof CACHEMAGIC_NEW - 1) == 0
197               && memcmp (cache_new->version, CACHE_VERSION,
198                          sizeof CACHE_VERSION - 1) == 0)
199             {
200               cache_data = (const char *) cache_new;
201               format = 1;
202             }
203         }
204     }
205
206   if (format == 0)
207     {
208       printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
209
210       /* Print everything.  */
211       for (unsigned int i = 0; i < cache->nlibs; i++)
212         print_entry (cache_data + cache->libs[i].key,
213                      cache->libs[i].flags, 0, 0,
214                      cache_data + cache->libs[i].value);
215     }
216   else if (format == 1)
217     {
218       printf (_("%d libs found in cache `%s'\n"),
219               cache_new->nlibs, cache_name);
220
221       /* Print everything.  */
222       for (unsigned int i = 0; i < cache_new->nlibs; i++)
223         print_entry (cache_data + cache_new->libs[i].key,
224                      cache_new->libs[i].flags,
225                      cache_new->libs[i].osversion,
226                      cache_new->libs[i].hwcap,
227                      cache_data + cache_new->libs[i].value);
228     }
229   /* Cleanup.  */
230   munmap (cache, cache_size);
231   close (fd);
232 }
233
234 /* Initialize cache data structures.  */
235 void
236 init_cache (void)
237 {
238   entries = NULL;
239 }
240
241 static int
242 compare (const struct cache_entry *e1, const struct cache_entry *e2)
243 {
244   /* We need to swap entries here to get the correct sort order.  */
245   int res = _dl_cache_libcmp (e2->lib, e1->lib);
246   if (res == 0)
247     {
248       if (e1->flags < e2->flags)
249         return 1;
250       else if (e1->flags > e2->flags)
251         return -1;
252       /* Sort by most specific hwcap.  */
253       else if (e2->bits_hwcap > e1->bits_hwcap)
254         return 1;
255       else if (e2->bits_hwcap < e1->bits_hwcap)
256         return -1;
257       else if (e2->hwcap > e1->hwcap)
258         return 1;
259       else if (e2->hwcap < e1->hwcap)
260         return -1;
261       if (e2->osversion > e1->osversion)
262         return 1;
263       if (e2->osversion < e1->osversion)
264         return -1;
265     }
266   return res;
267 }
268
269 /* Save the contents of the cache.  */
270 void
271 save_cache (const char *cache_name)
272 {
273   /* The cache entries are sorted already, save them in this order. */
274
275   /* Count the length of all strings.  */
276   /* The old format doesn't contain hwcap entries and doesn't contain
277      libraries in subdirectories with hwcaps entries.  Count therefore
278      also all entries with hwcap == 0.  */
279   size_t total_strlen = 0;
280   struct cache_entry *entry;
281   /* Number of cache entries.  */
282   int cache_entry_count = 0;
283   /* Number of normal cache entries.  */
284   int cache_entry_old_count = 0;
285
286   for (entry = entries; entry != NULL; entry = entry->next)
287     {
288       /* Account the final NULs.  */
289       total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
290       ++cache_entry_count;
291       if (entry->hwcap == 0)
292         ++cache_entry_old_count;
293     }
294
295   /* Create the on disk cache structure.  */
296   struct cache_file *file_entries = NULL;
297   size_t file_entries_size = 0;
298
299   if (opt_format != 2)
300     {
301       /* struct cache_file_new is 64-bit aligned on some arches while
302          only 32-bit aligned on other arches.  Duplicate last old
303          cache entry so that new cache in ld.so.cache can be used by
304          both.  */
305       if (opt_format != 0)
306         cache_entry_old_count = (cache_entry_old_count + 1) & ~1;
307
308       /* And the list of all entries in the old format.  */
309       file_entries_size = sizeof (struct cache_file)
310         + cache_entry_old_count * sizeof (struct file_entry);
311       file_entries = xmalloc (file_entries_size);
312
313       /* Fill in the header.  */
314       memset (file_entries, '\0', sizeof (struct cache_file));
315       memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1);
316
317       file_entries->nlibs = cache_entry_old_count;
318     }
319
320   struct cache_file_new *file_entries_new = NULL;
321   size_t file_entries_new_size = 0;
322
323   if (opt_format != 0)
324     {
325       /* And the list of all entries in the new format.  */
326       file_entries_new_size = sizeof (struct cache_file_new)
327         + cache_entry_count * sizeof (struct file_entry_new);
328       file_entries_new = xmalloc (file_entries_new_size);
329
330       /* Fill in the header.  */
331       memset (file_entries_new, '\0', sizeof (struct cache_file_new));
332       memcpy (file_entries_new->magic, CACHEMAGIC_NEW,
333               sizeof CACHEMAGIC_NEW - 1);
334       memcpy (file_entries_new->version, CACHE_VERSION,
335               sizeof CACHE_VERSION - 1);
336
337       file_entries_new->nlibs = cache_entry_count;
338       file_entries_new->len_strings = total_strlen;
339     }
340
341   /* Pad for alignment of cache_file_new.  */
342   size_t pad = ALIGN_CACHE (file_entries_size) - file_entries_size;
343
344   /* If we have both formats, we hide the new format in the strings
345      table, we have to adjust all string indices for this so that
346      old libc5/glibc 2 dynamic linkers just ignore them.  */
347   unsigned int str_offset;
348   if (opt_format != 0)
349     str_offset = file_entries_new_size;
350   else
351     str_offset = 0;
352
353   /* An array for all strings.  */
354   char *strings = xmalloc (total_strlen);
355   char *str = strings;
356   int idx_old;
357   int idx_new;
358
359   for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL;
360        entry = entry->next, ++idx_new)
361     {
362       /* First the library.  */
363       if (opt_format != 2 && entry->hwcap == 0)
364         {
365           file_entries->libs[idx_old].flags = entry->flags;
366           /* XXX: Actually we can optimize here and remove duplicates.  */
367           file_entries->libs[idx_old].key = str_offset + pad;
368         }
369       if (opt_format != 0)
370         {
371           /* We could subtract file_entries_new_size from str_offset -
372              not doing so makes the code easier, the string table
373              always begins at the beginning of the new cache
374              struct.  */
375           file_entries_new->libs[idx_new].flags = entry->flags;
376           file_entries_new->libs[idx_new].osversion = entry->osversion;
377           file_entries_new->libs[idx_new].hwcap = entry->hwcap;
378           file_entries_new->libs[idx_new].key = str_offset;
379         }
380
381       size_t len = strlen (entry->lib) + 1;
382       str = mempcpy (str, entry->lib, len);
383       str_offset += len;
384       /* Then the path.  */
385       if (opt_format != 2 && entry->hwcap == 0)
386         file_entries->libs[idx_old].value = str_offset + pad;
387       if (opt_format != 0)
388         file_entries_new->libs[idx_new].value = str_offset;
389       len = strlen (entry->path) + 1;
390       str = mempcpy (str, entry->path, len);
391       str_offset += len;
392       /* Ignore entries with hwcap for old format.  */
393       if (entry->hwcap == 0)
394         ++idx_old;
395     }
396
397   /* Duplicate last old cache entry if needed.  */
398   if (opt_format != 2
399       && idx_old < cache_entry_old_count)
400     file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
401
402   /* Write out the cache.  */
403
404   /* Write cache first to a temporary file and rename it later.  */
405   char *temp_name = xmalloc (strlen (cache_name) + 2);
406   sprintf (temp_name, "%s~", cache_name);
407
408   /* Create file.  */
409   int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW,
410                  S_IRUSR|S_IWUSR);
411   if (fd < 0)
412     error (EXIT_FAILURE, errno, _("Can't create temporary cache file %s"),
413            temp_name);
414
415   /* Write contents.  */
416   if (opt_format != 2)
417     {
418       if (write (fd, file_entries, file_entries_size)
419           != (ssize_t) file_entries_size)
420         error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
421     }
422   if (opt_format != 0)
423     {
424       /* Align cache.  */
425       if (opt_format != 2)
426         {
427           char zero[pad];
428           memset (zero, '\0', pad);
429           if (write (fd, zero, pad) != (ssize_t) pad)
430             error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
431         }
432       if (write (fd, file_entries_new, file_entries_new_size)
433           != (ssize_t) file_entries_new_size)
434         error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
435     }
436
437   if (write (fd, strings, total_strlen) != (ssize_t) total_strlen
438       || close (fd))
439     error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
440
441   /* Make sure user can always read cache file */
442   if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR))
443     error (EXIT_FAILURE, errno,
444            _("Changing access rights of %s to %#o failed"), temp_name,
445            S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR);
446
447   /* Move temporary to its final location.  */
448   if (rename (temp_name, cache_name))
449     error (EXIT_FAILURE, errno, _("Renaming of %s to %s failed"), temp_name,
450            cache_name);
451
452   /* Free all allocated memory.  */
453   free (file_entries_new);
454   free (file_entries);
455   free (strings);
456
457   while (entries)
458     {
459       entry = entries;
460       entries = entries->next;
461       free (entry);
462     }
463 }
464
465
466 /* Add one library to the cache.  */
467 void
468 add_to_cache (const char *path, const char *lib, int flags,
469               unsigned int osversion, uint64_t hwcap)
470 {
471   size_t liblen = strlen (lib) + 1;
472   size_t len = liblen + strlen (path) + 1;
473   struct cache_entry *new_entry
474     = xmalloc (sizeof (struct cache_entry) + liblen + len);
475
476   new_entry->lib = memcpy ((char *) (new_entry + 1), lib, liblen);
477   new_entry->path = new_entry->lib + liblen;
478   snprintf (new_entry->path, len, "%s/%s", path, lib);
479   new_entry->flags = flags;
480   new_entry->osversion = osversion;
481   new_entry->hwcap = hwcap;
482   new_entry->bits_hwcap = 0;
483
484   /* Count the number of bits set in the masked value.  */
485   for (size_t i = 0;
486        (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i)
487     if ((hwcap & (1ULL << i)) != 0)
488       ++new_entry->bits_hwcap;
489
490
491   /* Keep the list sorted - search for right place to insert.  */
492   struct cache_entry *ptr = entries;
493   struct cache_entry *prev = entries;
494   while (ptr != NULL)
495     {
496       if (compare (ptr, new_entry) > 0)
497         break;
498       prev = ptr;
499       ptr = ptr->next;
500     }
501   /* Is this the first entry?  */
502   if (ptr == entries)
503     {
504       new_entry->next = entries;
505       entries = new_entry;
506     }
507   else
508     {
509       new_entry->next = prev->next;
510       prev->next = new_entry;
511     }
512 }
513
514
515 /* Auxiliary cache.  */
516
517 struct aux_cache_entry_id
518 {
519   uint64_t ino;
520   uint64_t ctime;
521   uint64_t size;
522   uint64_t dev;
523 };
524
525 struct aux_cache_entry
526 {
527   struct aux_cache_entry_id id;
528   int flags;
529   unsigned int osversion;
530   int used;
531   char *soname;
532   struct aux_cache_entry *next;
533 };
534
535 #define AUX_CACHEMAGIC          "glibc-ld.so.auxcache-1.0"
536
537 struct aux_cache_file_entry
538 {
539   struct aux_cache_entry_id id; /* Unique id of entry.  */
540   int32_t flags;                /* This is 1 for an ELF library.  */
541   uint32_t soname;              /* String table indice.  */
542   uint32_t osversion;           /* Required OS version.  */
543   int32_t pad;
544 };
545
546 /* ldconfig maintains an auxiliary cache file that allows
547    only reading those libraries that have changed since the last iteration.
548    For this for each library some information is cached in the auxiliary
549    cache.  */
550 struct aux_cache_file
551 {
552   char magic[sizeof AUX_CACHEMAGIC - 1];
553   uint32_t nlibs;               /* Number of entries.  */
554   uint32_t len_strings;         /* Size of string table. */
555   struct aux_cache_file_entry libs[0]; /* Entries describing libraries.  */
556   /* After this the string table of size len_strings is found.  */
557 };
558
559 static const unsigned int primes[] =
560 {
561   1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139,
562   524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393,
563   67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647
564 };
565
566 static size_t aux_hash_size;
567 static struct aux_cache_entry **aux_hash;
568
569 /* Simplistic hash function for aux_cache_entry_id.  */
570 static unsigned int
571 aux_cache_entry_id_hash (struct aux_cache_entry_id *id)
572 {
573   uint64_t ret = ((id->ino * 11 + id->ctime) * 11 + id->size) * 11 + id->dev;
574   return ret ^ (ret >> 32);
575 }
576
577 static size_t nextprime (size_t x)
578 {
579   for (unsigned int i = 0; i < sizeof (primes) / sizeof (primes[0]); ++i)
580     if (primes[i] >= x)
581       return primes[i];
582   return x;
583 }
584
585 void
586 init_aux_cache (void)
587 {
588   aux_hash_size = primes[3];
589   aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *));
590 }
591
592 int
593 search_aux_cache (struct stat64 *stat_buf, int *flags,
594                   unsigned int *osversion, char **soname)
595 {
596   struct aux_cache_entry_id id;
597   id.ino = (uint64_t) stat_buf->st_ino;
598   id.ctime = (uint64_t) stat_buf->st_ctime;
599   id.size = (uint64_t) stat_buf->st_size;
600   id.dev = (uint64_t) stat_buf->st_dev;
601
602   unsigned int hash = aux_cache_entry_id_hash (&id);
603   struct aux_cache_entry *entry;
604   for (entry = aux_hash[hash % aux_hash_size]; entry; entry = entry->next)
605     if (id.ino == entry->id.ino
606         && id.ctime == entry->id.ctime
607         && id.size == entry->id.size
608         && id.dev == entry->id.dev)
609       {
610         *flags = entry->flags;
611         *osversion = entry->osversion;
612         if (entry->soname != NULL)
613           *soname = xstrdup (entry->soname);
614         else
615           *soname = NULL;
616         entry->used = 1;
617         return 1;
618       }
619
620   return 0;
621 }
622
623 static void
624 insert_to_aux_cache (struct aux_cache_entry_id *id, int flags,
625                      unsigned int osversion, const char *soname, int used)
626 {
627   size_t hash = aux_cache_entry_id_hash (id) % aux_hash_size;
628   struct aux_cache_entry *entry;
629   for (entry = aux_hash[hash]; entry; entry = entry->next)
630     if (id->ino == entry->id.ino
631         && id->ctime == entry->id.ctime
632         && id->size == entry->id.size
633         && id->dev == entry->id.dev)
634       abort ();
635
636   size_t len = soname ? strlen (soname) + 1 : 0;
637   entry = xmalloc (sizeof (struct aux_cache_entry) + len);
638   entry->id = *id;
639   entry->flags = flags;
640   entry->osversion = osversion;
641   entry->used = used;
642   if (soname != NULL)
643     entry->soname = memcpy ((char *) (entry + 1), soname, len);
644   else
645     entry->soname = NULL;
646   entry->next = aux_hash[hash];
647   aux_hash[hash] = entry;
648 }
649
650 void
651 add_to_aux_cache (struct stat64 *stat_buf, int flags,
652                   unsigned int osversion, const char *soname)
653 {
654   struct aux_cache_entry_id id;
655   id.ino = (uint64_t) stat_buf->st_ino;
656   id.ctime = (uint64_t) stat_buf->st_ctime;
657   id.size = (uint64_t) stat_buf->st_size;
658   id.dev = (uint64_t) stat_buf->st_dev;
659   insert_to_aux_cache (&id, flags, osversion, soname, 1);
660 }
661
662 /* Load auxiliary cache to search for unchanged entries.   */
663 void
664 load_aux_cache (const char *aux_cache_name)
665 {
666   int fd = open (aux_cache_name, O_RDONLY);
667   if (fd < 0)
668     {
669       init_aux_cache ();
670       return;
671     }
672
673   struct stat64 st;
674   if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (struct aux_cache_file))
675     {
676       close (fd);
677       init_aux_cache ();
678       return;
679     }
680
681   size_t aux_cache_size = st.st_size;
682   struct aux_cache_file *aux_cache
683     = mmap (NULL, aux_cache_size, PROT_READ, MAP_PRIVATE, fd, 0);
684   if (aux_cache == MAP_FAILED
685       || aux_cache_size < sizeof (struct aux_cache_file)
686       || memcmp (aux_cache->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1)
687       || aux_cache->nlibs >= aux_cache_size)
688     {
689       close (fd);
690       init_aux_cache ();
691       return;
692     }
693
694   aux_hash_size = nextprime (aux_cache->nlibs);
695   aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *));
696
697   const char *aux_cache_data
698     = (const char *) &aux_cache->libs[aux_cache->nlibs];
699   for (unsigned int i = 0; i < aux_cache->nlibs; ++i)
700     insert_to_aux_cache (&aux_cache->libs[i].id,
701                          aux_cache->libs[i].flags,
702                          aux_cache->libs[i].osversion,
703                          aux_cache->libs[i].soname == 0
704                          ? NULL : aux_cache_data + aux_cache->libs[i].soname,
705                          0);
706
707   munmap (aux_cache, aux_cache_size);
708   close (fd);
709 }
710
711 /* Save the contents of the auxiliary cache.  */
712 void
713 save_aux_cache (const char *aux_cache_name)
714 {
715   /* Count the length of all sonames.  We start with empty string.  */
716   size_t total_strlen = 1;
717   /* Number of cache entries.  */
718   int cache_entry_count = 0;
719
720   for (size_t i = 0; i < aux_hash_size; ++i)
721     for (struct aux_cache_entry *entry = aux_hash[i];
722          entry != NULL; entry = entry->next)
723       if (entry->used)
724         {
725           ++cache_entry_count;
726           if (entry->soname != NULL)
727             total_strlen += strlen (entry->soname) + 1;
728         }
729
730   /* Auxiliary cache.  */
731   size_t file_entries_size
732     = sizeof (struct aux_cache_file)
733       + cache_entry_count * sizeof (struct aux_cache_file_entry);
734   struct aux_cache_file *file_entries
735     = xmalloc (file_entries_size + total_strlen);
736
737   /* Fill in the header of the auxiliary cache.  */
738   memset (file_entries, '\0', sizeof (struct aux_cache_file));
739   memcpy (file_entries->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1);
740
741   file_entries->nlibs = cache_entry_count;
742   file_entries->len_strings = total_strlen;
743
744   /* Initial String offset for auxiliary cache is always after the
745      special empty string.  */
746   unsigned int str_offset = 1;
747
748   /* An array for all strings.  */
749   char *str = (char *) file_entries + file_entries_size;
750   *str++ = '\0';
751
752   size_t idx = 0;
753   for (size_t i = 0; i < aux_hash_size; ++i)
754     for (struct aux_cache_entry *entry = aux_hash[i];
755          entry != NULL; entry = entry->next)
756       if (entry->used)
757         {
758           file_entries->libs[idx].id = entry->id;
759           file_entries->libs[idx].flags = entry->flags;
760           if (entry->soname == NULL)
761             file_entries->libs[idx].soname = 0;
762           else
763             {
764               file_entries->libs[idx].soname = str_offset;
765
766               size_t len = strlen (entry->soname) + 1;
767               str = mempcpy (str, entry->soname, len);
768               str_offset += len;
769             }
770           file_entries->libs[idx].osversion = entry->osversion;
771           file_entries->libs[idx++].pad = 0;
772         }
773
774   /* Write out auxiliary cache file.  */
775   /* Write auxiliary cache first to a temporary file and rename it later.  */
776
777   char *temp_name = xmalloc (strlen (aux_cache_name) + 2);
778   sprintf (temp_name, "%s~", aux_cache_name);
779
780   /* Check that directory exists and create if needed.  */
781   char *dir = strdupa (aux_cache_name);
782   dir = dirname (dir);
783
784   struct stat64 st;
785   if (stat64 (dir, &st) < 0)
786     {
787       if (mkdir (dir, 0700) < 0)
788         goto out_fail;
789     }
790
791   /* Create file.  */
792   int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW,
793                  S_IRUSR|S_IWUSR);
794   if (fd < 0)
795     goto out_fail;
796
797   if (write (fd, file_entries, file_entries_size + total_strlen)
798       != (ssize_t) (file_entries_size + total_strlen)
799       || close (fd))
800     {
801       unlink (temp_name);
802       goto out_fail;
803     }
804
805   /* Move temporary to its final location.  */
806   if (rename (temp_name, aux_cache_name))
807     unlink (temp_name);
808
809 out_fail:
810   /* Free allocated memory.  */
811   free (temp_name);
812   free (file_entries);
813 }