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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.239 - (hide annotations) (download) (as text)
Mon Jan 23 22:28:17 2012 UTC (2 years, 6 months ago) by vapier
Branch: MAIN
Changes since 1.238: +3 -3 lines
File MIME type: text/x-csrc
clarify --ldcache help option

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

  ViewVC Help
Powered by ViewVC 1.1.20