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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.274 - (hide annotations) (download) (as text)
Sun Feb 22 02:27:39 2015 UTC (3 years, 4 months ago) by vapier
Branch: MAIN
Changes since 1.273: +38 -24 lines
File MIME type: text/x-csrc
scanelf: rework interp lookup

Prefer PT_INTERP over the section headers since that gets used at runtime.
Add verification to the pointer we do find.

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.274 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.273 2015/02/22 01:38:28 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.274 static const char rcsid[] = "$Id: scanelf.c,v 1.273 2015/02/22 01:38:28 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 vapier 1.274 uint64_t offset = 0;
1073 vapier 1.39
1074 vapier 1.41 if (!show_interp) return NULL;
1075 vapier 1.32
1076 vapier 1.274 if (elf->phdr) {
1077     /* Walk all the program headers to find the PT_INTERP */
1078     #define SHOW_PT_INTERP(B) \
1079     if (elf->elf_class == ELFCLASS ## B) { \
1080     size_t i; \
1081     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
1082     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
1083     for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \
1084     if (EGET(phdr[i].p_type) == PT_INTERP) { \
1085     offset = EGET(phdr[i].p_offset); \
1086     break; \
1087     } \
1088     } \
1089     }
1090     SHOW_PT_INTERP(32)
1091     SHOW_PT_INTERP(64)
1092     } else if (elf->shdr) {
1093     /* Use the section headers to find it */
1094     void *strtbl_void = elf_findsecbyname(elf, ".interp");
1095 vapier 1.38
1096     #define SHOW_INTERP(B) \
1097     if (elf->elf_class == ELFCLASS ## B) { \
1098 solar 1.40 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
1099 vapier 1.274 offset = EGET(strtbl->sh_offset); \
1100     }
1101     if (strtbl_void) {
1102     SHOW_INTERP(32)
1103     SHOW_INTERP(64)
1104 vapier 1.38 }
1105 vapier 1.274 }
1106    
1107     /* Validate the pointer even if we don't use it in output */
1108     if (offset && offset <= (uint64_t)elf->len) {
1109     char *interp = elf->data + offset;
1110    
1111     /* If it isn't a C pointer, it's garbage */
1112     if (memchr(interp, 0, elf->len - offset)) {
1113     *found_interp = 1;
1114     if (!be_wewy_wewy_quiet)
1115     return interp;
1116 vapier 1.251 }
1117 vapier 1.38 }
1118 vapier 1.251
1119 vapier 1.41 return NULL;
1120 vapier 1.39 }
1121 vapier 1.257 static const char *scanelf_file_bind(elfobj *elf, char *found_bind)
1122 vapier 1.49 {
1123     unsigned long i;
1124     struct stat s;
1125 vapier 1.258 bool dynamic = false;
1126 vapier 1.49
1127     if (!show_bind) return NULL;
1128 vapier 1.51 if (!elf->phdr) return NULL;
1129 vapier 1.49
1130     #define SHOW_BIND(B) \
1131     if (elf->elf_class == ELFCLASS ## B) { \
1132     Elf ## B ## _Dyn *dyn; \
1133     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
1134     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
1135     Elf ## B ## _Off offset; \
1136     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
1137 vapier 1.185 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
1138 vapier 1.258 dynamic = true; \
1139 vapier 1.49 offset = EGET(phdr[i].p_offset); \
1140     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
1141 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
1142 vapier 1.49 while (EGET(dyn->d_tag) != DT_NULL) { \
1143     if (EGET(dyn->d_tag) == DT_BIND_NOW || \
1144     (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
1145     { \
1146     if (be_quiet) return NULL; \
1147     *found_bind = 1; \
1148 solar 1.127 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
1149 vapier 1.49 } \
1150     ++dyn; \
1151     } \
1152     } \
1153     }
1154     SHOW_BIND(32)
1155     SHOW_BIND(64)
1156    
1157 solar 1.127 if (be_wewy_wewy_quiet) return NULL;
1158 vapier 1.70
1159 vapier 1.159 /* don't output anything if quiet mode and the ELF is static or not setuid */
1160     if (be_quiet && (!dynamic || (!fstat(elf->fd, &s) && !(s.st_mode & (S_ISUID|S_ISGID))))) {
1161 vapier 1.49 return NULL;
1162     } else {
1163     *found_bind = 1;
1164 vapier 1.257 return dynamic ? "LAZY" : "STATIC";
1165 vapier 1.49 }
1166     }
1167 vapier 1.84 static char *scanelf_file_soname(elfobj *elf, char *found_soname)
1168     {
1169     unsigned long i;
1170     char *soname;
1171     void *strtbl_void;
1172    
1173     if (!show_soname) return NULL;
1174    
1175     strtbl_void = elf_findsecbyname(elf, ".dynstr");
1176    
1177     if (elf->phdr && strtbl_void) {
1178     #define SHOW_SONAME(B) \
1179     if (elf->elf_class == ELFCLASS ## B) { \
1180     Elf ## B ## _Dyn *dyn; \
1181     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
1182     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
1183     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
1184     Elf ## B ## _Off offset; \
1185     /* only look for soname in shared objects */ \
1186 solar 1.188 if (EGET(ehdr->e_type) != ET_DYN) \
1187 vapier 1.84 return NULL; \
1188     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
1189 vapier 1.185 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
1190 vapier 1.84 offset = EGET(phdr[i].p_offset); \
1191     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
1192 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
1193 vapier 1.84 while (EGET(dyn->d_tag) != DT_NULL) { \
1194     if (EGET(dyn->d_tag) == DT_SONAME) { \
1195     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
1196     if (offset >= (Elf ## B ## _Off)elf->len) { \
1197     ++dyn; \
1198     continue; \
1199     } \
1200 vapier 1.217 soname = elf->data + offset; \
1201 vapier 1.84 *found_soname = 1; \
1202 solar 1.127 return (be_wewy_wewy_quiet ? NULL : soname); \
1203 vapier 1.84 } \
1204     ++dyn; \
1205     } \
1206     } }
1207     SHOW_SONAME(32)
1208     SHOW_SONAME(64)
1209     }
1210    
1211     return NULL;
1212     }
1213 solar 1.177
1214 vapier 1.215 /*
1215     * We support the symbol form:
1216     * [%[modifiers]%][[+-]<symbol name>][,[.....]]
1217     * If the symbol name is empty, then all symbols are matched.
1218     * If the symbol name is a glob ("*"), then all symbols are dumped (debug).
1219     * Do not rely on this output format at all.
1220     * Otherwise the symbol name is used to search (either regex or string compare).
1221     * If the first char of the symbol name is a plus ("+"), then only match
1222     * defined symbols. If it's a minus ("-"), only match undefined symbols.
1223     * Putting modifiers in between the percent signs allows for more in depth
1224     * filters. There are groups of modifiers. If you don't specify a member
1225     * of a group, then all types in that group are matched. The current
1226     * groups and their types are:
1227 vapier 1.238 * STT group: STT_NOTYPE:n STT_OBJECT:o STT_FUNC:f STT_FILE:F
1228 vapier 1.215 * STB group: STB_LOCAL:l STB_GLOBAL:g STB_WEAK:w
1229 vapier 1.266 * STV group: STV_DEFAULT:p STV_INTERNAL:i STV_HIDDEN:h STV_PROTECTED:P
1230 vapier 1.215 * SHN group: SHN_UNDEF:u SHN_ABS:a SHN_COMMON:c {defined}:d
1231     * The "defined" value in the SHN group does not correspond to a SHN_xxx define.
1232     * You can search for multiple symbols at once by seperating with a comma (",").
1233     *
1234     * Some examples:
1235     * ELFs with a weak function "foo":
1236     * scanelf -s %wf%foo <ELFs>
1237     * ELFs that define the symbol "main":
1238     * scanelf -s +main <ELFs>
1239     * scanelf -s %d%main <ELFs>
1240     * ELFs that refer to the undefined symbol "brk":
1241     * scanelf -s -brk <ELFs>
1242     * scanelf -s %u%brk <ELFs>
1243     * All global defined objects in an ELF:
1244     * scanelf -s %ogd% <ELF>
1245     */
1246     static void
1247     scanelf_match_symname(elfobj *elf, char *found_sym, char **ret, size_t *ret_len, const char *symname,
1248 vapier 1.266 unsigned int stt, unsigned int stb, unsigned int stv, unsigned int shn, unsigned long size)
1249 vapier 1.215 {
1250 vapier 1.248 const char *this_sym;
1251     size_t n;
1252 vapier 1.215
1253 vapier 1.248 array_for_each(find_sym_arr, n, this_sym) {
1254 vapier 1.215 bool inc_notype, inc_object, inc_func, inc_file,
1255     inc_local, inc_global, inc_weak,
1256 vapier 1.266 inc_visdef, inc_intern, inc_hidden, inc_prot,
1257 vapier 1.215 inc_def, inc_undef, inc_abs, inc_common;
1258    
1259     /* symbol selection! */
1260 vapier 1.266 inc_notype = inc_object = inc_func = inc_file =
1261     inc_local = inc_global = inc_weak =
1262     inc_visdef = inc_intern = inc_hidden = inc_prot =
1263     inc_def = inc_undef = inc_abs = inc_common =
1264 vapier 1.215 (*this_sym != '%');
1265    
1266     /* parse the contents of %...% */
1267     if (!inc_notype) {
1268     while (*(this_sym++)) {
1269     if (*this_sym == '%') {
1270     ++this_sym;
1271     break;
1272     }
1273     switch (*this_sym) {
1274     case 'n': inc_notype = true; break;
1275     case 'o': inc_object = true; break;
1276     case 'f': inc_func = true; break;
1277     case 'F': inc_file = true; break;
1278     case 'l': inc_local = true; break;
1279     case 'g': inc_global = true; break;
1280     case 'w': inc_weak = true; break;
1281 vapier 1.266 case 'p': inc_visdef = true; break;
1282     case 'i': inc_intern = true; break;
1283     case 'h': inc_hidden = true; break;
1284     case 'P': inc_prot = true; break;
1285 vapier 1.215 case 'd': inc_def = true; break;
1286     case 'u': inc_undef = true; break;
1287     case 'a': inc_abs = true; break;
1288     case 'c': inc_common = true; break;
1289     default: err("invalid symbol selector '%c'", *this_sym);
1290     }
1291     }
1292    
1293     /* If no types are matched, not match all */
1294     if (!inc_notype && !inc_object && !inc_func && !inc_file)
1295     inc_notype = inc_object = inc_func = inc_file = true;
1296     if (!inc_local && !inc_global && !inc_weak)
1297     inc_local = inc_global = inc_weak = true;
1298 vapier 1.266 if (!inc_visdef && !inc_intern && !inc_hidden && !inc_prot)
1299     inc_visdef = inc_intern = inc_hidden = inc_prot = true;
1300 vapier 1.215 if (!inc_def && !inc_undef && !inc_abs && !inc_common)
1301     inc_def = inc_undef = inc_abs = inc_common = true;
1302    
1303     /* backwards compat for defined/undefined short hand */
1304     } else if (*this_sym == '+') {
1305     inc_undef = false;
1306     ++this_sym;
1307     } else if (*this_sym == '-') {
1308     inc_def = inc_abs = inc_common = false;
1309     ++this_sym;
1310     }
1311    
1312     /* filter symbols */
1313 vapier 1.266 if ((!inc_notype && stt == STT_NOTYPE ) || \
1314     (!inc_object && stt == STT_OBJECT ) || \
1315     (!inc_func && stt == STT_FUNC ) || \
1316     (!inc_file && stt == STT_FILE ) || \
1317     (!inc_local && stb == STB_LOCAL ) || \
1318     (!inc_global && stb == STB_GLOBAL ) || \
1319     (!inc_weak && stb == STB_WEAK ) || \
1320     (!inc_visdef && stv == STV_DEFAULT ) || \
1321     (!inc_intern && stv == STV_INTERNAL ) || \
1322     (!inc_hidden && stv == STV_HIDDEN ) || \
1323     (!inc_prot && stv == STV_PROTECTED) || \
1324 vapier 1.215 (!inc_def && shn && shn < SHN_LORESERVE) || \
1325 vapier 1.266 (!inc_undef && shn == SHN_UNDEF ) || \
1326     (!inc_abs && shn == SHN_ABS ) || \
1327     (!inc_common && shn == SHN_COMMON ))
1328 vapier 1.215 continue;
1329    
1330     if (*this_sym == '*') {
1331     /* a "*" symbol gets you debug output */
1332 vapier 1.266 printf("%s(%s) %5lX %-15s %-15s %-15s %-15s %s\n",
1333 vapier 1.215 ((*found_sym == 0) ? "\n\t" : "\t"),
1334     elf->base_filename,
1335     size,
1336     get_elfstttype(stt),
1337     get_elfstbtype(stb),
1338 vapier 1.266 get_elfstvtype(stv),
1339 vapier 1.215 get_elfshntype(shn),
1340     symname);
1341     goto matched;
1342    
1343     } else {
1344     if (g_match) {
1345     /* regex match the symbol */
1346 vapier 1.249 if (regexec(find_sym_regex_arr->eles[n], symname, 0, NULL, 0) == REG_NOMATCH)
1347 vapier 1.215 continue;
1348    
1349     } else if (*this_sym) {
1350     /* give empty symbols a "pass", else do a normal compare */
1351     const size_t len = strlen(this_sym);
1352     if (!(strncmp(this_sym, symname, len) == 0 &&
1353     /* Accept unversioned symbol names */
1354     (symname[len] == '\0' || symname[len] == '@')))
1355     continue;
1356     }
1357    
1358     if (be_semi_verbose) {
1359     char buf[1024];
1360     snprintf(buf, sizeof(buf), "%lX %s %s",
1361     size,
1362     get_elfstttype(stt),
1363     this_sym);
1364     *ret = xstrdup(buf);
1365     } else {
1366     if (*ret) xchrcat(ret, ',', ret_len);
1367     xstrcat(ret, symname, ret_len);
1368     }
1369    
1370     goto matched;
1371     }
1372 vapier 1.248 }
1373 vapier 1.215
1374     return;
1375    
1376     matched:
1377     *found_sym = 1;
1378 flameeyes 1.197 }
1379    
1380 vapier 1.225 static const char *scanelf_file_sym(elfobj *elf, char *found_sym)
1381 vapier 1.39 {
1382 vapier 1.95 char *ret;
1383 vapier 1.39 void *symtab_void, *strtab_void;
1384 vapier 1.38
1385 vapier 1.41 if (!find_sym) return NULL;
1386 vapier 1.214 ret = NULL;
1387 vapier 1.32
1388 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
1389 vapier 1.27
1390 vapier 1.39 if (symtab_void && strtab_void) {
1391 vapier 1.27 #define FIND_SYM(B) \
1392     if (elf->elf_class == ELFCLASS ## B) { \
1393     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
1394     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
1395 vapier 1.217 Elf ## B ## _Sym *sym = SYM ## B (elf->vdata + EGET(symtab->sh_offset)); \
1396 vapier 1.234 Elf ## B ## _Word i, cnt = EGET(symtab->sh_entsize); \
1397 vapier 1.27 char *symname; \
1398 vapier 1.214 size_t ret_len = 0; \
1399 vapier 1.115 if (cnt) \
1400     cnt = EGET(symtab->sh_size) / cnt; \
1401 vapier 1.27 for (i = 0; i < cnt; ++i) { \
1402 vapier 1.217 if ((void*)sym > elf->data_end) { \
1403 flameeyes 1.195 warnf("%s: corrupt ELF symbols - aborting", elf->filename); \
1404     goto break_out; \
1405     } \
1406 vapier 1.27 if (sym->st_name) { \
1407 vapier 1.134 /* make sure the symbol name is in acceptable memory range */ \
1408 vapier 1.217 symname = elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name); \
1409     if ((void*)symname > elf->data_end) { \
1410 vapier 1.115 warnf("%s: corrupt ELF symbols", elf->filename); \
1411 vapier 1.134 ++sym; \
1412 vapier 1.111 continue; \
1413     } \
1414 vapier 1.215 scanelf_match_symname(elf, found_sym, \
1415     &ret, &ret_len, symname, \
1416     ELF##B##_ST_TYPE(EGET(sym->st_info)), \
1417     ELF##B##_ST_BIND(EGET(sym->st_info)), \
1418 vapier 1.266 ELF##B##_ST_VISIBILITY(EGET(sym->st_other)), \
1419 vapier 1.215 EGET(sym->st_shndx), \
1420     /* st_size can be 64bit, but no one is really that big, so screw em */ \
1421     EGET(sym->st_size)); \
1422 vapier 1.27 } \
1423     ++sym; \
1424 vapier 1.234 } \
1425     }
1426 vapier 1.27 FIND_SYM(32)
1427     FIND_SYM(64)
1428 vapier 1.39 }
1429 vapier 1.70
1430 vapier 1.95 break_out:
1431 solar 1.127 if (be_wewy_wewy_quiet) return NULL;
1432 vapier 1.70
1433 vapier 1.41 if (*find_sym != '*' && *found_sym)
1434 vapier 1.95 return ret;
1435 vapier 1.41 if (be_quiet)
1436     return NULL;
1437     else
1438 vapier 1.225 return " - ";
1439 vapier 1.39 }
1440 solar 1.119
1441 vapier 1.225 static const char *scanelf_file_sections(elfobj *elf, char *found_section)
1442 solar 1.124 {
1443     if (!find_section)
1444     return NULL;
1445    
1446     #define FIND_SECTION(B) \
1447     if (elf->elf_class == ELFCLASS ## B) { \
1448 vapier 1.226 size_t matched, n; \
1449 solar 1.136 int invert; \
1450 vapier 1.226 const char *section_name; \
1451 solar 1.124 Elf ## B ## _Shdr *section; \
1452 vapier 1.226 \
1453     matched = 0; \
1454     array_for_each(find_section_arr, n, section_name) { \
1455     invert = (*section_name == '!' ? 1 : 0); \
1456     section = SHDR ## B (elf_findsecbyname(elf, section_name + invert)); \
1457     if ((section == NULL && invert) || (section != NULL && !invert)) \
1458     ++matched; \
1459     } \
1460     \
1461     if (matched == array_cnt(find_section_arr)) \
1462 solar 1.124 *found_section = 1; \
1463     }
1464     FIND_SECTION(32)
1465     FIND_SECTION(64)
1466    
1467 vapier 1.138 if (be_wewy_wewy_quiet)
1468     return NULL;
1469 solar 1.124
1470     if (*found_section)
1471     return find_section;
1472    
1473     if (be_quiet)
1474     return NULL;
1475     else
1476 vapier 1.225 return " - ";
1477 solar 1.124 }
1478    
1479 vapier 1.39 /* scan an elf file and show all the fun stuff */
1480 vapier 1.210 #define prints(str) ({ ssize_t ret = write(fileno(stdout), str, strlen(str)); ret; })
1481 vapier 1.106 static int scanelf_elfobj(elfobj *elf)
1482 vapier 1.39 {
1483 vapier 1.44 unsigned long i;
1484 vapier 1.162 char found_pax, found_phdr, found_relro, found_load, found_textrel,
1485     found_rpath, found_needed, found_interp, found_bind, found_soname,
1486 solar 1.124 found_sym, found_lib, found_file, found_textrels, found_section;
1487 vapier 1.41 static char *out_buffer = NULL;
1488     static size_t out_len;
1489 vapier 1.39
1490 vapier 1.76 found_pax = found_phdr = found_relro = found_load = found_textrel = \
1491 vapier 1.84 found_rpath = found_needed = found_interp = found_bind = found_soname = \
1492 solar 1.124 found_sym = found_lib = found_file = found_textrels = found_section = 0;
1493 vapier 1.39
1494 vapier 1.114 if (be_verbose > 2)
1495 vapier 1.106 printf("%s: scanning file {%s,%s}\n", elf->filename,
1496 vapier 1.69 get_elfeitype(EI_CLASS, elf->elf_class),
1497     get_elfeitype(EI_DATA, elf->data[EI_DATA]));
1498 vapier 1.114 else if (be_verbose > 1)
1499 vapier 1.106 printf("%s: scanning file\n", elf->filename);
1500 vapier 1.39
1501 vapier 1.41 /* init output buffer */
1502     if (!out_buffer) {
1503     out_len = sizeof(char) * 80;
1504 vapier 1.186 out_buffer = xmalloc(out_len);
1505 vapier 1.41 }
1506     *out_buffer = '\0';
1507    
1508 vapier 1.39 /* show the header */
1509     if (!be_quiet && show_banner) {
1510 vapier 1.49 for (i = 0; out_format[i]; ++i) {
1511 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
1512 vapier 1.41
1513     switch (out_format[++i]) {
1514 solar 1.132 case '+': break;
1515 vapier 1.41 case '%': break;
1516 vapier 1.70 case '#': break;
1517 vapier 1.66 case 'F':
1518     case 'p':
1519     case 'f': prints("FILE "); found_file = 1; break;
1520 vapier 1.41 case 'o': prints(" TYPE "); break;
1521     case 'x': prints(" PAX "); break;
1522 vapier 1.71 case 'e': prints("STK/REL/PTL "); break;
1523 vapier 1.41 case 't': prints("TEXTREL "); break;
1524     case 'r': prints("RPATH "); break;
1525 vapier 1.171 case 'M': prints("CLASS "); break;
1526 vapier 1.240 case 'l':
1527 vapier 1.41 case 'n': prints("NEEDED "); break;
1528     case 'i': prints("INTERP "); break;
1529 vapier 1.49 case 'b': prints("BIND "); break;
1530 solar 1.190 case 'Z': prints("SIZE "); break;
1531 vapier 1.84 case 'S': prints("SONAME "); break;
1532 vapier 1.41 case 's': prints("SYM "); break;
1533 vapier 1.72 case 'N': prints("LIB "); break;
1534 vapier 1.76 case 'T': prints("TEXTRELS "); break;
1535 vapier 1.126 case 'k': prints("SECTION "); break;
1536 vapier 1.166 case 'a': prints("ARCH "); break;
1537 solar 1.191 case 'I': prints("OSABI "); break;
1538     case 'Y': prints("EABI "); break;
1539 solar 1.180 case 'O': prints("PERM "); break;
1540 solar 1.181 case 'D': prints("ENDIAN "); break;
1541 vapier 1.76 default: warnf("'%c' has no title ?", out_format[i]);
1542 vapier 1.39 }
1543 vapier 1.27 }
1544 vapier 1.49 if (!found_file) prints("FILE ");
1545 vapier 1.41 prints("\n");
1546 vapier 1.49 found_file = 0;
1547 vapier 1.39 show_banner = 0;
1548     }
1549    
1550     /* dump all the good stuff */
1551 vapier 1.49 for (i = 0; out_format[i]; ++i) {
1552 vapier 1.41 const char *out;
1553 vapier 1.66 const char *tmp;
1554 solar 1.190 static char ubuf[sizeof(unsigned long)*2];
1555 vapier 1.70 if (!IS_MODIFIER(out_format[i])) {
1556 vapier 1.41 xchrcat(&out_buffer, out_format[i], &out_len);
1557     continue;
1558     }
1559 vapier 1.39
1560 vapier 1.41 out = NULL;
1561 solar 1.127 be_wewy_wewy_quiet = (out_format[i] == '#');
1562 solar 1.132 be_semi_verbose = (out_format[i] == '+');
1563 vapier 1.41 switch (out_format[++i]) {
1564 solar 1.132 case '+':
1565 vapier 1.70 case '%':
1566     case '#':
1567     xchrcat(&out_buffer, out_format[i], &out_len); break;
1568     case 'F':
1569 vapier 1.76 found_file = 1;
1570 solar 1.127 if (be_wewy_wewy_quiet) break;
1571 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1572 vapier 1.70 break;
1573 vapier 1.66 case 'p':
1574 vapier 1.76 found_file = 1;
1575 solar 1.127 if (be_wewy_wewy_quiet) break;
1576 vapier 1.106 tmp = elf->filename;
1577 vapier 1.66 if (search_path) {
1578     ssize_t len_search = strlen(search_path);
1579 vapier 1.106 ssize_t len_file = strlen(elf->filename);
1580     if (!strncmp(elf->filename, search_path, len_search) && \
1581 vapier 1.66 len_file > len_search)
1582     tmp += len_search;
1583     if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
1584     }
1585     xstrcat(&out_buffer, tmp, &out_len);
1586     break;
1587     case 'f':
1588 vapier 1.76 found_file = 1;
1589 solar 1.127 if (be_wewy_wewy_quiet) break;
1590 vapier 1.106 tmp = strrchr(elf->filename, '/');
1591     tmp = (tmp == NULL ? elf->filename : tmp+1);
1592 vapier 1.66 xstrcat(&out_buffer, tmp, &out_len);
1593     break;
1594 vapier 1.41 case 'o': out = get_elfetype(elf); break;
1595     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
1596 solar 1.73 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
1597 vapier 1.41 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1598 vapier 1.79 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1599 vapier 1.41 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1600 vapier 1.171 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break;
1601 solar 1.181 case 'D': out = get_endian(elf); break;
1602 vapier 1.206 case 'O': out = strfileperms(elf->filename); break;
1603 vapier 1.72 case 'n':
1604     case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1605 vapier 1.41 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1606 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1607 vapier 1.84 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1608 vapier 1.72 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1609 solar 1.124 case 'k': out = scanelf_file_sections(elf, &found_section); break;
1610 vapier 1.166 case 'a': out = get_elfemtype(elf); break;
1611 solar 1.191 case 'I': out = get_elfosabi(elf); break;
1612     case 'Y': out = get_elf_eabi(elf); break;
1613 vapier 1.193 case 'Z': snprintf(ubuf, sizeof(ubuf), "%lu", (unsigned long)elf->len); out = ubuf; break;;
1614 vapier 1.76 default: warnf("'%c' has no scan code?", out_format[i]);
1615 vapier 1.29 }
1616 vapier 1.214 if (out)
1617     xstrcat(&out_buffer, out, &out_len);
1618 vapier 1.39 }
1619    
1620 vapier 1.54 #define FOUND_SOMETHING() \
1621 solar 1.73 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
1622 vapier 1.76 found_rpath || found_needed || found_interp || found_bind || \
1623 solar 1.124 found_soname || found_sym || found_lib || found_textrels || found_section )
1624 vapier 1.54
1625     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
1626     xchrcat(&out_buffer, ' ', &out_len);
1627 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1628 vapier 1.27 }
1629 vapier 1.79 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
1630 vapier 1.41 puts(out_buffer);
1631 vapier 1.79 fflush(stdout);
1632     }
1633 vapier 1.10
1634 vapier 1.105 return 0;
1635     }
1636    
1637 vapier 1.106 /* scan a single elf */
1638     static int scanelf_elf(const char *filename, int fd, size_t len)
1639     {
1640 solar 1.120 int ret = 1;
1641 vapier 1.262 size_t n;
1642     const char *match_etype;
1643 vapier 1.106 elfobj *elf;
1644    
1645 vapier 1.263 /* Verify this is a real ELF */
1646 vapier 1.106 if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1647     if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1648 vapier 1.235 return 2;
1649 solar 1.120 }
1650 vapier 1.263
1651     /* Possibly filter based on ELF bitness */
1652 solar 1.120 switch (match_bits) {
1653 vapier 1.263 case 32:
1654     if (elf->elf_class != ELFCLASS32)
1655     goto done;
1656     break;
1657     case 64:
1658     if (elf->elf_class != ELFCLASS64)
1659     goto done;
1660     break;
1661 vapier 1.106 }
1662 vapier 1.262
1663 vapier 1.263 /* Possibly filter based on the ELF's e_type field */
1664 vapier 1.262 array_for_each(match_etypes, n, match_etype)
1665     if (etype_lookup(match_etype) == get_etype(elf))
1666 vapier 1.263 goto scanit;
1667 vapier 1.262 if (array_cnt(match_etypes))
1668 vapier 1.263 goto done;
1669 solar 1.119
1670 vapier 1.263 scanit:
1671 vapier 1.106 ret = scanelf_elfobj(elf);
1672 solar 1.119
1673 vapier 1.263 done:
1674 vapier 1.106 unreadelf(elf);
1675     return ret;
1676     }
1677 solar 1.119
1678 vapier 1.105 /* scan an archive of elfs */
1679 vapier 1.106 static int scanelf_archive(const char *filename, int fd, size_t len)
1680 vapier 1.105 {
1681 vapier 1.106 archive_handle *ar;
1682 vapier 1.105 archive_member *m;
1683 vapier 1.106 char *ar_buffer;
1684     elfobj *elf;
1685    
1686     ar = ar_open_fd(filename, fd);
1687     if (ar == NULL)
1688     return 1;
1689    
1690 vapier 1.201 ar_buffer = mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1691 vapier 1.218 while ((m = ar_next(ar)) != NULL) {
1692     off_t cur_pos = lseek(fd, 0, SEEK_CUR);
1693     if (cur_pos == -1)
1694     errp("lseek() failed");
1695     elf = readelf_buffer(m->name, ar_buffer + cur_pos, m->size);
1696 vapier 1.106 if (elf) {
1697     scanelf_elfobj(elf);
1698     unreadelf(elf);
1699     }
1700     }
1701     munmap(ar_buffer, len);
1702    
1703 vapier 1.105 return 0;
1704     }
1705     /* scan a file which may be an elf or an archive or some other magical beast */
1706 vapier 1.232 static int scanelf_fileat(int dir_fd, const char *filename, const struct stat *st_cache)
1707 vapier 1.105 {
1708 vapier 1.161 const struct stat *st = st_cache;
1709     struct stat symlink_st;
1710 vapier 1.106 int fd;
1711 vapier 1.105
1712     /* always handle regular files and handle symlinked files if no -y */
1713 vapier 1.161 if (S_ISLNK(st->st_mode)) {
1714 vapier 1.232 if (!scan_symlink)
1715     return 1;
1716     fstatat(dir_fd, filename, &symlink_st, 0);
1717 vapier 1.161 st = &symlink_st;
1718 vapier 1.105 }
1719 vapier 1.161
1720     if (!S_ISREG(st->st_mode)) {
1721 vapier 1.105 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1722 solar 1.164 return 1;
1723 vapier 1.105 }
1724    
1725 solar 1.180 if (match_perms) {
1726     if ((st->st_mode | match_perms) != st->st_mode)
1727     return 1;
1728     }
1729 vapier 1.232 fd = openat(dir_fd, filename, (fix_elf ? O_RDWR : O_RDONLY) | O_CLOEXEC);
1730 vapier 1.245 if (fd == -1) {
1731     if (fix_elf && errno == ETXTBSY)
1732     warnp("%s: could not fix", filename);
1733     else if (be_verbose > 2)
1734     printf("%s: skipping file: %s\n", filename, strerror(errno));
1735 solar 1.164 return 1;
1736 vapier 1.245 }
1737 vapier 1.106
1738 vapier 1.235 if (scanelf_elf(filename, fd, st->st_size) == 2) {
1739 vapier 1.105 /* if it isn't an ELF, maybe it's an .a archive */
1740 vapier 1.235 if (scan_archives)
1741     scanelf_archive(filename, fd, st->st_size);
1742    
1743     /*
1744     * unreadelf() implicitly closes its fd, so only close it
1745     * when we are returning it in the non-ELF case
1746     */
1747     close(fd);
1748     }
1749 vapier 1.106
1750 solar 1.164 return 0;
1751 solar 1.6 }
1752    
1753 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
1754 vapier 1.232 static int scanelf_dirat(int dir_fd, const char *path)
1755 solar 1.1 {
1756 vapier 1.10 register DIR *dir;
1757     register struct dirent *dentry;
1758 vapier 1.14 struct stat st_top, st;
1759 vapier 1.232 char buf[__PAX_UTILS_PATH_MAX], *subpath;
1760 vapier 1.32 size_t pathlen = 0, len = 0;
1761 solar 1.164 int ret = 0;
1762 vapier 1.232 int subdir_fd;
1763 solar 1.223
1764 vapier 1.10 /* make sure path exists */
1765 vapier 1.232 if (fstatat(dir_fd, path, &st_top, AT_SYMLINK_NOFOLLOW) == -1) {
1766 vapier 1.39 if (be_verbose > 2) printf("%s: does not exist\n", path);
1767 solar 1.164 return 1;
1768 vapier 1.39 }
1769 solar 1.11
1770 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
1771 vapier 1.232 if (!S_ISDIR(st_top.st_mode))
1772     return scanelf_fileat(dir_fd, path, &st_top);
1773 vapier 1.10
1774     /* now scan the dir looking for fun stuff */
1775 vapier 1.232 subdir_fd = openat(dir_fd, path, O_RDONLY|O_CLOEXEC);
1776     if (subdir_fd == -1)
1777     dir = NULL;
1778     else
1779     dir = fdopendir(subdir_fd);
1780     if (dir == NULL) {
1781     if (subdir_fd != -1)
1782     close(subdir_fd);
1783 vapier 1.255 else if (be_verbose > 2)
1784     printf("%s: skipping dir: %s\n", path, strerror(errno));
1785 solar 1.164 return 1;
1786 vapier 1.10 }
1787 vapier 1.114 if (be_verbose > 1) printf("%s: scanning dir\n", path);
1788 solar 1.11
1789 vapier 1.232 subpath = stpcpy(buf, path);
1790 vapier 1.243 if (subpath[-1] != '/')
1791     *subpath++ = '/';
1792 vapier 1.232 pathlen = subpath - buf;
1793 vapier 1.10 while ((dentry = readdir(dir))) {
1794     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
1795     continue;
1796 vapier 1.232
1797     if (fstatat(subdir_fd, dentry->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1)
1798     continue;
1799    
1800     len = strlen(dentry->d_name);
1801     if (len + pathlen + 1 >= sizeof(buf)) {
1802     warnf("Skipping '%s%s': len > sizeof(buf); %zu > %zu\n",
1803     path, dentry->d_name, len + pathlen + 1, sizeof(buf));
1804 vapier 1.32 continue;
1805     }
1806 vapier 1.232 memcpy(subpath, dentry->d_name, len);
1807     subpath[len] = '\0';
1808    
1809     if (S_ISREG(st.st_mode))
1810     ret = scanelf_fileat(dir_fd, buf, &st);
1811     else if (dir_recurse && S_ISDIR(st.st_mode)) {
1812     if (dir_crossmount || (st_top.st_dev == st.st_dev))
1813     ret = scanelf_dirat(dir_fd, buf);
1814 vapier 1.10 }
1815     }
1816     closedir(dir);
1817 vapier 1.232
1818 solar 1.164 return ret;
1819 solar 1.1 }
1820 vapier 1.232 static int scanelf_dir(const char *path)
1821     {
1822     return scanelf_dirat(root_fd, root_rel_path(path));
1823     }
1824 solar 1.1
1825 vapier 1.133 static int scanelf_from_file(const char *filename)
1826 vapier 1.47 {
1827 vapier 1.230 FILE *fp;
1828     char *p, *path;
1829     size_t len;
1830     int ret;
1831 solar 1.45
1832 solar 1.132 if (strcmp(filename, "-") == 0)
1833 solar 1.45 fp = stdin;
1834     else if ((fp = fopen(filename, "r")) == NULL)
1835     return 1;
1836    
1837 vapier 1.230 path = NULL;
1838     len = 0;
1839     ret = 0;
1840     while (getline(&path, &len, fp) != -1) {
1841 solar 1.45 if ((p = strchr(path, '\n')) != NULL)
1842     *p = 0;
1843 vapier 1.66 search_path = path;
1844 solar 1.164 ret = scanelf_dir(path);
1845 solar 1.45 }
1846 vapier 1.230 free(path);
1847    
1848 solar 1.45 if (fp != stdin)
1849     fclose(fp);
1850 vapier 1.230
1851 solar 1.164 return ret;
1852 solar 1.45 }
1853    
1854 solar 1.154 #if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
1855 vapier 1.148
1856 vapier 1.240 static int _load_ld_cache_config(const char *fname)
1857 vapier 1.48 {
1858     FILE *fp = NULL;
1859 vapier 1.230 char *p, *path;
1860     size_t len;
1861 vapier 1.232 int curr_fd = -1;
1862 solar 1.223
1863 vapier 1.232 fp = fopenat_r(root_fd, root_rel_path(fname));
1864     if (fp == NULL)
1865 vapier 1.240 return -1;
1866 vapier 1.48
1867 vapier 1.230 path = NULL;
1868     len = 0;
1869     while (getline(&path, &len, fp) != -1) {
1870 vapier 1.48 if ((p = strrchr(path, '\r')) != NULL)
1871     *p = 0;
1872     if ((p = strchr(path, '\n')) != NULL)
1873     *p = 0;
1874 vapier 1.219
1875 vapier 1.186 /* recursive includes of the same file will make this segfault. */
1876 solar 1.129 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1877 vapier 1.219 glob_t gl;
1878 solar 1.123 size_t x;
1879 vapier 1.232 const char *gpath;
1880 solar 1.123
1881 vapier 1.232 /* re-use existing path buffer ... need to be creative */
1882 solar 1.123 if (path[8] != '/')
1883 vapier 1.232 gpath = memcpy(path + 3, "/etc/", 5);
1884 solar 1.123 else
1885 vapier 1.232 gpath = path + 8;
1886     if (root_fd != AT_FDCWD) {
1887     if (curr_fd == -1) {
1888     curr_fd = open(".", O_RDONLY|O_CLOEXEC);
1889     if (fchdir(root_fd))
1890     errp("unable to change to root dir");
1891     }
1892     gpath = root_rel_path(gpath);
1893     }
1894 solar 1.123
1895 vapier 1.219 if (glob(gpath, 0, NULL, &gl) == 0) {
1896 solar 1.123 for (x = 0; x < gl.gl_pathc; ++x) {
1897     /* try to avoid direct loops */
1898     if (strcmp(gl.gl_pathv[x], fname) == 0)
1899     continue;
1900 vapier 1.240 _load_ld_cache_config(gl.gl_pathv[x]);
1901 solar 1.123 }
1902 vapier 1.219 globfree(&gl);
1903 solar 1.160 }
1904 vapier 1.232
1905     /* failed globs are ignored by glibc */
1906     continue;
1907 solar 1.123 }
1908 vapier 1.219
1909 solar 1.123 if (*path != '/')
1910     continue;
1911 vapier 1.48
1912 vapier 1.233 xarraypush_str(ldpaths, path);
1913 vapier 1.48 }
1914 vapier 1.230 free(path);
1915 vapier 1.48
1916     fclose(fp);
1917 vapier 1.230
1918 vapier 1.232 if (curr_fd != -1) {
1919     if (fchdir(curr_fd))
1920 vapier 1.257 {/* don't care */}
1921 vapier 1.232 close(curr_fd);
1922     }
1923    
1924 vapier 1.240 return 0;
1925 vapier 1.48 }
1926 flameeyes 1.141
1927 vapier 1.211 #elif defined(__FreeBSD__) || defined(__DragonFly__)
1928 vapier 1.148
1929 vapier 1.240 static int _load_ld_cache_config(const char *fname)
1930 flameeyes 1.141 {
1931     FILE *fp = NULL;
1932     char *b = NULL, *p;
1933     struct elfhints_hdr hdr;
1934 vapier 1.152
1935 vapier 1.232 fp = fopenat_r(root_fd, root_rel_path(fname));
1936     if (fp == NULL)
1937 vapier 1.240 return -1;
1938 flameeyes 1.141
1939 vapier 1.152 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
1940     hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
1941     fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
1942     {
1943 flameeyes 1.141 fclose(fp);
1944 vapier 1.240 return -1;
1945 flameeyes 1.141 }
1946 vapier 1.152
1947 vapier 1.186 b = xmalloc(hdr.dirlistlen + 1);
1948 vapier 1.152 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1949 flameeyes 1.141 fclose(fp);
1950     free(b);
1951 vapier 1.240 return -1;
1952 flameeyes 1.141 }
1953 vapier 1.152
1954     while ((p = strsep(&b, ":"))) {
1955 vapier 1.227 if (*p == '\0')
1956     continue;
1957 vapier 1.233 xarraypush_str(ldpaths, p);
1958 flameeyes 1.141 }
1959 vapier 1.157
1960 flameeyes 1.141 free(b);
1961     fclose(fp);
1962 vapier 1.240 return 0;
1963 flameeyes 1.141 }
1964 vapier 1.148
1965     #else
1966 vapier 1.192 #ifdef __ELF__
1967 vapier 1.148 #warning Cache config support not implemented for your target
1968 vapier 1.192 #endif
1969 vapier 1.240 static int _load_ld_cache_config(const char *fname)
1970 vapier 1.148 {
1971 vapier 1.192 return 0;
1972 vapier 1.148 }
1973 flameeyes 1.141 #endif
1974 vapier 1.48
1975 vapier 1.240 static void load_ld_cache_config(const char *fname)
1976     {
1977     bool scan_l, scan_ul, scan_ull;
1978     size_t n;
1979     const char *ldpath;
1980    
1981     _load_ld_cache_config(fname);
1982    
1983     scan_l = scan_ul = scan_ull = false;
1984 vapier 1.261 array_for_each(ldpaths, n, ldpath) {
1985     if (!scan_l && !strcmp(ldpath, "/lib")) scan_l = true;
1986     if (!scan_ul && !strcmp(ldpath, "/usr/lib")) scan_ul = true;
1987     if (!scan_ull && !strcmp(ldpath, "/usr/local/lib")) scan_ull = true;
1988 vapier 1.240 }
1989    
1990     if (!scan_l) xarraypush_str(ldpaths, "/lib");
1991     if (!scan_ul) xarraypush_str(ldpaths, "/usr/lib");
1992     if (!scan_ull) xarraypush_str(ldpaths, "/usr/local/lib");
1993     }
1994    
1995 vapier 1.10 /* scan /etc/ld.so.conf for paths */
1996 vapier 1.184 static void scanelf_ldpath(void)
1997 vapier 1.10 {
1998 vapier 1.227 size_t n;
1999     const char *ldpath;
2000 vapier 1.17
2001 vapier 1.240 array_for_each(ldpaths, n, ldpath)
2002 vapier 1.227 scanelf_dir(ldpath);
2003 vapier 1.10 }
2004 solar 1.1
2005 vapier 1.10 /* scan env PATH for paths */
2006 vapier 1.184 static void scanelf_envpath(void)
2007 solar 1.1 {
2008 solar 1.34 char *path, *p;
2009 vapier 1.10
2010     path = getenv("PATH");
2011     if (!path)
2012     err("PATH is not set in your env !");
2013 vapier 1.41 path = xstrdup(path);
2014 vapier 1.10
2015     while ((p = strrchr(path, ':')) != NULL) {
2016     scanelf_dir(p + 1);
2017     *p = 0;
2018     }
2019 vapier 1.17
2020 solar 1.34 free(path);
2021 solar 1.1 }
2022    
2023 vapier 1.221 /* usage / invocation handling functions */ /* Free Flags: c d j u w G H J K P Q U W */
2024     #define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:DIYO:ZCBhV"
2025 vapier 1.27 #define a_argument required_argument
2026 vapier 1.10 static struct option const long_opts[] = {
2027     {"path", no_argument, NULL, 'p'},
2028     {"ldpath", no_argument, NULL, 'l'},
2029 vapier 1.240 {"use-ldpath",no_argument, NULL, 129},
2030 solar 1.223 {"root", a_argument, NULL, 128},
2031 vapier 1.10 {"recursive", no_argument, NULL, 'R'},
2032 vapier 1.14 {"mount", no_argument, NULL, 'm'},
2033 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
2034 vapier 1.105 {"archives", no_argument, NULL, 'A'},
2035     {"ldcache", no_argument, NULL, 'L'},
2036 vapier 1.101 {"fix", no_argument, NULL, 'X'},
2037 solar 1.127 {"setpax", a_argument, NULL, 'z'},
2038 vapier 1.10 {"pax", no_argument, NULL, 'x'},
2039 solar 1.16 {"header", no_argument, NULL, 'e'},
2040 vapier 1.10 {"textrel", no_argument, NULL, 't'},
2041     {"rpath", no_argument, NULL, 'r'},
2042 vapier 1.32 {"needed", no_argument, NULL, 'n'},
2043 vapier 1.38 {"interp", no_argument, NULL, 'i'},
2044 vapier 1.49 {"bind", no_argument, NULL, 'b'},
2045 vapier 1.84 {"soname", no_argument, NULL, 'S'},
2046 vapier 1.76 {"symbol", a_argument, NULL, 's'},
2047 solar 1.124 {"section", a_argument, NULL, 'k'},
2048 vapier 1.76 {"lib", a_argument, NULL, 'N'},
2049 solar 1.86 {"gmatch", no_argument, NULL, 'g'},
2050 vapier 1.76 {"textrels", no_argument, NULL, 'T'},
2051 solar 1.119 {"etype", a_argument, NULL, 'E'},
2052 solar 1.120 {"bits", a_argument, NULL, 'M'},
2053 solar 1.181 {"endian", no_argument, NULL, 'D'},
2054 solar 1.191 {"osabi", no_argument, NULL, 'I'},
2055     {"eabi", no_argument, NULL, 'Y'},
2056 solar 1.180 {"perms", a_argument, NULL, 'O'},
2057 solar 1.190 {"size", no_argument, NULL, 'Z'},
2058 vapier 1.10 {"all", no_argument, NULL, 'a'},
2059     {"quiet", no_argument, NULL, 'q'},
2060 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
2061 vapier 1.76 {"format", a_argument, NULL, 'F'},
2062     {"from", a_argument, NULL, 'f'},
2063     {"file", a_argument, NULL, 'o'},
2064 vapier 1.221 {"nocolor", no_argument, NULL, 'C'},
2065 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
2066 vapier 1.10 {"help", no_argument, NULL, 'h'},
2067     {"version", no_argument, NULL, 'V'},
2068     {NULL, no_argument, NULL, 0x0}
2069     };
2070 solar 1.57
2071 vapier 1.222 static const char * const opts_help[] = {
2072 vapier 1.10 "Scan all directories in PATH environment",
2073     "Scan all directories in /etc/ld.so.conf",
2074 vapier 1.240 "Use ld.so.conf to show full path (use with -r/-n)",
2075 solar 1.223 "Root directory (use with -l or -p)",
2076 vapier 1.14 "Scan directories recursively",
2077 vapier 1.37 "Don't recursively cross mount points",
2078 vapier 1.101 "Don't scan symlinks",
2079 vapier 1.105 "Scan archives (.a files)",
2080 vapier 1.239 "Utilize ld.so.cache to show full path (use with -r/-n)",
2081 solar 1.127 "Try and 'fix' bad things (use with -r/-e)",
2082     "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
2083 vapier 1.10 "Print PaX markings",
2084 vapier 1.71 "Print GNU_STACK/PT_LOAD markings",
2085 vapier 1.10 "Print TEXTREL information",
2086     "Print RPATH information",
2087 vapier 1.32 "Print NEEDED information",
2088 vapier 1.38 "Print INTERP information",
2089 vapier 1.49 "Print BIND information",
2090 vapier 1.84 "Print SONAME information",
2091 vapier 1.27 "Find a specified symbol",
2092 solar 1.124 "Find a specified section",
2093 vapier 1.72 "Find a specified library",
2094 vapier 1.246 "Use regex rather than string compare (with -s); specify twice for case insensitive",
2095 vapier 1.76 "Locate cause of TEXTREL",
2096 solar 1.129 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
2097 solar 1.120 "Print only ELF files matching numeric bits",
2098 solar 1.181 "Print Endianness",
2099 solar 1.191 "Print OSABI",
2100     "Print EABI (EM_ARM Only)",
2101 solar 1.180 "Print only ELF files matching octal permissions",
2102 solar 1.190 "Print ELF file size",
2103 vapier 1.216 "Print all useful/simple info\n",
2104 vapier 1.14 "Only output 'bad' things",
2105     "Be verbose (can be specified more than once)",
2106 vapier 1.39 "Use specified format for output",
2107 solar 1.45 "Read input stream from a filename",
2108 vapier 1.24 "Write output stream to a filename",
2109 vapier 1.221 "Don't emit color in output",
2110 vapier 1.14 "Don't display the header",
2111 vapier 1.10 "Print this help and exit",
2112     "Print version and exit",
2113     NULL
2114     };
2115    
2116     /* display usage and exit */
2117     static void usage(int status)
2118 solar 1.1 {
2119 vapier 1.256 const char a_arg[] = "<arg>";
2120     size_t a_arg_len = strlen(a_arg) + 2;
2121     size_t i;
2122     int optlen;
2123 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
2124 vapier 1.105 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
2125 solar 1.35 printf("Options: -[%s]\n", PARSE_FLAGS);
2126 vapier 1.256
2127     /* prescan the --long opt length to auto-align */
2128     optlen = 0;
2129     for (i = 0; long_opts[i].name; ++i) {
2130     int l = strlen(long_opts[i].name);
2131     if (long_opts[i].has_arg == a_argument)
2132     l += a_arg_len;
2133     optlen = max(l, optlen);
2134     }
2135    
2136 vapier 1.241 for (i = 0; long_opts[i].name; ++i) {
2137     /* first output the short flag if it has one */
2138     if (long_opts[i].val > '~')
2139     printf(" ");
2140     else
2141     printf(" -%c, ", long_opts[i].val);
2142    
2143     /* then the long flag */
2144 vapier 1.27 if (long_opts[i].has_arg == no_argument)
2145 vapier 1.256 printf("--%-*s", optlen, long_opts[i].name);
2146 vapier 1.27 else
2147 vapier 1.256 printf("--%s %s %*s", long_opts[i].name, a_arg,
2148     (int)(optlen - strlen(long_opts[i].name) - a_arg_len), "");
2149 vapier 1.241
2150     /* finally the help text */
2151     printf("* %s\n", opts_help[i]);
2152     }
2153 solar 1.45
2154 vapier 1.173 puts("\nFor more information, see the scanelf(1) manpage");
2155 vapier 1.10 exit(status);
2156 solar 1.1 }
2157    
2158     /* parse command line arguments and preform needed actions */
2159 vapier 1.165 #define do_pax_state(option, flag) \
2160     if (islower(option)) { \
2161     flags &= ~PF_##flag; \
2162     flags |= PF_NO##flag; \
2163     } else { \
2164     flags &= ~PF_NO##flag; \
2165     flags |= PF_##flag; \
2166     }
2167 vapier 1.262 static void parse_delimited(array_t *arr, char *arg, const char *delim)
2168     {
2169     char *ele = strtok(arg, delim);
2170     if (!ele) /* edge case: -s '' */
2171     xarraypush_str(arr, "");
2172     while (ele) {
2173     xarraypush_str(arr, ele);
2174     ele = strtok(NULL, delim);
2175     }
2176     }
2177 solar 1.164 static int parseargs(int argc, char *argv[])
2178 vapier 1.10 {
2179 vapier 1.48 int i;
2180 vapier 1.133 const char *from_file = NULL;
2181 solar 1.164 int ret = 0;
2182 vapier 1.240 char load_cache_config = 0;
2183 vapier 1.10
2184     opterr = 0;
2185 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
2186     switch (i) {
2187 vapier 1.10
2188 vapier 1.39 case 'V':
2189 vapier 1.272 printf("pax-utils-%s: %s\n%s\n"
2190 vapier 1.80 "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
2191 vapier 1.272 VERSION, __FILE__, rcsid, argv0);
2192 vapier 1.10 exit(EXIT_SUCCESS);
2193     break;
2194     case 'h': usage(EXIT_SUCCESS); break;
2195 solar 1.45 case 'f':
2196 vapier 1.100 if (from_file) warn("You prob don't want to specify -f twice");
2197     from_file = optarg;
2198 solar 1.45 break;
2199 solar 1.119 case 'E':
2200 vapier 1.262 /* historically, this was comma delimited */
2201     parse_delimited(match_etypes, optarg, ",");
2202 solar 1.119 break;
2203 solar 1.120 case 'M':
2204     match_bits = atoi(optarg);
2205 solar 1.182 if (match_bits == 0) {
2206     if (strcmp(optarg, "ELFCLASS32") == 0)
2207     match_bits = 32;
2208     if (strcmp(optarg, "ELFCLASS64") == 0)
2209     match_bits = 64;
2210     }
2211 solar 1.120 break;
2212 solar 1.180 case 'O':
2213 vapier 1.205 if (sscanf(optarg, "%o", &match_perms) == -1)
2214 solar 1.180 match_bits = 0;
2215     break;
2216 vapier 1.24 case 'o': {
2217 vapier 1.146 if (freopen(optarg, "w", stdout) == NULL)
2218 vapier 1.237 errp("Could not freopen(%s)", optarg);
2219 solar 1.21 break;
2220     }
2221 solar 1.124 case 'k':
2222 vapier 1.233 xarraypush_str(find_section_arr, optarg);
2223 solar 1.124 break;
2224 vapier 1.262 case 's':
2225 vapier 1.248 /* historically, this was comma delimited */
2226 vapier 1.262 parse_delimited(find_sym_arr, optarg, ",");
2227 vapier 1.39 break;
2228 vapier 1.226 case 'N':
2229 vapier 1.233 xarraypush_str(find_lib_arr, optarg);
2230 vapier 1.72 break;
2231 vapier 1.39 case 'F': {
2232 vapier 1.93 if (out_format) warn("You prob don't want to specify -F twice");
2233     out_format = optarg;
2234 vapier 1.39 break;
2235     }
2236 solar 1.127 case 'z': {
2237 solar 1.129 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
2238 solar 1.127 size_t x;
2239 vapier 1.27
2240 vapier 1.186 for (x = 0; x < strlen(optarg); x++) {
2241     switch (optarg[x]) {
2242 solar 1.127 case 'p':
2243     case 'P':
2244 vapier 1.165 do_pax_state(optarg[x], PAGEEXEC);
2245 solar 1.127 break;
2246     case 's':
2247     case 'S':
2248 vapier 1.165 do_pax_state(optarg[x], SEGMEXEC);
2249 solar 1.127 break;
2250     case 'm':
2251     case 'M':
2252 vapier 1.165 do_pax_state(optarg[x], MPROTECT);
2253 solar 1.127 break;
2254     case 'e':
2255     case 'E':
2256 vapier 1.165 do_pax_state(optarg[x], EMUTRAMP);
2257 solar 1.127 break;
2258     case 'r':
2259     case 'R':
2260 vapier 1.165 do_pax_state(optarg[x], RANDMMAP);
2261 solar 1.127 break;
2262     case 'x':
2263     case 'X':
2264 vapier 1.165 do_pax_state(optarg[x], RANDEXEC);
2265 solar 1.127 break;
2266     default:
2267     break;
2268     }
2269     }
2270     if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
2271     ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
2272     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
2273     ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
2274     ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
2275     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
2276     setpax = flags;
2277     break;
2278     }
2279 solar 1.190 case 'Z': show_size = 1; break;
2280 vapier 1.246 case 'g': ++g_match; break;
2281 vapier 1.240 case 'L': load_cache_config = use_ldcache = 1; break;
2282 vapier 1.37 case 'y': scan_symlink = 0; break;
2283 vapier 1.105 case 'A': scan_archives = 1; break;
2284 vapier 1.221 case 'C': color_init(true); break;
2285 solar 1.16 case 'B': show_banner = 0; break;
2286 vapier 1.240 case 'l': load_cache_config = scan_ldpath = 1; break;
2287 vapier 1.10 case 'p': scan_envpath = 1; break;
2288     case 'R': dir_recurse = 1; break;
2289 vapier 1.14 case 'm': dir_crossmount = 0; break;
2290 vapier 1.101 case 'X': ++fix_elf; break;
2291 vapier 1.10 case 'x': show_pax = 1; break;
2292 solar 1.73 case 'e': show_phdr = 1; break;
2293 vapier 1.10 case 't': show_textrel = 1; break;
2294     case 'r': show_rpath = 1; break;
2295 vapier 1.32 case 'n': show_needed = 1; break;
2296 vapier 1.38 case 'i': show_interp = 1; break;
2297 vapier 1.49 case 'b': show_bind = 1; break;
2298 vapier 1.84 case 'S': show_soname = 1; break;
2299 vapier 1.76 case 'T': show_textrels = 1; break;
2300 vapier 1.254 case 'q': be_quiet = min(be_quiet, 20) + 1; break;
2301     case 'v': be_verbose = min(be_verbose, 20) + 1; break;
2302 solar 1.183 case 'a': show_perms = show_pax = show_phdr = show_textrel = show_rpath = show_bind = show_endian = 1; break;
2303 solar 1.181 case 'D': show_endian = 1; break;
2304 solar 1.191 case 'I': show_osabi = 1; break;
2305     case 'Y': show_eabi = 1; break;
2306 solar 1.223 case 128:
2307 vapier 1.232 root_fd = open(optarg, O_RDONLY|O_CLOEXEC);
2308     if (root_fd == -1)
2309     err("Could not open root: %s", optarg);
2310 solar 1.223 break;
2311 vapier 1.240 case 129: load_cache_config = use_ldpath = 1; break;
2312 vapier 1.10 case ':':
2313 vapier 1.113 err("Option '%c' is missing parameter", optopt);
2314 vapier 1.10 case '?':
2315 solar 1.119 err("Unknown option '%c' or argument missing", optopt);
2316 vapier 1.10 default:
2317 vapier 1.113 err("Unhandled option '%c'; please report this", i);
2318 vapier 1.10 }
2319     }
2320 vapier 1.230 if (show_textrels && be_verbose)
2321 vapier 1.264 objdump = which("objdump", "OBJDUMP");
2322 vapier 1.249 /* precompile all the regexes */
2323     if (g_match) {
2324     regex_t preg;
2325     const char *this_sym;
2326     size_t n;
2327     int flags = REG_EXTENDED | REG_NOSUB | (g_match > 1 ? REG_ICASE : 0);
2328    
2329     array_for_each(find_sym_arr, n, this_sym) {
2330     /* see scanelf_match_symname for logic info */
2331     switch (this_sym[0]) {
2332     case '%':
2333     while (*(this_sym++))
2334     if (*this_sym == '%') {
2335     ++this_sym;
2336     break;
2337     }
2338     break;
2339     case '+':
2340     case '-':
2341     ++this_sym;
2342     break;
2343     }
2344     if (*this_sym == '*')
2345     ++this_sym;
2346    
2347     ret = regcomp(&preg, this_sym, flags);
2348     if (ret) {
2349     char err[256];
2350     regerror(ret, &preg, err, sizeof(err));
2351     err("regcomp of %s failed: %s", this_sym, err);
2352     }
2353     xarraypush(find_sym_regex_arr, &preg, sizeof(preg));
2354     }
2355     }
2356 vapier 1.226 /* flatten arrays for display */
2357 vapier 1.261 find_sym = array_flatten_str(find_sym_arr);
2358     find_lib = array_flatten_str(find_lib_arr);
2359     find_section = array_flatten_str(find_section_arr);
2360 vapier 1.39 /* let the format option override all other options */
2361     if (out_format) {
2362 solar 1.73 show_pax = show_phdr = show_textrel = show_rpath = \
2363 vapier 1.84 show_needed = show_interp = show_bind = show_soname = \
2364 solar 1.191 show_textrels = show_perms = show_endian = show_size = \
2365     show_osabi = show_eabi = 0;
2366 vapier 1.48 for (i = 0; out_format[i]; ++i) {
2367 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
2368 vapier 1.39
2369 vapier 1.48 switch (out_format[++i]) {
2370 solar 1.132 case '+': break;
2371 vapier 1.39 case '%': break;
2372 vapier 1.70 case '#': break;
2373 vapier 1.39 case 'F': break;
2374 vapier 1.66 case 'p': break;
2375     case 'f': break;
2376 solar 1.124 case 'k': break;
2377 vapier 1.39 case 's': break;
2378 vapier 1.72 case 'N': break;
2379 vapier 1.41 case 'o': break;
2380 vapier 1.166 case 'a': break;
2381 vapier 1.171 case 'M': break;
2382 solar 1.190 case 'Z': show_size = 1; break;
2383 solar 1.181 case 'D': show_endian = 1; break;
2384 solar 1.191 case 'I': show_osabi = 1; break;
2385     case 'Y': show_eabi = 1; break;
2386 solar 1.180 case 'O': show_perms = 1; break;
2387 vapier 1.39 case 'x': show_pax = 1; break;
2388 solar 1.73 case 'e': show_phdr = 1; break;
2389 vapier 1.39 case 't': show_textrel = 1; break;
2390     case 'r': show_rpath = 1; break;
2391     case 'n': show_needed = 1; break;
2392     case 'i': show_interp = 1; break;
2393 vapier 1.49 case 'b': show_bind = 1; break;
2394 vapier 1.84 case 'S': show_soname = 1; break;
2395 vapier 1.76 case 'T': show_textrels = 1; break;
2396 vapier 1.39 default:
2397 vapier 1.240 err("invalid format specifier '%c' (byte %i)",
2398 vapier 1.48 out_format[i], i+1);
2399 vapier 1.39 }
2400     }
2401 vapier 1.41
2402     /* construct our default format */
2403     } else {
2404     size_t fmt_len = 30;
2405 vapier 1.186 out_format = xmalloc(sizeof(char) * fmt_len);
2406 vapier 1.194 *out_format = '\0';
2407 vapier 1.76 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
2408     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
2409 solar 1.180 if (show_perms) xstrcat(&out_format, "%O ", &fmt_len);
2410 solar 1.190 if (show_size) xstrcat(&out_format, "%Z ", &fmt_len);
2411 solar 1.181 if (show_endian) xstrcat(&out_format, "%D ", &fmt_len);
2412 solar 1.191 if (show_osabi) xstrcat(&out_format, "%I ", &fmt_len);
2413     if (show_eabi) xstrcat(&out_format, "%Y ", &fmt_len);
2414 vapier 1.76 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
2415     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
2416     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
2417     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
2418     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
2419     if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
2420 vapier 1.84 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
2421 vapier 1.76 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
2422     if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
2423 solar 1.124 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
2424 vapier 1.76 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);