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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.278 - (hide annotations) (download) (as text)
Mon Mar 2 09:24:30 2015 UTC (21 months, 1 week ago) by vapier
Branch: MAIN
CVS Tags: HEAD
Changes since 1.277: +2 -2 lines
File MIME type: text/x-csrc
FILE REMOVED
moved to git://git.overlays.gentoo.org/proj/pax-utils.git

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