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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.266 - (hide annotations) (download) (as text)
Wed Jun 18 03:16:52 2014 UTC (5 years, 2 months ago) by vapier
Branch: MAIN
Changes since 1.265: +32 -17 lines
File MIME type: text/x-csrc
add support for symbol visibility

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