| 1 | /* |
1 | /* |
| 2 | * Copyright 2003 Ned Ludd <solar@gentoo.org> |
2 | * Copyright 2003 Ned Ludd <solar@gentoo.org> |
| 3 | * Copyright 1999-2005 Gentoo Foundation |
3 | * Copyright 1999-2005 Gentoo Foundation |
| 4 | * Distributed under the terms of the GNU General Public License v2 |
4 | * Distributed under the terms of the GNU General Public License v2 |
| 5 | * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.47 2005/05/18 01:08:46 vapier Exp $ |
5 | * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.48 2005/05/18 02:51:02 vapier Exp $ |
| 6 | * |
6 | * |
| 7 | ******************************************************************** |
7 | ******************************************************************** |
| 8 | * This program is free software; you can redistribute it and/or |
8 | * This program is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU General Public License as |
9 | * modify it under the terms of the GNU General Public License as |
| 10 | * published by the Free Software Foundation; either version 2 of the |
10 | * published by the Free Software Foundation; either version 2 of the |
| … | |
… | |
| 33 | #include <getopt.h> |
33 | #include <getopt.h> |
| 34 | #include <assert.h> |
34 | #include <assert.h> |
| 35 | |
35 | |
| 36 | #include "paxelf.h" |
36 | #include "paxelf.h" |
| 37 | |
37 | |
| 38 | static const char *rcsid = "$Id: scanelf.c,v 1.47 2005/05/18 01:08:46 vapier Exp $"; |
38 | static const char *rcsid = "$Id: scanelf.c,v 1.48 2005/05/18 02:51:02 vapier Exp $"; |
| 39 | #define argv0 "scanelf" |
39 | #define argv0 "scanelf" |
| 40 | |
40 | |
| 41 | |
41 | |
| 42 | |
42 | |
| 43 | /* prototypes */ |
43 | /* prototypes */ |
| … | |
… | |
| 52 | static void xstrcat(char **dst, const char *src, size_t *curr_len); |
52 | static void xstrcat(char **dst, const char *src, size_t *curr_len); |
| 53 | static inline void xchrcat(char **dst, const char append, size_t *curr_len); |
53 | static inline void xchrcat(char **dst, const char append, size_t *curr_len); |
| 54 | static int xemptybuffer(const char *buff); |
54 | static int xemptybuffer(const char *buff); |
| 55 | |
55 | |
| 56 | /* variables to control behavior */ |
56 | /* variables to control behavior */ |
|
|
57 | static char *ldpaths[256]; |
| 57 | static char scan_ldpath = 0; |
58 | static char scan_ldpath = 0; |
| 58 | static char scan_envpath = 0; |
59 | static char scan_envpath = 0; |
| 59 | static char scan_symlink = 1; |
60 | static char scan_symlink = 1; |
| 60 | static char dir_recurse = 0; |
61 | static char dir_recurse = 0; |
| 61 | static char dir_crossmount = 1; |
62 | static char dir_crossmount = 1; |
| … | |
… | |
| 166 | else |
167 | else |
| 167 | return " - "; |
168 | return " - "; |
| 168 | } |
169 | } |
| 169 | static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len) |
170 | static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len) |
| 170 | { |
171 | { |
| 171 | /* TODO: if be_quiet, only output RPATH's which aren't in /etc/ld.so.conf */ |
172 | /* TODO: when checking RPATH entries, check each subpath (between :) in ld.so.conf */ |
| 172 | unsigned long i; |
173 | unsigned long i, s; |
| 173 | char *rpath, *runpath; |
174 | char *rpath, *runpath, **r; |
| 174 | void *strtbl_void; |
175 | void *strtbl_void; |
| 175 | |
176 | |
| 176 | if (!show_rpath) return; |
177 | if (!show_rpath) return; |
| 177 | |
178 | |
| 178 | strtbl_void = elf_findsecbyname(elf, ".dynstr"); |
179 | strtbl_void = elf_findsecbyname(elf, ".dynstr"); |
| … | |
… | |
| 184 | Elf ## B ## _Dyn *dyn; \ |
185 | Elf ## B ## _Dyn *dyn; \ |
| 185 | Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ |
186 | Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ |
| 186 | Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ |
187 | Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ |
| 187 | Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ |
188 | Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ |
| 188 | Elf ## B ## _Off offset; \ |
189 | Elf ## B ## _Off offset; \ |
|
|
190 | Elf ## B ## _Sxword word; \ |
|
|
191 | /* Scan all the program headers */ \ |
| 189 | for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ |
192 | for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ |
|
|
193 | /* Just scan dynamic headers */ \ |
| 190 | if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ |
194 | if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ |
| 191 | offset = EGET(phdr[i].p_offset); \ |
195 | offset = EGET(phdr[i].p_offset); \ |
| 192 | if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ |
196 | if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ |
|
|
197 | /* Just scan dynamic RPATH/RUNPATH headers */ \ |
| 193 | dyn = DYN ## B (elf->data + offset); \ |
198 | dyn = DYN ## B (elf->data + offset); \ |
| 194 | while (EGET(dyn->d_tag) != DT_NULL) { \ |
199 | while ((word=EGET(dyn->d_tag)) != DT_NULL) { \ |
| 195 | if (EGET(dyn->d_tag) == DT_RPATH) { \ |
200 | if (word == DT_RPATH) { \ |
| 196 | if (rpath) warn("ELF has multiple DT_RPATH's !?"); \ |
201 | r = &rpath; \ |
|
|
202 | } else if (word == DT_RUNPATH) { \ |
|
|
203 | r = &runpath; \ |
|
|
204 | } else { \ |
|
|
205 | ++dyn; \ |
|
|
206 | continue; \ |
|
|
207 | } \ |
|
|
208 | /* Verify the memory is somewhat sane */ \ |
| 197 | offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ |
209 | offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ |
| 198 | if (offset >= elf->len) continue; \ |
210 | if (offset < elf->len) { \ |
|
|
211 | if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \ |
| 199 | rpath = (char*)(elf->data + offset); \ |
212 | *r = (char*)(elf->data + offset); \ |
|
|
213 | /* If quiet, don't output paths in ld.so.conf */ \ |
|
|
214 | if (be_quiet) \ |
|
|
215 | for (s = 0; ldpaths[s]; ++s) \ |
|
|
216 | if (!strcmp(ldpaths[s], *r)) { \ |
|
|
217 | *r = NULL; \ |
|
|
218 | break; \ |
|
|
219 | } \ |
| 200 | *found_rpath = 1; \ |
220 | if (*r) *found_rpath = 1; \ |
| 201 | } else if (EGET(dyn->d_tag) == DT_RUNPATH) { \ |
|
|
| 202 | if (runpath) warn("ELF has multiple DT_RUNPATH's !?"); \ |
|
|
| 203 | offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ |
|
|
| 204 | if (offset >= elf->len) continue; \ |
|
|
| 205 | runpath = (char*)(elf->data + offset); \ |
|
|
| 206 | *found_rpath = 1; \ |
|
|
| 207 | } \ |
221 | } \ |
| 208 | ++dyn; \ |
222 | ++dyn; \ |
| 209 | } \ |
223 | } \ |
| 210 | } } |
224 | } } |
| 211 | SHOW_RPATH(32) |
225 | SHOW_RPATH(32) |
| … | |
… | |
| 511 | if (fp != stdin) |
525 | if (fp != stdin) |
| 512 | fclose(fp); |
526 | fclose(fp); |
| 513 | return 0; |
527 | return 0; |
| 514 | } |
528 | } |
| 515 | |
529 | |
|
|
530 | static void load_ld_so_conf() |
|
|
531 | { |
|
|
532 | FILE *fp = NULL; |
|
|
533 | char *p; |
|
|
534 | char path[_POSIX_PATH_MAX]; |
|
|
535 | int i = 0; |
|
|
536 | |
|
|
537 | if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL) |
|
|
538 | return; |
|
|
539 | |
|
|
540 | while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) { |
|
|
541 | if (*path != '/') |
|
|
542 | continue; |
|
|
543 | |
|
|
544 | if ((p = strrchr(path, '\r')) != NULL) |
|
|
545 | *p = 0; |
|
|
546 | if ((p = strchr(path, '\n')) != NULL) |
|
|
547 | *p = 0; |
|
|
548 | |
|
|
549 | ldpaths[i++] = xstrdup(path); |
|
|
550 | |
|
|
551 | if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths)) |
|
|
552 | break; |
|
|
553 | } |
|
|
554 | ldpaths[i] = NULL; |
|
|
555 | |
|
|
556 | fclose(fp); |
|
|
557 | } |
|
|
558 | |
| 516 | /* scan /etc/ld.so.conf for paths */ |
559 | /* scan /etc/ld.so.conf for paths */ |
| 517 | static void scanelf_ldpath() |
560 | static void scanelf_ldpath() |
| 518 | { |
561 | { |
| 519 | char scan_l, scan_ul, scan_ull; |
562 | char scan_l, scan_ul, scan_ull; |
| 520 | char *path, *p; |
563 | int i = 0; |
| 521 | FILE *fp; |
|
|
| 522 | |
564 | |
| 523 | if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL) |
565 | if (!ldpaths[0]) |
| 524 | err("Unable to open ld.so.conf: %s", strerror(errno)); |
566 | err("Unable to load any paths from ld.so.conf"); |
| 525 | |
567 | |
| 526 | scan_l = scan_ul = scan_ull = 0; |
568 | scan_l = scan_ul = scan_ull = 0; |
| 527 | |
569 | |
| 528 | path = (char*)xmalloc(_POSIX_PATH_MAX); |
570 | while (ldpaths[i]) { |
| 529 | while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) |
|
|
| 530 | if (*path == '/') { |
|
|
| 531 | if ((p = strrchr(path, '\r')) != NULL) |
|
|
| 532 | *p = 0; |
|
|
| 533 | if ((p = strrchr(path, '\n')) != NULL) |
|
|
| 534 | *p = 0; |
|
|
| 535 | if (!scan_l && !strcmp(path, "/lib")) scan_l = 1; |
571 | if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1; |
| 536 | if (!scan_ul && !strcmp(path, "/usr/lib")) scan_ul = 1; |
572 | if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1; |
| 537 | if (!scan_ull && !strcmp(path, "/usr/local/lib")) scan_ull = 1; |
573 | if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1; |
| 538 | scanelf_dir(path); |
574 | scanelf_dir(ldpaths[i]); |
|
|
575 | ++i; |
| 539 | } |
576 | } |
| 540 | free(path); |
|
|
| 541 | fclose(fp); |
|
|
| 542 | |
577 | |
| 543 | if (!scan_l) scanelf_dir("/lib"); |
578 | if (!scan_l) scanelf_dir("/lib"); |
| 544 | if (!scan_ul) scanelf_dir("/usr/lib"); |
579 | if (!scan_ul) scanelf_dir("/usr/lib"); |
| 545 | if (!scan_ull) scanelf_dir("/usr/local/lib"); |
580 | if (!scan_ull) scanelf_dir("/usr/local/lib"); |
| 546 | } |
581 | } |
| … | |
… | |
| 644 | } |
679 | } |
| 645 | |
680 | |
| 646 | /* parse command line arguments and preform needed actions */ |
681 | /* parse command line arguments and preform needed actions */ |
| 647 | static void parseargs(int argc, char *argv[]) |
682 | static void parseargs(int argc, char *argv[]) |
| 648 | { |
683 | { |
| 649 | int flag; |
684 | int i; |
| 650 | char *from_file = NULL; |
685 | char *from_file = NULL; |
| 651 | |
686 | |
| 652 | opterr = 0; |
687 | opterr = 0; |
| 653 | while ((flag=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { |
688 | while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { |
| 654 | switch (flag) { |
689 | switch (i) { |
| 655 | |
690 | |
| 656 | case 'V': |
691 | case 'V': |
| 657 | printf("%s compiled %s\n%s\n" |
692 | printf("%s compiled %s\n%s\n" |
| 658 | "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n", |
693 | "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n", |
| 659 | __FILE__, __DATE__, rcsid, argv0); |
694 | __FILE__, __DATE__, rcsid, argv0); |
| … | |
… | |
| 710 | case '?': |
745 | case '?': |
| 711 | warn("Unknown option\n"); |
746 | warn("Unknown option\n"); |
| 712 | usage(EXIT_FAILURE); |
747 | usage(EXIT_FAILURE); |
| 713 | break; |
748 | break; |
| 714 | default: |
749 | default: |
| 715 | err("Unhandled option '%c'", flag); |
750 | err("Unhandled option '%c'", i); |
| 716 | break; |
751 | break; |
| 717 | } |
752 | } |
| 718 | } |
753 | } |
| 719 | |
754 | |
| 720 | if (be_quiet && be_verbose) |
755 | if (be_quiet && be_verbose) |
| 721 | err("You can be quiet or you can be verbose, not both, stupid"); |
756 | err("You can be quiet or you can be verbose, not both, stupid"); |
| 722 | |
757 | |
| 723 | /* let the format option override all other options */ |
758 | /* let the format option override all other options */ |
| 724 | if (out_format) { |
759 | if (out_format) { |
| 725 | show_pax = show_stack = show_textrel = show_rpath = show_needed = show_interp = 0; |
760 | show_pax = show_stack = show_textrel = show_rpath = show_needed = show_interp = 0; |
| 726 | for (flag=0; out_format[flag]; ++flag) { |
761 | for (i = 0; out_format[i]; ++i) { |
| 727 | if (out_format[flag] != '%') continue; |
762 | if (out_format[i] != '%') continue; |
| 728 | |
763 | |
| 729 | switch (out_format[++flag]) { |
764 | switch (out_format[++i]) { |
| 730 | case '%': break; |
765 | case '%': break; |
| 731 | case 'F': break; |
766 | case 'F': break; |
| 732 | case 's': break; |
767 | case 's': break; |
| 733 | case 'o': break; |
768 | case 'o': break; |
| 734 | case 'x': show_pax = 1; break; |
769 | case 'x': show_pax = 1; break; |
| … | |
… | |
| 737 | case 'r': show_rpath = 1; break; |
772 | case 'r': show_rpath = 1; break; |
| 738 | case 'n': show_needed = 1; break; |
773 | case 'n': show_needed = 1; break; |
| 739 | case 'i': show_interp = 1; break; |
774 | case 'i': show_interp = 1; break; |
| 740 | default: |
775 | default: |
| 741 | err("Invalid format specifier '%c' (byte %i)", |
776 | err("Invalid format specifier '%c' (byte %i)", |
| 742 | out_format[flag], flag+1); |
777 | out_format[i], i+1); |
| 743 | } |
778 | } |
| 744 | } |
779 | } |
| 745 | |
780 | |
| 746 | /* construct our default format */ |
781 | /* construct our default format */ |
| 747 | } else { |
782 | } else { |
| … | |
… | |
| 758 | if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len); |
793 | if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len); |
| 759 | } |
794 | } |
| 760 | if (be_verbose > 2) printf("Format: %s\n", out_format); |
795 | if (be_verbose > 2) printf("Format: %s\n", out_format); |
| 761 | |
796 | |
| 762 | /* now lets actually do the scanning */ |
797 | /* now lets actually do the scanning */ |
|
|
798 | if (scan_ldpath || (show_rpath && be_quiet)) |
|
|
799 | load_ld_so_conf(); |
| 763 | if (scan_ldpath) scanelf_ldpath(); |
800 | if (scan_ldpath) scanelf_ldpath(); |
| 764 | if (scan_envpath) scanelf_envpath(); |
801 | if (scan_envpath) scanelf_envpath(); |
| 765 | if (from_file) { |
802 | if (from_file) { |
| 766 | scanelf_from_file(from_file); |
803 | scanelf_from_file(from_file); |
| 767 | free(from_file); |
804 | free(from_file); |
| … | |
… | |
| 776 | if (find_sym) { |
813 | if (find_sym) { |
| 777 | free(find_sym); |
814 | free(find_sym); |
| 778 | free(versioned_symname); |
815 | free(versioned_symname); |
| 779 | } |
816 | } |
| 780 | if (out_format) free(out_format); |
817 | if (out_format) free(out_format); |
|
|
818 | for (i = 0; ldpaths[i]; ++i) |
|
|
819 | free(ldpaths[i]); |
| 781 | } |
820 | } |
| 782 | |
821 | |
| 783 | |
822 | |
| 784 | |
823 | |
| 785 | /* utility funcs */ |
824 | /* utility funcs */ |