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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.267 - (hide annotations) (download) (as text)
Sun Oct 19 07:31:20 2014 UTC (3 years, 2 months ago) by vapier
Branch: MAIN
Changes since 1.266: +23 -11 lines
File MIME type: text/x-csrc
scanelf: do not use debug syms with runtime strings or vice versa

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