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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.272 - (hide annotations) (download) (as text)
Sun Feb 22 00:54:34 2015 UTC (4 years, 7 months ago) by vapier
Branch: MAIN
Changes since 1.271: +4 -4 lines
File MIME type: text/x-csrc
drop __DATE__ usage as the rcsid provides all the details we care about

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