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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.248 - (hide annotations) (download) (as text)
Sun Nov 4 07:48:42 2012 UTC (22 months, 3 weeks ago) by vapier
Branch: MAIN
Changes since 1.247: +17 -23 lines
File MIME type: text/x-csrc
scanelf: convert -s parsing to arrays and allow multiple -s options

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

  ViewVC Help
Powered by ViewVC 1.1.20