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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.244 - (hide annotations) (download) (as text)
Sun Apr 29 06:21:36 2012 UTC (2 years, 6 months ago) by vapier
Branch: MAIN
Changes since 1.243: +26 -10 lines
File MIME type: text/x-csrc
when looking for symbol sections, ignore ones labeled NOBITS as that means they dont have any actual data (like in split debug files) #411023 by Mark

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

  ViewVC Help
Powered by ViewVC 1.1.20