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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.247 - (hide annotations) (download) (as text)
Sun Nov 4 07:26:24 2012 UTC (17 months, 1 week ago) by vapier
Branch: MAIN
Changes since 1.246: +5 -5 lines
File MIME type: text/x-csrc
update copyright years

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

  ViewVC Help
Powered by ViewVC 1.1.20