/[gentoo-projects]/pax-utils/scanelf.c
Gentoo

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.242 - (hide annotations) (download) (as text)
Sat Apr 28 05:14:26 2012 UTC (2 years, 8 months ago) by vapier
Branch: MAIN
Changes since 1.241: +8 -12 lines
File MIME type: text/x-csrc
drop EI_PAX setting when modifying pax flags #411919

1 solar 1.1 /*
2 solar 1.178 * Copyright 2003-2007 Gentoo Foundation
3 solar 1.1 * Distributed under the terms of the GNU General Public License v2
4 vapier 1.242 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.241 2012/01/25 01:58:29 vapier Exp $
5 solar 1.1 *
6 solar 1.179 * Copyright 2003-2007 Ned Ludd - <solar@gentoo.org>
7     * Copyright 2004-2007 Mike Frysinger - <vapier@gentoo.org>
8 solar 1.1 */
9    
10 vapier 1.242 static const char rcsid[] = "$Id: scanelf.c,v 1.241 2012/01/25 01:58:29 vapier Exp $";
11 vapier 1.220 const char argv0[] = "scanelf";
12 vapier 1.186
13 vapier 1.89 #include "paxinc.h"
14 flameeyes 1.141
15 solar 1.132 #define IS_MODIFIER(c) (c == '%' || c == '#' || c == '+')
16 vapier 1.70
17 vapier 1.10 /* prototypes */
18 kevquinn 1.142 static int file_matches_list(const char *filename, char **matchlist);
19 vapier 1.10
20     /* variables to control behavior */
21 vapier 1.228 static char *match_etypes = NULL;
22 vapier 1.227 static array_t _ldpaths = array_init_decl, *ldpaths = &_ldpaths;
23 vapier 1.10 static char scan_ldpath = 0;
24     static char scan_envpath = 0;
25 vapier 1.37 static char scan_symlink = 1;
26 vapier 1.105 static char scan_archives = 0;
27 vapier 1.10 static char dir_recurse = 0;
28 vapier 1.14 static char dir_crossmount = 1;
29 vapier 1.10 static char show_pax = 0;
30 solar 1.180 static char show_perms = 0;
31 solar 1.190 static char show_size = 0;
32 solar 1.73 static char show_phdr = 0;
33 vapier 1.10 static char show_textrel = 0;
34     static char show_rpath = 0;
35 vapier 1.32 static char show_needed = 0;
36 vapier 1.38 static char show_interp = 0;
37 vapier 1.49 static char show_bind = 0;
38 vapier 1.84 static char show_soname = 0;
39 vapier 1.76 static char show_textrels = 0;
40 solar 1.16 static char show_banner = 1;
41 solar 1.181 static char show_endian = 0;
42 solar 1.191 static char show_osabi = 0;
43     static char show_eabi = 0;
44 vapier 1.10 static char be_quiet = 0;
45 vapier 1.14 static char be_verbose = 0;
46 solar 1.127 static char be_wewy_wewy_quiet = 0;
47 solar 1.132 static char be_semi_verbose = 0;
48 flameeyes 1.198 static char *find_sym = NULL;
49 vapier 1.72 static char *find_lib = NULL;
50 vapier 1.226 static array_t _find_lib_arr = array_init_decl, *find_lib_arr = &_find_lib_arr;
51 solar 1.124 static char *find_section = NULL;
52 vapier 1.226 static array_t _find_section_arr = array_init_decl, *find_section_arr = &_find_section_arr;
53 vapier 1.39 static char *out_format = NULL;
54 vapier 1.66 static char *search_path = NULL;
55 vapier 1.101 static char fix_elf = 0;
56 solar 1.176 static char g_match = 0;
57 vapier 1.102 static char use_ldcache = 0;
58 vapier 1.240 static char use_ldpath = 0;
59 solar 1.1
60 kevquinn 1.142 static char **qa_textrels = NULL;
61     static char **qa_execstack = NULL;
62 kevquinn 1.145 static char **qa_wx_load = NULL;
63 vapier 1.232 static int root_fd = AT_FDCWD;
64 kevquinn 1.142
65 vapier 1.203 static int match_bits = 0;
66     static unsigned int match_perms = 0;
67 vapier 1.212 static void *ldcache = NULL;
68 vapier 1.203 static size_t ldcache_size = 0;
69     static unsigned long setpax = 0UL;
70 solar 1.96
71 vapier 1.203 static int has_objdump = 0;
72 solar 1.175
73 solar 1.174 /* find the path to a file by name */
74 vapier 1.230 static int bin_in_path(const char *fname)
75 solar 1.170 {
76 vapier 1.230 char fullpath[__PAX_UTILS_PATH_MAX];
77 vapier 1.172 char *path, *p;
78 solar 1.170
79 vapier 1.172 path = getenv("PATH");
80     if (!path)
81 vapier 1.230 return 0;
82 solar 1.170
83 vapier 1.172 while ((p = strrchr(path, ':')) != NULL) {
84 solar 1.170 snprintf(fullpath, sizeof(fullpath), "%s/%s", p + 1, fname);
85 vapier 1.172 *p = 0;
86 vapier 1.230 if (access(fullpath, R_OK) != -1)
87     return 1;
88 vapier 1.172 }
89 vapier 1.230
90     return 0;
91 solar 1.170 }
92 vapier 1.152
93 vapier 1.232 static FILE *fopenat_r(int dir_fd, const char *path)
94     {
95     int fd = openat(dir_fd, path, O_RDONLY|O_CLOEXEC);
96     if (fd == -1)
97     return NULL;
98     return fdopen(fd, "re");
99     }
100    
101     static const char *root_rel_path(const char *path)
102     {
103     /*
104     * openat() will ignore the dirfd if path starts with
105     * a /, so consume all of that noise
106     *
107     * XXX: we don't handle relative paths like ../ that
108     * break out of the --root option, but for now, just
109     * don't do that :P.
110     */
111     if (root_fd != AT_FDCWD) {
112     while (*path == '/')
113     ++path;
114     if (*path == '\0')
115     path = ".";
116     }
117    
118     return path;
119     }
120    
121 solar 1.175 /* 1 on failure. 0 otherwise */
122 solar 1.174 static int rematch(const char *regex, const char *match, int cflags)
123     {
124     regex_t preg;
125     int ret;
126    
127     if ((match == NULL) || (regex == NULL))
128     return EXIT_FAILURE;
129    
130     if ((ret = regcomp(&preg, regex, cflags))) {
131     char err[256];
132    
133     if (regerror(ret, &preg, err, sizeof(err)))
134     fprintf(stderr, "regcomp failed: %s", err);
135     else
136     fprintf(stderr, "regcomp failed");
137    
138     return EXIT_FAILURE;
139     }
140     ret = regexec(&preg, match, 0, NULL, 0);
141     regfree(&preg);
142    
143     return ret;
144     }
145    
146 vapier 1.232 /* sub-funcs for scanelf_fileat() */
147 vapier 1.234 static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **str)
148 vapier 1.77 {
149     /* find the best SHT_DYNSYM and SHT_STRTAB sections */
150 vapier 1.213
151     /* debug sections */
152     void *symtab = elf_findsecbyname(elf, ".symtab");
153     void *strtab = elf_findsecbyname(elf, ".strtab");
154     /* runtime sections */
155     void *dynsym = elf_findsecbyname(elf, ".dynsym");
156     void *dynstr = elf_findsecbyname(elf, ".dynstr");
157    
158 vapier 1.77 #define GET_SYMTABS(B) \
159     if (elf->elf_class == ELFCLASS ## B) { \
160 vapier 1.213 if (symtab && dynsym) { \
161     Elf ## B ## _Shdr *esymtab = symtab; \
162     Elf ## B ## _Shdr *edynsym = dynsym; \
163     *sym = (EGET(esymtab->sh_size) > EGET(edynsym->sh_size)) ? symtab : dynsym; \
164     } else \
165     *sym = symtab ? symtab : dynsym; \
166     if (strtab && dynstr) { \
167     Elf ## B ## _Shdr *estrtab = strtab; \
168     Elf ## B ## _Shdr *edynstr = dynstr; \
169 vapier 1.234 *str = (EGET(estrtab->sh_size) > EGET(edynstr->sh_size)) ? strtab : dynstr; \
170 vapier 1.213 } else \
171 vapier 1.234 *str = strtab ? strtab : dynstr; \
172 vapier 1.77 }
173     GET_SYMTABS(32)
174     GET_SYMTABS(64)
175 vapier 1.234
176     if (*sym && *str)
177     return;
178    
179     /*
180     * damn, they're really going to make us work for it huh?
181     * reconstruct the section header info out of the dynamic
182     * tags so we can see what symbols this guy uses at runtime.
183     */
184     #define GET_SYMTABS_DT(B) \
185     if (elf->elf_class == ELFCLASS ## B) { \
186     size_t i; \
187     static Elf ## B ## _Shdr sym_shdr, str_shdr; \
188     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
189     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
190     Elf ## B ## _Addr vsym, vstr, vhash, vgnu_hash; \
191     Elf ## B ## _Dyn *dyn; \
192     Elf ## B ## _Off offset; \
193     \
194     /* lookup symbols used at runtime with DT_SYMTAB / DT_STRTAB */ \
195     vsym = vstr = vhash = vgnu_hash = 0; \
196     memset(&sym_shdr, 0, sizeof(sym_shdr)); \
197     memset(&str_shdr, 0, sizeof(str_shdr)); \
198     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
199     if (EGET(phdr[i].p_type) != PT_DYNAMIC) \
200     continue; \
201     \
202     offset = EGET(phdr[i].p_offset); \
203     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) \
204     continue; \
205     \
206     dyn = DYN ## B (elf->vdata + offset); \
207     while (EGET(dyn->d_tag) != DT_NULL) { \
208     switch (EGET(dyn->d_tag)) { \
209     case DT_SYMTAB: vsym = EGET(dyn->d_un.d_val); break; \
210     case DT_SYMENT: sym_shdr.sh_entsize = dyn->d_un.d_val; break; \
211     case DT_STRTAB: vstr = EGET(dyn->d_un.d_val); break; \
212     case DT_STRSZ: str_shdr.sh_size = dyn->d_un.d_val; break; \
213     case DT_HASH: vhash = EGET(dyn->d_un.d_val); break; \
214     /*case DT_GNU_HASH: vgnu_hash = EGET(dyn->d_un.d_val); break;*/ \
215     } \
216     ++dyn; \
217     } \
218     if (vsym && vstr) \
219     break; \
220     } \
221     if (!vsym || !vstr || !(vhash || vgnu_hash)) \
222     return; \
223     \
224     /* calc offset into the ELF by finding the load addr of the syms */ \
225     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
226     Elf ## B ## _Addr vaddr = EGET(phdr[i].p_vaddr); \
227     Elf ## B ## _Addr filesz = EGET(phdr[i].p_filesz); \
228     offset = EGET(phdr[i].p_offset); \
229     \
230     if (EGET(phdr[i].p_type) != PT_LOAD) \
231     continue; \
232     \
233     if (vhash >= vaddr && vhash < vaddr + filesz) { \
234     /* Scan the hash table to see how many entries we have */ \
235     Elf32_Word max_sym_idx = 0; \
236     Elf32_Word *hashtbl = elf->vdata + offset + (vhash - vaddr); \
237     Elf32_Word b, nbuckets = EGET(hashtbl[0]); \
238     Elf32_Word nchains = EGET(hashtbl[1]); \
239     Elf32_Word *buckets = &hashtbl[2]; \
240     Elf32_Word *chains = &buckets[nbuckets]; \
241     Elf32_Word sym_idx; \
242     \
243     for (b = 0; b < nbuckets; ++b) { \
244     if (!buckets[b]) \
245     continue; \
246     for (sym_idx = buckets[b]; sym_idx < nchains && sym_idx; sym_idx = chains[sym_idx]) \
247     if (max_sym_idx < sym_idx) \
248     max_sym_idx = sym_idx; \
249     } \
250     ESET(sym_shdr.sh_size, sym_shdr.sh_entsize * max_sym_idx); \
251     } \
252     \
253     if (vsym >= vaddr && vsym < vaddr + filesz) { \
254     ESET(sym_shdr.sh_offset, offset + (vsym - vaddr)); \
255     *sym = &sym_shdr; \
256     } \
257     \
258     if (vstr >= vaddr && vstr < vaddr + filesz) { \
259     ESET(str_shdr.sh_offset, offset + (vstr - vaddr)); \
260     *str = &str_shdr; \
261     } \
262     } \
263     }
264     GET_SYMTABS_DT(32)
265     GET_SYMTABS_DT(64)
266 vapier 1.77 }
267 solar 1.127
268 vapier 1.41 static char *scanelf_file_pax(elfobj *elf, char *found_pax)
269 solar 1.6 {
270 solar 1.61 static char ret[7];
271     unsigned long i, shown;
272    
273 vapier 1.41 if (!show_pax) return NULL;
274 vapier 1.10
275 solar 1.61 shown = 0;
276     memset(&ret, 0, sizeof(ret));
277    
278     if (elf->phdr) {
279     #define SHOW_PAX(B) \
280     if (elf->elf_class == ELFCLASS ## B) { \
281     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
282     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
283     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
284     if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
285     continue; \
286 solar 1.127 if (fix_elf && setpax) { \
287     /* set the paxctl flags */ \
288     ESET(phdr[i].p_flags, setpax); \
289     } \
290 solar 1.129 if (be_quiet && (EGET(phdr[i].p_flags) == (PF_NOEMUTRAMP | PF_NORANDEXEC))) \
291 solar 1.61 continue; \
292     memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
293     *found_pax = 1; \
294     ++shown; \
295     break; \
296     } \
297     }
298     SHOW_PAX(32)
299     SHOW_PAX(64)
300     }
301    
302 vapier 1.242 /* Note: We do not support setting EI_PAX if not PT_PAX_FLAGS
303     * was found. This is known to break ELFs on glibc systems,
304     * and mainline PaX has deprecated use of this for a long time.
305     * We could support changing PT_GNU_STACK, but that doesn't
306     * seem like it's worth the effort. #411919
307     */
308 solar 1.128
309 solar 1.61 /* fall back to EI_PAX if no PT_PAX was found */
310     if (!*ret) {
311 vapier 1.90 static char *paxflags;
312 solar 1.61 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
313     if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
314     *found_pax = 1;
315 solar 1.127 return (be_wewy_wewy_quiet ? NULL : paxflags);
316 solar 1.61 }
317     strncpy(ret, paxflags, sizeof(ret));
318 vapier 1.14 }
319 vapier 1.41
320 solar 1.127 if (be_wewy_wewy_quiet || (be_quiet && !shown))
321 solar 1.61 return NULL;
322 vapier 1.90 else
323     return ret;
324     }
325 solar 1.61
326 solar 1.73 static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
327 vapier 1.39 {
328 vapier 1.71 static char ret[12];
329 vapier 1.41 char *found;
330 vapier 1.99 unsigned long i, shown, multi_stack, multi_relro, multi_load;
331     int max_pt_load;
332 vapier 1.41
333 solar 1.73 if (!show_phdr) return NULL;
334 vapier 1.41
335 vapier 1.71 memcpy(ret, "--- --- ---\0", 12);
336    
337 vapier 1.41 shown = 0;
338 vapier 1.71 multi_stack = multi_relro = multi_load = 0;
339 vapier 1.99 max_pt_load = elf_max_pt_load(elf);
340 vapier 1.44
341 vapier 1.108 #define NOTE_GNU_STACK ".note.GNU-stack"
342 solar 1.73 #define SHOW_PHDR(B) \
343 vapier 1.39 if (elf->elf_class == ELFCLASS ## B) { \
344     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
345 vapier 1.91 Elf ## B ## _Off offset; \
346     uint32_t flags, check_flags; \
347     if (elf->phdr != NULL) { \
348     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
349     for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \
350     if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
351 vapier 1.152 if (multi_stack++) \
352     warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
353     if (file_matches_list(elf->filename, qa_execstack)) \
354     continue; \
355     found = found_phdr; \
356     offset = 0; \
357     check_flags = PF_X; \
358 vapier 1.91 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
359 vapier 1.152 if (multi_relro++) \
360     warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
361 vapier 1.91 found = found_relro; \
362     offset = 4; \
363     check_flags = PF_X; \
364     } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
365 solar 1.188 if (EGET(ehdr->e_type) == ET_DYN || EGET(ehdr->e_type) == ET_EXEC) \
366 vapier 1.152 if (multi_load++ > max_pt_load) \
367     warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
368     if (file_matches_list(elf->filename, qa_wx_load)) \
369     continue; \
370     found = found_load; \
371     offset = 8; \
372     check_flags = PF_W|PF_X; \
373 vapier 1.91 } else \
374     continue; \
375     flags = EGET(phdr[i].p_flags); \
376     if (be_quiet && ((flags & check_flags) != check_flags)) \
377     continue; \
378 solar 1.130 if ((EGET(phdr[i].p_type) != PT_LOAD) && (fix_elf && ((flags & PF_X) != flags))) { \
379 vapier 1.101 ESET(phdr[i].p_flags, flags & (PF_X ^ (size_t)-1)); \
380     ret[3] = ret[7] = '!'; \
381     flags = EGET(phdr[i].p_flags); \
382     } \
383 vapier 1.91 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \
384     *found = 1; \
385     ++shown; \
386     } \
387     } else if (elf->shdr != NULL) { \
388     /* no program headers which means this is prob an object file */ \
389     Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
390     Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \
391 vapier 1.108 char *str; \
392 vapier 1.217 if ((void*)strtbl > elf->data_end) \
393 vapier 1.108 goto skip_this_shdr##B; \
394 vapier 1.91 check_flags = SHF_WRITE|SHF_EXECINSTR; \
395     for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \
396     if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \
397     offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \
398 vapier 1.108 str = elf->data + offset; \
399     if (str > elf->data + offset + sizeof(NOTE_GNU_STACK)) continue; \
400     if (!strcmp(str, NOTE_GNU_STACK)) { \
401 vapier 1.91 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \
402     flags = EGET(shdr[i].sh_flags); \
403     if (be_quiet && ((flags & check_flags) != check_flags)) \
404     continue; \
405     ++*found_phdr; \
406     shown = 1; \
407     if (flags & SHF_WRITE) ret[0] = 'W'; \
408     if (flags & SHF_ALLOC) ret[1] = 'A'; \
409     if (flags & SHF_EXECINSTR) ret[2] = 'X'; \
410     if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \
411     break; \
412     } \
413     } \
414 vapier 1.108 skip_this_shdr##B: \
415 vapier 1.91 if (!multi_stack) { \
416 vapier 1.158 if (file_matches_list(elf->filename, qa_execstack)) \
417     return NULL; \
418 vapier 1.91 *found_phdr = 1; \
419     shown = 1; \
420     memcpy(ret, "!WX", 3); \
421     } \
422 vapier 1.39 } \
423     }
424 solar 1.73 SHOW_PHDR(32)
425     SHOW_PHDR(64)
426 vapier 1.44
427 solar 1.127 if (be_wewy_wewy_quiet || (be_quiet && !shown))
428 vapier 1.41 return NULL;
429     else
430     return ret;
431 vapier 1.39 }
432 kevquinn 1.142
433 vapier 1.186 /*
434     * See if this ELF contains a DT_TEXTREL tag in any of its
435     * PT_DYNAMIC sections.
436     */
437 vapier 1.90 static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
438 vapier 1.39 {
439 vapier 1.90 static const char *ret = "TEXTREL";
440 vapier 1.44 unsigned long i;
441 vapier 1.41
442 vapier 1.79 if (!show_textrel && !show_textrels) return NULL;
443 vapier 1.41
444 kevquinn 1.142 if (file_matches_list(elf->filename, qa_textrels)) return NULL;
445    
446 vapier 1.44 if (elf->phdr) {
447 vapier 1.39 #define SHOW_TEXTREL(B) \
448     if (elf->elf_class == ELFCLASS ## B) { \
449     Elf ## B ## _Dyn *dyn; \
450     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
451     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
452 vapier 1.44 Elf ## B ## _Off offset; \
453 vapier 1.39 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
454 vapier 1.185 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
455 vapier 1.44 offset = EGET(phdr[i].p_offset); \
456     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
457 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
458 vapier 1.39 while (EGET(dyn->d_tag) != DT_NULL) { \
459     if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
460     *found_textrel = 1; \
461     /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
462 solar 1.127 return (be_wewy_wewy_quiet ? NULL : ret); \
463 vapier 1.39 } \
464     ++dyn; \
465 vapier 1.26 } \
466 vapier 1.39 } }
467     SHOW_TEXTREL(32)
468     SHOW_TEXTREL(64)
469 vapier 1.44 }
470    
471 solar 1.127 if (be_quiet || be_wewy_wewy_quiet)
472 vapier 1.41 return NULL;
473     else
474 vapier 1.90 return " - ";
475 vapier 1.39 }
476 solar 1.180
477 vapier 1.186 /*
478     * Scan the .text section to see if there are any relocations in it.
479     * Should rewrite this to check PT_LOAD sections that are marked
480     * Executable rather than the section named '.text'.
481     */
482 vapier 1.79 static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
483 vapier 1.76 {
484 vapier 1.82 unsigned long s, r, rmax;
485     void *symtab_void, *strtab_void, *text_void;
486 vapier 1.76
487     if (!show_textrels) return NULL;
488    
489 vapier 1.79 /* don't search for TEXTREL's if the ELF doesn't have any */
490     if (!*found_textrel) scanelf_file_textrel(elf, found_textrel);
491     if (!*found_textrel) return NULL;
492    
493 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
494 vapier 1.82 text_void = elf_findsecbyname(elf, ".text");
495 vapier 1.76
496 vapier 1.82 if (symtab_void && strtab_void && text_void && elf->shdr) {
497 vapier 1.76 #define SHOW_TEXTRELS(B) \
498     if (elf->elf_class == ELFCLASS ## B) { \
499     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
500     Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
501     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
502     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
503 vapier 1.82 Elf ## B ## _Shdr *text = SHDR ## B (text_void); \
504     Elf ## B ## _Addr vaddr = EGET(text->sh_addr); \
505     uint ## B ## _t memsz = EGET(text->sh_size); \
506 vapier 1.76 Elf ## B ## _Rel *rel; \
507     Elf ## B ## _Rela *rela; \
508     /* search the section headers for relocations */ \
509     for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
510     uint32_t sh_type = EGET(shdr[s].sh_type); \
511     if (sh_type == SHT_REL) { \
512 vapier 1.217 rel = REL ## B (elf->vdata + EGET(shdr[s].sh_offset)); \
513 vapier 1.76 rela = NULL; \
514     rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
515     } else if (sh_type == SHT_RELA) { \
516     rel = NULL; \
517 vapier 1.217 rela = RELA ## B (elf->vdata + EGET(shdr[s].sh_offset)); \
518 vapier 1.76 rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
519     } else \
520     continue; \
521 vapier 1.82 /* now see if any of the relocs are in the .text */ \
522     for (r = 0; r < rmax; ++r) { \
523     unsigned long sym_max; \
524     Elf ## B ## _Addr offset_tmp; \
525     Elf ## B ## _Sym *func; \
526     Elf ## B ## _Sym *sym; \
527     Elf ## B ## _Addr r_offset; \
528     uint ## B ## _t r_info; \
529     if (sh_type == SHT_REL) { \
530     r_offset = EGET(rel[r].r_offset); \
531     r_info = EGET(rel[r].r_info); \
532     } else { \
533     r_offset = EGET(rela[r].r_offset); \
534     r_info = EGET(rela[r].r_info); \
535     } \
536     /* make sure this relocation is inside of the .text */ \
537     if (r_offset < vaddr || r_offset >= vaddr + memsz) { \
538     if (be_verbose <= 2) continue; \
539     } else \
540 vapier 1.78 *found_textrels = 1; \
541 vapier 1.82 /* locate this relocation symbol name */ \
542 vapier 1.217 sym = SYM ## B (elf->vdata + EGET(symtab->sh_offset)); \
543     if ((void*)sym > elf->data_end) { \
544 vapier 1.111 warn("%s: corrupt ELF symbol", elf->filename); \
545 vapier 1.109 continue; \
546     } \
547 vapier 1.82 sym_max = ELF ## B ## _R_SYM(r_info); \
548     if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
549     sym += sym_max; \
550     else \
551     sym = NULL; \
552     sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
553     /* show the raw details about this reloc */ \
554 vapier 1.88 printf(" %s: ", elf->base_filename); \
555 vapier 1.82 if (sym && sym->st_name) \
556 vapier 1.217 printf("%s", elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
557 vapier 1.82 else \
558 vapier 1.169 printf("(memory/data?)"); \
559 vapier 1.82 printf(" [0x%lX]", (unsigned long)r_offset); \
560     /* now try to find the closest symbol that this rel is probably in */ \
561 vapier 1.217 sym = SYM ## B (elf->vdata + EGET(symtab->sh_offset)); \
562 vapier 1.82 func = NULL; \
563     offset_tmp = 0; \
564     while (sym_max--) { \
565     if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
566     func = sym; \
567     offset_tmp = EGET(sym->st_value); \
568 vapier 1.76 } \
569 vapier 1.82 ++sym; \
570 vapier 1.76 } \
571 vapier 1.82 printf(" in "); \
572 vapier 1.169 if (func && func->st_name) { \
573     const char *func_name = elf->data + EGET(strtab->sh_offset) + EGET(func->st_name); \
574     if (r_offset > EGET(func->st_size)) \
575     printf("(optimized out: previous %s)", func_name); \
576     else \
577     printf("%s", func_name); \
578     } else \
579     printf("(optimized out)"); \
580 vapier 1.82 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
581 solar 1.170 if (be_verbose && has_objdump) { \
582 vapier 1.187 Elf ## B ## _Addr end_addr = offset_tmp + EGET(func->st_size); \
583 vapier 1.167 char *sysbuf; \
584     size_t syslen; \
585     const char sysfmt[] = "objdump -r -R -d -w -l --start-address=0x%lX --stop-address=0x%lX %s | grep --color -i -C 3 '.*[[:space:]]%lX:[[:space:]]*R_.*'\n"; \
586     syslen = sizeof(sysfmt) + strlen(elf->filename) + 3 * sizeof(unsigned long) + 1; \
587     sysbuf = xmalloc(syslen); \
588 vapier 1.187 if (end_addr < r_offset) \
589     /* not uncommon when things are optimized out */ \
590     end_addr = r_offset + 0x100; \
591     snprintf(sysbuf, syslen, sysfmt, \
592     (unsigned long)offset_tmp, \
593     (unsigned long)end_addr, \
594     elf->filename, \
595     (unsigned long)r_offset); \
596     fflush(stdout); \
597 vapier 1.232 if (system(sysbuf)) /* don't care */; \
598 vapier 1.187 fflush(stdout); \
599     free(sysbuf); \
600 vapier 1.167 } \
601 vapier 1.76 } \
602     } }
603     SHOW_TEXTRELS(32)
604     SHOW_TEXTRELS(64)
605     }
606 vapier 1.82 if (!*found_textrels)
607     warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
608 vapier 1.76
609     return NULL;
610     }
611 solar 1.83
612 vapier 1.102 static void rpath_security_checks(elfobj *elf, char *item, const char *dt_type)
613 vapier 1.100 {
614 solar 1.83 struct stat st;
615 vapier 1.84 switch (*item) {
616 vapier 1.89 case '/': break;
617     case '.':
618 vapier 1.102 warnf("Security problem with relative %s '%s' in %s", dt_type, item, elf->filename);
619 vapier 1.89 break;
620 vapier 1.100 case ':':
621 vapier 1.89 case '\0':
622 vapier 1.102 warnf("Security problem NULL %s in %s", dt_type, elf->filename);
623 solar 1.83 break;
624     case '$':
625 vapier 1.84 if (fstat(elf->fd, &st) != -1)
626 solar 1.83 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
627 vapier 1.157 warnf("Security problem with %s='%s' in %s with mode set of %o",
628 solar 1.176 dt_type, item, elf->filename, (unsigned int) st.st_mode & 07777);
629 solar 1.83 break;
630     default:
631 vapier 1.102 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename);
632 solar 1.83 break;
633     }
634     }
635 vapier 1.41 static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
636 vapier 1.39 {
637 vapier 1.227 unsigned long i;
638 vapier 1.48 char *rpath, *runpath, **r;
639 vapier 1.39 void *strtbl_void;
640 vapier 1.10
641 vapier 1.39 if (!show_rpath) return;
642 vapier 1.10
643 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
644     rpath = runpath = NULL;
645 vapier 1.10
646 vapier 1.44 if (elf->phdr && strtbl_void) {
647 vapier 1.26 #define SHOW_RPATH(B) \
648     if (elf->elf_class == ELFCLASS ## B) { \
649     Elf ## B ## _Dyn *dyn; \
650     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
651     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
652     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
653 vapier 1.44 Elf ## B ## _Off offset; \
654 vapier 1.60 Elf ## B ## _Xword word; \
655 vapier 1.48 /* Scan all the program headers */ \
656 vapier 1.26 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
657 vapier 1.48 /* Just scan dynamic headers */ \
658 vapier 1.185 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
659 vapier 1.44 offset = EGET(phdr[i].p_offset); \
660     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
661 vapier 1.48 /* Just scan dynamic RPATH/RUNPATH headers */ \
662 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
663 vapier 1.48 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
664     if (word == DT_RPATH) { \
665     r = &rpath; \
666     } else if (word == DT_RUNPATH) { \
667     r = &runpath; \
668     } else { \
669     ++dyn; \
670     continue; \
671     } \
672     /* Verify the memory is somewhat sane */ \
673     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
674 vapier 1.69 if (offset < (Elf ## B ## _Off)elf->len) { \
675 vapier 1.48 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
676 vapier 1.217 *r = elf->data + offset; \
677 vapier 1.103 /* cache the length in case we need to nuke this section later on */ \
678     if (fix_elf) \
679     offset = strlen(*r); \
680 vapier 1.48 /* If quiet, don't output paths in ld.so.conf */ \
681 vapier 1.69 if (be_quiet) { \
682     size_t len; \
683     char *start, *end; \
684 vapier 1.75 /* note that we only 'chop' off leading known paths. */ \
685     /* since *r is read-only memory, we can only move the ptr forward. */ \
686     start = *r; \
687     /* scan each path in : delimited list */ \
688     while (start) { \
689 vapier 1.102 rpath_security_checks(elf, start, get_elfdtype(word)); \
690 vapier 1.69 end = strchr(start, ':'); \
691 vapier 1.75 len = (end ? abs(end - start) : strlen(start)); \
692 vapier 1.227 if (use_ldcache) { \
693     size_t n; \
694     const char *ldpath; \
695     array_for_each(ldpaths, n, ldpath) \
696     if (!strncmp(ldpath, start, len) && !ldpath[len]) { \
697 vapier 1.102 *r = end; \
698     /* corner case ... if RPATH reads "/usr/lib:", we want \
699     * to show ':' rather than '' */ \
700     if (end && end[1] != '\0') \
701     (*r)++; \
702     break; \
703     } \
704 vapier 1.227 } \
705 vapier 1.100 if (!*r || !end) \
706     break; \
707 vapier 1.75 else \
708     start = start + len + 1; \
709 vapier 1.69 } \
710     } \
711 vapier 1.101 if (*r) { \
712 solar 1.107 if (fix_elf > 2 || (fix_elf && **r == '\0')) { \
713 vapier 1.101 /* just nuke it */ \
714     nuke_it##B: \
715 vapier 1.103 memset(*r, 0x00, offset); \
716 vapier 1.102 *r = NULL; \
717 vapier 1.101 ESET(dyn->d_tag, DT_DEBUG); \
718 vapier 1.103 ESET(dyn->d_un.d_ptr, 0); \
719 vapier 1.101 } else if (fix_elf) { \
720     /* try to clean "bad" paths */ \
721     size_t len, tmpdir_len; \
722     char *start, *end; \
723     const char *tmpdir; \
724     start = *r; \
725     tmpdir = (getenv("TMPDIR") ? : "."); \
726     tmpdir_len = strlen(tmpdir); \
727     while (1) { \
728     end = strchr(start, ':'); \
729     if (start == end) { \
730     eat_this_path##B: \
731     len = strlen(end); \
732     memmove(start, end+1, len); \
733     start[len-1] = '\0'; \
734     end = start - 1; \
735     } else if (tmpdir && !strncmp(start, tmpdir, tmpdir_len)) { \
736     if (!end) { \
737     if (start == *r) \
738     goto nuke_it##B; \
739     *--start = '\0'; \
740     } else \
741     goto eat_this_path##B; \
742     } \
743     if (!end) \
744     break; \
745     start = end + 1; \
746     } \
747 vapier 1.102 if (**r == '\0') \
748     goto nuke_it##B; \
749 vapier 1.101 } \
750 vapier 1.102 if (*r) \
751     *found_rpath = 1; \
752 vapier 1.101 } \
753 vapier 1.26 } \
754     ++dyn; \
755     } \
756     } }
757     SHOW_RPATH(32)
758     SHOW_RPATH(64)
759 vapier 1.10 }
760 vapier 1.41
761 solar 1.127 if (be_wewy_wewy_quiet) return;
762 vapier 1.70
763 vapier 1.39 if (rpath && runpath) {
764 vapier 1.41 if (!strcmp(rpath, runpath)) {
765     xstrcat(ret, runpath, ret_len);
766     } else {
767 vapier 1.39 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
768 vapier 1.41 xchrcat(ret, '{', ret_len);
769     xstrcat(ret, rpath, ret_len);
770     xchrcat(ret, ',', ret_len);
771     xstrcat(ret, runpath, ret_len);
772     xchrcat(ret, '}', ret_len);
773 vapier 1.39 }
774     } else if (rpath || runpath)
775 vapier 1.41 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
776     else if (!be_quiet)
777     xstrcat(ret, " - ", ret_len);
778 vapier 1.39 }
779 solar 1.96
780     #define LDSO_CACHE_MAGIC "ld.so-"
781     #define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
782     #define LDSO_CACHE_VER "1.7.0"
783     #define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
784 vapier 1.97 #define FLAG_ANY -1
785     #define FLAG_TYPE_MASK 0x00ff
786     #define FLAG_LIBC4 0x0000
787     #define FLAG_ELF 0x0001
788     #define FLAG_ELF_LIBC5 0x0002
789     #define FLAG_ELF_LIBC6 0x0003
790     #define FLAG_REQUIRED_MASK 0xff00
791     #define FLAG_SPARC_LIB64 0x0100
792     #define FLAG_IA64_LIB64 0x0200
793     #define FLAG_X8664_LIB64 0x0300
794     #define FLAG_S390_LIB64 0x0400
795     #define FLAG_POWERPC_LIB64 0x0500
796     #define FLAG_MIPS64_LIBN32 0x0600
797     #define FLAG_MIPS64_LIBN64 0x0700
798 solar 1.96
799 flameeyes 1.141 #if defined(__GLIBC__) || defined(__UCLIBC__)
800 vapier 1.148
801 vapier 1.240 static char *lookup_cache_lib(elfobj *elf, const char *fname)
802 solar 1.96 {
803 vapier 1.212 int fd;
804 solar 1.96 char *strs;
805 vapier 1.104 static char buf[__PAX_UTILS_PATH_MAX] = "";
806 vapier 1.232 const char *cachefile = root_rel_path("/etc/ld.so.cache");
807 solar 1.96 struct stat st;
808    
809     typedef struct {
810     char magic[LDSO_CACHE_MAGIC_LEN];
811     char version[LDSO_CACHE_VER_LEN];
812     int nlibs;
813     } header_t;
814 vapier 1.97 header_t *header;
815 solar 1.96
816     typedef struct {
817     int flags;
818     int sooffset;
819     int liboffset;
820     } libentry_t;
821     libentry_t *libent;
822    
823     if (fname == NULL)
824     return NULL;
825    
826 vapier 1.212 if (ldcache == NULL) {
827 vapier 1.232 if (fstatat(root_fd, cachefile, &st, 0))
828 vapier 1.212 return NULL;
829    
830 vapier 1.232 fd = openat(root_fd, cachefile, O_RDONLY);
831 vapier 1.212 if (fd == -1)
832 solar 1.96 return NULL;
833 vapier 1.97
834     /* cache these values so we only map/unmap the cache file once */
835 solar 1.96 ldcache_size = st.st_size;
836 vapier 1.212 header = ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
837 vapier 1.97 close(fd);
838 solar 1.96
839 vapier 1.202 if (ldcache == MAP_FAILED) {
840 vapier 1.212 ldcache = NULL;
841 solar 1.96 return NULL;
842 solar 1.116 }
843 solar 1.96
844 vapier 1.212 if (memcmp(header->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN) ||
845     memcmp(header->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
846     {
847     munmap(ldcache, ldcache_size);
848     ldcache = NULL;
849 solar 1.96 return NULL;
850 vapier 1.212 }
851     } else
852     header = ldcache;
853 solar 1.96
854 vapier 1.212 libent = ldcache + sizeof(header_t);
855 solar 1.96 strs = (char *) &libent[header->nlibs];
856    
857 vapier 1.212 for (fd = 0; fd < header->nlibs; ++fd) {
858     /* This should be more fine grained, but for now we assume that
859     * diff arches will not be cached together, and we ignore the
860     * the different multilib mips cases.
861     */
862 vapier 1.97 if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
863     continue;
864     if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
865     continue;
866    
867 solar 1.96 if (strcmp(fname, strs + libent[fd].sooffset) != 0)
868     continue;
869 vapier 1.212
870     /* Return first hit because that is how the ldso rolls */
871 solar 1.96 strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
872 vapier 1.212 break;
873 solar 1.96 }
874 vapier 1.212
875 solar 1.96 return buf;
876     }
877 vapier 1.212
878 solar 1.154 #elif defined(__NetBSD__)
879 vapier 1.240 static char *lookup_cache_lib(elfobj *elf, const char *fname)
880 solar 1.154 {
881     static char buf[__PAX_UTILS_PATH_MAX] = "";
882     static struct stat st;
883 vapier 1.227 size_t n;
884     char *ldpath;
885 solar 1.154
886 vapier 1.227 array_for_each(ldpath, n, ldpath) {
887     if ((unsigned) snprintf(buf, sizeof(buf), "%s/%s", ldpath, fname) >= sizeof(buf))
888 solar 1.154 continue; /* if the pathname is too long, or something went wrong, ignore */
889    
890     if (stat(buf, &st) != 0)
891     continue; /* if the lib doesn't exist in *ldpath, look further */
892    
893     /* NetBSD doesn't actually do sanity checks, it just loads the file
894     * and if that doesn't work, continues looking in other directories.
895     * This cannot easily be safely emulated, unfortunately. For now,
896     * just assume that if it exists, it's a valid library. */
897 vapier 1.148
898 solar 1.154 return buf;
899     }
900    
901     /* not found in any path */
902     return NULL;
903     }
904 flameeyes 1.141 #else
905 vapier 1.192 #ifdef __ELF__
906 vapier 1.148 #warning Cache support not implemented for your target
907 vapier 1.192 #endif
908 vapier 1.240 static char *lookup_cache_lib(elfobj *elf, const char *fname)
909 flameeyes 1.141 {
910     return NULL;
911     }
912     #endif
913 solar 1.96
914 vapier 1.240 static char *lookup_config_lib(elfobj *elf, char *fname)
915     {
916     static char buf[__PAX_UTILS_PATH_MAX] = "";
917     const char *ldpath;
918     size_t n;
919    
920     array_for_each(ldpaths, n, ldpath) {
921     snprintf(buf, sizeof(buf), "%s/%s", root_rel_path(ldpath), fname);
922     if (faccessat(root_fd, buf, F_OK, AT_SYMLINK_NOFOLLOW) == 0)
923     return buf;
924     }
925    
926     return NULL;
927     }
928    
929 vapier 1.84 static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
930 vapier 1.39 {
931 vapier 1.44 unsigned long i;
932 vapier 1.39 char *needed;
933     void *strtbl_void;
934 solar 1.96 char *p;
935 vapier 1.39
936 vapier 1.226 /*
937     * -n -> op==0 -> print all
938     * -N -> op==1 -> print requested
939     */
940     if ((op == 0 && !show_needed) || (op == 1 && !find_lib))
941     return NULL;
942 vapier 1.10
943 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
944 vapier 1.32
945 vapier 1.44 if (elf->phdr && strtbl_void) {
946 vapier 1.32 #define SHOW_NEEDED(B) \
947     if (elf->elf_class == ELFCLASS ## B) { \
948     Elf ## B ## _Dyn *dyn; \
949     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
950     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
951     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
952 vapier 1.44 Elf ## B ## _Off offset; \
953 vapier 1.226 size_t matched = 0; \
954     /* Walk all the program headers to find the PT_DYNAMIC */ \
955 vapier 1.32 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
956 vapier 1.226 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) \
957     continue; \
958 vapier 1.44 offset = EGET(phdr[i].p_offset); \
959 vapier 1.226 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) \
960     continue; \
961     /* Walk all the dynamic tags to find NEEDED entries */ \
962 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
963 vapier 1.32 while (EGET(dyn->d_tag) != DT_NULL) { \
964     if (EGET(dyn->d_tag) == DT_NEEDED) { \
965 vapier 1.44 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
966 vapier 1.69 if (offset >= (Elf ## B ## _Off)elf->len) { \
967 vapier 1.49 ++dyn; \
968     continue; \
969     } \
970 vapier 1.217 needed = elf->data + offset; \
971 vapier 1.72 if (op == 0) { \
972 vapier 1.226 /* -n -> print all entries */ \
973 solar 1.127 if (!be_wewy_wewy_quiet) { \
974 vapier 1.72 if (*found_needed) xchrcat(ret, ',', ret_len); \
975 vapier 1.240 if (use_ldpath) { \
976     if ((p = lookup_config_lib(elf, needed)) != NULL) \
977     needed = p; \
978     } else if (use_ldcache) { \
979 vapier 1.97 if ((p = lookup_cache_lib(elf, needed)) != NULL) \
980 solar 1.96 needed = p; \
981 vapier 1.240 } \
982 vapier 1.72 xstrcat(ret, needed, ret_len); \
983     } \
984     *found_needed = 1; \
985     } else { \
986 vapier 1.226 /* -N -> print matching entries */ \
987     size_t n; \
988     const char *find_lib_name; \
989     \
990 vapier 1.236 array_for_each(find_lib_arr, n, find_lib_name) { \
991     int invert = 1; \
992     if (find_lib_name[0] == '!') \
993     invert = 0, ++find_lib_name; \
994     if (!strcmp(find_lib_name, needed) == invert) \
995 vapier 1.226 ++matched; \
996 vapier 1.236 } \
997 vapier 1.226 \
998     if (matched == array_cnt(find_lib_arr)) { \
999 vapier 1.81 *found_lib = 1; \
1000 vapier 1.226 return (be_wewy_wewy_quiet ? NULL : find_lib); \
1001 vapier 1.81 } \
1002 vapier 1.72 } \
1003 vapier 1.32 } \
1004     ++dyn; \
1005     } \
1006     } }
1007     SHOW_NEEDED(32)
1008     SHOW_NEEDED(64)
1009 vapier 1.85 if (op == 0 && !*found_needed && be_verbose)
1010 vapier 1.84 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
1011 vapier 1.32 }
1012 vapier 1.72
1013     return NULL;
1014 vapier 1.39 }
1015 vapier 1.41 static char *scanelf_file_interp(elfobj *elf, char *found_interp)
1016 vapier 1.39 {
1017     void *strtbl_void;
1018    
1019 vapier 1.41 if (!show_interp) return NULL;
1020 vapier 1.32
1021 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".interp");
1022 vapier 1.38
1023 vapier 1.39 if (strtbl_void) {
1024 vapier 1.38 #define SHOW_INTERP(B) \
1025     if (elf->elf_class == ELFCLASS ## B) { \
1026 solar 1.40 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
1027     *found_interp = 1; \
1028 solar 1.127 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
1029 vapier 1.38 }
1030     SHOW_INTERP(32)
1031     SHOW_INTERP(64)
1032     }
1033 vapier 1.41 return NULL;
1034 vapier 1.39 }
1035 vapier 1.49 static char *scanelf_file_bind(elfobj *elf, char *found_bind)
1036     {
1037     unsigned long i;
1038     struct stat s;
1039 solar 1.131 char dynamic = 0;
1040 vapier 1.49
1041     if (!show_bind) return NULL;
1042 vapier 1.51 if (!elf->phdr) return NULL;
1043 vapier 1.49
1044     #define SHOW_BIND(B) \
1045     if (elf->elf_class == ELFCLASS ## B) { \
1046     Elf ## B ## _Dyn *dyn; \
1047     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
1048     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
1049     Elf ## B ## _Off offset; \
1050     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
1051 vapier 1.185 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
1052 solar 1.131 dynamic = 1; \
1053 vapier 1.49 offset = EGET(phdr[i].p_offset); \
1054     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
1055 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
1056 vapier 1.49 while (EGET(dyn->d_tag) != DT_NULL) { \
1057     if (EGET(dyn->d_tag) == DT_BIND_NOW || \
1058     (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
1059     { \
1060     if (be_quiet) return NULL; \
1061     *found_bind = 1; \
1062 solar 1.127 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
1063 vapier 1.49 } \
1064     ++dyn; \
1065     } \
1066     } \
1067     }
1068     SHOW_BIND(32)
1069     SHOW_BIND(64)
1070    
1071 solar 1.127 if (be_wewy_wewy_quiet) return NULL;
1072 vapier 1.70
1073 vapier 1.159 /* don't output anything if quiet mode and the ELF is static or not setuid */
1074     if (be_quiet && (!dynamic || (!fstat(elf->fd, &s) && !(s.st_mode & (S_ISUID|S_ISGID))))) {
1075 vapier 1.49 return NULL;
1076     } else {
1077     *found_bind = 1;
1078 vapier 1.217 return (char *)(dynamic ? "LAZY" : "STATIC");
1079 vapier 1.49 }
1080     }
1081 vapier 1.84 static char *scanelf_file_soname(elfobj *elf, char *found_soname)
1082     {
1083     unsigned long i;
1084     char *soname;
1085     void *strtbl_void;
1086    
1087     if (!show_soname) return NULL;
1088    
1089     strtbl_void = elf_findsecbyname(elf, ".dynstr");
1090    
1091     if (elf->phdr && strtbl_void) {
1092     #define SHOW_SONAME(B) \
1093     if (elf->elf_class == ELFCLASS ## B) { \
1094     Elf ## B ## _Dyn *dyn; \
1095     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
1096     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
1097     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
1098     Elf ## B ## _Off offset; \
1099     /* only look for soname in shared objects */ \
1100 solar 1.188 if (EGET(ehdr->e_type) != ET_DYN) \
1101 vapier 1.84 return NULL; \
1102     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
1103 vapier 1.185 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
1104 vapier 1.84 offset = EGET(phdr[i].p_offset); \
1105     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
1106 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
1107 vapier 1.84 while (EGET(dyn->d_tag) != DT_NULL) { \
1108     if (EGET(dyn->d_tag) == DT_SONAME) { \
1109     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
1110     if (offset >= (Elf ## B ## _Off)elf->len) { \
1111     ++dyn; \
1112     continue; \
1113     } \
1114 vapier 1.217 soname = elf->data + offset; \
1115 vapier 1.84 *found_soname = 1; \
1116 solar 1.127 return (be_wewy_wewy_quiet ? NULL : soname); \
1117 vapier 1.84 } \
1118     ++dyn; \
1119     } \
1120     } }
1121     SHOW_SONAME(32)
1122     SHOW_SONAME(64)
1123     }
1124    
1125     return NULL;
1126     }
1127 solar 1.177
1128 vapier 1.215 /*
1129     * We support the symbol form:
1130     * [%[modifiers]%][[+-]<symbol name>][,[.....]]
1131     * If the symbol name is empty, then all symbols are matched.
1132     * If the symbol name is a glob ("*"), then all symbols are dumped (debug).
1133     * Do not rely on this output format at all.
1134     * Otherwise the symbol name is used to search (either regex or string compare).
1135     * If the first char of the symbol name is a plus ("+"), then only match
1136     * defined symbols. If it's a minus ("-"), only match undefined symbols.
1137     * Putting modifiers in between the percent signs allows for more in depth
1138     * filters. There are groups of modifiers. If you don't specify a member
1139     * of a group, then all types in that group are matched. The current
1140     * groups and their types are:
1141 vapier 1.238 * STT group: STT_NOTYPE:n STT_OBJECT:o STT_FUNC:f STT_FILE:F
1142 vapier 1.215 * STB group: STB_LOCAL:l STB_GLOBAL:g STB_WEAK:w
1143     * SHN group: SHN_UNDEF:u SHN_ABS:a SHN_COMMON:c {defined}:d
1144     * The "defined" value in the SHN group does not correspond to a SHN_xxx define.
1145     * You can search for multiple symbols at once by seperating with a comma (",").
1146     *
1147     * Some examples:
1148     * ELFs with a weak function "foo":
1149     * scanelf -s %wf%foo <ELFs>
1150     * ELFs that define the symbol "main":
1151     * scanelf -s +main <ELFs>
1152     * scanelf -s %d%main <ELFs>
1153     * ELFs that refer to the undefined symbol "brk":
1154     * scanelf -s -brk <ELFs>
1155     * scanelf -s %u%brk <ELFs>
1156     * All global defined objects in an ELF:
1157     * scanelf -s %ogd% <ELF>
1158     */
1159     static void
1160     scanelf_match_symname(elfobj *elf, char *found_sym, char **ret, size_t *ret_len, const char *symname,
1161     unsigned int stt, unsigned int stb, unsigned int shn, unsigned long size)
1162     {
1163     char *this_sym, *next_sym, saved = saved;
1164    
1165     /* allow the user to specify a comma delimited list of symbols to search for */
1166     next_sym = NULL;
1167     do {
1168     bool inc_notype, inc_object, inc_func, inc_file,
1169     inc_local, inc_global, inc_weak,
1170     inc_def, inc_undef, inc_abs, inc_common;
1171    
1172     if (next_sym) {
1173     next_sym[-1] = saved;
1174     this_sym = next_sym;
1175     } else
1176     this_sym = find_sym;
1177     if ((next_sym = strchr(this_sym, ','))) {
1178     /* make parsing easier by killing the comma temporarily */
1179     saved = *next_sym;
1180     *next_sym = '\0';
1181     next_sym += 1;
1182     }
1183    
1184     /* symbol selection! */
1185     inc_notype = inc_object = inc_func = inc_file = \
1186     inc_local = inc_global = inc_weak = \
1187     inc_def = inc_undef = inc_abs = inc_common = \
1188     (*this_sym != '%');
1189    
1190     /* parse the contents of %...% */
1191     if (!inc_notype) {
1192     while (*(this_sym++)) {
1193     if (*this_sym == '%') {
1194     ++this_sym;
1195     break;
1196     }
1197     switch (*this_sym) {
1198     case 'n': inc_notype = true; break;
1199     case 'o': inc_object = true; break;
1200     case 'f': inc_func = true; break;
1201     case 'F': inc_file = true; break;
1202     case 'l': inc_local = true; break;
1203     case 'g': inc_global = true; break;
1204     case 'w': inc_weak = true; break;
1205     case 'd': inc_def = true; break;
1206     case 'u': inc_undef = true; break;
1207     case 'a': inc_abs = true; break;
1208     case 'c': inc_common = true; break;
1209     default: err("invalid symbol selector '%c'", *this_sym);
1210     }
1211     }
1212    
1213     /* If no types are matched, not match all */
1214     if (!inc_notype && !inc_object && !inc_func && !inc_file)
1215     inc_notype = inc_object = inc_func = inc_file = true;
1216     if (!inc_local && !inc_global && !inc_weak)
1217     inc_local = inc_global = inc_weak = true;
1218     if (!inc_def && !inc_undef && !inc_abs && !inc_common)
1219     inc_def = inc_undef = inc_abs = inc_common = true;
1220    
1221     /* backwards compat for defined/undefined short hand */
1222     } else if (*this_sym == '+') {
1223     inc_undef = false;
1224     ++this_sym;
1225     } else if (*this_sym == '-') {
1226     inc_def = inc_abs = inc_common = false;
1227     ++this_sym;
1228     }
1229    
1230     /* filter symbols */
1231     if ((!inc_notype && stt == STT_NOTYPE) || \
1232     (!inc_object && stt == STT_OBJECT) || \
1233     (!inc_func && stt == STT_FUNC ) || \
1234     (!inc_file && stt == STT_FILE ) || \
1235     (!inc_local && stb == STB_LOCAL ) || \
1236     (!inc_global && stb == STB_GLOBAL) || \
1237     (!inc_weak && stb == STB_WEAK ) || \
1238     (!inc_def && shn && shn < SHN_LORESERVE) || \
1239     (!inc_undef && shn == SHN_UNDEF ) || \
1240     (!inc_abs && shn == SHN_ABS ) || \
1241     (!inc_common && shn == SHN_COMMON))
1242     continue;
1243    
1244     if (*this_sym == '*') {
1245     /* a "*" symbol gets you debug output */
1246     printf("%s(%s) %5lX %15s %15s %15s %s\n",
1247     ((*found_sym == 0) ? "\n\t" : "\t"),
1248     elf->base_filename,
1249     size,
1250     get_elfstttype(stt),
1251     get_elfstbtype(stb),
1252     get_elfshntype(shn),
1253     symname);
1254     goto matched;
1255    
1256     } else {
1257     if (g_match) {
1258     /* regex match the symbol */
1259     if (rematch(this_sym, symname, REG_EXTENDED) != 0)
1260     continue;
1261    
1262     } else if (*this_sym) {
1263     /* give empty symbols a "pass", else do a normal compare */
1264     const size_t len = strlen(this_sym);
1265     if (!(strncmp(this_sym, symname, len) == 0 &&
1266     /* Accept unversioned symbol names */
1267     (symname[len] == '\0' || symname[len] == '@')))
1268     continue;
1269     }
1270    
1271     if (be_semi_verbose) {
1272     char buf[1024];
1273     snprintf(buf, sizeof(buf), "%lX %s %s",
1274     size,
1275     get_elfstttype(stt),
1276     this_sym);
1277     *ret = xstrdup(buf);
1278     } else {
1279     if (*ret) xchrcat(ret, ',', ret_len);
1280     xstrcat(ret, symname, ret_len);
1281     }
1282    
1283     goto matched;
1284     }
1285     } while (next_sym);
1286    
1287     return;
1288    
1289     matched:
1290     *found_sym = 1;
1291     if (next_sym)
1292     next_sym[-1] = saved;
1293 flameeyes 1.197 }
1294    
1295 vapier 1.225 static const char *scanelf_file_sym(elfobj *elf, char *found_sym)
1296 vapier 1.39 {
1297 vapier 1.95 char *ret;
1298 vapier 1.39 void *symtab_void, *strtab_void;
1299 vapier 1.38
1300 vapier 1.41 if (!find_sym) return NULL;
1301 vapier 1.214 ret = NULL;
1302 vapier 1.32
1303 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
1304 vapier 1.27
1305 vapier 1.39 if (symtab_void && strtab_void) {
1306 vapier 1.27 #define FIND_SYM(B) \
1307     if (elf->elf_class == ELFCLASS ## B) { \
1308     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
1309     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
1310 vapier 1.217 Elf ## B ## _Sym *sym = SYM ## B (elf->vdata + EGET(symtab->sh_offset)); \
1311 vapier 1.234 Elf ## B ## _Word i, cnt = EGET(symtab->sh_entsize); \
1312 vapier 1.27 char *symname; \
1313 vapier 1.214 size_t ret_len = 0; \
1314 vapier 1.115 if (cnt) \
1315     cnt = EGET(symtab->sh_size) / cnt; \
1316 vapier 1.27 for (i = 0; i < cnt; ++i) { \
1317 vapier 1.217 if ((void*)sym > elf->data_end) { \
1318 flameeyes 1.195 warnf("%s: corrupt ELF symbols - aborting", elf->filename); \
1319     goto break_out; \
1320     } \
1321 vapier 1.27 if (sym->st_name) { \
1322 vapier 1.134 /* make sure the symbol name is in acceptable memory range */ \
1323 vapier 1.217 symname = elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name); \
1324     if ((void*)symname > elf->data_end) { \
1325 vapier 1.115 warnf("%s: corrupt ELF symbols", elf->filename); \
1326 vapier 1.134 ++sym; \
1327 vapier 1.111 continue; \
1328     } \
1329 vapier 1.215 scanelf_match_symname(elf, found_sym, \
1330     &ret, &ret_len, symname, \
1331     ELF##B##_ST_TYPE(EGET(sym->st_info)), \
1332     ELF##B##_ST_BIND(EGET(sym->st_info)), \
1333     EGET(sym->st_shndx), \
1334     /* st_size can be 64bit, but no one is really that big, so screw em */ \
1335     EGET(sym->st_size)); \
1336 vapier 1.27 } \
1337     ++sym; \
1338 vapier 1.234 } \
1339     }
1340 vapier 1.27 FIND_SYM(32)
1341     FIND_SYM(64)
1342 vapier 1.39 }
1343 vapier 1.70
1344 vapier 1.95 break_out:
1345 solar 1.127 if (be_wewy_wewy_quiet) return NULL;
1346 vapier 1.70
1347 vapier 1.41 if (*find_sym != '*' && *found_sym)
1348 vapier 1.95 return ret;
1349 vapier 1.41 if (be_quiet)
1350     return NULL;
1351     else
1352 vapier 1.225 return " - ";
1353 vapier 1.39 }
1354 solar 1.119
1355 vapier 1.225 static const char *scanelf_file_sections(elfobj *elf, char *found_section)
1356 solar 1.124 {
1357     if (!find_section)
1358     return NULL;
1359    
1360     #define FIND_SECTION(B) \
1361     if (elf->elf_class == ELFCLASS ## B) { \
1362 vapier 1.226 size_t matched, n; \
1363 solar 1.136 int invert; \
1364 vapier 1.226 const char *section_name; \
1365 solar 1.124 Elf ## B ## _Shdr *section; \
1366 vapier 1.226 \
1367     matched = 0; \
1368     array_for_each(find_section_arr, n, section_name) { \
1369     invert = (*section_name == '!' ? 1 : 0); \
1370     section = SHDR ## B (elf_findsecbyname(elf, section_name + invert)); \
1371     if ((section == NULL && invert) || (section != NULL && !invert)) \
1372     ++matched; \
1373     } \
1374     \
1375     if (matched == array_cnt(find_section_arr)) \
1376 solar 1.124 *found_section = 1; \
1377     }
1378     FIND_SECTION(32)
1379     FIND_SECTION(64)
1380    
1381 vapier 1.138 if (be_wewy_wewy_quiet)
1382     return NULL;
1383 solar 1.124
1384     if (*found_section)
1385     return find_section;
1386    
1387     if (be_quiet)
1388     return NULL;
1389     else
1390 vapier 1.225 return " - ";
1391 solar 1.124 }
1392    
1393 vapier 1.39 /* scan an elf file and show all the fun stuff */
1394 vapier 1.210 #define prints(str) ({ ssize_t ret = write(fileno(stdout), str, strlen(str)); ret; })
1395 vapier 1.106 static int scanelf_elfobj(elfobj *elf)
1396 vapier 1.39 {
1397 vapier 1.44 unsigned long i;
1398 vapier 1.162 char found_pax, found_phdr, found_relro, found_load, found_textrel,
1399     found_rpath, found_needed, found_interp, found_bind, found_soname,
1400 solar 1.124 found_sym, found_lib, found_file, found_textrels, found_section;
1401 vapier 1.41 static char *out_buffer = NULL;
1402     static size_t out_len;
1403 vapier 1.39
1404 vapier 1.76 found_pax = found_phdr = found_relro = found_load = found_textrel = \
1405 vapier 1.84 found_rpath = found_needed = found_interp = found_bind = found_soname = \
1406 solar 1.124 found_sym = found_lib = found_file = found_textrels = found_section = 0;
1407 vapier 1.39
1408 vapier 1.114 if (be_verbose > 2)
1409 vapier 1.106 printf("%s: scanning file {%s,%s}\n", elf->filename,
1410 vapier 1.69 get_elfeitype(EI_CLASS, elf->elf_class),
1411     get_elfeitype(EI_DATA, elf->data[EI_DATA]));
1412 vapier 1.114 else if (be_verbose > 1)
1413 vapier 1.106 printf("%s: scanning file\n", elf->filename);
1414 vapier 1.39
1415 vapier 1.41 /* init output buffer */
1416     if (!out_buffer) {
1417     out_len = sizeof(char) * 80;
1418 vapier 1.186 out_buffer = xmalloc(out_len);
1419 vapier 1.41 }
1420     *out_buffer = '\0';
1421    
1422 vapier 1.39 /* show the header */
1423     if (!be_quiet && show_banner) {
1424 vapier 1.49 for (i = 0; out_format[i]; ++i) {
1425 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
1426 vapier 1.41
1427     switch (out_format[++i]) {
1428 solar 1.132 case '+': break;
1429 vapier 1.41 case '%': break;
1430 vapier 1.70 case '#': break;
1431 vapier 1.66 case 'F':
1432     case 'p':
1433     case 'f': prints("FILE "); found_file = 1; break;
1434 vapier 1.41 case 'o': prints(" TYPE "); break;
1435     case 'x': prints(" PAX "); break;
1436 vapier 1.71 case 'e': prints("STK/REL/PTL "); break;
1437 vapier 1.41 case 't': prints("TEXTREL "); break;
1438     case 'r': prints("RPATH "); break;
1439 vapier 1.171 case 'M': prints("CLASS "); break;
1440 vapier 1.240 case 'l':
1441 vapier 1.41 case 'n': prints("NEEDED "); break;
1442     case 'i': prints("INTERP "); break;
1443 vapier 1.49 case 'b': prints("BIND "); break;
1444 solar 1.190 case 'Z': prints("SIZE "); break;
1445 vapier 1.84 case 'S': prints("SONAME "); break;
1446 vapier 1.41 case 's': prints("SYM "); break;
1447 vapier 1.72 case 'N': prints("LIB "); break;
1448 vapier 1.76 case 'T': prints("TEXTRELS "); break;
1449 vapier 1.126 case 'k': prints("SECTION "); break;
1450 vapier 1.166 case 'a': prints("ARCH "); break;
1451 solar 1.191 case 'I': prints("OSABI "); break;
1452     case 'Y': prints("EABI "); break;
1453 solar 1.180 case 'O': prints("PERM "); break;
1454 solar 1.181 case 'D': prints("ENDIAN "); break;
1455 vapier 1.76 default: warnf("'%c' has no title ?", out_format[i]);
1456 vapier 1.39 }
1457 vapier 1.27 }
1458 vapier 1.49 if (!found_file) prints("FILE ");
1459 vapier 1.41 prints("\n");
1460 vapier 1.49 found_file = 0;
1461 vapier 1.39 show_banner = 0;
1462     }
1463    
1464     /* dump all the good stuff */
1465 vapier 1.49 for (i = 0; out_format[i]; ++i) {
1466 vapier 1.41 const char *out;
1467 vapier 1.66 const char *tmp;
1468 solar 1.190 static char ubuf[sizeof(unsigned long)*2];
1469 vapier 1.70 if (!IS_MODIFIER(out_format[i])) {
1470 vapier 1.41 xchrcat(&out_buffer, out_format[i], &out_len);
1471     continue;
1472     }
1473 vapier 1.39
1474 vapier 1.41 out = NULL;
1475 solar 1.127 be_wewy_wewy_quiet = (out_format[i] == '#');
1476 solar 1.132 be_semi_verbose = (out_format[i] == '+');
1477 vapier 1.41 switch (out_format[++i]) {
1478 solar 1.132 case '+':
1479 vapier 1.70 case '%':
1480     case '#':
1481     xchrcat(&out_buffer, out_format[i], &out_len); break;
1482     case 'F':
1483 vapier 1.76 found_file = 1;
1484 solar 1.127 if (be_wewy_wewy_quiet) break;
1485 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1486 vapier 1.70 break;
1487 vapier 1.66 case 'p':
1488 vapier 1.76 found_file = 1;
1489 solar 1.127 if (be_wewy_wewy_quiet) break;
1490 vapier 1.106 tmp = elf->filename;
1491 vapier 1.66 if (search_path) {
1492     ssize_t len_search = strlen(search_path);
1493 vapier 1.106 ssize_t len_file = strlen(elf->filename);
1494     if (!strncmp(elf->filename, search_path, len_search) && \
1495 vapier 1.66 len_file > len_search)
1496     tmp += len_search;
1497     if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
1498     }
1499     xstrcat(&out_buffer, tmp, &out_len);
1500     break;
1501     case 'f':
1502 vapier 1.76 found_file = 1;
1503 solar 1.127 if (be_wewy_wewy_quiet) break;
1504 vapier 1.106 tmp = strrchr(elf->filename, '/');
1505     tmp = (tmp == NULL ? elf->filename : tmp+1);
1506 vapier 1.66 xstrcat(&out_buffer, tmp, &out_len);
1507     break;
1508 vapier 1.41 case 'o': out = get_elfetype(elf); break;
1509     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
1510 solar 1.73 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
1511 vapier 1.41 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1512 vapier 1.79 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1513 vapier 1.41 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1514 vapier 1.171 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break;
1515 solar 1.181 case 'D': out = get_endian(elf); break;
1516 vapier 1.206 case 'O': out = strfileperms(elf->filename); break;
1517 vapier 1.72 case 'n':
1518     case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1519 vapier 1.41 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1520 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1521 vapier 1.84 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1522 vapier 1.72 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1523 solar 1.124 case 'k': out = scanelf_file_sections(elf, &found_section); break;
1524 vapier 1.166 case 'a': out = get_elfemtype(elf); break;
1525 solar 1.191 case 'I': out = get_elfosabi(elf); break;
1526     case 'Y': out = get_elf_eabi(elf); break;
1527 vapier 1.193 case 'Z': snprintf(ubuf, sizeof(ubuf), "%lu", (unsigned long)elf->len); out = ubuf; break;;
1528 vapier 1.76 default: warnf("'%c' has no scan code?", out_format[i]);
1529 vapier 1.29 }
1530 vapier 1.214 if (out)
1531     xstrcat(&out_buffer, out, &out_len);
1532 vapier 1.39 }
1533    
1534 vapier 1.54 #define FOUND_SOMETHING() \
1535 solar 1.73 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
1536 vapier 1.76 found_rpath || found_needed || found_interp || found_bind || \
1537 solar 1.124 found_soname || found_sym || found_lib || found_textrels || found_section )
1538 vapier 1.54
1539     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
1540     xchrcat(&out_buffer, ' ', &out_len);
1541 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1542 vapier 1.27 }
1543 vapier 1.79 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
1544 vapier 1.41 puts(out_buffer);
1545 vapier 1.79 fflush(stdout);
1546     }
1547 vapier 1.10
1548 vapier 1.105 return 0;
1549     }
1550    
1551 vapier 1.106 /* scan a single elf */
1552     static int scanelf_elf(const char *filename, int fd, size_t len)
1553     {
1554 solar 1.120 int ret = 1;
1555 vapier 1.106 elfobj *elf;
1556    
1557     /* verify this is real ELF */
1558     if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1559     if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1560 vapier 1.235 return 2;
1561 solar 1.120 }
1562     switch (match_bits) {
1563     case 32:
1564     if (elf->elf_class != ELFCLASS32)
1565     goto label_done;
1566     break;
1567     case 64:
1568     if (elf->elf_class != ELFCLASS64)
1569     goto label_done;
1570     break;
1571     default: break;
1572 vapier 1.106 }
1573 vapier 1.228 if (match_etypes) {
1574 solar 1.119 char sbuf[126];
1575     strncpy(sbuf, match_etypes, sizeof(sbuf));
1576     if (strchr(match_etypes, ',') != NULL) {
1577     char *p;
1578 vapier 1.186 while ((p = strrchr(sbuf, ',')) != NULL) {
1579 solar 1.119 *p = 0;
1580 solar 1.129 if (etype_lookup(p+1) == get_etype(elf))
1581 solar 1.119 goto label_ret;
1582     }
1583     }
1584 solar 1.129 if (etype_lookup(sbuf) != get_etype(elf))
1585 solar 1.119 goto label_done;
1586     }
1587    
1588     label_ret:
1589 vapier 1.106 ret = scanelf_elfobj(elf);
1590 solar 1.119
1591     label_done:
1592 vapier 1.106 unreadelf(elf);
1593     return ret;
1594     }
1595 solar 1.119
1596 vapier 1.105 /* scan an archive of elfs */
1597 vapier 1.106 static int scanelf_archive(const char *filename, int fd, size_t len)
1598 vapier 1.105 {
1599 vapier 1.106 archive_handle *ar;
1600 vapier 1.105 archive_member *m;
1601 vapier 1.106 char *ar_buffer;
1602     elfobj *elf;
1603    
1604     ar = ar_open_fd(filename, fd);
1605     if (ar == NULL)
1606     return 1;
1607    
1608 vapier 1.201 ar_buffer = mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1609 vapier 1.218 while ((m = ar_next(ar)) != NULL) {
1610     off_t cur_pos = lseek(fd, 0, SEEK_CUR);
1611     if (cur_pos == -1)
1612     errp("lseek() failed");
1613     elf = readelf_buffer(m->name, ar_buffer + cur_pos, m->size);
1614 vapier 1.106 if (elf) {
1615     scanelf_elfobj(elf);
1616     unreadelf(elf);
1617     }
1618     }
1619     munmap(ar_buffer, len);
1620    
1621 vapier 1.105 return 0;
1622     }
1623     /* scan a file which may be an elf or an archive or some other magical beast */
1624 vapier 1.232 static int scanelf_fileat(int dir_fd, const char *filename, const struct stat *st_cache)
1625 vapier 1.105 {
1626 vapier 1.161 const struct stat *st = st_cache;
1627     struct stat symlink_st;
1628 vapier 1.106 int fd;
1629 vapier 1.105
1630     /* always handle regular files and handle symlinked files if no -y */
1631 vapier 1.161 if (S_ISLNK(st->st_mode)) {
1632 vapier 1.232 if (!scan_symlink)
1633     return 1;
1634     fstatat(dir_fd, filename, &symlink_st, 0);
1635 vapier 1.161 st = &symlink_st;
1636 vapier 1.105 }
1637 vapier 1.161
1638     if (!S_ISREG(st->st_mode)) {
1639 vapier 1.105 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1640 solar 1.164 return 1;
1641 vapier 1.105 }
1642    
1643 solar 1.180 if (match_perms) {
1644     if ((st->st_mode | match_perms) != st->st_mode)
1645     return 1;
1646     }
1647 vapier 1.232 fd = openat(dir_fd, filename, (fix_elf ? O_RDWR : O_RDONLY) | O_CLOEXEC);
1648     if (fd == -1)
1649 solar 1.164 return 1;
1650 vapier 1.106
1651 vapier 1.235 if (scanelf_elf(filename, fd, st->st_size) == 2) {
1652 vapier 1.105 /* if it isn't an ELF, maybe it's an .a archive */
1653 vapier 1.235 if (scan_archives)
1654     scanelf_archive(filename, fd, st->st_size);
1655    
1656     /*
1657     * unreadelf() implicitly closes its fd, so only close it
1658     * when we are returning it in the non-ELF case
1659     */
1660     close(fd);
1661     }
1662 vapier 1.106
1663 solar 1.164 return 0;
1664 solar 1.6 }
1665    
1666 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
1667 vapier 1.232 static int scanelf_dirat(int dir_fd, const char *path)
1668 solar 1.1 {
1669 vapier 1.10 register DIR *dir;
1670     register struct dirent *dentry;
1671 vapier 1.14 struct stat st_top, st;
1672 vapier 1.232 char buf[__PAX_UTILS_PATH_MAX], *subpath;
1673 vapier 1.32 size_t pathlen = 0, len = 0;
1674 solar 1.164 int ret = 0;
1675 vapier 1.232 int subdir_fd;
1676 solar 1.223
1677 vapier 1.10 /* make sure path exists */
1678 vapier 1.232 if (fstatat(dir_fd, path, &st_top, AT_SYMLINK_NOFOLLOW) == -1) {
1679 vapier 1.39 if (be_verbose > 2) printf("%s: does not exist\n", path);
1680 solar 1.164 return 1;
1681 vapier 1.39 }
1682 solar 1.11
1683 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
1684 vapier 1.232 if (!S_ISDIR(st_top.st_mode))
1685     return scanelf_fileat(dir_fd, path, &st_top);
1686 vapier 1.10
1687     /* now scan the dir looking for fun stuff */
1688 vapier 1.232 subdir_fd = openat(dir_fd, path, O_RDONLY|O_CLOEXEC);
1689     if (subdir_fd == -1)
1690     dir = NULL;
1691     else
1692     dir = fdopendir(subdir_fd);
1693     if (dir == NULL) {
1694     if (subdir_fd != -1)
1695     close(subdir_fd);
1696 vapier 1.237 warnfp("could not opendir(%s)", path);
1697 solar 1.164 return 1;
1698 vapier 1.10 }
1699 vapier 1.114 if (be_verbose > 1) printf("%s: scanning dir\n", path);
1700 solar 1.11
1701 vapier 1.232 subpath = stpcpy(buf, path);
1702     *subpath++ = '/';
1703     pathlen = subpath - buf;
1704 vapier 1.10 while ((dentry = readdir(dir))) {
1705     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
1706     continue;
1707 vapier 1.232
1708     if (fstatat(subdir_fd, dentry->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1)
1709     continue;
1710    
1711     len = strlen(dentry->d_name);
1712     if (len + pathlen + 1 >= sizeof(buf)) {
1713     warnf("Skipping '%s%s': len > sizeof(buf); %zu > %zu\n",
1714     path, dentry->d_name, len + pathlen + 1, sizeof(buf));
1715 vapier 1.32 continue;
1716     }
1717 vapier 1.232 memcpy(subpath, dentry->d_name, len);
1718     subpath[len] = '\0';
1719    
1720     if (S_ISREG(st.st_mode))
1721     ret = scanelf_fileat(dir_fd, buf, &st);
1722     else if (dir_recurse && S_ISDIR(st.st_mode)) {
1723     if (dir_crossmount || (st_top.st_dev == st.st_dev))
1724     ret = scanelf_dirat(dir_fd, buf);
1725 vapier 1.10 }
1726     }
1727     closedir(dir);
1728 vapier 1.232
1729 solar 1.164 return ret;
1730 solar 1.1 }
1731 vapier 1.232 static int scanelf_dir(const char *path)
1732     {
1733     return scanelf_dirat(root_fd, root_rel_path(path));
1734     }
1735 solar 1.1
1736 vapier 1.133 static int scanelf_from_file(const char *filename)
1737 vapier 1.47 {
1738 vapier 1.230 FILE *fp;
1739     char *p, *path;
1740     size_t len;
1741     int ret;
1742 solar 1.45
1743 solar 1.132 if (strcmp(filename, "-") == 0)
1744 solar 1.45 fp = stdin;
1745     else if ((fp = fopen(filename, "r")) == NULL)
1746     return 1;
1747    
1748 vapier 1.230 path = NULL;
1749     len = 0;
1750     ret = 0;
1751     while (getline(&path, &len, fp) != -1) {
1752 solar 1.45 if ((p = strchr(path, '\n')) != NULL)
1753     *p = 0;
1754 vapier 1.66 search_path = path;
1755 solar 1.164 ret = scanelf_dir(path);
1756 solar 1.45 }
1757 vapier 1.230 free(path);
1758    
1759 solar 1.45 if (fp != stdin)
1760     fclose(fp);
1761 vapier 1.230
1762 solar 1.164 return ret;
1763 solar 1.45 }
1764    
1765 solar 1.154 #if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
1766 vapier 1.148
1767 vapier 1.240 static int _load_ld_cache_config(const char *fname)
1768 vapier 1.48 {
1769     FILE *fp = NULL;
1770 vapier 1.230 char *p, *path;
1771     size_t len;
1772 vapier 1.232 int curr_fd = -1;
1773 solar 1.223
1774 vapier 1.232 fp = fopenat_r(root_fd, root_rel_path(fname));
1775     if (fp == NULL)
1776 vapier 1.240 return -1;
1777 vapier 1.48
1778 vapier 1.230 path = NULL;
1779     len = 0;
1780     while (getline(&path, &len, fp) != -1) {
1781 vapier 1.48 if ((p = strrchr(path, '\r')) != NULL)
1782     *p = 0;
1783     if ((p = strchr(path, '\n')) != NULL)
1784     *p = 0;
1785 vapier 1.219
1786 vapier 1.186 /* recursive includes of the same file will make this segfault. */
1787 solar 1.129 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1788 vapier 1.219 glob_t gl;
1789 solar 1.123 size_t x;
1790 vapier 1.232 const char *gpath;
1791 solar 1.123
1792 vapier 1.232 /* re-use existing path buffer ... need to be creative */
1793 solar 1.123 if (path[8] != '/')
1794 vapier 1.232 gpath = memcpy(path + 3, "/etc/", 5);
1795 solar 1.123 else
1796 vapier 1.232 gpath = path + 8;
1797     if (root_fd != AT_FDCWD) {
1798     if (curr_fd == -1) {
1799     curr_fd = open(".", O_RDONLY|O_CLOEXEC);
1800     if (fchdir(root_fd))
1801     errp("unable to change to root dir");
1802     }
1803     gpath = root_rel_path(gpath);
1804     }
1805 solar 1.123
1806 vapier 1.219 if (glob(gpath, 0, NULL, &gl) == 0) {
1807 solar 1.123 for (x = 0; x < gl.gl_pathc; ++x) {
1808     /* try to avoid direct loops */
1809     if (strcmp(gl.gl_pathv[x], fname) == 0)
1810     continue;
1811 vapier 1.240 _load_ld_cache_config(gl.gl_pathv[x]);
1812 solar 1.123 }
1813 vapier 1.219 globfree(&gl);
1814 solar 1.160 }
1815 vapier 1.232
1816     /* failed globs are ignored by glibc */
1817     continue;
1818 solar 1.123 }
1819 vapier 1.219
1820 solar 1.123 if (*path != '/')
1821     continue;
1822 vapier 1.48
1823 vapier 1.233 xarraypush_str(ldpaths, path);
1824 vapier 1.48 }
1825 vapier 1.230 free(path);
1826 vapier 1.48
1827     fclose(fp);
1828 vapier 1.230
1829 vapier 1.232 if (curr_fd != -1) {
1830     if (fchdir(curr_fd))
1831     /* don't care */;
1832     close(curr_fd);
1833     }
1834    
1835 vapier 1.240 return 0;
1836 vapier 1.48 }
1837 flameeyes 1.141
1838 vapier 1.211 #elif defined(__FreeBSD__) || defined(__DragonFly__)
1839 vapier 1.148
1840 vapier 1.240 static int _load_ld_cache_config(const char *fname)
1841 flameeyes 1.141 {
1842     FILE *fp = NULL;
1843     char *b = NULL, *p;
1844     struct elfhints_hdr hdr;
1845 vapier 1.152
1846 vapier 1.232 fp = fopenat_r(root_fd, root_rel_path(fname));
1847     if (fp == NULL)
1848 vapier 1.240 return -1;
1849 flameeyes 1.141
1850 vapier 1.152 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
1851     hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
1852     fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
1853     {
1854 flameeyes 1.141 fclose(fp);
1855 vapier 1.240 return -1;
1856 flameeyes 1.141 }
1857 vapier 1.152
1858 vapier 1.186 b = xmalloc(hdr.dirlistlen + 1);
1859 vapier 1.152 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1860 flameeyes 1.141 fclose(fp);
1861     free(b);
1862 vapier 1.240 return -1;
1863 flameeyes 1.141 }
1864 vapier 1.152
1865     while ((p = strsep(&b, ":"))) {
1866 vapier 1.227 if (*p == '\0')
1867     continue;
1868 vapier 1.233 xarraypush_str(ldpaths, p);
1869 flameeyes 1.141 }
1870 vapier 1.157
1871 flameeyes 1.141 free(b);
1872     fclose(fp);
1873 vapier 1.240 return 0;
1874 flameeyes 1.141 }
1875 vapier 1.148
1876     #else
1877 vapier 1.192 #ifdef __ELF__
1878 vapier 1.148 #warning Cache config support not implemented for your target
1879 vapier 1.192 #endif
1880 vapier 1.240 static int _load_ld_cache_config(const char *fname)
1881 vapier 1.148 {
1882 vapier 1.192 return 0;
1883 vapier 1.148 }
1884 flameeyes 1.141 #endif
1885 vapier 1.48
1886 vapier 1.240 static void load_ld_cache_config(const char *fname)
1887     {
1888     bool scan_l, scan_ul, scan_ull;
1889     size_t n;
1890     const char *ldpath;
1891    
1892     _load_ld_cache_config(fname);
1893    
1894     scan_l = scan_ul = scan_ull = false;
1895     if (array_cnt(ldpaths)) {
1896     array_for_each(ldpaths, n, ldpath) {
1897     if (!scan_l && !strcmp(ldpath, "/lib")) scan_l = true;
1898     if (!scan_ul && !strcmp(ldpath, "/usr/lib")) scan_ul = true;
1899     if (!scan_ull && !strcmp(ldpath, "/usr/local/lib")) scan_ull = true;
1900     }
1901     }
1902    
1903     if (!scan_l) xarraypush_str(ldpaths, "/lib");
1904     if (!scan_ul) xarraypush_str(ldpaths, "/usr/lib");
1905     if (!scan_ull) xarraypush_str(ldpaths, "/usr/local/lib");
1906     }
1907    
1908 vapier 1.10 /* scan /etc/ld.so.conf for paths */
1909 vapier 1.184 static void scanelf_ldpath(void)
1910 vapier 1.10 {
1911 vapier 1.227 size_t n;
1912     const char *ldpath;
1913 vapier 1.17
1914 vapier 1.240 array_for_each(ldpaths, n, ldpath)
1915 vapier 1.227 scanelf_dir(ldpath);
1916 vapier 1.10 }
1917 solar 1.1
1918 vapier 1.10 /* scan env PATH for paths */
1919 vapier 1.184 static void scanelf_envpath(void)
1920 solar 1.1 {
1921 solar 1.34 char *path, *p;
1922 vapier 1.10
1923     path = getenv("PATH");
1924     if (!path)
1925     err("PATH is not set in your env !");
1926 vapier 1.41 path = xstrdup(path);
1927 vapier 1.10
1928     while ((p = strrchr(path, ':')) != NULL) {
1929     scanelf_dir(p + 1);
1930     *p = 0;
1931     }
1932 vapier 1.17
1933 solar 1.34 free(path);
1934 solar 1.1 }
1935    
1936 vapier 1.221 /* usage / invocation handling functions */ /* Free Flags: c d j u w G H J K P Q U W */
1937     #define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:DIYO:ZCBhV"
1938 vapier 1.27 #define a_argument required_argument
1939 vapier 1.10 static struct option const long_opts[] = {
1940     {"path", no_argument, NULL, 'p'},
1941     {"ldpath", no_argument, NULL, 'l'},
1942 vapier 1.240 {"use-ldpath",no_argument, NULL, 129},
1943 solar 1.223 {"root", a_argument, NULL, 128},
1944 vapier 1.10 {"recursive", no_argument, NULL, 'R'},
1945 vapier 1.14 {"mount", no_argument, NULL, 'm'},
1946 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
1947 vapier 1.105 {"archives", no_argument, NULL, 'A'},
1948     {"ldcache", no_argument, NULL, 'L'},
1949 vapier 1.101 {"fix", no_argument, NULL, 'X'},
1950 solar 1.127 {"setpax", a_argument, NULL, 'z'},
1951 vapier 1.10 {"pax", no_argument, NULL, 'x'},
1952 solar 1.16 {"header", no_argument, NULL, 'e'},
1953 vapier 1.10 {"textrel", no_argument, NULL, 't'},
1954     {"rpath", no_argument, NULL, 'r'},
1955 vapier 1.32 {"needed", no_argument, NULL, 'n'},
1956 vapier 1.38 {"interp", no_argument, NULL, 'i'},
1957 vapier 1.49 {"bind", no_argument, NULL, 'b'},
1958 vapier 1.84 {"soname", no_argument, NULL, 'S'},
1959 vapier 1.76 {"symbol", a_argument, NULL, 's'},
1960 solar 1.124 {"section", a_argument, NULL, 'k'},
1961 vapier 1.76 {"lib", a_argument, NULL, 'N'},
1962 solar 1.86 {"gmatch", no_argument, NULL, 'g'},
1963 vapier 1.76 {"textrels", no_argument, NULL, 'T'},
1964 solar 1.119 {"etype", a_argument, NULL, 'E'},
1965 solar 1.120 {"bits", a_argument, NULL, 'M'},
1966 solar 1.181 {"endian", no_argument, NULL, 'D'},
1967 solar 1.191 {"osabi", no_argument, NULL, 'I'},
1968     {"eabi", no_argument, NULL, 'Y'},
1969 solar 1.180 {"perms", a_argument, NULL, 'O'},
1970 solar 1.190 {"size", no_argument, NULL, 'Z'},
1971 vapier 1.10 {"all", no_argument, NULL, 'a'},
1972     {"quiet", no_argument, NULL, 'q'},
1973 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
1974 vapier 1.76 {"format", a_argument, NULL, 'F'},
1975     {"from", a_argument, NULL, 'f'},
1976     {"file", a_argument, NULL, 'o'},
1977 vapier 1.221 {"nocolor", no_argument, NULL, 'C'},
1978 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
1979 vapier 1.10 {"help", no_argument, NULL, 'h'},
1980     {"version", no_argument, NULL, 'V'},
1981     {NULL, no_argument, NULL, 0x0}
1982     };
1983 solar 1.57
1984 vapier 1.222 static const char * const opts_help[] = {
1985 vapier 1.10 "Scan all directories in PATH environment",
1986     "Scan all directories in /etc/ld.so.conf",
1987 vapier 1.240 "Use ld.so.conf to show full path (use with -r/-n)",
1988 solar 1.223 "Root directory (use with -l or -p)",
1989 vapier 1.14 "Scan directories recursively",
1990 vapier 1.37 "Don't recursively cross mount points",
1991 vapier 1.101 "Don't scan symlinks",
1992 vapier 1.105 "Scan archives (.a files)",
1993 vapier 1.239 "Utilize ld.so.cache to show full path (use with -r/-n)",
1994 solar 1.127 "Try and 'fix' bad things (use with -r/-e)",
1995     "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
1996 vapier 1.10 "Print PaX markings",
1997 vapier 1.71 "Print GNU_STACK/PT_LOAD markings",
1998 vapier 1.10 "Print TEXTREL information",
1999     "Print RPATH information",
2000 vapier 1.32 "Print NEEDED information",
2001 vapier 1.38 "Print INTERP information",
2002 vapier 1.49 "Print BIND information",
2003 vapier 1.84 "Print SONAME information",
2004 vapier 1.27 "Find a specified symbol",
2005 solar 1.124 "Find a specified section",
2006 vapier 1.72 "Find a specified library",
2007 vapier 1.226 "Use regex matching rather than string compare (use with -s)",
2008 vapier 1.76 "Locate cause of TEXTREL",
2009 solar 1.129 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
2010 solar 1.120 "Print only ELF files matching numeric bits",
2011 solar 1.181 "Print Endianness",
2012 solar 1.191 "Print OSABI",
2013     "Print EABI (EM_ARM Only)",
2014 solar 1.180 "Print only ELF files matching octal permissions",
2015 solar 1.190 "Print ELF file size",
2016 vapier 1.216 "Print all useful/simple info\n",
2017 vapier 1.14 "Only output 'bad' things",
2018     "Be verbose (can be specified more than once)",
2019 vapier 1.39 "Use specified format for output",
2020 solar 1.45 "Read input stream from a filename",
2021 vapier 1.24 "Write output stream to a filename",
2022 vapier 1.221 "Don't emit color in output",
2023 vapier 1.14 "Don't display the header",
2024 vapier 1.10 "Print this help and exit",
2025     "Print version and exit",
2026     NULL
2027     };
2028    
2029     /* display usage and exit */
2030     static void usage(int status)
2031 solar 1.1 {
2032 vapier 1.44 unsigned long i;
2033 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
2034 vapier 1.105 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
2035 solar 1.35 printf("Options: -[%s]\n", PARSE_FLAGS);
2036 vapier 1.241 for (i = 0; long_opts[i].name; ++i) {
2037     /* first output the short flag if it has one */
2038     if (long_opts[i].val > '~')
2039     printf(" ");
2040     else
2041     printf(" -%c, ", long_opts[i].val);
2042    
2043     /* then the long flag */
2044 vapier 1.27 if (long_opts[i].has_arg == no_argument)
2045 vapier 1.241 printf("--%-14s", long_opts[i].name);
2046 vapier 1.27 else
2047 vapier 1.241 printf("--%-7s <arg> ", long_opts[i].name);
2048    
2049     /* finally the help text */
2050     printf("* %s\n", opts_help[i]);
2051     }
2052 solar 1.45
2053 vapier 1.173 puts("\nFor more information, see the scanelf(1) manpage");
2054 vapier 1.10 exit(status);
2055 solar 1.1 }
2056    
2057     /* parse command line arguments and preform needed actions */
2058 vapier 1.165 #define do_pax_state(option, flag) \
2059     if (islower(option)) { \
2060     flags &= ~PF_##flag; \
2061     flags |= PF_NO##flag; \
2062     } else { \
2063     flags &= ~PF_NO##flag; \
2064     flags |= PF_##flag; \
2065     }
2066 solar 1.164 static int parseargs(int argc, char *argv[])
2067 vapier 1.10 {
2068 vapier 1.48 int i;
2069 vapier 1.133 const char *from_file = NULL;
2070 solar 1.164 int ret = 0;
2071 vapier 1.240 char load_cache_config = 0;
2072 vapier 1.10
2073     opterr = 0;
2074 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
2075     switch (i) {
2076 vapier 1.10
2077 vapier 1.39 case 'V':
2078 vapier 1.80 printf("pax-utils-%s: %s compiled %s\n%s\n"
2079     "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
2080     VERSION, __FILE__, __DATE__, rcsid, argv0);
2081 vapier 1.10 exit(EXIT_SUCCESS);
2082     break;
2083     case 'h': usage(EXIT_SUCCESS); break;
2084 solar 1.45 case 'f':
2085 vapier 1.100 if (from_file) warn("You prob don't want to specify -f twice");
2086     from_file = optarg;
2087 solar 1.45 break;
2088 solar 1.119 case 'E':
2089 vapier 1.228 match_etypes = optarg;
2090 solar 1.119 break;
2091 solar 1.120 case 'M':
2092     match_bits = atoi(optarg);
2093 solar 1.182 if (match_bits == 0) {
2094     if (strcmp(optarg, "ELFCLASS32") == 0)
2095     match_bits = 32;
2096     if (strcmp(optarg, "ELFCLASS64") == 0)
2097     match_bits = 64;
2098     }
2099 solar 1.120 break;
2100 solar 1.180 case 'O':
2101 vapier 1.205 if (sscanf(optarg, "%o", &match_perms) == -1)
2102 solar 1.180 match_bits = 0;
2103     break;
2104 vapier 1.24 case 'o': {
2105 vapier 1.146 if (freopen(optarg, "w", stdout) == NULL)
2106 vapier 1.237 errp("Could not freopen(%s)", optarg);
2107 solar 1.21 break;
2108     }
2109 solar 1.124 case 'k':
2110 vapier 1.233 xarraypush_str(find_section_arr, optarg);
2111 solar 1.124 break;
2112 vapier 1.39 case 's': {
2113 vapier 1.93 if (find_sym) warn("You prob don't want to specify -s twice");
2114     find_sym = optarg;
2115 vapier 1.39 break;
2116     }
2117 vapier 1.226 case 'N':
2118 vapier 1.233 xarraypush_str(find_lib_arr, optarg);
2119 vapier 1.72 break;
2120 vapier 1.39 case 'F': {
2121 vapier 1.93 if (out_format) warn("You prob don't want to specify -F twice");
2122     out_format = optarg;
2123 vapier 1.39 break;
2124     }
2125 solar 1.127 case 'z': {
2126 solar 1.129 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
2127 solar 1.127 size_t x;
2128 vapier 1.27
2129 vapier 1.186 for (x = 0; x < strlen(optarg); x++) {
2130     switch (optarg[x]) {
2131 solar 1.127 case 'p':
2132     case 'P':
2133 vapier 1.165 do_pax_state(optarg[x], PAGEEXEC);
2134 solar 1.127 break;
2135     case 's':
2136     case 'S':
2137 vapier 1.165 do_pax_state(optarg[x], SEGMEXEC);
2138 solar 1.127 break;
2139     case 'm':
2140     case 'M':
2141 vapier 1.165 do_pax_state(optarg[x], MPROTECT);
2142 solar 1.127 break;
2143     case 'e':
2144     case 'E':
2145 vapier 1.165 do_pax_state(optarg[x], EMUTRAMP);
2146 solar 1.127 break;
2147     case 'r':
2148     case 'R':
2149 vapier 1.165 do_pax_state(optarg[x], RANDMMAP);
2150 solar 1.127 break;
2151     case 'x':
2152     case 'X':
2153 vapier 1.165 do_pax_state(optarg[x], RANDEXEC);
2154 solar 1.127 break;
2155     default:
2156     break;
2157     }
2158     }
2159     if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
2160     ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
2161     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
2162     ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
2163     ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
2164     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
2165     setpax = flags;
2166     break;
2167     }
2168 solar 1.190 case 'Z': show_size = 1; break;
2169 solar 1.176 case 'g': g_match = 1; break;
2170 vapier 1.240 case 'L': load_cache_config = use_ldcache = 1; break;
2171 vapier 1.37 case 'y': scan_symlink = 0; break;
2172 vapier 1.105 case 'A': scan_archives = 1; break;
2173 vapier 1.221 case 'C': color_init(true); break;
2174 solar 1.16 case 'B': show_banner = 0; break;
2175 vapier 1.240 case 'l': load_cache_config = scan_ldpath = 1; break;
2176 vapier 1.10 case 'p': scan_envpath = 1; break;
2177     case 'R': dir_recurse = 1; break;
2178 vapier 1.14 case 'm': dir_crossmount = 0; break;
2179 vapier 1.101 case 'X': ++fix_elf; break;
2180 vapier 1.10 case 'x': show_pax = 1; break;
2181 solar 1.73 case 'e': show_phdr = 1; break;
2182 vapier 1.10 case 't': show_textrel = 1; break;
2183     case 'r': show_rpath = 1; break;
2184 vapier 1.32 case 'n': show_needed = 1; break;
2185 vapier 1.38 case 'i': show_interp = 1; break;
2186 vapier 1.49 case 'b': show_bind = 1; break;
2187 vapier 1.84 case 'S': show_soname = 1; break;
2188 vapier 1.76 case 'T': show_textrels = 1; break;
2189 vapier 1.10 case 'q': be_quiet = 1; break;
2190 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
2191 solar 1.183 case 'a': show_perms = show_pax = show_phdr = show_textrel = show_rpath = show_bind = show_endian = 1; break;
2192 solar 1.181 case 'D': show_endian = 1; break;
2193 solar 1.191 case 'I': show_osabi = 1; break;
2194     case 'Y': show_eabi = 1; break;
2195 solar 1.223 case 128:
2196 vapier 1.232 root_fd = open(optarg, O_RDONLY|O_CLOEXEC);
2197     if (root_fd == -1)
2198     err("Could not open root: %s", optarg);
2199 solar 1.223 break;
2200 vapier 1.240 case 129: load_cache_config = use_ldpath = 1; break;
2201 vapier 1.10 case ':':
2202 vapier 1.113 err("Option '%c' is missing parameter", optopt);
2203 vapier 1.10 case '?':
2204 solar 1.119 err("Unknown option '%c' or argument missing", optopt);
2205 vapier 1.10 default:
2206 vapier 1.113 err("Unhandled option '%c'; please report this", i);
2207 vapier 1.10 }
2208     }
2209 vapier 1.230 if (show_textrels && be_verbose)
2210     has_objdump = bin_in_path("objdump");
2211 vapier 1.226 /* flatten arrays for display */
2212     if (array_cnt(find_lib_arr))
2213     find_lib = array_flatten_str(find_lib_arr);
2214     if (array_cnt(find_section_arr))
2215     find_section = array_flatten_str(find_section_arr);
2216 vapier 1.39 /* let the format option override all other options */
2217     if (out_format) {
2218 solar 1.73 show_pax = show_phdr = show_textrel = show_rpath = \
2219 vapier 1.84 show_needed = show_interp = show_bind = show_soname = \
2220 solar 1.191 show_textrels = show_perms = show_endian = show_size = \
2221     show_osabi = show_eabi = 0;
2222 vapier 1.48 for (i = 0; out_format[i]; ++i) {
2223 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
2224 vapier 1.39
2225 vapier 1.48 switch (out_format[++i]) {
2226 solar 1.132 case '+': break;
2227 vapier 1.39 case '%': break;
2228 vapier 1.70 case '#': break;
2229 vapier 1.39 case 'F': break;
2230 vapier 1.66 case 'p': break;
2231     case 'f': break;
2232 solar 1.124 case 'k': break;
2233 vapier 1.39 case 's': break;
2234 vapier 1.72 case 'N': break;
2235 vapier 1.41 case 'o': break;
2236 vapier 1.166 case 'a': break;
2237 vapier 1.171 case 'M': break;
2238 solar 1.190 case 'Z': show_size = 1; break;
2239 solar 1.181 case 'D': show_endian = 1; break;
2240 solar 1.191 case 'I': show_osabi = 1; break;
2241     case 'Y': show_eabi = 1; break;
2242 solar 1.180 case 'O': show_perms = 1; break;
2243 vapier 1.39 case 'x': show_pax = 1; break;
2244 solar 1.73 case 'e': show_phdr = 1; break;
2245 vapier 1.39 case 't': show_textrel = 1; break;
2246     case 'r': show_rpath = 1; break;
2247     case 'n': show_needed = 1; break;
2248     case 'i': show_interp = 1; break;
2249 vapier 1.49 case 'b': show_bind = 1; break;
2250 vapier 1.84 case 'S': show_soname = 1; break;
2251 vapier 1.76 case 'T': show_textrels = 1; break;
2252 vapier 1.39 default:
2253 vapier 1.240 err("invalid format specifier '%c' (byte %i)",
2254 vapier 1.48 out_format[i], i+1);
2255 vapier 1.39 }
2256     }
2257 vapier 1.41
2258     /* construct our default format */
2259     } else {
2260     size_t fmt_len = 30;
2261 vapier 1.186 out_format = xmalloc(sizeof(char) * fmt_len);
2262 vapier 1.194 *out_format = '\0';
2263 vapier 1.76 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
2264     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
2265 solar 1.180 if (show_perms) xstrcat(&out_format, "%O ", &fmt_len);
2266 solar 1.190 if (show_size) xstrcat(&out_format, "%Z ", &fmt_len);
2267 solar 1.181 if (show_endian) xstrcat(&out_format, "%D ", &fmt_len);
2268 solar 1.191 if (show_osabi) xstrcat(&out_format, "%I ", &fmt_len);
2269     if (show_eabi) xstrcat(&out_format, "%Y ", &fmt_len);
2270 vapier 1.76 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
2271     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
2272     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
2273     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
2274     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
2275     if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
2276 vapier 1.84 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
2277 vapier 1.76 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
2278     if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
2279 solar 1.124 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
2280 vapier 1.76 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
2281     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
2282 vapier 1.39 }
2283 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
2284 vapier 1.39
2285     /* now lets actually do the scanning */
2286 vapier 1.240 if (load_cache_config)
2287     load_ld_cache_config(__PAX_UTILS_DEFAULT_LD_CACHE_CONFIG);
2288 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
2289     if (scan_envpath) scanelf_envpath();
2290 solar 1.153 if (!from_file && optind == argc && ttyname(0) == NULL && !scan_ldpath && !scan_envpath)
2291 vapier 1.133 from_file = "-";
2292 solar 1.45 if (from_file) {
2293     scanelf_from_file(from_file);
2294     from_file = *argv;
2295     }
2296     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
2297 vapier 1.25 err("Nothing to scan !?");
2298 vapier 1.66 while (optind < argc) {
2299     search_path = argv[optind++];
2300 solar 1.164 ret = scanelf_dir(search_path);
2301 vapier 1.66 }
2302 vapier 1.27
2303 vapier 1.39 /* clean up */
2304 vapier 1.227 xarrayfree(ldpaths);
2305     xarrayfree(find_lib_arr);
2306     xarrayfree(find_section_arr);
2307 vapier 1.226 free(find_lib);
2308     free(find_section);
2309 solar 1.96
2310     if (ldcache != 0)
2311     munmap(ldcache, ldcache_size);
2312 solar 1.164 return ret;
2313 vapier 1.10 }
2314    
2315 vapier 1.150 static char **get_split_env(const char *envvar)
2316     {
2317 vapier 1.152 const char *delims = " \t\n";
2318 solar 1.143 char **envvals = NULL;
2319 vapier 1.152 char *env, *s;
2320 kevquinn 1.142 int nentry;
2321    
2322 solar 1.143 if ((env = getenv(envvar)) == NULL)
2323     return NULL;
2324 kevquinn 1.142
2325 solar 1.143 env = xstrdup(env);
2326     if (env == NULL)
2327     return NULL;
2328 kevquinn 1.142
2329 vapier 1.152 s = strtok(env, delims);
2330     if (s == NULL) {
2331     free(env);
2332     return NULL;
2333     }
2334    
2335 solar 1.143 nentry = 0;
2336 vapier 1.152 while (s != NULL) {
2337     ++nentry;
2338     envvals = xrealloc(envvals, sizeof(*envvals) * (nentry+1));
2339     envvals[nentry-1] = s;
2340     s = strtok(NULL, delims);
2341 kevquinn 1.142 }
2342 vapier 1.152 envvals[nentry] = NULL;
2343 kevquinn 1.142
2344 vapier 1.152 /* don't want to free(env) as it contains the memory that backs
2345     * the envvals array of strings */
2346 kevquinn 1.142 return envvals;
2347     }
2348 solar 1.181
2349 vapier 1.184 static void parseenv(void)
2350 vapier 1.150 {
2351 vapier 1.221 color_init(false);
2352 vapier 1.150 qa_textrels = get_split_env("QA_TEXTRELS");
2353     qa_execstack = get_split_env("QA_EXECSTACK");
2354     qa_wx_load = get_split_env("QA_WX_LOAD");
2355 vapier 1.41 }
2356 solar 1.163
2357 vapier 1.152 #ifdef __PAX_UTILS_CLEANUP
2358 vapier 1.184 static void cleanup(void)
2359 vapier 1.152 {
2360     free(out_format);
2361     free(qa_textrels);
2362     free(qa_execstack);
2363     free(qa_wx_load);
2364     }
2365     #endif
2366 vapier 1.41
2367 vapier 1.10 int main(int argc, char *argv[])
2368 solar 1.1 {
2369 solar 1.164 int ret;
2370 vapier 1.10 if (argc < 2)
2371     usage(EXIT_FAILURE);
2372 kevquinn 1.142 parseenv();
2373 solar 1.164 ret = parseargs(argc, argv);
2374 solar 1.21 fclose(stdout);
2375 vapier 1.152 #ifdef __PAX_UTILS_CLEANUP
2376     cleanup();
2377     warn("The calls to add/delete heap should be off:\n"
2378 vapier 1.232 "\t- 1 due to the out_buffer not being freed in scanelf_fileat()\n"
2379 vapier 1.152 "\t- 1 per QA_TEXTRELS/QA_EXECSTACK/QA_WX_LOAD");
2380 solar 1.61 #endif
2381 solar 1.164 return ret;
2382 solar 1.1 }
2383 vapier 1.155
2384     /* Match filename against entries in matchlist, return TRUE
2385     * if the file is listed */
2386     static int file_matches_list(const char *filename, char **matchlist)
2387     {
2388     char **file;
2389     char *match;
2390     char buf[__PAX_UTILS_PATH_MAX];
2391    
2392     if (matchlist == NULL)
2393     return 0;
2394    
2395     for (file = matchlist; *file != NULL; file++) {
2396     if (search_path) {
2397     snprintf(buf, sizeof(buf), "%s%s", search_path, *file);
2398     match = buf;
2399     } else {
2400     match = *file;
2401     }
2402     if (fnmatch(match, filename, 0) == 0)
2403     return 1;
2404     }
2405     return 0;
2406     }

  ViewVC Help
Powered by ViewVC 1.1.20