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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.240 - (hide annotations) (download) (as text)
Mon Jan 23 23:48:54 2012 UTC (2 years, 8 months ago) by vapier
Branch: MAIN
Changes since 1.239: +69 -38 lines
File MIME type: text/x-csrc
add --use-ldpath option that considers ld.so.conf when outputting full paths for %n

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

  ViewVC Help
Powered by ViewVC 1.1.20