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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.273 - (hide annotations) (download) (as text)
Sun Feb 22 01:38:28 2015 UTC (4 years, 6 months ago) by vapier
Branch: MAIN
Changes since 1.272: +5 -3 lines
File MIME type: text/x-csrc
scanelf: add a few more elf->phdr checks

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