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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.264 - (hide annotations) (download) (as text)
Fri Mar 21 05:27:21 2014 UTC (5 years, 5 months ago) by vapier
Branch: MAIN
Changes since 1.263: +60 -19 lines
File MIME type: text/x-csrc
rewrite which() so that it works and does not break $PATH

we were walking the $PATH in reverse which it should have been forwards.  we were also modifying the pointer we got back from getenv() which meant any time we ran external code, $PATH would be truncated.  finally, we never actually checked the first element -- we would bail before we got a chance.

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