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 #define PROCINFO_CLASS static
18 #include <assert.h>
19 #include <alloca.h>
20 #include <argp.h>
21 #include <dirent.h>
22 #include <elf.h>
23 #include <error.h>
24 #include <errno.h>
25 #include <inttypes.h>
26 #include <libintl.h>
27 #include <locale.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdio_ext.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdint.h>
35 #include <sys/fcntl.h>
36 #include <sys/mman.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <glob.h>
40 #include <libgen.h>
41
42 #include <ldconfig.h>
43 #include <dl-cache.h>
44 #include <dl-hwcaps.h>
45 #include <dl-is_dso.h>
46
47 #include <dl-procinfo.h>
48
49 /* This subpath in search path entries is always supported and
50 included in the cache for backwards compatibility. */
51 #define TLS_SUBPATH "tls"
52
53 /* The MSB of the hwcap field is set for objects in TLS_SUBPATH
54 directories. There is always TLS support in glibc, so the dynamic
55 loader does not check the bit directly. But more hwcap bits make a
56 an object more preferred, so the bit still has meaning. */
57 #define TLS_HWCAP_BIT 63
58
59 #ifndef LD_SO_CONF
60 # define LD_SO_CONF SYSCONFDIR "/ld.so.conf"
61 #endif
62
63 /* Get libc version number. */
64 #include <version.h>
65
66 #define PACKAGE _libc_intl_domainname
67
68 static const struct
69 {
70 const char *name;
71 int flag;
72 } lib_types[] =
73 {
74 {"libc4", FLAG_LIBC4},
75 {"libc5", FLAG_ELF_LIBC5},
76 {"libc6", FLAG_ELF_LIBC6},
77 {"glibc2", FLAG_ELF_LIBC6}
78 };
79
80
81 /* List of directories to handle. */
82 struct dir_entry
83 {
84 char *path;
85 int flag;
86 ino64_t ino;
87 dev_t dev;
88 const char *from_file;
89 int from_line;
90
91 /* Non-NULL for subdirectories under a glibc-hwcaps subdirectory. */
92 struct glibc_hwcaps_subdirectory *hwcaps;
93
94 struct dir_entry *next;
95 };
96
97 /* The list is unsorted, contains no duplicates. Entries are added at
98 the end. */
99 static struct dir_entry *dir_entries;
100
101 /* Flags for different options. */
102 /* Print Cache. */
103 static int opt_print_cache;
104
105 /* Be verbose. */
106 int opt_verbose;
107
108 /* Format to support. */
109 enum opt_format opt_format = opt_format_new;
110
111 /* Build cache. */
112 static int opt_build_cache = 1;
113
114 /* Enable symbolic link processing. If set, create or update symbolic
115 links, and remove stale symbolic links. */
116 static int opt_link = 1;
117
118 /* Only process directories specified on the command line. */
119 static int opt_only_cline;
120
121 /* Path to root for chroot. */
122 static char *opt_chroot;
123
124 /* Manually link given shared libraries. */
125 static int opt_manual_link;
126
127 /* Should we ignore an old auxiliary cache file? */
128 static int opt_ignore_aux_cache;
129
130 /* Cache file to use. */
131 static char *cache_file;
132
133 /* Configuration file. */
134 static const char *config_file;
135
136 /* Mask to use for important hardware capabilities. */
137 static unsigned long int hwcap_mask = HWCAP_IMPORTANT;
138
139 /* Name and version of program. */
140 static void print_version (FILE *stream, struct argp_state *state);
141 void (*argp_program_version_hook) (FILE *, struct argp_state *)
142 = print_version;
143
144 /* Function to print some extra text in the help message. */
145 static char *more_help (int key, const char *text, void *input);
146
147 /* Definitions of arguments for argp functions. */
148 static const struct argp_option options[] =
149 {
150 { "print-cache", 'p', NULL, 0, N_("Print cache"), 0},
151 { "verbose", 'v', NULL, 0, N_("Generate verbose messages"), 0},
152 { NULL, 'N', NULL, 0, N_("Don't build cache"), 0},
153 { NULL, 'X', NULL, 0, N_("Don't update symbolic links"), 0},
154 { NULL, 'r', N_("ROOT"), 0, N_("Change to and use ROOT as root directory"), 0},
155 { NULL, 'C', N_("CACHE"), 0, N_("Use CACHE as cache file"), 0},
156 { NULL, 'f', N_("CONF"), 0, N_("Use CONF as configuration file"), 0},
157 { NULL, 'n', NULL, 0, N_("Only process directories specified on the command line. Don't build cache."), 0},
158 { NULL, 'l', NULL, 0, N_("Manually link individual libraries."), 0},
159 { "format", 'c', N_("FORMAT"), 0, N_("Format to use: new (default), old, or compat"), 0},
160 { "ignore-aux-cache", 'i', NULL, 0, N_("Ignore auxiliary cache file"), 0},
161 { NULL, 0, NULL, 0, NULL, 0 }
162 };
163
164 #define PROCINFO_CLASS static
165 #include <dl-procinfo.c>
166
167 /* Short description of program. */
168 static const char doc[] = N_("Configure Dynamic Linker Run Time Bindings.");
169
170 /* Prototype for option handler. */
171 static error_t parse_opt (int key, char *arg, struct argp_state *state);
172
173 /* Data structure to communicate with argp functions. */
174 static struct argp argp =
175 {
176 options, parse_opt, NULL, doc, NULL, more_help, NULL
177 };
178
179 /* Check if string corresponds to an important hardware capability or
180 a platform. */
181 static int
is_hwcap_platform(const char * name)182 is_hwcap_platform (const char *name)
183 {
184 int hwcap_idx = _dl_string_hwcap (name);
185
186 /* Is this a normal hwcap for the machine like "fpu?" */
187 if (hwcap_idx != -1 && ((1 << hwcap_idx) & hwcap_mask))
188 return 1;
189
190 /* Is this a platform pseudo-hwcap like "i686?" */
191 hwcap_idx = _dl_string_platform (name);
192 if (hwcap_idx != -1)
193 return 1;
194
195 /* Backwards-compatibility for the "tls" subdirectory. */
196 if (strcmp (name, TLS_SUBPATH) == 0)
197 return 1;
198
199 return 0;
200 }
201
202 /* Get hwcap (including platform) encoding of path. */
203 static uint64_t
path_hwcap(const char * path)204 path_hwcap (const char *path)
205 {
206 char *str = xstrdup (path);
207 char *ptr;
208 uint64_t hwcap = 0;
209 uint64_t h;
210
211 size_t len;
212
213 len = strlen (str);
214 if (str[len] == '/')
215 str[len] = '\0';
216
217 /* Search pathname from the end and check for hwcap strings. */
218 for (;;)
219 {
220 ptr = strrchr (str, '/');
221
222 if (ptr == NULL)
223 break;
224
225 h = _dl_string_hwcap (ptr + 1);
226
227 if (h == (uint64_t) -1)
228 {
229 h = _dl_string_platform (ptr + 1);
230 if (h == (uint64_t) -1)
231 {
232 if (strcmp (ptr + 1, TLS_SUBPATH) == 0)
233 h = TLS_HWCAP_BIT;
234 else
235 break;
236 }
237 }
238 hwcap += 1ULL << h;
239
240 /* Search the next part of the path. */
241 *ptr = '\0';
242 }
243
244 free (str);
245 return hwcap;
246 }
247
248 /* Handle program arguments. */
249 static error_t
parse_opt(int key,char * arg,struct argp_state * state)250 parse_opt (int key, char *arg, struct argp_state *state)
251 {
252 switch (key)
253 {
254 case 'C':
255 cache_file = arg;
256 /* Ignore auxiliary cache since we use non-standard cache. */
257 opt_ignore_aux_cache = 1;
258 break;
259 case 'f':
260 config_file = arg;
261 break;
262 case 'i':
263 opt_ignore_aux_cache = 1;
264 break;
265 case 'l':
266 opt_manual_link = 1;
267 break;
268 case 'N':
269 opt_build_cache = 0;
270 break;
271 case 'n':
272 opt_build_cache = 0;
273 opt_only_cline = 1;
274 break;
275 case 'p':
276 opt_print_cache = 1;
277 break;
278 case 'r':
279 opt_chroot = arg;
280 break;
281 case 'v':
282 opt_verbose = 1;
283 break;
284 case 'X':
285 opt_link = 0;
286 break;
287 case 'c':
288 if (strcmp (arg, "old") == 0)
289 opt_format = opt_format_old;
290 else if (strcmp (arg, "compat") == 0)
291 opt_format = opt_format_compat;
292 else if (strcmp (arg, "new") == 0)
293 opt_format = opt_format_new;
294 break;
295 default:
296 return ARGP_ERR_UNKNOWN;
297 }
298
299 return 0;
300 }
301
302 /* Print bug-reporting information in the help message. */
303 static char *
more_help(int key,const char * text,void * input)304 more_help (int key, const char *text, void *input)
305 {
306 char *tp = NULL;
307 switch (key)
308 {
309 case ARGP_KEY_HELP_EXTRA:
310 /* We print some extra information. */
311 if (asprintf (&tp, gettext ("\
312 For bug reporting instructions, please see:\n\
313 %s.\n"), REPORT_BUGS_TO) < 0)
314 return NULL;
315 return tp;
316 default:
317 break;
318 }
319 return (char *) text;
320 }
321
322 /* Print the version information. */
323 static void
print_version(FILE * stream,struct argp_state * state)324 print_version (FILE *stream, struct argp_state *state)
325 {
326 fprintf (stream, "ldconfig %s%s\n", PKGVERSION, VERSION);
327 fprintf (stream, gettext ("\
328 Copyright (C) %s Free Software Foundation, Inc.\n\
329 This is free software; see the source for copying conditions. There is NO\n\
330 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
331 "), "2022");
332 fprintf (stream, gettext ("Written by %s.\n"),
333 "Andreas Jaeger");
334 }
335
336 /* Allocate a new subdirectory with full path PATH under ENTRY, using
337 inode data from *ST. */
338 static struct dir_entry *
new_sub_entry(const struct dir_entry * entry,const char * path,const struct stat * st)339 new_sub_entry (const struct dir_entry *entry, const char *path,
340 const struct stat *st)
341 {
342 struct dir_entry *new_entry = xmalloc (sizeof (struct dir_entry));
343 new_entry->from_file = entry->from_file;
344 new_entry->from_line = entry->from_line;
345 new_entry->path = xstrdup (path);
346 new_entry->flag = entry->flag;
347 new_entry->hwcaps = NULL;
348 new_entry->next = NULL;
349 new_entry->ino = st->st_ino;
350 new_entry->dev = st->st_dev;
351 return new_entry;
352 }
353
354 /* Add a single directory entry. Return true if the directory is
355 actually added (because it is not a duplicate). */
356 static bool
add_single_dir(struct dir_entry * entry,int verbose)357 add_single_dir (struct dir_entry *entry, int verbose)
358 {
359 struct dir_entry *ptr, *prev;
360 bool added = true;
361
362 ptr = dir_entries;
363 prev = ptr;
364 while (ptr != NULL)
365 {
366 /* Check for duplicates. */
367 if (ptr->ino == entry->ino && ptr->dev == entry->dev)
368 {
369 if (opt_verbose && verbose)
370 {
371 error (0, 0, _("Path `%s' given more than once"), entry->path);
372 fprintf (stderr, _("(from %s:%d and %s:%d)\n"),
373 entry->from_file, entry->from_line,
374 ptr->from_file, ptr->from_line);
375 }
376 /* Use the newer information. */
377 ptr->flag = entry->flag;
378 free (entry->path);
379 free (entry);
380 added = false;
381 break;
382 }
383 prev = ptr;
384 ptr = ptr->next;
385 }
386 /* Is this the first entry? */
387 if (ptr == NULL && dir_entries == NULL)
388 dir_entries = entry;
389 else if (ptr == NULL)
390 prev->next = entry;
391 return added;
392 }
393
394 /* Check if PATH contains a "glibc-hwcaps" subdirectory. If so, queue
395 its subdirectories for glibc-hwcaps processing. */
396 static void
add_glibc_hwcaps_subdirectories(struct dir_entry * entry,const char * path)397 add_glibc_hwcaps_subdirectories (struct dir_entry *entry, const char *path)
398 {
399 /* glibc-hwcaps subdirectories do not nest. */
400 assert (entry->hwcaps == NULL);
401
402 char *glibc_hwcaps;
403 if (asprintf (&glibc_hwcaps, "%s/" GLIBC_HWCAPS_SUBDIRECTORY, path) < 0)
404 error (EXIT_FAILURE, errno, _("Could not form glibc-hwcaps path"));
405
406 DIR *dir = opendir (glibc_hwcaps);
407 if (dir != NULL)
408 {
409 while (true)
410 {
411 errno = 0;
412 struct dirent64 *e = readdir64 (dir);
413 if (e == NULL)
414 {
415 if (errno == 0)
416 break;
417 else
418 error (EXIT_FAILURE, errno, _("Listing directory %s"), path);
419 }
420
421 /* Ignore hidden subdirectories, including "." and "..", and
422 regular files. File names containing a ':' cannot be
423 looked up by the dynamic loader, so skip those as
424 well. */
425 if (e->d_name[0] == '.' || e->d_type == DT_REG
426 || strchr (e->d_name, ':') != NULL)
427 continue;
428
429 /* See if this entry eventually resolves to a directory. */
430 struct stat st;
431 if (fstatat (dirfd (dir), e->d_name, &st, 0) < 0)
432 /* Ignore unreadable entries. */
433 continue;
434
435 if (S_ISDIR (st.st_mode))
436 {
437 /* This is a directory, so it needs to be scanned for
438 libraries, associated with the hwcaps implied by the
439 subdirectory name. */
440 char *new_path;
441 if (asprintf (&new_path, "%s/" GLIBC_HWCAPS_SUBDIRECTORY "/%s",
442 /* Use non-canonicalized path here. */
443 entry->path, e->d_name) < 0)
444 error (EXIT_FAILURE, errno,
445 _("Could not form glibc-hwcaps path"));
446 struct dir_entry *new_entry = new_sub_entry (entry, new_path,
447 &st);
448 free (new_path);
449 new_entry->hwcaps = new_glibc_hwcaps_subdirectory (e->d_name);
450 add_single_dir (new_entry, 0);
451 }
452 }
453
454 closedir (dir);
455 }
456
457 free (glibc_hwcaps);
458 }
459
460 /* Add one directory to the list of directories to process. */
461 static void
add_dir_1(const char * line,const char * from_file,int from_line)462 add_dir_1 (const char *line, const char *from_file, int from_line)
463 {
464 unsigned int i;
465 struct dir_entry *entry = xmalloc (sizeof (struct dir_entry));
466 entry->hwcaps = NULL;
467 entry->next = NULL;
468
469 entry->from_file = strdup (from_file);
470 entry->from_line = from_line;
471
472 /* Search for an '=' sign. */
473 entry->path = xstrdup (line);
474 char *equal_sign = strchr (entry->path, '=');
475 if (equal_sign)
476 {
477 *equal_sign = '\0';
478 ++equal_sign;
479 entry->flag = FLAG_ANY;
480 for (i = 0; i < sizeof (lib_types) / sizeof (lib_types[0]); ++i)
481 if (strcmp (equal_sign, lib_types[i].name) == 0)
482 {
483 entry->flag = lib_types[i].flag;
484 break;
485 }
486 if (entry->flag == FLAG_ANY)
487 error (0, 0, _("%s is not a known library type"), equal_sign);
488 }
489 else
490 {
491 entry->flag = FLAG_ANY;
492 }
493
494 /* Canonify path: for now only remove leading and trailing
495 whitespace and the trailing slashes. */
496 i = strlen (entry->path);
497
498 while (i > 0 && isspace (entry->path[i - 1]))
499 entry->path[--i] = '\0';
500
501 while (i > 0 && entry->path[i - 1] == '/')
502 entry->path[--i] = '\0';
503
504 if (i == 0)
505 {
506 free (entry->path);
507 free (entry);
508 return;
509 }
510
511 char *path = entry->path;
512 if (opt_chroot != NULL)
513 path = chroot_canon (opt_chroot, path);
514
515 struct stat stat_buf;
516 if (path == NULL || stat (path, &stat_buf))
517 {
518 if (opt_verbose)
519 error (0, errno, _("Can't stat %s"), entry->path);
520 free (entry->path);
521 free (entry);
522 }
523 else
524 {
525 entry->ino = stat_buf.st_ino;
526 entry->dev = stat_buf.st_dev;
527
528 if (add_single_dir (entry, 1))
529 /* Add glibc-hwcaps subdirectories if present. */
530 add_glibc_hwcaps_subdirectories (entry, path);
531 }
532
533 if (opt_chroot != NULL)
534 free (path);
535 }
536
537 static void
add_dir(const char * line)538 add_dir (const char *line)
539 {
540 add_dir_1 (line, "<builtin>", 0);
541 }
542
543 static int
chroot_stat(const char * real_path,const char * path,struct stat * st)544 chroot_stat (const char *real_path, const char *path, struct stat *st)
545 {
546 int ret;
547 char *canon_path;
548
549 if (!opt_chroot)
550 return stat (real_path, st);
551
552 ret = lstat (real_path, st);
553 if (ret || !S_ISLNK (st->st_mode))
554 return ret;
555
556 canon_path = chroot_canon (opt_chroot, path);
557 if (canon_path == NULL)
558 return -1;
559
560 ret = stat (canon_path, st);
561 free (canon_path);
562 return ret;
563 }
564
565 /* Create a symbolic link from soname to libname in directory path. */
566 static void
create_links(const char * real_path,const char * path,const char * libname,const char * soname)567 create_links (const char *real_path, const char *path, const char *libname,
568 const char *soname)
569 {
570 char *full_libname, *full_soname;
571 char *real_full_libname, *real_full_soname;
572 struct stat stat_lib, stat_so, lstat_so;
573 int do_link = 1;
574 int do_remove = 1;
575 /* XXX: The logics in this function should be simplified. */
576
577 /* Get complete path. */
578 full_libname = alloca (strlen (path) + strlen (libname) + 2);
579 full_soname = alloca (strlen (path) + strlen (soname) + 2);
580 sprintf (full_libname, "%s/%s", path, libname);
581 sprintf (full_soname, "%s/%s", path, soname);
582 if (opt_chroot != NULL)
583 {
584 real_full_libname = alloca (strlen (real_path) + strlen (libname) + 2);
585 real_full_soname = alloca (strlen (real_path) + strlen (soname) + 2);
586 sprintf (real_full_libname, "%s/%s", real_path, libname);
587 sprintf (real_full_soname, "%s/%s", real_path, soname);
588 }
589 else
590 {
591 real_full_libname = full_libname;
592 real_full_soname = full_soname;
593 }
594
595 /* Does soname already exist and point to the right library? */
596 if (chroot_stat (real_full_soname, full_soname, &stat_so) == 0)
597 {
598 if (chroot_stat (real_full_libname, full_libname, &stat_lib))
599 {
600 error (0, 0, _("Can't stat %s\n"), full_libname);
601 return;
602 }
603 if (stat_lib.st_dev == stat_so.st_dev
604 && stat_lib.st_ino == stat_so.st_ino)
605 /* Link is already correct. */
606 do_link = 0;
607 else if (lstat (full_soname, &lstat_so) == 0
608 && !S_ISLNK (lstat_so.st_mode))
609 {
610 error (0, 0, _("%s is not a symbolic link\n"), full_soname);
611 do_link = 0;
612 do_remove = 0;
613 }
614 }
615 else if (lstat (real_full_soname, &lstat_so) != 0
616 || !S_ISLNK (lstat_so.st_mode))
617 /* Unless it is a stale symlink, there is no need to remove. */
618 do_remove = 0;
619
620 if (opt_verbose)
621 printf ("\t%s -> %s", soname, libname);
622
623 if (do_link && opt_link)
624 {
625 /* Remove old link. */
626 if (do_remove)
627 if (unlink (real_full_soname))
628 {
629 error (0, 0, _("Can't unlink %s"), full_soname);
630 do_link = 0;
631 }
632 /* Create symbolic link. */
633 if (do_link && symlink (libname, real_full_soname))
634 {
635 error (0, 0, _("Can't link %s to %s"), full_soname, libname);
636 do_link = 0;
637 }
638 if (opt_verbose)
639 {
640 if (do_link)
641 fputs (_(" (changed)\n"), stdout);
642 else
643 fputs (_(" (SKIPPED)\n"), stdout);
644 }
645 }
646 else if (opt_verbose)
647 fputs ("\n", stdout);
648 }
649
650 /* Manually link the given library. */
651 static void
manual_link(char * library)652 manual_link (char *library)
653 {
654 char *path;
655 char *real_path;
656 char *real_library;
657 char *libname;
658 char *soname;
659 struct stat stat_buf;
660 int flag;
661 unsigned int isa_level;
662
663 /* Prepare arguments for create_links call. Split library name in
664 directory and filename first. Since path is allocated, we've got
665 to be careful to free at the end. */
666 path = xstrdup (library);
667 libname = strrchr (path, '/');
668
669 if (libname)
670 {
671 /* Successfully split names. Check if path is just "/" to avoid
672 an empty path. */
673 if (libname == path)
674 {
675 libname = library + 1;
676 path = xrealloc (path, 2);
677 strcpy (path, "/");
678 }
679 else
680 {
681 *libname = '\0';
682 ++libname;
683 }
684 }
685 else
686 {
687 /* There's no path, construct one. */
688 libname = library;
689 path = xrealloc (path, 2);
690 strcpy (path, ".");
691 }
692
693 if (opt_chroot != NULL)
694 {
695 real_path = chroot_canon (opt_chroot, path);
696 if (real_path == NULL)
697 {
698 error (0, errno, _("Can't find %s"), path);
699 free (path);
700 return;
701 }
702 real_library = alloca (strlen (real_path) + strlen (libname) + 2);
703 sprintf (real_library, "%s/%s", real_path, libname);
704 }
705 else
706 {
707 real_path = path;
708 real_library = library;
709 }
710
711 /* Do some sanity checks first. */
712 if (lstat (real_library, &stat_buf))
713 {
714 error (0, errno, _("Cannot lstat %s"), library);
715 goto out;
716 }
717 /* We don't want links here! */
718 else if (!S_ISREG (stat_buf.st_mode))
719 {
720 error (0, 0, _("Ignored file %s since it is not a regular file."),
721 library);
722 goto out;
723 }
724
725 if (process_file (real_library, library, libname, &flag, &isa_level, &soname,
726 0, &stat_buf))
727 {
728 error (0, 0, _("No link created since soname could not be found for %s"),
729 library);
730 goto out;
731 }
732 if (soname == NULL)
733 soname = implicit_soname (libname, flag);
734 create_links (real_path, path, libname, soname);
735 free (soname);
736 out:
737 if (path != real_path)
738 free (real_path);
739 free (path);
740 }
741
742
743 /* Read a whole directory and search for libraries.
744 The purpose is two-fold:
745 - search for libraries which will be added to the cache
746 - create symbolic links to the soname for each library
747
748 This has to be done separatly for each directory.
749
750 To keep track of which libraries to add to the cache and which
751 links to create, we save a list of all libraries.
752
753 The algorithm is basically:
754 for all libraries in the directory do
755 get soname of library
756 if soname is already in list
757 if new library is newer, replace entry
758 otherwise ignore this library
759 otherwise add library to list
760
761 For example, if the two libraries libxy.so.1.1 and libxy.so.1.2
762 exist and both have the same soname, e.g. libxy.so, a symbolic link
763 is created from libxy.so.1.2 (the newer one) to libxy.so.
764 libxy.so.1.2 and libxy.so are added to the cache - but not
765 libxy.so.1.1. */
766
767 /* Information for one library. */
768 struct dlib_entry
769 {
770 char *name;
771 char *soname;
772 int flag;
773 int is_link;
774 unsigned int isa_level;
775 struct dlib_entry *next;
776 };
777
778
779 static void
search_dir(const struct dir_entry * entry)780 search_dir (const struct dir_entry *entry)
781 {
782 uint64_t hwcap;
783 if (entry->hwcaps == NULL)
784 {
785 hwcap = path_hwcap (entry->path);
786 if (opt_verbose)
787 {
788 if (hwcap != 0)
789 printf ("%s: (hwcap: %#.16" PRIx64 ")", entry->path, hwcap);
790 else
791 printf ("%s:", entry->path);
792 }
793 }
794 else
795 {
796 hwcap = 0;
797 if (opt_verbose)
798 printf ("%s: (hwcap: \"%s\")", entry->path,
799 glibc_hwcaps_subdirectory_name (entry->hwcaps));
800 }
801 if (opt_verbose)
802 printf (_(" (from %s:%d)\n"), entry->from_file, entry->from_line);
803
804 char *dir_name;
805 char *real_file_name;
806 size_t real_file_name_len;
807 size_t file_name_len = PATH_MAX;
808 char *file_name = alloca (file_name_len);
809 if (opt_chroot != NULL)
810 {
811 dir_name = chroot_canon (opt_chroot, entry->path);
812 real_file_name_len = PATH_MAX;
813 real_file_name = alloca (real_file_name_len);
814 }
815 else
816 {
817 dir_name = entry->path;
818 real_file_name_len = 0;
819 real_file_name = file_name;
820 }
821
822 DIR *dir;
823 if (dir_name == NULL || (dir = opendir (dir_name)) == NULL)
824 {
825 if (opt_verbose)
826 error (0, errno, _("Can't open directory %s"), entry->path);
827 if (opt_chroot != NULL && dir_name != NULL)
828 free (dir_name);
829 return;
830 }
831
832 struct dirent64 *direntry;
833 struct dlib_entry *dlibs = NULL;
834 while ((direntry = readdir64 (dir)) != NULL)
835 {
836 int flag;
837 /* We only look at links and regular files. */
838 if (direntry->d_type != DT_UNKNOWN
839 && direntry->d_type != DT_LNK
840 && direntry->d_type != DT_REG
841 && direntry->d_type != DT_DIR)
842 continue;
843 /* Does this file look like a shared library or is it a hwcap
844 subdirectory (if not already processing a glibc-hwcaps
845 subdirectory)? The dynamic linker is also considered as
846 shared library. */
847 if (!_dl_is_dso (direntry->d_name)
848 && (direntry->d_type == DT_REG
849 || (entry->hwcaps == NULL
850 && !is_hwcap_platform (direntry->d_name))))
851 continue;
852
853 size_t len = strlen (direntry->d_name);
854 /* Skip temporary files created by the prelink program. Files with
855 names like these are never really DSOs we want to look at. */
856 if (len >= sizeof (".#prelink#") - 1)
857 {
858 if (strcmp (direntry->d_name + len - sizeof (".#prelink#") + 1,
859 ".#prelink#") == 0)
860 continue;
861 if (len >= sizeof (".#prelink#.XXXXXX") - 1
862 && memcmp (direntry->d_name + len - sizeof (".#prelink#.XXXXXX")
863 + 1, ".#prelink#.", sizeof (".#prelink#.") - 1) == 0)
864 continue;
865 }
866 len += strlen (entry->path) + 2;
867 if (len > file_name_len)
868 {
869 file_name_len = len;
870 file_name = alloca (file_name_len);
871 if (!opt_chroot)
872 real_file_name = file_name;
873 }
874 sprintf (file_name, "%s/%s", entry->path, direntry->d_name);
875 if (opt_chroot != NULL)
876 {
877 len = strlen (dir_name) + strlen (direntry->d_name) + 2;
878 if (len > real_file_name_len)
879 {
880 real_file_name_len = len;
881 real_file_name = alloca (real_file_name_len);
882 }
883 sprintf (real_file_name, "%s/%s", dir_name, direntry->d_name);
884 }
885
886 struct stat lstat_buf;
887 /* We optimize and try to do the lstat call only if needed. */
888 if (direntry->d_type != DT_UNKNOWN)
889 lstat_buf.st_mode = DTTOIF (direntry->d_type);
890 else
891 if (__glibc_unlikely (lstat (real_file_name, &lstat_buf)))
892 {
893 error (0, errno, _("Cannot lstat %s"), file_name);
894 continue;
895 }
896
897 struct stat stat_buf;
898 bool is_dir;
899 int is_link = S_ISLNK (lstat_buf.st_mode);
900 if (is_link)
901 {
902 /* In case of symlink, we check if the symlink refers to
903 a directory. */
904 char *target_name = real_file_name;
905 if (opt_chroot != NULL)
906 {
907 target_name = chroot_canon (opt_chroot, file_name);
908 if (target_name == NULL)
909 {
910 if (strstr (file_name, ".so") == NULL)
911 error (0, 0, _("Input file %s not found.\n"), file_name);
912 continue;
913 }
914 }
915 if (__glibc_unlikely (stat (target_name, &stat_buf)))
916 {
917 if (opt_verbose)
918 error (0, errno, _("Cannot stat %s"), file_name);
919
920 /* Remove stale symlinks. */
921 if (opt_link && strstr (direntry->d_name, ".so."))
922 unlink (real_file_name);
923
924 if (opt_chroot != NULL)
925 free (target_name);
926
927 continue;
928 }
929
930 if (opt_chroot != NULL)
931 free (target_name);
932
933 is_dir = S_ISDIR (stat_buf.st_mode);
934
935 /* lstat_buf is later stored, update contents. */
936 lstat_buf.st_dev = stat_buf.st_dev;
937 lstat_buf.st_ino = stat_buf.st_ino;
938 lstat_buf.st_size = stat_buf.st_size;
939 lstat_buf.st_ctime = stat_buf.st_ctime;
940 }
941 else
942 is_dir = S_ISDIR (lstat_buf.st_mode);
943
944 /* No descending into subdirectories if this directory is a
945 glibc-hwcaps subdirectory (which are not recursive). */
946 if (entry->hwcaps == NULL
947 && is_dir && is_hwcap_platform (direntry->d_name))
948 {
949 if (!is_link
950 && direntry->d_type != DT_UNKNOWN
951 && __builtin_expect (lstat (real_file_name, &lstat_buf), 0))
952 {
953 error (0, errno, _("Cannot lstat %s"), file_name);
954 continue;
955 }
956
957 /* Handle subdirectory later. */
958 struct dir_entry *new_entry = new_sub_entry (entry, file_name,
959 &lstat_buf);
960 add_single_dir (new_entry, 0);
961 continue;
962 }
963 else if (!S_ISREG (lstat_buf.st_mode) && !is_link)
964 continue;
965
966 char *real_name;
967 if (opt_chroot != NULL && is_link)
968 {
969 real_name = chroot_canon (opt_chroot, file_name);
970 if (real_name == NULL)
971 {
972 if (strstr (file_name, ".so") == NULL)
973 error (0, 0, _("Input file %s not found.\n"), file_name);
974 continue;
975 }
976 }
977 else
978 real_name = real_file_name;
979
980 /* Call lstat if not done yet. */
981 if (!is_link
982 && direntry->d_type != DT_UNKNOWN
983 && __builtin_expect (lstat (real_file_name, &lstat_buf), 0))
984 {
985 error (0, errno, _("Cannot lstat %s"), file_name);
986 continue;
987 }
988
989 /* First search whether the auxiliary cache contains this
990 library already and it's not changed. */
991 char *soname;
992 unsigned int isa_level;
993 if (!search_aux_cache (&lstat_buf, &flag, &isa_level, &soname))
994 {
995 if (process_file (real_name, file_name, direntry->d_name, &flag,
996 &isa_level, &soname, is_link, &lstat_buf))
997 {
998 if (real_name != real_file_name)
999 free (real_name);
1000 continue;
1001 }
1002 else if (opt_build_cache)
1003 add_to_aux_cache (&lstat_buf, flag, isa_level, soname);
1004 }
1005
1006 if (soname == NULL)
1007 soname = implicit_soname (direntry->d_name, flag);
1008
1009 /* A link may just point to itself. */
1010 if (is_link)
1011 {
1012 /* If the path the link points to isn't its soname or it is not
1013 the .so symlink for ld(1), we treat it as a normal file.
1014
1015 You should always do this:
1016
1017 libfoo.so -> SONAME -> Arbitrary package-chosen name.
1018
1019 e.g. libfoo.so -> libfoo.so.1 -> libfooimp.so.9.99.
1020 Given a SONAME of libfoo.so.1.
1021
1022 You should *never* do this:
1023
1024 libfoo.so -> libfooimp.so.9.99
1025
1026 If you do, and your SONAME is libfoo.so.1, then libfoo.so
1027 fails to point at the SONAME. In that case ldconfig may consider
1028 libfoo.so as another implementation of SONAME and will create
1029 symlinks against it causing problems when you try to upgrade
1030 or downgrade. The problems will arise because ldconfig will,
1031 depending on directory ordering, creat symlinks against libfoo.so
1032 e.g. libfoo.so.1.2 -> libfoo.so, but when libfoo.so is removed
1033 (typically by the removal of a development pacakge not required
1034 for the runtime) it will break the libfoo.so.1.2 symlink and the
1035 application will fail to start. */
1036 const char *real_base_name = basename (real_file_name);
1037
1038 if (strcmp (real_base_name, soname) != 0)
1039 {
1040 len = strlen (real_base_name);
1041 if (len < strlen (".so")
1042 || strcmp (real_base_name + len - strlen (".so"), ".so") != 0
1043 || strncmp (real_base_name, soname, len) != 0)
1044 is_link = 0;
1045 }
1046 }
1047
1048 if (real_name != real_file_name)
1049 free (real_name);
1050
1051 if (is_link)
1052 {
1053 free (soname);
1054 soname = xstrdup (direntry->d_name);
1055 }
1056
1057 if (flag == FLAG_ELF
1058 && (entry->flag == FLAG_ELF_LIBC5
1059 || entry->flag == FLAG_ELF_LIBC6))
1060 flag = entry->flag;
1061
1062 /* Some sanity checks to print warnings. */
1063 if (opt_verbose)
1064 {
1065 if (flag == FLAG_ELF_LIBC5 && entry->flag != FLAG_ELF_LIBC5
1066 && entry->flag != FLAG_ANY)
1067 error (0, 0, _("libc5 library %s in wrong directory"), file_name);
1068 if (flag == FLAG_ELF_LIBC6 && entry->flag != FLAG_ELF_LIBC6
1069 && entry->flag != FLAG_ANY)
1070 error (0, 0, _("libc6 library %s in wrong directory"), file_name);
1071 if (flag == FLAG_LIBC4 && entry->flag != FLAG_LIBC4
1072 && entry->flag != FLAG_ANY)
1073 error (0, 0, _("libc4 library %s in wrong directory"), file_name);
1074 }
1075
1076 /* Add library to list. */
1077 struct dlib_entry *dlib_ptr;
1078 for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
1079 {
1080 /* Is soname already in list? */
1081 if (strcmp (dlib_ptr->soname, soname) == 0)
1082 {
1083 /* Prefer a file to a link, otherwise check which one
1084 is newer. */
1085 if ((!is_link && dlib_ptr->is_link)
1086 || (is_link == dlib_ptr->is_link
1087 && _dl_cache_libcmp (dlib_ptr->name, direntry->d_name) < 0))
1088 {
1089 /* It's newer - add it. */
1090 /* Flag should be the same - sanity check. */
1091 if (dlib_ptr->flag != flag)
1092 {
1093 if (dlib_ptr->flag == FLAG_ELF
1094 && (flag == FLAG_ELF_LIBC5 || flag == FLAG_ELF_LIBC6))
1095 dlib_ptr->flag = flag;
1096 else if ((dlib_ptr->flag == FLAG_ELF_LIBC5
1097 || dlib_ptr->flag == FLAG_ELF_LIBC6)
1098 && flag == FLAG_ELF)
1099 dlib_ptr->flag = flag;
1100 else
1101 error (0, 0, _("libraries %s and %s in directory %s have same soname but different type."),
1102 dlib_ptr->name, direntry->d_name,
1103 entry->path);
1104 }
1105 free (dlib_ptr->name);
1106 dlib_ptr->name = xstrdup (direntry->d_name);
1107 dlib_ptr->is_link = is_link;
1108 dlib_ptr->isa_level = isa_level;
1109 }
1110 /* Don't add this library, abort loop. */
1111 /* Also free soname, since it's dynamically allocated. */
1112 free (soname);
1113 break;
1114 }
1115 }
1116 /* Add the library if it's not already in. */
1117 if (dlib_ptr == NULL)
1118 {
1119 dlib_ptr = (struct dlib_entry *)xmalloc (sizeof (struct dlib_entry));
1120 dlib_ptr->name = xstrdup (direntry->d_name);
1121 dlib_ptr->soname = soname;
1122 dlib_ptr->flag = flag;
1123 dlib_ptr->is_link = is_link;
1124 dlib_ptr->isa_level = isa_level;
1125 /* Add at head of list. */
1126 dlib_ptr->next = dlibs;
1127 dlibs = dlib_ptr;
1128 }
1129 }
1130
1131 closedir (dir);
1132
1133 /* Now dlibs contains a list of all libs - add those to the cache
1134 and created all symbolic links. */
1135 struct dlib_entry *dlib_ptr;
1136 for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next)
1137 {
1138 /* The cached file name is the soname for non-glibc-hwcaps
1139 subdirectories (relying on symbolic links; this helps with
1140 library updates that change the file name), and the actual
1141 file for glibc-hwcaps subdirectories. */
1142 const char *filename;
1143 if (entry->hwcaps == NULL)
1144 {
1145 /* Don't create links to links. */
1146 if (dlib_ptr->is_link == 0)
1147 create_links (dir_name, entry->path, dlib_ptr->name,
1148 dlib_ptr->soname);
1149 filename = dlib_ptr->soname;
1150 }
1151 else
1152 {
1153 /* Do not create links in glibc-hwcaps subdirectories, but
1154 still log the cache addition. */
1155 if (opt_verbose)
1156 printf ("\t%s -> %s\n", dlib_ptr->soname, dlib_ptr->name);
1157 filename = dlib_ptr->name;
1158 }
1159 if (opt_build_cache)
1160 add_to_cache (entry->path, filename, dlib_ptr->soname,
1161 dlib_ptr->flag, dlib_ptr->isa_level, hwcap,
1162 entry->hwcaps);
1163 }
1164
1165 /* Free all resources. */
1166 while (dlibs)
1167 {
1168 dlib_ptr = dlibs;
1169 free (dlib_ptr->soname);
1170 free (dlib_ptr->name);
1171 dlibs = dlibs->next;
1172 free (dlib_ptr);
1173 }
1174
1175 if (opt_chroot != NULL && dir_name != NULL)
1176 free (dir_name);
1177 }
1178
1179 /* Search through all libraries. */
1180 static void
search_dirs(void)1181 search_dirs (void)
1182 {
1183 struct dir_entry *entry;
1184
1185 for (entry = dir_entries; entry != NULL; entry = entry->next)
1186 search_dir (entry);
1187
1188 /* Free all allocated memory. */
1189 while (dir_entries)
1190 {
1191 entry = dir_entries;
1192 dir_entries = dir_entries->next;
1193 free (entry->path);
1194 free (entry);
1195 }
1196 }
1197
1198
1199 static void parse_conf_include (const char *config_file, unsigned int lineno,
1200 bool do_chroot, const char *pattern);
1201
1202 /* Parse configuration file. */
1203 static void
parse_conf(const char * filename,bool do_chroot)1204 parse_conf (const char *filename, bool do_chroot)
1205 {
1206 FILE *file = NULL;
1207 char *line = NULL;
1208 const char *canon;
1209 size_t len = 0;
1210 unsigned int lineno;
1211
1212 if (do_chroot && opt_chroot)
1213 {
1214 canon = chroot_canon (opt_chroot, filename);
1215 if (canon)
1216 file = fopen (canon, "r");
1217 else
1218 canon = filename;
1219 }
1220 else
1221 {
1222 canon = filename;
1223 file = fopen (filename, "r");
1224 }
1225
1226 if (file == NULL)
1227 {
1228 if (errno != ENOENT)
1229 error (0, errno, _("\
1230 Warning: ignoring configuration file that cannot be opened: %s"),
1231 canon);
1232 if (canon != filename)
1233 free ((char *) canon);
1234 return;
1235 }
1236
1237 /* No threads use this stream. */
1238 __fsetlocking (file, FSETLOCKING_BYCALLER);
1239
1240 if (canon != filename)
1241 free ((char *) canon);
1242
1243 lineno = 0;
1244 do
1245 {
1246 ssize_t n = getline (&line, &len, file);
1247 if (n < 0)
1248 break;
1249
1250 ++lineno;
1251 if (line[n - 1] == '\n')
1252 line[n - 1] = '\0';
1253
1254 /* Because the file format does not know any form of quoting we
1255 can search forward for the next '#' character and if found
1256 make it terminating the line. */
1257 *strchrnul (line, '#') = '\0';
1258
1259 /* Remove leading whitespace. NUL is no whitespace character. */
1260 char *cp = line;
1261 while (isspace (*cp))
1262 ++cp;
1263
1264 /* If the line is blank it is ignored. */
1265 if (cp[0] == '\0')
1266 continue;
1267
1268 if (!strncmp (cp, "include", 7) && isblank (cp[7]))
1269 {
1270 char *dir;
1271 cp += 8;
1272 while ((dir = strsep (&cp, " \t")) != NULL)
1273 if (dir[0] != '\0')
1274 parse_conf_include (filename, lineno, do_chroot, dir);
1275 }
1276 else if (!strncasecmp (cp, "hwcap", 5) && isblank (cp[5]))
1277 error (0, 0, _("%s:%u: hwcap directive ignored"), filename, lineno);
1278 else
1279 add_dir_1 (cp, filename, lineno);
1280 }
1281 while (!feof_unlocked (file));
1282
1283 /* Free buffer and close file. */
1284 free (line);
1285 fclose (file);
1286 }
1287
1288 /* Handle one word in an `include' line, a glob pattern of additional
1289 config files to read. */
1290 static void
parse_conf_include(const char * config_file,unsigned int lineno,bool do_chroot,const char * pattern)1291 parse_conf_include (const char *config_file, unsigned int lineno,
1292 bool do_chroot, const char *pattern)
1293 {
1294 if (opt_chroot != NULL && pattern[0] != '/')
1295 error (EXIT_FAILURE, 0,
1296 _("need absolute file name for configuration file when using -r"));
1297
1298 char *copy = NULL;
1299 if (pattern[0] != '/' && strchr (config_file, '/') != NULL)
1300 {
1301 if (asprintf (©, "%s/%s", dirname (strdupa (config_file)),
1302 pattern) < 0)
1303 error (EXIT_FAILURE, 0, _("memory exhausted"));
1304 pattern = copy;
1305 }
1306
1307 glob64_t gl;
1308 int result;
1309 if (do_chroot && opt_chroot)
1310 {
1311 char *canon = chroot_canon (opt_chroot, pattern);
1312 if (canon == NULL)
1313 return;
1314 result = glob64 (canon, 0, NULL, &gl);
1315 free (canon);
1316 }
1317 else
1318 result = glob64 (pattern, 0, NULL, &gl);
1319
1320 switch (result)
1321 {
1322 case 0:
1323 for (size_t i = 0; i < gl.gl_pathc; ++i)
1324 parse_conf (gl.gl_pathv[i], false);
1325 globfree64 (&gl);
1326 break;
1327
1328 case GLOB_NOMATCH:
1329 break;
1330
1331 case GLOB_NOSPACE:
1332 errno = ENOMEM;
1333 /* Fall through. */
1334 case GLOB_ABORTED:
1335 if (opt_verbose)
1336 error (0, errno, _("%s:%u: cannot read directory %s"),
1337 config_file, lineno, pattern);
1338 break;
1339
1340 default:
1341 abort ();
1342 break;
1343 }
1344
1345 free (copy);
1346 }
1347
1348 /* Honour LD_HWCAP_MASK. */
1349 static void
set_hwcap(void)1350 set_hwcap (void)
1351 {
1352 char *mask = getenv ("LD_HWCAP_MASK");
1353
1354 if (mask)
1355 hwcap_mask = strtoul (mask, NULL, 0);
1356 }
1357
1358
1359 int
main(int argc,char ** argv)1360 main (int argc, char **argv)
1361 {
1362 /* Set locale via LC_ALL. */
1363 setlocale (LC_ALL, "");
1364
1365 /* But keep the C collation. That way `include' directives using
1366 globbing patterns are processed in a locale-independent order. */
1367 setlocale (LC_COLLATE, "C");
1368
1369 /* Set the text message domain. */
1370 textdomain (_libc_intl_domainname);
1371
1372 /* Parse and process arguments. */
1373 int remaining;
1374 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
1375
1376 /* Remaining arguments are additional directories if opt_manual_link
1377 is not set. */
1378 if (remaining != argc && !opt_manual_link)
1379 {
1380 int i;
1381 for (i = remaining; i < argc; ++i)
1382 if (opt_build_cache && argv[i][0] != '/')
1383 error (EXIT_FAILURE, 0,
1384 _("relative path `%s' used to build cache"),
1385 argv[i]);
1386 else
1387 add_dir_1 (argv[i], "<cmdline>", 0);
1388 }
1389
1390 set_hwcap ();
1391
1392 if (opt_chroot != NULL)
1393 {
1394 /* Normalize the path a bit, we might need it for printing later. */
1395 char *endp = rawmemchr (opt_chroot, '\0');
1396 while (endp > opt_chroot && endp[-1] == '/')
1397 --endp;
1398 *endp = '\0';
1399 if (endp == opt_chroot)
1400 opt_chroot = NULL;
1401
1402 if (opt_chroot != NULL)
1403 {
1404 /* It is faster to use chroot if we can. */
1405 if (!chroot (opt_chroot))
1406 {
1407 if (chdir ("/"))
1408 error (EXIT_FAILURE, errno, _("Can't chdir to /"));
1409 opt_chroot = NULL;
1410 }
1411 }
1412 }
1413
1414 if (cache_file == NULL)
1415 {
1416 cache_file = alloca (strlen (LD_SO_CACHE) + 1);
1417 strcpy (cache_file, LD_SO_CACHE);
1418 }
1419
1420 if (config_file == NULL)
1421 config_file = LD_SO_CONF;
1422
1423 if (opt_print_cache)
1424 {
1425 if (opt_chroot != NULL)
1426 {
1427 char *p = chroot_canon (opt_chroot, cache_file);
1428 if (p == NULL)
1429 error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"),
1430 cache_file);
1431 cache_file = p;
1432 }
1433 print_cache (cache_file);
1434 if (opt_chroot != NULL)
1435 free (cache_file);
1436 exit (0);
1437 }
1438
1439 if (opt_chroot != NULL)
1440 {
1441 /* Canonicalize the directory name of cache_file, not cache_file,
1442 because we'll rename a temporary cache file to it. */
1443 char *p = strrchr (cache_file, '/');
1444 char *canon = chroot_canon (opt_chroot,
1445 p ? (*p = '\0', cache_file) : "/");
1446
1447 if (canon == NULL)
1448 error (EXIT_FAILURE, errno,
1449 _("Can't open cache file directory %s\n"),
1450 p ? cache_file : "/");
1451
1452 if (p)
1453 ++p;
1454 else
1455 p = cache_file;
1456
1457 cache_file = alloca (strlen (canon) + strlen (p) + 2);
1458 sprintf (cache_file, "%s/%s", canon, p);
1459 free (canon);
1460 }
1461
1462 if (opt_manual_link)
1463 {
1464 /* Link all given libraries manually. */
1465 int i;
1466
1467 for (i = remaining; i < argc; ++i)
1468 manual_link (argv[i]);
1469
1470 exit (0);
1471 }
1472
1473
1474 if (opt_build_cache)
1475 init_cache ();
1476
1477 if (!opt_only_cline)
1478 {
1479 parse_conf (config_file, true);
1480
1481 /* Always add the standard search paths. */
1482 add_system_dir (SLIBDIR);
1483 if (strcmp (SLIBDIR, LIBDIR))
1484 add_system_dir (LIBDIR);
1485 }
1486
1487 const char *aux_cache_file = _PATH_LDCONFIG_AUX_CACHE;
1488 if (opt_chroot != NULL)
1489 aux_cache_file = chroot_canon (opt_chroot, aux_cache_file);
1490
1491 if (! opt_ignore_aux_cache && aux_cache_file)
1492 load_aux_cache (aux_cache_file);
1493 else
1494 init_aux_cache ();
1495
1496 search_dirs ();
1497
1498 if (opt_build_cache)
1499 {
1500 save_cache (cache_file);
1501 if (aux_cache_file)
1502 save_aux_cache (aux_cache_file);
1503 }
1504
1505 return 0;
1506 }
1507