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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.268 - (hide annotations) (download) (as text)
Wed Nov 5 02:02:03 2014 UTC (2 years, 11 months ago) by vapier
Branch: MAIN
Changes since 1.267: +4 -2 lines
File MIME type: text/x-csrc
scanelf: make sure to clear the symtab inputs when the section does not match #527490 by Juergen Rose

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