| 1 | /* |
1 | /* |
| 2 | * Copyright 2003-2005 Gentoo Foundation |
2 | * Copyright 2003-2005 Gentoo Foundation |
| 3 | * Distributed under the terms of the GNU General Public License v2 |
3 | * Distributed under the terms of the GNU General Public License v2 |
| 4 | * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.72 2005/06/04 06:23:06 vapier Exp $ |
4 | * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.73 2005/06/04 13:52:54 solar Exp $ |
| 5 | * |
5 | * |
| 6 | ******************************************************************** |
6 | ******************************************************************** |
| 7 | * This program is free software; you can redistribute it and/or |
7 | * This program is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU General Public License as |
8 | * modify it under the terms of the GNU General Public License as |
| 9 | * published by the Free Software Foundation; either version 2 of the |
9 | * published by the Free Software Foundation; either version 2 of the |
| … | |
… | |
| 33 | #include <dirent.h> |
33 | #include <dirent.h> |
| 34 | #include <getopt.h> |
34 | #include <getopt.h> |
| 35 | #include <assert.h> |
35 | #include <assert.h> |
| 36 | #include "paxelf.h" |
36 | #include "paxelf.h" |
| 37 | |
37 | |
| 38 | static const char *rcsid = "$Id: scanelf.c,v 1.72 2005/06/04 06:23:06 vapier Exp $"; |
38 | static const char *rcsid = "$Id: scanelf.c,v 1.73 2005/06/04 13:52:54 solar Exp $"; |
| 39 | #define argv0 "scanelf" |
39 | #define argv0 "scanelf" |
| 40 | |
40 | |
| 41 | #define IS_MODIFIER(c) (c == '%' || c == '#') |
41 | #define IS_MODIFIER(c) (c == '%' || c == '#') |
| 42 | |
42 | |
| 43 | |
43 | |
| … | |
… | |
| 60 | static char scan_envpath = 0; |
60 | static char scan_envpath = 0; |
| 61 | static char scan_symlink = 1; |
61 | static char scan_symlink = 1; |
| 62 | static char dir_recurse = 0; |
62 | static char dir_recurse = 0; |
| 63 | static char dir_crossmount = 1; |
63 | static char dir_crossmount = 1; |
| 64 | static char show_pax = 0; |
64 | static char show_pax = 0; |
| 65 | static char show_stack = 0; |
65 | static char show_phdr = 0; |
| 66 | static char show_textrel = 0; |
66 | static char show_textrel = 0; |
| 67 | static char show_rpath = 0; |
67 | static char show_rpath = 0; |
| 68 | static char show_needed = 0; |
68 | static char show_needed = 0; |
| 69 | static char show_interp = 0; |
69 | static char show_interp = 0; |
| 70 | static char show_bind = 0; |
70 | static char show_bind = 0; |
| … | |
… | |
| 126 | if (be_quiet && !shown) |
126 | if (be_quiet && !shown) |
| 127 | return NULL; |
127 | return NULL; |
| 128 | return ret; |
128 | return ret; |
| 129 | |
129 | |
| 130 | } |
130 | } |
| 131 | static char *scanelf_file_stack(elfobj *elf, char *found_stack, char *found_relro, char *found_load) |
131 | static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load) |
| 132 | { |
132 | { |
| 133 | static char ret[12]; |
133 | static char ret[12]; |
| 134 | char *found; |
134 | char *found; |
| 135 | unsigned long i, off, shown, check_flags; |
135 | unsigned long i, off, shown, check_flags; |
| 136 | unsigned char multi_stack, multi_relro, multi_load; |
136 | unsigned char multi_stack, multi_relro, multi_load; |
| 137 | |
137 | |
| 138 | if (!show_stack) return NULL; |
138 | if (!show_phdr) return NULL; |
| 139 | |
139 | |
| 140 | memcpy(ret, "--- --- ---\0", 12); |
140 | memcpy(ret, "--- --- ---\0", 12); |
| 141 | |
141 | |
| 142 | shown = 0; |
142 | shown = 0; |
| 143 | multi_stack = multi_relro = multi_load = 0; |
143 | multi_stack = multi_relro = multi_load = 0; |
| 144 | |
144 | |
| 145 | if (elf->phdr) { |
145 | if (elf->phdr) { |
| 146 | #define SHOW_STACK(B) \ |
146 | #define SHOW_PHDR(B) \ |
| 147 | if (elf->elf_class == ELFCLASS ## B) { \ |
147 | if (elf->elf_class == ELFCLASS ## B) { \ |
| 148 | Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ |
148 | Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ |
| 149 | Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ |
149 | Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ |
| 150 | uint32_t flags; \ |
150 | uint32_t flags; \ |
| 151 | for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ |
151 | for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ |
| 152 | if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \ |
152 | if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \ |
| 153 | if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \ |
153 | if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \ |
| 154 | found = found_stack; \ |
154 | found = found_phdr; \ |
| 155 | off = 0; \ |
155 | off = 0; \ |
| 156 | check_flags = PF_X; \ |
156 | check_flags = PF_X; \ |
| 157 | } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \ |
157 | } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \ |
| 158 | if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \ |
158 | if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \ |
| 159 | found = found_relro; \ |
159 | found = found_relro; \ |
| … | |
… | |
| 172 | memcpy(ret+off, gnu_short_stack_flags(flags), 3); \ |
172 | memcpy(ret+off, gnu_short_stack_flags(flags), 3); \ |
| 173 | *found = 1; \ |
173 | *found = 1; \ |
| 174 | ++shown; \ |
174 | ++shown; \ |
| 175 | } \ |
175 | } \ |
| 176 | } |
176 | } |
| 177 | SHOW_STACK(32) |
177 | SHOW_PHDR(32) |
| 178 | SHOW_STACK(64) |
178 | SHOW_PHDR(64) |
| 179 | } |
179 | } |
| 180 | |
180 | |
| 181 | if (be_quiet && !shown) |
181 | if (be_quiet && !shown) |
| 182 | return NULL; |
182 | return NULL; |
| 183 | else |
183 | else |
| … | |
… | |
| 483 | /* scan an elf file and show all the fun stuff */ |
483 | /* scan an elf file and show all the fun stuff */ |
| 484 | #define prints(str) write(fileno(stdout), str, strlen(str)) |
484 | #define prints(str) write(fileno(stdout), str, strlen(str)) |
| 485 | static void scanelf_file(const char *filename) |
485 | static void scanelf_file(const char *filename) |
| 486 | { |
486 | { |
| 487 | unsigned long i; |
487 | unsigned long i; |
| 488 | char found_pax, found_stack, found_relro, found_load, found_textrel, |
488 | char found_pax, found_phdr, found_relro, found_load, found_textrel, |
| 489 | found_rpath, found_needed, found_interp, found_bind, |
489 | found_rpath, found_needed, found_interp, found_bind, |
| 490 | found_sym, found_lib, found_file; |
490 | found_sym, found_lib, found_file; |
| 491 | elfobj *elf; |
491 | elfobj *elf; |
| 492 | struct stat st; |
492 | struct stat st; |
| 493 | static char *out_buffer = NULL; |
493 | static char *out_buffer = NULL; |
| … | |
… | |
| 506 | if (!S_ISREG(st.st_mode)) { |
506 | if (!S_ISREG(st.st_mode)) { |
| 507 | if (be_verbose > 2) printf("%s: skipping non-file\n", filename); |
507 | if (be_verbose > 2) printf("%s: skipping non-file\n", filename); |
| 508 | return; |
508 | return; |
| 509 | } |
509 | } |
| 510 | |
510 | |
| 511 | found_pax = found_stack = found_relro = found_load = \ |
511 | found_pax = found_phdr = found_relro = found_load = \ |
| 512 | found_textrel = found_rpath = found_needed = found_interp = \ |
512 | found_textrel = found_rpath = found_needed = found_interp = \ |
| 513 | found_bind = found_sym = found_lib = found_file = 0; |
513 | found_bind = found_sym = found_lib = found_file = 0; |
| 514 | |
514 | |
| 515 | /* verify this is real ELF */ |
515 | /* verify this is real ELF */ |
| 516 | if ((elf = readelf(filename)) == NULL) { |
516 | if ((elf = readelf(filename)) == NULL) { |
| … | |
… | |
| 607 | tmp = (tmp == NULL ? filename : tmp+1); |
607 | tmp = (tmp == NULL ? filename : tmp+1); |
| 608 | xstrcat(&out_buffer, tmp, &out_len); |
608 | xstrcat(&out_buffer, tmp, &out_len); |
| 609 | break; |
609 | break; |
| 610 | case 'o': out = get_elfetype(elf); break; |
610 | case 'o': out = get_elfetype(elf); break; |
| 611 | case 'x': out = scanelf_file_pax(elf, &found_pax); break; |
611 | case 'x': out = scanelf_file_pax(elf, &found_pax); break; |
| 612 | case 'e': out = scanelf_file_stack(elf, &found_stack, &found_relro, &found_load); break; |
612 | case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break; |
| 613 | case 't': out = scanelf_file_textrel(elf, &found_textrel); break; |
613 | case 't': out = scanelf_file_textrel(elf, &found_textrel); break; |
| 614 | case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break; |
614 | case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break; |
| 615 | case 'n': |
615 | case 'n': |
| 616 | case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break; |
616 | case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break; |
| 617 | case 'i': out = scanelf_file_interp(elf, &found_interp); break; |
617 | case 'i': out = scanelf_file_interp(elf, &found_interp); break; |
| … | |
… | |
| 620 | } |
620 | } |
| 621 | if (out) xstrcat(&out_buffer, out, &out_len); |
621 | if (out) xstrcat(&out_buffer, out, &out_len); |
| 622 | } |
622 | } |
| 623 | |
623 | |
| 624 | #define FOUND_SOMETHING() \ |
624 | #define FOUND_SOMETHING() \ |
| 625 | (found_pax || found_stack || found_relro || found_load || found_textrel || \ |
625 | (found_pax || found_phdr || found_relro || found_load || found_textrel || \ |
| 626 | found_rpath || found_needed || found_interp || found_bind || found_sym || found_lib) |
626 | found_rpath || found_needed || found_interp || found_bind || found_sym || found_lib) |
| 627 | |
627 | |
| 628 | if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) { |
628 | if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) { |
| 629 | xchrcat(&out_buffer, ' ', &out_len); |
629 | xchrcat(&out_buffer, ' ', &out_len); |
| 630 | xstrcat(&out_buffer, filename, &out_len); |
630 | xstrcat(&out_buffer, filename, &out_len); |
| … | |
… | |
| 667 | while ((dentry = readdir(dir))) { |
667 | while ((dentry = readdir(dir))) { |
| 668 | if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) |
668 | if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) |
| 669 | continue; |
669 | continue; |
| 670 | len = (pathlen + 1 + strlen(dentry->d_name) + 1); |
670 | len = (pathlen + 1 + strlen(dentry->d_name) + 1); |
| 671 | if (len >= sizeof(buf)) { |
671 | if (len >= sizeof(buf)) { |
| 672 | warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf)); |
672 | warnf("Skipping '%s': len > sizeof(buf); %u > %u\n", path, len, sizeof(buf)); |
| 673 | continue; |
673 | continue; |
| 674 | } |
674 | } |
| 675 | sprintf(buf, "%s/%s", path, dentry->d_name); |
675 | sprintf(buf, "%s/%s", path, dentry->d_name); |
| 676 | if (lstat(buf, &st) != -1) { |
676 | if (lstat(buf, &st) != -1) { |
| 677 | if (S_ISREG(st.st_mode)) |
677 | if (S_ISREG(st.st_mode)) |
| … | |
… | |
| 795 | {"rpath", no_argument, NULL, 'r'}, |
795 | {"rpath", no_argument, NULL, 'r'}, |
| 796 | {"needed", no_argument, NULL, 'n'}, |
796 | {"needed", no_argument, NULL, 'n'}, |
| 797 | {"interp", no_argument, NULL, 'i'}, |
797 | {"interp", no_argument, NULL, 'i'}, |
| 798 | {"bind", no_argument, NULL, 'b'}, |
798 | {"bind", no_argument, NULL, 'b'}, |
| 799 | {"symbol", a_argument, NULL, 's'}, |
799 | {"symbol", a_argument, NULL, 's'}, |
| 800 | {"lib", a_argument, NULL, 'N'}, |
800 | {"lib", a_argument, NULL, 'N'}, |
| 801 | {"all", no_argument, NULL, 'a'}, |
801 | {"all", no_argument, NULL, 'a'}, |
| 802 | {"quiet", no_argument, NULL, 'q'}, |
802 | {"quiet", no_argument, NULL, 'q'}, |
| 803 | {"verbose", no_argument, NULL, 'v'}, |
803 | {"verbose", no_argument, NULL, 'v'}, |
| 804 | {"format", a_argument, NULL, 'F'}, |
804 | {"format", a_argument, NULL, 'F'}, |
| 805 | {"from", a_argument, NULL, 'f'}, |
805 | {"from", a_argument, NULL, 'f'}, |
| … | |
… | |
| 922 | case 'l': scan_ldpath = 1; break; |
922 | case 'l': scan_ldpath = 1; break; |
| 923 | case 'p': scan_envpath = 1; break; |
923 | case 'p': scan_envpath = 1; break; |
| 924 | case 'R': dir_recurse = 1; break; |
924 | case 'R': dir_recurse = 1; break; |
| 925 | case 'm': dir_crossmount = 0; break; |
925 | case 'm': dir_crossmount = 0; break; |
| 926 | case 'x': show_pax = 1; break; |
926 | case 'x': show_pax = 1; break; |
| 927 | case 'e': show_stack = 1; break; |
927 | case 'e': show_phdr = 1; break; |
| 928 | case 't': show_textrel = 1; break; |
928 | case 't': show_textrel = 1; break; |
| 929 | case 'r': show_rpath = 1; break; |
929 | case 'r': show_rpath = 1; break; |
| 930 | case 'n': show_needed = 1; break; |
930 | case 'n': show_needed = 1; break; |
| 931 | case 'i': show_interp = 1; break; |
931 | case 'i': show_interp = 1; break; |
| 932 | case 'b': show_bind = 1; break; |
932 | case 'b': show_bind = 1; break; |
| 933 | case 'q': be_quiet = 1; break; |
933 | case 'q': be_quiet = 1; break; |
| 934 | case 'v': be_verbose = (be_verbose % 20) + 1; break; |
934 | case 'v': be_verbose = (be_verbose % 20) + 1; break; |
| 935 | case 'a': show_pax = show_stack = show_textrel = show_rpath = \ |
935 | case 'a': show_pax = show_phdr = show_textrel = show_rpath = \ |
| 936 | show_needed = show_interp = show_bind = 1; break; |
936 | show_needed = show_interp = show_bind = 1; break; |
| 937 | |
937 | |
| 938 | case ':': |
938 | case ':': |
| 939 | err("Option missing parameter\n"); |
939 | err("Option missing parameter\n"); |
| 940 | case '?': |
940 | case '?': |
| … | |
… | |
| 944 | } |
944 | } |
| 945 | } |
945 | } |
| 946 | |
946 | |
| 947 | /* let the format option override all other options */ |
947 | /* let the format option override all other options */ |
| 948 | if (out_format) { |
948 | if (out_format) { |
| 949 | show_pax = show_stack = show_textrel = show_rpath = \ |
949 | show_pax = show_phdr = show_textrel = show_rpath = \ |
| 950 | show_needed = show_interp = show_bind = 0; |
950 | show_needed = show_interp = show_bind = 0; |
| 951 | for (i = 0; out_format[i]; ++i) { |
951 | for (i = 0; out_format[i]; ++i) { |
| 952 | if (!IS_MODIFIER(out_format[i])) continue; |
952 | if (!IS_MODIFIER(out_format[i])) continue; |
| 953 | |
953 | |
| 954 | switch (out_format[++i]) { |
954 | switch (out_format[++i]) { |
| … | |
… | |
| 959 | case 'f': break; |
959 | case 'f': break; |
| 960 | case 's': break; |
960 | case 's': break; |
| 961 | case 'N': break; |
961 | case 'N': break; |
| 962 | case 'o': break; |
962 | case 'o': break; |
| 963 | case 'x': show_pax = 1; break; |
963 | case 'x': show_pax = 1; break; |
| 964 | case 'e': show_stack = 1; break; |
964 | case 'e': show_phdr = 1; break; |
| 965 | case 't': show_textrel = 1; break; |
965 | case 't': show_textrel = 1; break; |
| 966 | case 'r': show_rpath = 1; break; |
966 | case 'r': show_rpath = 1; break; |
| 967 | case 'n': show_needed = 1; break; |
967 | case 'n': show_needed = 1; break; |
| 968 | case 'i': show_interp = 1; break; |
968 | case 'i': show_interp = 1; break; |
| 969 | case 'b': show_bind = 1; break; |
969 | case 'b': show_bind = 1; break; |
| … | |
… | |
| 977 | } else { |
977 | } else { |
| 978 | size_t fmt_len = 30; |
978 | size_t fmt_len = 30; |
| 979 | out_format = (char*)xmalloc(sizeof(char) * fmt_len); |
979 | out_format = (char*)xmalloc(sizeof(char) * fmt_len); |
| 980 | if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len); |
980 | if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len); |
| 981 | if (show_pax) xstrcat(&out_format, "%x ", &fmt_len); |
981 | if (show_pax) xstrcat(&out_format, "%x ", &fmt_len); |
| 982 | if (show_stack) xstrcat(&out_format, "%e ", &fmt_len); |
982 | if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len); |
| 983 | if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len); |
983 | if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len); |
| 984 | if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len); |
984 | if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len); |
| 985 | if (show_needed) xstrcat(&out_format, "%n ", &fmt_len); |
985 | if (show_needed) xstrcat(&out_format, "%n ", &fmt_len); |
| 986 | if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); |
986 | if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); |
| 987 | if (show_bind) xstrcat(&out_format, "%b ", &fmt_len); |
987 | if (show_bind) xstrcat(&out_format, "%b ", &fmt_len); |