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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.250 - (hide annotations) (download) (as text)
Sun Nov 4 08:25:41 2012 UTC (23 months, 2 weeks ago) by vapier
Branch: MAIN
Changes since 1.249: +5 -2 lines
File MIME type: text/x-csrc
skip freeing of memory at exit since the kernel takes care of that for us

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

  ViewVC Help
Powered by ViewVC 1.1.20