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