1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2020 Joyent, Inc. 14 * Copyright 2022 Jason King 15 */ 16 17 #include <sys/debug.h> 18 #include <sys/types.h> 19 #include <sys/stat.h> 20 #include <sys/avl.h> 21 #include <sys/fcntl.h> 22 #include <sys/sysmacros.h> 23 #include <dirent.h> 24 #include <err.h> 25 #include <errno.h> 26 #include <fcntl.h> 27 #include <gelf.h> 28 #include <libcustr.h> 29 #include <libelf.h> 30 #include <libgen.h> 31 #include <limits.h> 32 #include <stdbool.h> 33 #include <stddef.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 static inline bool 40 is_same(const struct stat *s1, const struct stat *s2) 41 { 42 if ((s1->st_ino == s2->st_ino) && (s1->st_dev == s2->st_dev)) 43 return (true); 44 return (false); 45 } 46 47 typedef enum dir_flags { 48 DF_NONE = 0, 49 DF_IS_SELF = 1 << 0, 50 DF_IS_SYMLINK = 1 << 2, 51 } dir_flags_t; 52 53 typedef struct path { 54 custr_t *p_name; 55 size_t p_pfxidx; 56 } path_t; 57 58 typedef struct name { 59 char *n_name; 60 bool n_is_symlink; 61 } name_t; 62 63 typedef struct names { 64 name_t *ns_names; 65 uint_t ns_num; 66 uint_t ns_alloc; 67 } names_t; 68 69 typedef struct elfinfo { 70 int ei_class; 71 uint16_t ei_type; 72 bool ei_hasverdef; 73 } elfinfo_t; 74 75 typedef struct obj { 76 avl_node_t obj_avlid; 77 avl_node_t obj_avlname; 78 dev_t obj_dev; 79 ino_t obj_inode; 80 names_t obj_names; 81 elfinfo_t obj_elfinfo; 82 } obj_t; 83 84 static void path_init(path_t *, const char *, bool); 85 static size_t path_append(path_t *, const char *); 86 static const char *path_name(const path_t *); 87 static const char *path_fullpath(const path_t *); 88 static void path_pop(path_t *, size_t); 89 90 static bool maybe_obj(const char *, mode_t); 91 static bool get_elfinfo(const path_t *, int, elfinfo_t *); 92 static void add_name(obj_t *, const path_t *, bool); 93 static int cmp_id(const void *, const void *); 94 static int cmp_objname(const void *, const void *); 95 static int cmp_names(const void *, const void *); 96 97 static void process_dir(path_t *, int, const struct stat *, dir_flags_t); 98 static void process_file(path_t *, int, const struct stat *, bool); 99 static void process_arg(char *); 100 101 static void sort_names(void *, void *); 102 static void print_entry(void *, void *); 103 104 static void foreach_avl(avl_tree_t *, void (*)(void *, void *), void *); 105 106 static void nomem(void); 107 static char *xstrdup(const char *); 108 static void *xcalloc(size_t, size_t); 109 110 static avl_tree_t avl_byid; 111 static avl_tree_t avl_byname; 112 113 static const char *special_paths[] = { 114 "usr/bin/alias", 115 "usr/lib/isaexec", 116 }; 117 118 static int rootfd = -1; 119 120 /* By default, we process aliases */ 121 static bool do_alias = true; 122 123 /* By default, we treat certain well known names (e.g. isaexec) as special */ 124 static bool do_special = true; 125 126 /* fast_mode, relpath, and so_only are all false by default */ 127 static bool fast_mode; 128 static bool relpath; 129 static bool so_only; 130 131 static void __NORETURN 132 usage(const char *name) 133 { 134 (void) fprintf(stderr, 135 "Usage: %s [-afnrs] file | dir\n" 136 "\t[-a]\texpand symlink aliases\n" 137 "\t[-f]\tuse file name at mode to speed search\n" 138 "\t[-h]\tshow this help\n" 139 "\t[-n]\tdon\'t treat well known paths as special in sorting\n" 140 "\t[-r]\treport relative paths\n" 141 "\t[-s]\tonly remote shareable (ET_DYN) objects\n", name); 142 exit(EXIT_FAILURE); 143 } 144 145 int 146 main(int argc, char **argv) 147 { 148 int c; 149 150 if (elf_version(EV_CURRENT) == EV_NONE) 151 errx(EXIT_FAILURE, "elf library is out of date"); 152 153 while ((c = getopt(argc, argv, "ahfnrs")) != -1) { 154 switch (c) { 155 case 'a': 156 do_alias = false; 157 break; 158 case 'f': 159 fast_mode = true; 160 break; 161 case 'n': 162 do_special = false; 163 break; 164 case 'r': 165 relpath = true; 166 break; 167 case 's': 168 so_only = true; 169 break; 170 case 'h': 171 usage(argv[0]); 172 case '?': 173 (void) fprintf(stderr, "Unknown option -%c\n", optopt); 174 usage(argv[0]); 175 } 176 } 177 178 if (optind == argc) { 179 (void) fprintf(stderr, "Missing file or dir parameter\n"); 180 usage(argv[0]); 181 } 182 183 if (argv[optind][0] == '\0') 184 errx(EXIT_FAILURE, "Invalid file or dir value"); 185 186 avl_create(&avl_byid, cmp_id, sizeof (obj_t), 187 offsetof(obj_t, obj_avlid)); 188 avl_create(&avl_byname, cmp_objname, sizeof (obj_t), 189 offsetof(obj_t, obj_avlname)); 190 191 process_arg(argv[optind]); 192 foreach_avl(&avl_byid, sort_names, &avl_byname); 193 foreach_avl(&avl_byname, print_entry, NULL); 194 195 return (EXIT_SUCCESS); 196 } 197 198 static void 199 process_arg(char *arg) 200 { 201 path_t path; 202 struct stat sb; 203 int fd; 204 205 if ((fd = open(arg, O_RDONLY)) == -1) { 206 err(EXIT_FAILURE, "could not open %s", arg); 207 } 208 209 if (fstat(fd, &sb) < 0) { 210 err(EXIT_FAILURE, "failed to stat %s", arg); 211 } 212 213 if (S_ISDIR(sb.st_mode)) { 214 path_init(&path, arg, relpath); 215 if (relpath) { 216 (void) printf("PREFIX %s\n", arg); 217 } 218 219 rootfd = fd; 220 /* process_dir() will close fd */ 221 process_dir(&path, fd, &sb, DF_NONE); 222 return; 223 } 224 225 char *argcopy = xstrdup(arg); 226 char *dir = dirname(argcopy); 227 228 if (!S_ISREG(sb.st_mode)) { 229 err(EXIT_FAILURE, "not a file or directory: %s", arg); 230 } 231 232 #ifndef O_DIRECTORY 233 struct stat tsb; 234 if (stat(dir, &tsb) == -1) { 235 err(EXIT_FAILURE, "failed to stat %s", dir); 236 } 237 if (!S_ISDIR(tsb.st_mode)) { 238 errx(EXIT_FAILURE, "not a directory: %s", dir); 239 } 240 rootfd = open(dir, O_RDONLY); 241 #else 242 rootfd = open(dir, O_RDONLY|O_DIRECTORY); 243 #endif 244 if (rootfd < 0) { 245 err(EXIT_FAILURE, "%s", dir); 246 } 247 248 path_init(&path, dir, relpath); 249 if (relpath) { 250 (void) printf("PREFIX %s\n", dir); 251 } 252 free(argcopy); 253 254 process_file(&path, fd, &sb, DF_NONE); 255 } 256 257 /* 258 * Processing a directory has some subtleties. When we encounter a 259 * subdirectory that's a symlink, we want any files we encounter when 260 * we traverse it to be treated as aliases. We may also have a symlink that's 261 * a link back to ourself (e.g. 32 -> .). In this special case, we still want 262 * to reprocess the directory to generate alias names, but we use the 263 * DFLAG_SELF flag to avoid recursing forever. 264 * 265 * A limitation of this approach is that we cannot handle a loop over multiple 266 * levels (e.g. foo -> ../..). Nothing currently in illumos-gate creates any 267 * such symlinks in the proto area, so we've opted to avoid complicating 268 * processing further to handle scenarios that aren't realistically expected 269 * to occur. 270 * 271 * Note that dirfd is always closed upon return from process_dir(). 272 */ 273 static void 274 process_dir(path_t *p, int dirfd, const struct stat *dirsb, dir_flags_t dflags) 275 { 276 DIR *d; 277 struct dirent *de; 278 279 d = fdopendir(dirfd); 280 if (d == NULL) { 281 warn("opendir(%s)", path_fullpath(p)); 282 VERIFY0(close(dirfd)); 283 return; 284 } 285 286 while ((de = readdir(d)) != NULL) { 287 struct stat sb; 288 int fd; 289 bool is_link = false; 290 size_t plen; 291 292 if (strcmp(de->d_name, ".") == 0 || 293 strcmp(de->d_name, "..") == 0) { 294 continue; 295 } 296 297 plen = path_append(p, de->d_name); 298 299 if (fstatat(rootfd, path_name(p), &sb, 300 AT_SYMLINK_NOFOLLOW) < 0) { 301 warn("%s", path_fullpath(p)); 302 path_pop(p, plen); 303 continue; 304 } 305 306 fd = openat(dirfd, de->d_name, O_RDONLY); 307 if (fd < 0) { 308 /* 309 * Symlinks in the proto area may point to a path 310 * that's not present (e.g. /dev/stdin -> fd/0). 311 * Silently skip such entries. 312 */ 313 if (errno != ENOENT || !S_ISLNK(sb.st_mode)) { 314 warn("%s", path_fullpath(p)); 315 } 316 path_pop(p, plen); 317 continue; 318 } 319 320 if (S_ISLNK(sb.st_mode)) { 321 is_link = true; 322 323 if (fstat(fd, &sb) < 0) { 324 warn("stat %s", path_fullpath(p)); 325 path_pop(p, plen); 326 continue; 327 } 328 } 329 330 if (S_ISDIR(sb.st_mode)) { 331 if ((dflags & DF_IS_SELF) != 0) { 332 VERIFY0(close(fd)); 333 path_pop(p, plen); 334 continue; 335 } 336 337 dir_flags_t newflags = dflags; 338 339 /* The recursive process_dir() call closes fd */ 340 if (is_link) 341 newflags |= DF_IS_SYMLINK; 342 if (is_same(&sb, dirsb)) 343 newflags |= DF_IS_SELF; 344 process_dir(p, fd, &sb, newflags); 345 } else if (S_ISREG(sb.st_mode)) { 346 if (!maybe_obj(de->d_name, sb.st_mode)) { 347 VERIFY0(close(fd)); 348 path_pop(p, plen); 349 continue; 350 } 351 352 if ((dflags & (DF_IS_SELF | DF_IS_SYMLINK)) != 0) 353 is_link = true; 354 355 /* process_file() closes fd */ 356 process_file(p, fd, &sb, is_link); 357 } 358 359 path_pop(p, plen); 360 } 361 362 /* Closes dirfd */ 363 VERIFY0(closedir(d)); 364 } 365 366 /* Note that fd is always closed upon return */ 367 static void 368 process_file(path_t *p, int fd, const struct stat *sb, bool is_link) 369 { 370 avl_index_t where = { 0 }; 371 obj_t templ = { 372 .obj_dev = sb->st_dev, 373 .obj_inode = sb->st_ino, 374 }; 375 obj_t *obj = avl_find(&avl_byid, &templ, &where); 376 elfinfo_t elfinfo = { 0 }; 377 378 if (obj != NULL) 379 goto done; 380 381 if (!get_elfinfo(p, fd, &elfinfo)) { 382 VERIFY0(close(fd)); 383 return; 384 } 385 386 obj = xcalloc(1, sizeof (*obj)); 387 obj->obj_dev = sb->st_dev; 388 obj->obj_inode = sb->st_ino; 389 obj->obj_elfinfo = elfinfo; 390 avl_add(&avl_byid, obj); 391 392 done: 393 add_name(obj, p, is_link); 394 VERIFY0(close(fd)); 395 } 396 397 static void 398 print_entry(void *a, void *arg __unused) 399 { 400 obj_t *obj = a; 401 const char *objname = obj->obj_names.ns_names[0].n_name; 402 const char *bits = ""; 403 const char *type = ""; 404 const char *verdef = obj->obj_elfinfo.ei_hasverdef ? 405 "VERDEF" : "NOVERDEF"; 406 407 switch (obj->obj_elfinfo.ei_class) { 408 case ELFCLASS32: 409 bits = "32"; 410 break; 411 case ELFCLASS64: 412 bits = "64"; 413 break; 414 default: 415 errx(EXIT_FAILURE, "unknown elfclass value %x for %s", 416 obj->obj_elfinfo.ei_class, objname); 417 } 418 419 switch (obj->obj_elfinfo.ei_type) { 420 case ET_REL: 421 type = "REL"; 422 break; 423 case ET_DYN: 424 type = "DYN"; 425 break; 426 case ET_EXEC: 427 type = "EXEC"; 428 break; 429 default: 430 errx(EXIT_FAILURE, "unexpected elf type %x for %s", 431 obj->obj_elfinfo.ei_type, objname); 432 } 433 434 names_t *names = &obj->obj_names; 435 436 VERIFY3U(names->ns_num, >, 0); 437 VERIFY(!names->ns_names[0].n_is_symlink); 438 439 (void) printf("OBJECT %2s %-4s %-8s %s\n", bits, type, verdef, 440 objname); 441 442 for (uint_t i = 1; i < names->ns_num; i++) { 443 if (do_alias) { 444 (void) printf("%-23s %s\t%s\n", 445 "ALIAS", objname, names->ns_names[i].n_name); 446 } else { 447 (void) printf("OBJECT %2s %-4s %-8s %s\n", bits, type, 448 verdef, names->ns_names[i].n_name); 449 } 450 } 451 } 452 453 /* 454 * Returns true and eip populated if name was an elf object, otherwise 455 * returns false. 456 */ 457 static bool 458 get_elfinfo(const path_t *p, int fd, elfinfo_t *eip) 459 { 460 Elf *elf = NULL; 461 Elf_Scn *scn = NULL; 462 GElf_Ehdr ehdr = { 0 }; 463 int eval; 464 465 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) 466 goto fail_noend; 467 468 if ((eip->ei_class = gelf_getclass(elf)) == ELFCLASSNONE) { 469 VERIFY0(elf_end(elf)); 470 return (false); 471 } 472 473 if (gelf_getehdr(elf, &ehdr) == NULL) 474 goto fail; 475 476 eip->ei_type = ehdr.e_type; 477 eip->ei_hasverdef = false; 478 479 while ((scn = elf_nextscn(elf, scn)) != NULL) { 480 Elf_Data *data = NULL; 481 GElf_Shdr shdr = { 0 }; 482 483 if (gelf_getshdr(scn, &shdr) == NULL) 484 goto fail; 485 486 if (shdr.sh_type != SHT_DYNAMIC) 487 continue; 488 489 if ((data = elf_getdata(scn, NULL)) == NULL) 490 continue; 491 492 size_t nent = shdr.sh_size / shdr.sh_entsize; 493 494 for (size_t i = 0; i < nent; i++) { 495 GElf_Dyn dyn = { 0 }; 496 497 if (gelf_getdyn(data, i, &dyn) == NULL) { 498 goto fail; 499 } 500 501 if (dyn.d_tag == DT_VERDEF) { 502 eip->ei_hasverdef = true; 503 break; 504 } 505 } 506 } 507 508 VERIFY0(elf_end(elf)); 509 return (true); 510 511 fail: 512 VERIFY0(elf_end(elf)); 513 514 fail_noend: 515 eval = elf_errno(); 516 517 warnx("%s: %s", path_fullpath(p), elf_errmsg(eval)); 518 return (false); 519 } 520 521 static bool 522 is_special(const char *name) 523 { 524 for (uint_t i = 0; i < ARRAY_SIZE(special_paths); i++) { 525 if (strcmp(special_paths[i], name) == 0) 526 return (true); 527 } 528 return (false); 529 } 530 531 static void 532 sort_names(void *a, void *b) 533 { 534 obj_t *obj = a; 535 avl_tree_t *name_avl = b; 536 names_t *names = &obj->obj_names; 537 538 /* We should always have at least one name */ 539 VERIFY3U(names->ns_num, >, 0); 540 541 /* If there is only one, they get the prize and we're done */ 542 if (names->ns_num == 1) 543 return; 544 545 name_t *first = NULL; 546 547 /* 548 * Find the first (in sorted order) pathname for this object 549 * that isn't a symlink. 550 */ 551 for (uint_t i = 0; i < names->ns_num; i++) { 552 name_t *n = &names->ns_names[i]; 553 554 if (n->n_is_symlink) 555 continue; 556 557 if (first == NULL) { 558 first = n; 559 continue; 560 } 561 562 /* 563 * If we're treating isaexec as special, we always 564 * want it to be the first entry. Otherwise, pick 565 * the 'lowest' sorted pathname. 566 */ 567 if (do_special) { 568 if (is_special(n->n_name)) { 569 first = n; 570 break; 571 } 572 573 if (strcmp(n->n_name, first->n_name) < 0) 574 first = n; 575 } 576 } 577 578 /* 579 * If the first pathname (in sorted order) isn't the first 580 * name entry, we swap it into first place (while also updating 581 * the names AVL tree). 582 */ 583 if (first != NULL && first != &names->ns_names[0]) { 584 name_t tmp = names->ns_names[0]; 585 586 avl_remove(name_avl, obj); 587 (void) memcpy(&names->ns_names[0], first, sizeof (name_t)); 588 (void) memcpy(first, &tmp, sizeof (name_t)); 589 avl_add(name_avl, obj); 590 } 591 592 /* 593 * The remaining names (symlinks or not) are sorted by 594 * their pathname. 595 */ 596 qsort(&names->ns_names[1], names->ns_num - 1, sizeof (name_t), 597 cmp_names); 598 } 599 600 /* 601 * We grow the names array by NAME_CHUNK entries every time we need to 602 * extend it. 603 */ 604 #define NAME_CHUNK 4 605 606 static name_t * 607 name_new(names_t *names) 608 { 609 if (names->ns_num < names->ns_alloc) 610 return (&names->ns_names[names->ns_num++]); 611 612 name_t *newn = NULL; 613 uint_t newamt = names->ns_alloc + NAME_CHUNK; 614 615 /* 616 * Since we may be building on a machine that doesn't 617 * have reallocarray or the like, we avoid their use here. 618 * If we ever officially designate an illumos-gate build 619 * as the minimum required to build master that contains 620 * reallocarray and such, we can replace most of this logic 621 * with it. 622 * 623 * Also use xcalloc so we get the unused entries zeroed already. 624 * Not strictly necessary, but useful for debugging. 625 */ 626 newn = xcalloc(newamt, sizeof (name_t)); 627 628 /* Move over existing entries */ 629 (void) memcpy(newn, names->ns_names, names->ns_num * sizeof (name_t)); 630 631 free(names->ns_names); 632 633 names->ns_names = newn; 634 names->ns_alloc = newamt; 635 return (&names->ns_names[names->ns_num++]); 636 } 637 638 static void 639 add_name(obj_t *obj, const path_t *p, bool is_symlink) 640 { 641 names_t *ns = &obj->obj_names; 642 const char *srcname = path_name(p); 643 644 /* We should never have duplicates */ 645 for (size_t i = 0; i < ns->ns_num; i++) 646 VERIFY3S(strcmp(ns->ns_names[i].n_name, srcname), !=, 0); 647 648 name_t *n = name_new(ns); 649 650 n->n_name = xstrdup(srcname); 651 n->n_is_symlink = is_symlink; 652 653 if (is_symlink) 654 return; 655 656 /* 657 * If the name was not a symlink, first determine if there are 658 * existing (hard) links to this name already, and if so, which entry 659 * is the first one. Typically this will be the name we just added 660 * unless the file does have multiple hard links (e.g. isaexec). 661 */ 662 uint_t nhlink = 0; 663 uint_t firsthlink = UINT_MAX; 664 665 for (uint_t i = 0; i < ns->ns_num; i++) { 666 if (ns->ns_names[i].n_is_symlink) 667 continue; 668 if (nhlink == 0) 669 firsthlink = i; 670 nhlink++; 671 } 672 673 if (nhlink > 1) 674 return; 675 676 /* 677 * Put the first non-symlink name as the very first 678 * entry. 679 */ 680 VERIFY3U(firsthlink, !=, UINT_MAX); 681 682 if (firsthlink != 0) { 683 name_t tmp = { 684 .n_name = ns->ns_names[0].n_name, 685 .n_is_symlink = ns->ns_names[0].n_is_symlink, 686 }; 687 688 (void) memcpy(&ns->ns_names[0], &ns->ns_names[firsthlink], 689 sizeof (name_t)); 690 (void) memcpy(&ns->ns_names[firsthlink], &tmp, sizeof (name_t)); 691 } 692 693 avl_add(&avl_byname, obj); 694 } 695 696 /* 697 * This is an arbitrary initial value -- basically we can nest 16 directories 698 * deep before having to grow p->p_idx (which seems like a nice power of 2). 699 */ 700 #define PATH_INIT 16 701 702 static void 703 path_init(path_t *p, const char *name, bool relpath) 704 { 705 (void) memset(p, '\0', sizeof (*p)); 706 707 if (custr_alloc(&p->p_name) < 0) { 708 nomem(); 709 } 710 711 if (name != NULL && custr_append(p->p_name, name) < 0) 712 nomem(); 713 714 size_t len = custr_len(p->p_name); 715 716 /* Trim off any trailing /'s, but don't trim '/' to an empty path */ 717 while (len > 1 && custr_cstr(p->p_name)[len - 1] == '/') { 718 VERIFY0(custr_rtrunc(p->p_name, 0)); 719 len--; 720 } 721 722 p->p_pfxidx = relpath ? len + 1 : 0; 723 } 724 725 static size_t 726 path_append(path_t *p, const char *name) 727 { 728 size_t clen = custr_len(p->p_name); 729 730 if (clen > 0) 731 VERIFY0(custr_appendc(p->p_name, '/')); 732 VERIFY0(custr_append(p->p_name, name)); 733 734 return (clen); 735 } 736 737 static const char * 738 path_name(const path_t *p) 739 { 740 return (custr_cstr(p->p_name) + p->p_pfxidx); 741 } 742 743 static const char * 744 path_fullpath(const path_t *p) 745 { 746 return (custr_cstr(p->p_name)); 747 } 748 749 static void 750 path_pop(path_t *p, size_t idx) 751 { 752 VERIFY0(custr_trunc(p->p_name, idx)); 753 } 754 755 static int 756 cmp_id(const void *a, const void *b) 757 { 758 const obj_t *l = a; 759 const obj_t *r = b; 760 761 if (l->obj_dev < r->obj_dev) 762 return (-1); 763 if (l->obj_dev > r->obj_dev) 764 return (1); 765 if (l->obj_inode < r->obj_inode) 766 return (-1); 767 if (l->obj_inode > r->obj_inode) 768 return (1); 769 return (0); 770 } 771 772 static int 773 cmp_objname(const void *a, const void *b) 774 { 775 const obj_t *l = a; 776 const obj_t *r = b; 777 const name_t *ln = &l->obj_names.ns_names[0]; 778 const name_t *rn = &r->obj_names.ns_names[0]; 779 780 return (cmp_names(ln, rn)); 781 } 782 783 static int 784 cmp_names(const void *a, const void *b) 785 { 786 const name_t *l = a; 787 const name_t *r = b; 788 int cmp = strcmp(l->n_name, r->n_name); 789 790 if (cmp < 0) 791 return (-1); 792 if (cmp > 0) 793 return (1); 794 return (0); 795 } 796 797 /* 798 * Use the filename and permission bits to determine if this file could 799 * possibly be an executable. 800 */ 801 static bool 802 maybe_obj(const char *name, mode_t mode) 803 { 804 /* If not in fast mode, check everything, so always return true */ 805 if (!fast_mode) 806 return (true); 807 808 size_t len = strlen(name); 809 810 /* If the file name ends in .so, we check */ 811 if (len >= 3 && strcmp(&name[len - 3], ".so") == 0) { 812 return (true); 813 } 814 815 /* If the file name contains '.so', we check */ 816 if (len >= 4 && strstr(name, ".so.") != NULL) { 817 return (true); 818 } 819 820 /* If the above checks fail, we assume it's not a shared library */ 821 if (so_only) 822 return (false); 823 824 /* 825 * The original perl script used a -x test which only looked at 826 * file permissions. This may return slightly different results 827 * than using access(2) or faccessat(2). 828 */ 829 if ((mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0) 830 return (false); 831 832 return (true); 833 } 834 835 static void 836 foreach_avl(avl_tree_t *avl, void (*cb)(void *, void *), void *arg) 837 { 838 void *obj; 839 840 for (obj = avl_first(avl); obj != NULL; obj = AVL_NEXT(avl, obj)) 841 cb(obj, arg); 842 } 843 844 static char * 845 xstrdup(const char *s) 846 { 847 char *news = strdup(s); 848 849 if (news == NULL) { 850 nomem(); 851 } 852 return (news); 853 } 854 855 static void * 856 xcalloc(size_t nelem, size_t elsize) 857 { 858 void *p = calloc(nelem, elsize); 859 860 if (p == NULL) { 861 nomem(); 862 } 863 864 return (p); 865 } 866 867 #define NOMEM_MSG "out of memory\n" 868 static void __NORETURN 869 nomem(void) 870 { 871 /* Try to get the error out before aborting */ 872 (void) write(STDERR_FILENO, NOMEM_MSG, sizeof (NOMEM_MSG)); 873 abort(); 874 } 875