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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.253 - (hide annotations) (download) (as text)
Fri Nov 30 23:25:07 2012 UTC (19 months, 2 weeks ago) by vapier
Branch: MAIN
Changes since 1.252: +6 -2 lines
File MIME type: text/x-csrc
update ldconfig flags

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

  ViewVC Help
Powered by ViewVC 1.1.20