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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.252 - (hide annotations) (download) (as text)
Sun Nov 18 07:39:45 2012 UTC (21 months, 1 week ago) by vapier
Branch: MAIN
Changes since 1.251: +2 -7 lines
File MIME type: text/x-csrc
scanelf/pspax: drop PT_LOAD counts since more than "normal" is not a bug and is semi-common with some targets, and the warning has out lived its usefulness -- it was added as an initial sanity check to get a feel for the real world

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.252 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.251 2012/11/10 09:43:00 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.252 static const char rcsid[] = "$Id: scanelf.c,v 1.251 2012/11/10 09:43:00 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     #define LDSO_CACHE_MAGIC "ld.so-"
769     #define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
770     #define LDSO_CACHE_VER "1.7.0"
771     #define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
772 vapier 1.97 #define FLAG_ANY -1
773     #define FLAG_TYPE_MASK 0x00ff
774     #define FLAG_LIBC4 0x0000
775     #define FLAG_ELF 0x0001
776     #define FLAG_ELF_LIBC5 0x0002
777     #define FLAG_ELF_LIBC6 0x0003
778     #define FLAG_REQUIRED_MASK 0xff00
779     #define FLAG_SPARC_LIB64 0x0100
780     #define FLAG_IA64_LIB64 0x0200
781     #define FLAG_X8664_LIB64 0x0300
782     #define FLAG_S390_LIB64 0x0400
783     #define FLAG_POWERPC_LIB64 0x0500
784     #define FLAG_MIPS64_LIBN32 0x0600
785     #define FLAG_MIPS64_LIBN64 0x0700
786 solar 1.96
787 flameeyes 1.141 #if defined(__GLIBC__) || defined(__UCLIBC__)
788 vapier 1.148
789 vapier 1.240 static char *lookup_cache_lib(elfobj *elf, const char *fname)
790 solar 1.96 {
791 vapier 1.212 int fd;
792 solar 1.96 char *strs;
793 vapier 1.104 static char buf[__PAX_UTILS_PATH_MAX] = "";
794 vapier 1.232 const char *cachefile = root_rel_path("/etc/ld.so.cache");
795 solar 1.96 struct stat st;
796    
797     typedef struct {
798     char magic[LDSO_CACHE_MAGIC_LEN];
799     char version[LDSO_CACHE_VER_LEN];
800     int nlibs;
801     } header_t;
802 vapier 1.97 header_t *header;
803 solar 1.96
804     typedef struct {
805     int flags;
806     int sooffset;
807     int liboffset;
808     } libentry_t;
809     libentry_t *libent;
810    
811     if (fname == NULL)
812     return NULL;
813    
814 vapier 1.212 if (ldcache == NULL) {
815 vapier 1.232 if (fstatat(root_fd, cachefile, &st, 0))
816 vapier 1.212 return NULL;
817    
818 vapier 1.232 fd = openat(root_fd, cachefile, O_RDONLY);
819 vapier 1.212 if (fd == -1)
820 solar 1.96 return NULL;
821 vapier 1.97
822     /* cache these values so we only map/unmap the cache file once */
823 solar 1.96 ldcache_size = st.st_size;
824 vapier 1.212 header = ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
825 vapier 1.97 close(fd);
826 solar 1.96
827 vapier 1.202 if (ldcache == MAP_FAILED) {
828 vapier 1.212 ldcache = NULL;
829 solar 1.96 return NULL;
830 solar 1.116 }
831 solar 1.96
832 vapier 1.212 if (memcmp(header->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN) ||
833     memcmp(header->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
834     {
835     munmap(ldcache, ldcache_size);
836     ldcache = NULL;
837 solar 1.96 return NULL;
838 vapier 1.212 }
839     } else
840     header = ldcache;
841 solar 1.96
842 vapier 1.212 libent = ldcache + sizeof(header_t);
843 solar 1.96 strs = (char *) &libent[header->nlibs];
844    
845 vapier 1.212 for (fd = 0; fd < header->nlibs; ++fd) {
846     /* This should be more fine grained, but for now we assume that
847     * diff arches will not be cached together, and we ignore the
848     * the different multilib mips cases.
849     */
850 vapier 1.97 if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
851     continue;
852     if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
853     continue;
854    
855 solar 1.96 if (strcmp(fname, strs + libent[fd].sooffset) != 0)
856     continue;
857 vapier 1.212
858     /* Return first hit because that is how the ldso rolls */
859 solar 1.96 strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
860 vapier 1.212 break;
861 solar 1.96 }
862 vapier 1.212
863 solar 1.96 return buf;
864     }
865 vapier 1.212
866 solar 1.154 #elif defined(__NetBSD__)
867 vapier 1.240 static char *lookup_cache_lib(elfobj *elf, const char *fname)
868 solar 1.154 {
869     static char buf[__PAX_UTILS_PATH_MAX] = "";
870     static struct stat st;
871 vapier 1.227 size_t n;
872     char *ldpath;
873 solar 1.154
874 vapier 1.227 array_for_each(ldpath, n, ldpath) {
875     if ((unsigned) snprintf(buf, sizeof(buf), "%s/%s", ldpath, fname) >= sizeof(buf))
876 solar 1.154 continue; /* if the pathname is too long, or something went wrong, ignore */
877    
878     if (stat(buf, &st) != 0)
879     continue; /* if the lib doesn't exist in *ldpath, look further */
880    
881     /* NetBSD doesn't actually do sanity checks, it just loads the file
882     * and if that doesn't work, continues looking in other directories.
883     * This cannot easily be safely emulated, unfortunately. For now,
884     * just assume that if it exists, it's a valid library. */
885 vapier 1.148
886 solar 1.154 return buf;
887     }
888    
889     /* not found in any path */
890     return NULL;
891     }
892 flameeyes 1.141 #else
893 vapier 1.192 #ifdef __ELF__
894 vapier 1.148 #warning Cache support not implemented for your target
895 vapier 1.192 #endif
896 vapier 1.240 static char *lookup_cache_lib(elfobj *elf, const char *fname)
897 flameeyes 1.141 {
898     return NULL;
899     }
900     #endif
901 solar 1.96
902 vapier 1.240 static char *lookup_config_lib(elfobj *elf, char *fname)
903     {
904     static char buf[__PAX_UTILS_PATH_MAX] = "";
905     const char *ldpath;
906     size_t n;
907    
908     array_for_each(ldpaths, n, ldpath) {
909     snprintf(buf, sizeof(buf), "%s/%s", root_rel_path(ldpath), fname);
910     if (faccessat(root_fd, buf, F_OK, AT_SYMLINK_NOFOLLOW) == 0)
911     return buf;
912     }
913    
914     return NULL;
915     }
916    
917 vapier 1.84 static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
918 vapier 1.39 {
919 vapier 1.44 unsigned long i;
920 vapier 1.39 char *needed;
921     void *strtbl_void;
922 solar 1.96 char *p;
923 vapier 1.39
924 vapier 1.226 /*
925     * -n -> op==0 -> print all
926     * -N -> op==1 -> print requested
927     */
928     if ((op == 0 && !show_needed) || (op == 1 && !find_lib))
929     return NULL;
930 vapier 1.10
931 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
932 vapier 1.32
933 vapier 1.44 if (elf->phdr && strtbl_void) {
934 vapier 1.32 #define SHOW_NEEDED(B) \
935     if (elf->elf_class == ELFCLASS ## B) { \
936     Elf ## B ## _Dyn *dyn; \
937     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
938     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
939     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
940 vapier 1.44 Elf ## B ## _Off offset; \
941 vapier 1.226 size_t matched = 0; \
942     /* Walk all the program headers to find the PT_DYNAMIC */ \
943 vapier 1.32 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
944 vapier 1.226 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) \
945     continue; \
946 vapier 1.44 offset = EGET(phdr[i].p_offset); \
947 vapier 1.226 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) \
948     continue; \
949     /* Walk all the dynamic tags to find NEEDED entries */ \
950 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
951 vapier 1.32 while (EGET(dyn->d_tag) != DT_NULL) { \
952     if (EGET(dyn->d_tag) == DT_NEEDED) { \
953 vapier 1.44 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
954 vapier 1.69 if (offset >= (Elf ## B ## _Off)elf->len) { \
955 vapier 1.49 ++dyn; \
956     continue; \
957     } \
958 vapier 1.217 needed = elf->data + offset; \
959 vapier 1.72 if (op == 0) { \
960 vapier 1.226 /* -n -> print all entries */ \
961 solar 1.127 if (!be_wewy_wewy_quiet) { \
962 vapier 1.72 if (*found_needed) xchrcat(ret, ',', ret_len); \
963 vapier 1.240 if (use_ldpath) { \
964     if ((p = lookup_config_lib(elf, needed)) != NULL) \
965     needed = p; \
966     } else if (use_ldcache) { \
967 vapier 1.97 if ((p = lookup_cache_lib(elf, needed)) != NULL) \
968 solar 1.96 needed = p; \
969 vapier 1.240 } \
970 vapier 1.72 xstrcat(ret, needed, ret_len); \
971     } \
972     *found_needed = 1; \
973     } else { \
974 vapier 1.226 /* -N -> print matching entries */ \
975     size_t n; \
976     const char *find_lib_name; \
977     \
978 vapier 1.236 array_for_each(find_lib_arr, n, find_lib_name) { \
979     int invert = 1; \
980     if (find_lib_name[0] == '!') \
981     invert = 0, ++find_lib_name; \
982     if (!strcmp(find_lib_name, needed) == invert) \
983 vapier 1.226 ++matched; \
984 vapier 1.236 } \
985 vapier 1.226 \
986     if (matched == array_cnt(find_lib_arr)) { \
987 vapier 1.81 *found_lib = 1; \
988 vapier 1.226 return (be_wewy_wewy_quiet ? NULL : find_lib); \
989 vapier 1.81 } \
990 vapier 1.72 } \
991 vapier 1.32 } \
992     ++dyn; \
993     } \
994     } }
995     SHOW_NEEDED(32)
996     SHOW_NEEDED(64)
997 vapier 1.85 if (op == 0 && !*found_needed && be_verbose)
998 vapier 1.84 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
999 vapier 1.32 }
1000 vapier 1.72
1001     return NULL;
1002 vapier 1.39 }
1003 vapier 1.41 static char *scanelf_file_interp(elfobj *elf, char *found_interp)
1004 vapier 1.39 {
1005     void *strtbl_void;
1006    
1007 vapier 1.41 if (!show_interp) return NULL;
1008 vapier 1.32
1009 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".interp");
1010 vapier 1.38
1011 vapier 1.39 if (strtbl_void) {
1012 vapier 1.38 #define SHOW_INTERP(B) \
1013     if (elf->elf_class == ELFCLASS ## B) { \
1014 solar 1.40 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
1015     *found_interp = 1; \
1016 solar 1.127 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
1017 vapier 1.38 }
1018     SHOW_INTERP(32)
1019     SHOW_INTERP(64)
1020 vapier 1.251 } else {
1021     /* Walk all the program headers to find the PT_INTERP */
1022     #define SHOW_PT_INTERP(B) \
1023     if (elf->elf_class == ELFCLASS ## B) { \
1024     unsigned long i; \
1025     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
1026     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
1027     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
1028     if (EGET(phdr[i].p_type) != PT_INTERP) \
1029     continue; \
1030     *found_interp = 1; \
1031     return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(phdr[i].p_offset)); \
1032     } \
1033     }
1034     SHOW_PT_INTERP(32)
1035     SHOW_PT_INTERP(64)
1036 vapier 1.38 }
1037 vapier 1.251
1038 vapier 1.41 return NULL;
1039 vapier 1.39 }
1040 vapier 1.49 static char *scanelf_file_bind(elfobj *elf, char *found_bind)
1041     {
1042     unsigned long i;
1043     struct stat s;
1044 solar 1.131 char dynamic = 0;
1045 vapier 1.49
1046     if (!show_bind) return NULL;
1047 vapier 1.51 if (!elf->phdr) return NULL;
1048 vapier 1.49
1049     #define SHOW_BIND(B) \
1050     if (elf->elf_class == ELFCLASS ## B) { \
1051     Elf ## B ## _Dyn *dyn; \
1052     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
1053     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
1054     Elf ## B ## _Off offset; \
1055     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
1056 vapier 1.185 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
1057 solar 1.131 dynamic = 1; \
1058 vapier 1.49 offset = EGET(phdr[i].p_offset); \
1059     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
1060 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
1061 vapier 1.49 while (EGET(dyn->d_tag) != DT_NULL) { \
1062     if (EGET(dyn->d_tag) == DT_BIND_NOW || \
1063     (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
1064     { \
1065     if (be_quiet) return NULL; \
1066     *found_bind = 1; \
1067 solar 1.127 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
1068 vapier 1.49 } \
1069     ++dyn; \
1070     } \
1071     } \
1072     }
1073     SHOW_BIND(32)
1074     SHOW_BIND(64)
1075    
1076 solar 1.127 if (be_wewy_wewy_quiet) return NULL;
1077 vapier 1.70
1078 vapier 1.159 /* don't output anything if quiet mode and the ELF is static or not setuid */
1079     if (be_quiet && (!dynamic || (!fstat(elf->fd, &s) && !(s.st_mode & (S_ISUID|S_ISGID))))) {
1080 vapier 1.49 return NULL;
1081     } else {
1082     *found_bind = 1;
1083 vapier 1.217 return (char *)(dynamic ? "LAZY" : "STATIC");
1084 vapier 1.49 }
1085     }
1086 vapier 1.84 static char *scanelf_file_soname(elfobj *elf, char *found_soname)
1087     {
1088     unsigned long i;
1089     char *soname;
1090     void *strtbl_void;
1091    
1092     if (!show_soname) return NULL;
1093    
1094     strtbl_void = elf_findsecbyname(elf, ".dynstr");
1095    
1096     if (elf->phdr && strtbl_void) {
1097     #define SHOW_SONAME(B) \
1098     if (elf->elf_class == ELFCLASS ## B) { \
1099     Elf ## B ## _Dyn *dyn; \
1100     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
1101     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
1102     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
1103     Elf ## B ## _Off offset; \
1104     /* only look for soname in shared objects */ \
1105 solar 1.188 if (EGET(ehdr->e_type) != ET_DYN) \
1106 vapier 1.84 return NULL; \
1107     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
1108 vapier 1.185 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
1109 vapier 1.84 offset = EGET(phdr[i].p_offset); \
1110     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
1111 vapier 1.217 dyn = DYN ## B (elf->vdata + offset); \
1112 vapier 1.84 while (EGET(dyn->d_tag) != DT_NULL) { \
1113     if (EGET(dyn->d_tag) == DT_SONAME) { \
1114     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
1115     if (offset >= (Elf ## B ## _Off)elf->len) { \
1116     ++dyn; \
1117     continue; \
1118     } \
1119 vapier 1.217 soname = elf->data + offset; \
1120 vapier 1.84 *found_soname = 1; \
1121 solar 1.127 return (be_wewy_wewy_quiet ? NULL : soname); \
1122 vapier 1.84 } \
1123     ++dyn; \
1124     } \
1125     } }
1126     SHOW_SONAME(32)
1127     SHOW_SONAME(64)
1128     }
1129    
1130     return NULL;
1131     }
1132 solar 1.177
1133 vapier 1.215 /*
1134     * We support the symbol form:
1135     * [%[modifiers]%][[+-]<symbol name>][,[.....]]
1136     * If the symbol name is empty, then all symbols are matched.
1137     * If the symbol name is a glob ("*"), then all symbols are dumped (debug).
1138     * Do not rely on this output format at all.
1139     * Otherwise the symbol name is used to search (either regex or string compare).
1140     * If the first char of the symbol name is a plus ("+"), then only match
1141     * defined symbols. If it's a minus ("-"), only match undefined symbols.
1142     * Putting modifiers in between the percent signs allows for more in depth
1143     * filters. There are groups of modifiers. If you don't specify a member
1144     * of a group, then all types in that group are matched. The current
1145     * groups and their types are:
1146 vapier 1.238 * STT group: STT_NOTYPE:n STT_OBJECT:o STT_FUNC:f STT_FILE:F
1147 vapier 1.215 * STB group: STB_LOCAL:l STB_GLOBAL:g STB_WEAK:w
1148     * SHN group: SHN_UNDEF:u SHN_ABS:a SHN_COMMON:c {defined}:d
1149     * The "defined" value in the SHN group does not correspond to a SHN_xxx define.
1150     * You can search for multiple symbols at once by seperating with a comma (",").
1151     *
1152     * Some examples:
1153     * ELFs with a weak function "foo":
1154     * scanelf -s %wf%foo <ELFs>
1155     * ELFs that define the symbol "main":
1156     * scanelf -s +main <ELFs>
1157     * scanelf -s %d%main <ELFs>
1158     * ELFs that refer to the undefined symbol "brk":
1159     * scanelf -s -brk <ELFs>
1160     * scanelf -s %u%brk <ELFs>
1161     * All global defined objects in an ELF:
1162     * scanelf -s %ogd% <ELF>
1163     */
1164     static void
1165     scanelf_match_symname(elfobj *elf, char *found_sym, char **ret, size_t *ret_len, const char *symname,
1166     unsigned int stt, unsigned int stb, unsigned int shn, unsigned long size)
1167     {
1168 vapier 1.248 const char *this_sym;
1169     size_t n;
1170 vapier 1.215
1171 vapier 1.248 array_for_each(find_sym_arr, n, this_sym) {
1172 vapier 1.215 bool inc_notype, inc_object, inc_func, inc_file,
1173     inc_local, inc_global, inc_weak,
1174     inc_def, inc_undef, inc_abs, inc_common;
1175    
1176     /* symbol selection! */
1177     inc_notype = inc_object = inc_func = inc_file = \
1178     inc_local = inc_global = inc_weak = \
1179     inc_def = inc_undef = inc_abs = inc_common = \
1180     (*this_sym != '%');
1181    
1182     /* parse the contents of %...% */
1183     if (!inc_notype) {
1184     while (*(this_sym++)) {
1185     if (*this_sym == '%') {
1186     ++this_sym;
1187     break;
1188     }
1189     switch (*this_sym) {
1190     case 'n': inc_notype = true; break;
1191     case 'o': inc_object = true; break;
1192     case 'f': inc_func = true; break;
1193     case 'F': inc_file = true; break;
1194     case 'l': inc_local = true; break;
1195     case 'g': inc_global = true; break;
1196     case 'w': inc_weak = true; break;
1197     case 'd': inc_def = true; break;
1198     case 'u': inc_undef = true; break;
1199     case 'a': inc_abs = true; break;
1200     case 'c': inc_common = true; break;
1201     default: err("invalid symbol selector '%c'", *this_sym);
1202     }
1203     }
1204    
1205     /* If no types are matched, not match all */
1206     if (!inc_notype && !inc_object && !inc_func && !inc_file)
1207     inc_notype = inc_object = inc_func = inc_file = true;
1208     if (!inc_local && !inc_global && !inc_weak)
1209     inc_local = inc_global = inc_weak = true;
1210     if (!inc_def && !inc_undef && !inc_abs && !inc_common)
1211     inc_def = inc_undef = inc_abs = inc_common = true;
1212    
1213     /* backwards compat for defined/undefined short hand */
1214     } else if (*this_sym == '+') {
1215     inc_undef = false;
1216     ++this_sym;
1217     } else if (*this_sym == '-') {
1218     inc_def = inc_abs = inc_common = false;
1219     ++this_sym;
1220     }
1221    
1222     /* filter symbols */
1223     if ((!inc_notype && stt == STT_NOTYPE) || \
1224     (!inc_object && stt == STT_OBJECT) || \
1225     (!inc_func && stt == STT_FUNC ) || \
1226     (!inc_file && stt == STT_FILE ) || \
1227     (!inc_local && stb == STB_LOCAL ) || \
1228     (!inc_global && stb == STB_GLOBAL) || \
1229     (!inc_weak && stb == STB_WEAK ) || \
1230     (!inc_def && shn && shn < SHN_LORESERVE) || \
1231     (!inc_undef && shn == SHN_UNDEF ) || \
1232     (!inc_abs && shn == SHN_ABS ) || \
1233     (!inc_common && shn == SHN_COMMON))
1234     continue;
1235    
1236     if (*this_sym == '*') {
1237     /* a "*" symbol gets you debug output */
1238     printf("%s(%s) %5lX %15s %15s %15s %s\n",
1239     ((*found_sym == 0) ? "\n\t" : "\t"),
1240     elf->base_filename,
1241     size,
1242     get_elfstttype(stt),
1243     get_elfstbtype(stb),
1244     get_elfshntype(shn),
1245     symname);
1246     goto matched;
1247    
1248     } else {
1249     if (g_match) {
1250     /* regex match the symbol */
1251 vapier 1.249 if (regexec(find_sym_regex_arr->eles[n], symname, 0, NULL, 0) == REG_NOMATCH)
1252 vapier 1.215 continue;
1253    
1254     } else if (*this_sym) {
1255     /* give empty symbols a "pass", else do a normal compare */
1256     const size_t len = strlen(this_sym);
1257     if (!(strncmp(this_sym, symname, len) == 0 &&
1258     /* Accept unversioned symbol names */
1259     (symname[len] == '\0' || symname[len] == '@')))
1260     continue;
1261     }
1262    
1263     if (be_semi_verbose) {
1264     char buf[1024];
1265     snprintf(buf, sizeof(buf), "%lX %s %s",
1266     size,
1267     get_elfstttype(stt),
1268     this_sym);
1269     *ret = xstrdup(buf);
1270     } else {
1271     if (*ret) xchrcat(ret, ',', ret_len);
1272     xstrcat(ret, symname, ret_len);
1273     }
1274    
1275     goto matched;
1276     }
1277 vapier 1.248 }
1278 vapier 1.215
1279     return;
1280    
1281     matched:
1282     *found_sym = 1;
1283 flameeyes 1.197 }
1284    
1285 vapier 1.225 static const char *scanelf_file_sym(elfobj *elf, char *found_sym)
1286 vapier 1.39 {
1287 vapier 1.95 char *ret;
1288 vapier 1.39 void *symtab_void, *strtab_void;
1289 vapier 1.38
1290 vapier 1.41 if (!find_sym) return NULL;
1291 vapier 1.214 ret = NULL;
1292 vapier 1.32
1293 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
1294 vapier 1.27
1295 vapier 1.39 if (symtab_void && strtab_void) {
1296 vapier 1.27 #define FIND_SYM(B) \
1297     if (elf->elf_class == ELFCLASS ## B) { \
1298     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
1299     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
1300 vapier 1.217 Elf ## B ## _Sym *sym = SYM ## B (elf->vdata + EGET(symtab->sh_offset)); \
1301 vapier 1.234 Elf ## B ## _Word i, cnt = EGET(symtab->sh_entsize); \
1302 vapier 1.27 char *symname; \
1303 vapier 1.214 size_t ret_len = 0; \
1304 vapier 1.115 if (cnt) \
1305     cnt = EGET(symtab->sh_size) / cnt; \
1306 vapier 1.27 for (i = 0; i < cnt; ++i) { \
1307 vapier 1.217 if ((void*)sym > elf->data_end) { \
1308 flameeyes 1.195 warnf("%s: corrupt ELF symbols - aborting", elf->filename); \
1309     goto break_out; \
1310     } \
1311 vapier 1.27 if (sym->st_name) { \
1312 vapier 1.134 /* make sure the symbol name is in acceptable memory range */ \
1313 vapier 1.217 symname = elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name); \
1314     if ((void*)symname > elf->data_end) { \
1315 vapier 1.115 warnf("%s: corrupt ELF symbols", elf->filename); \
1316 vapier 1.134 ++sym; \
1317 vapier 1.111 continue; \
1318     } \
1319 vapier 1.215 scanelf_match_symname(elf, found_sym, \
1320     &ret, &ret_len, symname, \
1321     ELF##B##_ST_TYPE(EGET(sym->st_info)), \
1322     ELF##B##_ST_BIND(EGET(sym->st_info)), \
1323     EGET(sym->st_shndx), \
1324     /* st_size can be 64bit, but no one is really that big, so screw em */ \
1325     EGET(sym->st_size)); \
1326 vapier 1.27 } \
1327     ++sym; \
1328 vapier 1.234 } \
1329     }
1330 vapier 1.27 FIND_SYM(32)
1331     FIND_SYM(64)
1332 vapier 1.39 }
1333 vapier 1.70
1334 vapier 1.95 break_out:
1335 solar 1.127 if (be_wewy_wewy_quiet) return NULL;
1336 vapier 1.70
1337 vapier 1.41 if (*find_sym != '*' && *found_sym)
1338 vapier 1.95 return ret;
1339 vapier 1.41 if (be_quiet)
1340     return NULL;
1341     else
1342 vapier 1.225 return " - ";
1343 vapier 1.39 }
1344 solar 1.119
1345 vapier 1.225 static const char *scanelf_file_sections(elfobj *elf, char *found_section)
1346 solar 1.124 {
1347     if (!find_section)
1348     return NULL;
1349    
1350     #define FIND_SECTION(B) \
1351     if (elf->elf_class == ELFCLASS ## B) { \
1352 vapier 1.226 size_t matched, n; \
1353 solar 1.136 int invert; \
1354 vapier 1.226 const char *section_name; \
1355 solar 1.124 Elf ## B ## _Shdr *section; \
1356 vapier 1.226 \
1357     matched = 0; \
1358     array_for_each(find_section_arr, n, section_name) { \
1359     invert = (*section_name == '!' ? 1 : 0); \
1360     section = SHDR ## B (elf_findsecbyname(elf, section_name + invert)); \
1361     if ((section == NULL && invert) || (section != NULL && !invert)) \
1362     ++matched; \
1363     } \
1364     \
1365     if (matched == array_cnt(find_section_arr)) \
1366 solar 1.124 *found_section = 1; \
1367     }
1368     FIND_SECTION(32)
1369     FIND_SECTION(64)
1370    
1371 vapier 1.138 if (be_wewy_wewy_quiet)
1372     return NULL;
1373 solar 1.124
1374     if (*found_section)
1375     return find_section;
1376    
1377     if (be_quiet)
1378     return NULL;
1379     else
1380 vapier 1.225 return " - ";
1381 solar 1.124 }
1382    
1383 vapier 1.39 /* scan an elf file and show all the fun stuff */
1384 vapier 1.210 #define prints(str) ({ ssize_t ret = write(fileno(stdout), str, strlen(str)); ret; })
1385 vapier 1.106 static int scanelf_elfobj(elfobj *elf)
1386 vapier 1.39 {
1387 vapier 1.44 unsigned long i;
1388 vapier 1.162 char found_pax, found_phdr, found_relro, found_load, found_textrel,
1389     found_rpath, found_needed, found_interp, found_bind, found_soname,
1390 solar 1.124 found_sym, found_lib, found_file, found_textrels, found_section;
1391 vapier 1.41 static char *out_buffer = NULL;
1392     static size_t out_len;
1393 vapier 1.39
1394 vapier 1.76 found_pax = found_phdr = found_relro = found_load = found_textrel = \
1395 vapier 1.84 found_rpath = found_needed = found_interp = found_bind = found_soname = \
1396 solar 1.124 found_sym = found_lib = found_file = found_textrels = found_section = 0;
1397 vapier 1.39
1398 vapier 1.114 if (be_verbose > 2)
1399 vapier 1.106 printf("%s: scanning file {%s,%s}\n", elf->filename,
1400 vapier 1.69 get_elfeitype(EI_CLASS, elf->elf_class),
1401     get_elfeitype(EI_DATA, elf->data[EI_DATA]));
1402 vapier 1.114 else if (be_verbose > 1)
1403 vapier 1.106 printf("%s: scanning file\n", elf->filename);
1404 vapier 1.39
1405 vapier 1.41 /* init output buffer */
1406     if (!out_buffer) {
1407     out_len = sizeof(char) * 80;
1408 vapier 1.186 out_buffer = xmalloc(out_len);
1409 vapier 1.41 }
1410     *out_buffer = '\0';
1411    
1412 vapier 1.39 /* show the header */
1413     if (!be_quiet && show_banner) {
1414 vapier 1.49 for (i = 0; out_format[i]; ++i) {
1415 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
1416 vapier 1.41
1417     switch (out_format[++i]) {
1418 solar 1.132 case '+': break;
1419 vapier 1.41 case '%': break;
1420 vapier 1.70 case '#': break;
1421 vapier 1.66 case 'F':
1422     case 'p':
1423     case 'f': prints("FILE "); found_file = 1; break;
1424 vapier 1.41 case 'o': prints(" TYPE "); break;
1425     case 'x': prints(" PAX "); break;
1426 vapier 1.71 case 'e': prints("STK/REL/PTL "); break;
1427 vapier 1.41 case 't': prints("TEXTREL "); break;
1428     case 'r': prints("RPATH "); break;
1429 vapier 1.171 case 'M': prints("CLASS "); break;
1430 vapier 1.240 case 'l':
1431 vapier 1.41 case 'n': prints("NEEDED "); break;
1432     case 'i': prints("INTERP "); break;
1433 vapier 1.49 case 'b': prints("BIND "); break;
1434 solar 1.190 case 'Z': prints("SIZE "); break;
1435 vapier 1.84 case 'S': prints("SONAME "); break;
1436 vapier 1.41 case 's': prints("SYM "); break;
1437 vapier 1.72 case 'N': prints("LIB "); break;
1438 vapier 1.76 case 'T': prints("TEXTRELS "); break;
1439 vapier 1.126 case 'k': prints("SECTION "); break;
1440 vapier 1.166 case 'a': prints("ARCH "); break;
1441 solar 1.191 case 'I': prints("OSABI "); break;
1442     case 'Y': prints("EABI "); break;
1443 solar 1.180 case 'O': prints("PERM "); break;
1444 solar 1.181 case 'D': prints("ENDIAN "); break;
1445 vapier 1.76 default: warnf("'%c' has no title ?", out_format[i]);
1446 vapier 1.39 }
1447 vapier 1.27 }
1448 vapier 1.49 if (!found_file) prints("FILE ");
1449 vapier 1.41 prints("\n");
1450 vapier 1.49 found_file = 0;
1451 vapier 1.39 show_banner = 0;
1452     }
1453    
1454     /* dump all the good stuff */
1455 vapier 1.49 for (i = 0; out_format[i]; ++i) {
1456 vapier 1.41 const char *out;
1457 vapier 1.66 const char *tmp;
1458 solar 1.190 static char ubuf[sizeof(unsigned long)*2];
1459 vapier 1.70 if (!IS_MODIFIER(out_format[i])) {
1460 vapier 1.41 xchrcat(&out_buffer, out_format[i], &out_len);
1461     continue;
1462     }
1463 vapier 1.39
1464 vapier 1.41 out = NULL;
1465 solar 1.127 be_wewy_wewy_quiet = (out_format[i] == '#');
1466 solar 1.132 be_semi_verbose = (out_format[i] == '+');
1467 vapier 1.41 switch (out_format[++i]) {
1468 solar 1.132 case '+':
1469 vapier 1.70 case '%':
1470     case '#':
1471     xchrcat(&out_buffer, out_format[i], &out_len); break;
1472     case 'F':
1473 vapier 1.76 found_file = 1;
1474 solar 1.127 if (be_wewy_wewy_quiet) break;
1475 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1476 vapier 1.70 break;
1477 vapier 1.66 case 'p':
1478 vapier 1.76 found_file = 1;
1479 solar 1.127 if (be_wewy_wewy_quiet) break;
1480 vapier 1.106 tmp = elf->filename;
1481 vapier 1.66 if (search_path) {
1482     ssize_t len_search = strlen(search_path);
1483 vapier 1.106 ssize_t len_file = strlen(elf->filename);
1484     if (!strncmp(elf->filename, search_path, len_search) && \
1485 vapier 1.66 len_file > len_search)
1486     tmp += len_search;
1487     if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
1488     }
1489     xstrcat(&out_buffer, tmp, &out_len);
1490     break;
1491     case 'f':
1492 vapier 1.76 found_file = 1;
1493 solar 1.127 if (be_wewy_wewy_quiet) break;
1494 vapier 1.106 tmp = strrchr(elf->filename, '/');
1495     tmp = (tmp == NULL ? elf->filename : tmp+1);
1496 vapier 1.66 xstrcat(&out_buffer, tmp, &out_len);
1497     break;
1498 vapier 1.41 case 'o': out = get_elfetype(elf); break;
1499     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
1500 solar 1.73 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
1501 vapier 1.41 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1502 vapier 1.79 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1503 vapier 1.41 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1504 vapier 1.171 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break;
1505 solar 1.181 case 'D': out = get_endian(elf); break;
1506 vapier 1.206 case 'O': out = strfileperms(elf->filename); break;
1507 vapier 1.72 case 'n':
1508     case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1509 vapier 1.41 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1510 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1511 vapier 1.84 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1512 vapier 1.72 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1513 solar 1.124 case 'k': out = scanelf_file_sections(elf, &found_section); break;
1514 vapier 1.166 case 'a': out = get_elfemtype(elf); break;
1515 solar 1.191 case 'I': out = get_elfosabi(elf); break;
1516     case 'Y': out = get_elf_eabi(elf); break;
1517 vapier 1.193 case 'Z': snprintf(ubuf, sizeof(ubuf), "%lu", (unsigned long)elf->len); out = ubuf; break;;
1518 vapier 1.76 default: warnf("'%c' has no scan code?", out_format[i]);
1519 vapier 1.29 }
1520 vapier 1.214 if (out)
1521     xstrcat(&out_buffer, out, &out_len);
1522 vapier 1.39 }
1523    
1524 vapier 1.54 #define FOUND_SOMETHING() \
1525 solar 1.73 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
1526 vapier 1.76 found_rpath || found_needed || found_interp || found_bind || \
1527 solar 1.124 found_soname || found_sym || found_lib || found_textrels || found_section )
1528 vapier 1.54
1529     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
1530     xchrcat(&out_buffer, ' ', &out_len);
1531 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1532 vapier 1.27 }
1533 vapier 1.79 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
1534 vapier 1.41 puts(out_buffer);
1535 vapier 1.79 fflush(stdout);
1536     }
1537 vapier 1.10
1538 vapier 1.105 return 0;
1539     }
1540    
1541 vapier 1.106 /* scan a single elf */
1542     static int scanelf_elf(const char *filename, int fd, size_t len)
1543     {
1544 solar 1.120 int ret = 1;
1545 vapier 1.106 elfobj *elf;
1546    
1547     /* verify this is real ELF */
1548     if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1549     if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1550 vapier 1.235 return 2;
1551 solar 1.120 }
1552     switch (match_bits) {
1553     case 32:
1554     if (elf->elf_class != ELFCLASS32)
1555     goto label_done;
1556     break;
1557     case 64:
1558     if (elf->elf_class != ELFCLASS64)
1559     goto label_done;
1560     break;
1561     default: break;
1562 vapier 1.106 }
1563 vapier 1.228 if (match_etypes) {
1564 solar 1.119 char sbuf[126];
1565     strncpy(sbuf, match_etypes, sizeof(sbuf));
1566     if (strchr(match_etypes, ',') != NULL) {
1567     char *p;
1568 vapier 1.186 while ((p = strrchr(sbuf, ',')) != NULL) {
1569 solar 1.119 *p = 0;
1570 solar 1.129 if (etype_lookup(p+1) == get_etype(elf))
1571 solar 1.119 goto label_ret;
1572     }
1573     }
1574 solar 1.129 if (etype_lookup(sbuf) != get_etype(elf))
1575 solar 1.119 goto label_done;
1576     }
1577    
1578     label_ret:
1579 vapier 1.106 ret = scanelf_elfobj(elf);
1580 solar 1.119
1581     label_done:
1582 vapier 1.106 unreadelf(elf);
1583     return ret;
1584     }
1585 solar 1.119
1586 vapier 1.105 /* scan an archive of elfs */
1587 vapier 1.106 static int scanelf_archive(const char *filename, int fd, size_t len)
1588 vapier 1.105 {
1589 vapier 1.106 archive_handle *ar;
1590 vapier 1.105 archive_member *m;
1591 vapier 1.106 char *ar_buffer;
1592     elfobj *elf;
1593    
1594     ar = ar_open_fd(filename, fd);
1595     if (ar == NULL)
1596     return 1;
1597    
1598 vapier 1.201 ar_buffer = mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1599 vapier 1.218 while ((m = ar_next(ar)) != NULL) {
1600     off_t cur_pos = lseek(fd, 0, SEEK_CUR);
1601     if (cur_pos == -1)
1602     errp("lseek() failed");
1603     elf = readelf_buffer(m->name, ar_buffer + cur_pos, m->size);
1604 vapier 1.106 if (elf) {
1605     scanelf_elfobj(elf);
1606     unreadelf(elf);
1607     }
1608     }
1609     munmap(ar_buffer, len);
1610    
1611 vapier 1.105 return 0;
1612     }
1613     /* scan a file which may be an elf or an archive or some other magical beast */
1614 vapier 1.232 static int scanelf_fileat(int dir_fd, const char *filename, const struct stat *st_cache)
1615 vapier 1.105 {
1616 vapier 1.161 const struct stat *st = st_cache;
1617     struct stat symlink_st;
1618 vapier 1.106 int fd;
1619 vapier 1.105
1620     /* always handle regular files and handle symlinked files if no -y */
1621 vapier 1.161 if (S_ISLNK(st->st_mode)) {
1622 vapier 1.232 if (!scan_symlink)
1623     return 1;
1624     fstatat(dir_fd, filename, &symlink_st, 0);
1625 vapier 1.161 st = &symlink_st;
1626 vapier 1.105 }
1627 vapier 1.161
1628     if (!S_ISREG(st->st_mode)) {
1629 vapier 1.105 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1630 solar 1.164 return 1;
1631 vapier 1.105 }
1632    
1633 solar 1.180 if (match_perms) {
1634     if ((st->st_mode | match_perms) != st->st_mode)
1635     return 1;
1636     }
1637 vapier 1.232 fd = openat(dir_fd, filename, (fix_elf ? O_RDWR : O_RDONLY) | O_CLOEXEC);
1638 vapier 1.245 if (fd == -1) {
1639     if (fix_elf && errno == ETXTBSY)
1640     warnp("%s: could not fix", filename);
1641     else if (be_verbose > 2)
1642     printf("%s: skipping file: %s\n", filename, strerror(errno));
1643 solar 1.164 return 1;
1644 vapier 1.245 }
1645 vapier 1.106
1646 vapier 1.235 if (scanelf_elf(filename, fd, st->st_size) == 2) {
1647 vapier 1.105 /* if it isn't an ELF, maybe it's an .a archive */
1648 vapier 1.235 if (scan_archives)
1649     scanelf_archive(filename, fd, st->st_size);
1650    
1651     /*
1652     * unreadelf() implicitly closes its fd, so only close it
1653     * when we are returning it in the non-ELF case
1654     */
1655     close(fd);
1656     }
1657 vapier 1.106
1658 solar 1.164 return 0;
1659 solar 1.6 }
1660    
1661 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
1662 vapier 1.232 static int scanelf_dirat(int dir_fd, const char *path)
1663 solar 1.1 {
1664 vapier 1.10 register DIR *dir;
1665     register struct dirent *dentry;
1666 vapier 1.14 struct stat st_top, st;
1667 vapier 1.232 char buf[__PAX_UTILS_PATH_MAX], *subpath;
1668 vapier 1.32 size_t pathlen = 0, len = 0;
1669 solar 1.164 int ret = 0;
1670 vapier 1.232 int subdir_fd;
1671 solar 1.223
1672 vapier 1.10 /* make sure path exists */
1673 vapier 1.232 if (fstatat(dir_fd, path, &st_top, AT_SYMLINK_NOFOLLOW) == -1) {
1674 vapier 1.39 if (be_verbose > 2) printf("%s: does not exist\n", path);
1675 solar 1.164 return 1;
1676 vapier 1.39 }
1677 solar 1.11
1678 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
1679 vapier 1.232 if (!S_ISDIR(st_top.st_mode))
1680     return scanelf_fileat(dir_fd, path, &st_top);
1681 vapier 1.10
1682     /* now scan the dir looking for fun stuff */
1683 vapier 1.232 subdir_fd = openat(dir_fd, path, O_RDONLY|O_CLOEXEC);
1684     if (subdir_fd == -1)
1685     dir = NULL;
1686     else
1687     dir = fdopendir(subdir_fd);
1688     if (dir == NULL) {
1689     if (subdir_fd != -1)
1690     close(subdir_fd);
1691 vapier 1.237 warnfp("could not opendir(%s)", path);
1692 solar 1.164 return 1;
1693 vapier 1.10 }
1694 vapier 1.114 if (be_verbose > 1) printf("%s: scanning dir\n", path);
1695 solar 1.11
1696 vapier 1.232 subpath = stpcpy(buf, path);
1697 vapier 1.243 if (subpath[-1] != '/')
1698     *subpath++ = '/';
1699 vapier 1.232 pathlen = subpath - buf;
1700 vapier 1.10 while ((dentry = readdir(dir))) {
1701     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
1702     continue;
1703 vapier 1.232
1704     if (fstatat(subdir_fd, dentry->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1)
1705     continue;
1706    
1707     len = strlen(dentry->d_name);
1708     if (len + pathlen + 1 >= sizeof(buf)) {
1709     warnf("Skipping '%s%s': len > sizeof(buf); %zu > %zu\n",
1710     path, dentry->d_name, len + pathlen + 1, sizeof(buf));
1711 vapier 1.32 continue;
1712     }
1713 vapier 1.232 memcpy(subpath, dentry->d_name, len);
1714     subpath[len] = '\0';
1715    
1716     if (S_ISREG(st.st_mode))
1717     ret = scanelf_fileat(dir_fd, buf, &st);
1718     else if (dir_recurse && S_ISDIR(st.st_mode)) {
1719     if (dir_crossmount || (st_top.st_dev == st.st_dev))
1720     ret = scanelf_dirat(dir_fd, buf);
1721 vapier 1.10 }
1722     }
1723     closedir(dir);
1724 vapier 1.232
1725 solar 1.164 return ret;
1726 solar 1.1 }
1727 vapier 1.232 static int scanelf_dir(const char *path)
1728     {
1729     return scanelf_dirat(root_fd, root_rel_path(path));
1730     }
1731 solar 1.1
1732 vapier 1.133 static int scanelf_from_file(const char *filename)
1733 vapier 1.47 {
1734 vapier 1.230 FILE *fp;
1735     char *p, *path;
1736     size_t len;
1737     int ret;
1738 solar 1.45
1739 solar 1.132 if (strcmp(filename, "-") == 0)
1740 solar 1.45 fp = stdin;
1741     else if ((fp = fopen(filename, "r")) == NULL)
1742     return 1;
1743    
1744 vapier 1.230 path = NULL;
1745     len = 0;
1746     ret = 0;
1747     while (getline(&path, &len, fp) != -1) {
1748 solar 1.45 if ((p = strchr(path, '\n')) != NULL)
1749     *p = 0;
1750 vapier 1.66 search_path = path;
1751 solar 1.164 ret = scanelf_dir(path);
1752 solar 1.45 }
1753 vapier 1.230 free(path);
1754    
1755 solar 1.45 if (fp != stdin)
1756     fclose(fp);
1757 vapier 1.230
1758 solar 1.164 return ret;
1759 solar 1.45 }
1760    
1761 solar 1.154 #if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
1762 vapier 1.148
1763 vapier 1.240 static int _load_ld_cache_config(const char *fname)
1764 vapier 1.48 {
1765     FILE *fp = NULL;
1766 vapier 1.230 char *p, *path;
1767     size_t len;
1768 vapier 1.232 int curr_fd = -1;
1769 solar 1.223
1770 vapier 1.232 fp = fopenat_r(root_fd, root_rel_path(fname));
1771     if (fp == NULL)
1772 vapier 1.240 return -1;
1773 vapier 1.48
1774 vapier 1.230 path = NULL;
1775     len = 0;
1776     while (getline(&path, &len, fp) != -1) {
1777 vapier 1.48 if ((p = strrchr(path, '\r')) != NULL)
1778     *p = 0;
1779     if ((p = strchr(path, '\n')) != NULL)
1780     *p = 0;
1781 vapier 1.219
1782 vapier 1.186 /* recursive includes of the same file will make this segfault. */
1783 solar 1.129 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1784 vapier 1.219 glob_t gl;
1785 solar 1.123 size_t x;
1786 vapier 1.232 const char *gpath;
1787 solar 1.123
1788 vapier 1.232 /* re-use existing path buffer ... need to be creative */
1789 solar 1.123 if (path[8] != '/')
1790 vapier 1.232 gpath = memcpy(path + 3, "/etc/", 5);
1791 solar 1.123 else
1792 vapier 1.232 gpath = path + 8;
1793     if (root_fd != AT_FDCWD) {
1794     if (curr_fd == -1) {
1795     curr_fd = open(".", O_RDONLY|O_CLOEXEC);
1796     if (fchdir(root_fd))
1797     errp("unable to change to root dir");
1798     }
1799     gpath = root_rel_path(gpath);
1800     }
1801 solar 1.123
1802 vapier 1.219 if (glob(gpath, 0, NULL, &gl) == 0) {
1803 solar 1.123 for (x = 0; x < gl.gl_pathc; ++x) {
1804     /* try to avoid direct loops */
1805     if (strcmp(gl.gl_pathv[x], fname) == 0)
1806     continue;
1807 vapier 1.240 _load_ld_cache_config(gl.gl_pathv[x]);
1808 solar 1.123 }
1809 vapier 1.219 globfree(&gl);
1810 solar 1.160 }
1811 vapier 1.232
1812     /* failed globs are ignored by glibc */
1813     continue;
1814 solar 1.123 }
1815 vapier 1.219
1816 solar 1.123 if (*path != '/')
1817     continue;
1818 vapier 1.48
1819 vapier 1.233 xarraypush_str(ldpaths, path);
1820 vapier 1.48 }
1821 vapier 1.230 free(path);
1822 vapier 1.48
1823     fclose(fp);
1824 vapier 1.230
1825 vapier 1.232 if (curr_fd != -1) {
1826     if (fchdir(curr_fd))
1827     /* don't care */;
1828     close(curr_fd);
1829     }
1830    
1831 vapier 1.240 return 0;
1832 vapier 1.48 }
1833 flameeyes 1.141
1834 vapier 1.211 #elif defined(__FreeBSD__) || defined(__DragonFly__)
1835 vapier 1.148
1836 vapier 1.240 static int _load_ld_cache_config(const char *fname)
1837 flameeyes 1.141 {
1838     FILE *fp = NULL;
1839     char *b = NULL, *p;
1840     struct elfhints_hdr hdr;
1841 vapier 1.152
1842 vapier 1.232 fp = fopenat_r(root_fd, root_rel_path(fname));
1843     if (fp == NULL)
1844 vapier 1.240 return -1;
1845 flameeyes 1.141
1846 vapier 1.152 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
1847     hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
1848     fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
1849     {
1850 flameeyes 1.141 fclose(fp);
1851 vapier 1.240 return -1;
1852 flameeyes 1.141 }
1853 vapier 1.152
1854 vapier 1.186 b = xmalloc(hdr.dirlistlen + 1);
1855 vapier 1.152 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1856 flameeyes 1.141 fclose(fp);
1857     free(b);
1858 vapier 1.240 return -1;
1859 flameeyes 1.141 }
1860 vapier 1.152
1861     while ((p = strsep(&b, ":"))) {
1862 vapier 1.227 if (*p == '\0')
1863     continue;
1864 vapier 1.233 xarraypush_str(ldpaths, p);
1865 flameeyes 1.141 }
1866 vapier 1.157
1867 flameeyes 1.141 free(b);
1868     fclose(fp);
1869 vapier 1.240 return 0;
1870 flameeyes 1.141 }
1871 vapier 1.148
1872     #else
1873 vapier 1.192 #ifdef __ELF__
1874 vapier 1.148 #warning Cache config support not implemented for your target
1875 vapier 1.192 #endif
1876 vapier 1.240 static int _load_ld_cache_config(const char *fname)
1877 vapier 1.148 {
1878 vapier 1.192 return 0;
1879 vapier 1.148 }
1880 flameeyes 1.141 #endif
1881 vapier 1.48
1882 vapier 1.240 static void load_ld_cache_config(const char *fname)
1883     {
1884     bool scan_l, scan_ul, scan_ull;
1885     size_t n;
1886     const char *ldpath;
1887    
1888     _load_ld_cache_config(fname);
1889    
1890     scan_l = scan_ul = scan_ull = false;
1891     if (array_cnt(ldpaths)) {
1892     array_for_each(ldpaths, n, ldpath) {
1893     if (!scan_l && !strcmp(ldpath, "/lib")) scan_l = true;
1894     if (!scan_ul && !strcmp(ldpath, "/usr/lib")) scan_ul = true;
1895     if (!scan_ull && !strcmp(ldpath, "/usr/local/lib")) scan_ull = true;
1896     }
1897     }
1898    
1899     if (!scan_l) xarraypush_str(ldpaths, "/lib");
1900     if (!scan_ul) xarraypush_str(ldpaths, "/usr/lib");
1901     if (!scan_ull) xarraypush_str(ldpaths, "/usr/local/lib");
1902     }
1903    
1904 vapier 1.10 /* scan /etc/ld.so.conf for paths */
1905 vapier 1.184 static void scanelf_ldpath(void)
1906 vapier 1.10 {
1907 vapier 1.227 size_t n;
1908     const char *ldpath;
1909 vapier 1.17
1910 vapier 1.240 array_for_each(ldpaths, n, ldpath)
1911 vapier 1.227 scanelf_dir(ldpath);
1912 vapier 1.10 }
1913 solar 1.1
1914 vapier 1.10 /* scan env PATH for paths */
1915 vapier 1.184 static void scanelf_envpath(void)
1916 solar 1.1 {
1917 solar 1.34 char *path, *p;
1918 vapier 1.10
1919     path = getenv("PATH");
1920     if (!path)
1921     err("PATH is not set in your env !");
1922 vapier 1.41 path = xstrdup(path);
1923 vapier 1.10
1924     while ((p = strrchr(path, ':')) != NULL) {
1925     scanelf_dir(p + 1);
1926     *p = 0;
1927     }
1928 vapier 1.17
1929 solar 1.34 free(path);
1930 solar 1.1 }
1931    
1932 vapier 1.221 /* usage / invocation handling functions */ /* Free Flags: c d j u w G H J K P Q U W */
1933     #define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:DIYO:ZCBhV"
1934 vapier 1.27 #define a_argument required_argument
1935 vapier 1.10 static struct option const long_opts[] = {
1936     {"path", no_argument, NULL, 'p'},
1937     {"ldpath", no_argument, NULL, 'l'},
1938 vapier 1.240 {"use-ldpath",no_argument, NULL, 129},
1939 solar 1.223 {"root", a_argument, NULL, 128},
1940 vapier 1.10 {"recursive", no_argument, NULL, 'R'},
1941 vapier 1.14 {"mount", no_argument, NULL, 'm'},
1942 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
1943 vapier 1.105 {"archives", no_argument, NULL, 'A'},
1944     {"ldcache", no_argument, NULL, 'L'},
1945 vapier 1.101 {"fix", no_argument, NULL, 'X'},
1946 solar 1.127 {"setpax", a_argument, NULL, 'z'},
1947 vapier 1.10 {"pax", no_argument, NULL, 'x'},
1948 solar 1.16 {"header", no_argument, NULL, 'e'},
1949 vapier 1.10 {"textrel", no_argument, NULL, 't'},
1950     {"rpath", no_argument, NULL, 'r'},
1951 vapier 1.32 {"needed", no_argument, NULL, 'n'},
1952 vapier 1.38 {"interp", no_argument, NULL, 'i'},
1953 vapier 1.49 {"bind", no_argument, NULL, 'b'},
1954 vapier 1.84 {"soname", no_argument, NULL, 'S'},
1955 vapier 1.76 {"symbol", a_argument, NULL, 's'},
1956 solar 1.124 {"section", a_argument, NULL, 'k'},
1957 vapier 1.76 {"lib", a_argument, NULL, 'N'},
1958 solar 1.86 {"gmatch", no_argument, NULL, 'g'},
1959 vapier 1.76 {"textrels", no_argument, NULL, 'T'},
1960 solar 1.119 {"etype", a_argument, NULL, 'E'},
1961 solar 1.120 {"bits", a_argument, NULL, 'M'},
1962 solar 1.181 {"endian", no_argument, NULL, 'D'},
1963 solar 1.191 {"osabi", no_argument, NULL, 'I'},
1964     {"eabi", no_argument, NULL, 'Y'},
1965 solar 1.180 {"perms", a_argument, NULL, 'O'},
1966 solar 1.190 {"size", no_argument, NULL, 'Z'},
1967 vapier 1.10 {"all", no_argument, NULL, 'a'},
1968     {"quiet", no_argument, NULL, 'q'},
1969 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
1970 vapier 1.76 {"format", a_argument, NULL, 'F'},
1971     {"from", a_argument, NULL, 'f'},
1972     {"file", a_argument, NULL, 'o'},
1973 vapier 1.221 {"nocolor", no_argument, NULL, 'C'},
1974 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
1975 vapier 1.10 {"help", no_argument, NULL, 'h'},
1976     {"version", no_argument, NULL, 'V'},
1977     {NULL, no_argument, NULL, 0x0}
1978     };
1979 solar 1.57
1980 vapier 1.222 static const char * const opts_help[] = {
1981 vapier 1.10 "Scan all directories in PATH environment",
1982     "Scan all directories in /etc/ld.so.conf",
1983 vapier 1.240 "Use ld.so.conf to show full path (use with -r/-n)",
1984 solar 1.223 "Root directory (use with -l or -p)",
1985 vapier 1.14 "Scan directories recursively",
1986 vapier 1.37 "Don't recursively cross mount points",
1987 vapier 1.101 "Don't scan symlinks",
1988 vapier 1.105 "Scan archives (.a files)",
1989 vapier 1.239 "Utilize ld.so.cache to show full path (use with -r/-n)",
1990 solar 1.127 "Try and 'fix' bad things (use with -r/-e)",
1991     "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
1992 vapier 1.10 "Print PaX markings",
1993 vapier 1.71 "Print GNU_STACK/PT_LOAD markings",
1994 vapier 1.10 "Print TEXTREL information",
1995     "Print RPATH information",
1996 vapier 1.32 "Print NEEDED information",
1997 vapier 1.38 "Print INTERP information",
1998 vapier 1.49 "Print BIND information",
1999 vapier 1.84 "Print SONAME information",
2000 vapier 1.27 "Find a specified symbol",
2001 solar 1.124 "Find a specified section",
2002 vapier 1.72 "Find a specified library",
2003 vapier 1.246 "Use regex rather than string compare (with -s); specify twice for case insensitive",
2004 vapier 1.76 "Locate cause of TEXTREL",
2005 solar 1.129 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
2006 solar 1.120 "Print only ELF files matching numeric bits",
2007 solar 1.181 "Print Endianness",
2008 solar 1.191 "Print OSABI",
2009     "Print EABI (EM_ARM Only)",
2010 solar 1.180 "Print only ELF files matching octal permissions",
2011 solar 1.190 "Print ELF file size",
2012 vapier 1.216 "Print all useful/simple info\n",
2013 vapier 1.14 "Only output 'bad' things",
2014     "Be verbose (can be specified more than once)",
2015 vapier 1.39 "Use specified format for output",
2016 solar 1.45 "Read input stream from a filename",
2017 vapier 1.24 "Write output stream to a filename",
2018 vapier 1.221 "Don't emit color in output",
2019 vapier 1.14 "Don't display the header",
2020 vapier 1.10 "Print this help and exit",
2021     "Print version and exit",
2022     NULL
2023     };
2024    
2025     /* display usage and exit */
2026     static void usage(int status)
2027 solar 1.1 {
2028 vapier 1.44 unsigned long i;
2029 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
2030 vapier 1.105 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
2031 solar 1.35 printf("Options: -[%s]\n", PARSE_FLAGS);
2032 vapier 1.241 for (i = 0; long_opts[i].name; ++i) {
2033     /* first output the short flag if it has one */
2034     if (long_opts[i].val > '~')
2035     printf(" ");
2036     else
2037     printf(" -%c, ", long_opts[i].val);
2038    
2039     /* then the long flag */
2040 vapier 1.27 if (long_opts[i].has_arg == no_argument)
2041 vapier 1.241 printf("--%-14s", long_opts[i].name);
2042 vapier 1.27 else
2043 vapier 1.241 printf("--%-7s <arg> ", long_opts[i].name);
2044    
2045     /* finally the help text */
2046     printf("* %s\n", opts_help[i]);
2047     }
2048 solar 1.45
2049 vapier 1.173 puts("\nFor more information, see the scanelf(1) manpage");
2050 vapier 1.10 exit(status);
2051 solar 1.1 }
2052    
2053     /* parse command line arguments and preform needed actions */
2054 vapier 1.165 #define do_pax_state(option, flag) \
2055     if (islower(option)) { \
2056     flags &= ~PF_##flag; \
2057     flags |= PF_NO##flag; \
2058     } else { \
2059     flags &= ~PF_NO##flag; \
2060     flags |= PF_##flag; \
2061     }
2062 solar 1.164 static int parseargs(int argc, char *argv[])
2063 vapier 1.10 {
2064 vapier 1.48 int i;
2065 vapier 1.133 const char *from_file = NULL;
2066 solar 1.164 int ret = 0;
2067 vapier 1.240 char load_cache_config = 0;
2068 vapier 1.10
2069     opterr = 0;
2070 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
2071     switch (i) {
2072 vapier 1.10
2073 vapier 1.39 case 'V':
2074 vapier 1.80 printf("pax-utils-%s: %s compiled %s\n%s\n"
2075     "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
2076     VERSION, __FILE__, __DATE__, rcsid, argv0);
2077 vapier 1.10 exit(EXIT_SUCCESS);
2078     break;
2079     case 'h': usage(EXIT_SUCCESS); break;
2080 solar 1.45 case 'f':
2081 vapier 1.100 if (from_file) warn("You prob don't want to specify -f twice");
2082     from_file = optarg;
2083 solar 1.45 break;
2084 solar 1.119 case 'E':
2085 vapier 1.228 match_etypes = optarg;
2086 solar 1.119 break;
2087 solar 1.120 case 'M':
2088     match_bits = atoi(optarg);
2089 solar 1.182 if (match_bits == 0) {
2090     if (strcmp(optarg, "ELFCLASS32") == 0)
2091     match_bits = 32;
2092     if (strcmp(optarg, "ELFCLASS64") == 0)
2093     match_bits = 64;
2094     }
2095 solar 1.120 break;
2096 solar 1.180 case 'O':
2097 vapier 1.205 if (sscanf(optarg, "%o", &match_perms) == -1)
2098 solar 1.180 match_bits = 0;
2099     break;
2100 vapier 1.24 case 'o': {
2101 vapier 1.146 if (freopen(optarg, "w", stdout) == NULL)
2102 vapier 1.237 errp("Could not freopen(%s)", optarg);
2103 solar 1.21 break;
2104     }
2105 solar 1.124 case 'k':
2106 vapier 1.233 xarraypush_str(find_section_arr, optarg);
2107 solar 1.124 break;
2108 vapier 1.39 case 's': {
2109 vapier 1.248 /* historically, this was comma delimited */
2110     char *this_sym = strtok(optarg, ",");
2111 vapier 1.249 if (!this_sym) /* edge case: -s '' */
2112     xarraypush_str(find_sym_arr, "");
2113 vapier 1.248 while (this_sym) {
2114     xarraypush_str(find_sym_arr, this_sym);
2115     this_sym = strtok(NULL, ",");
2116     }
2117 vapier 1.39 break;
2118     }
2119 vapier 1.226 case 'N':
2120 vapier 1.233 xarraypush_str(find_lib_arr, optarg);
2121 vapier 1.72 break;
2122 vapier 1.39 case 'F': {
2123 vapier 1.93 if (out_format) warn("You prob don't want to specify -F twice");
2124     out_format = optarg;
2125 vapier 1.39 break;
2126     }
2127 solar 1.127 case 'z': {
2128 solar 1.129 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
2129 solar 1.127 size_t x;
2130 vapier 1.27
2131 vapier 1.186 for (x = 0; x < strlen(optarg); x++) {
2132     switch (optarg[x]) {
2133 solar 1.127 case 'p':
2134     case 'P':
2135 vapier 1.165 do_pax_state(optarg[x], PAGEEXEC);
2136 solar 1.127 break;
2137     case 's':
2138     case 'S':
2139 vapier 1.165 do_pax_state(optarg[x], SEGMEXEC);
2140 solar 1.127 break;
2141     case 'm':
2142     case 'M':
2143 vapier 1.165 do_pax_state(optarg[x], MPROTECT);
2144 solar 1.127 break;
2145     case 'e':
2146     case 'E':
2147 vapier 1.165 do_pax_state(optarg[x], EMUTRAMP);
2148 solar 1.127 break;
2149     case 'r':
2150     case 'R':
2151 vapier 1.165 do_pax_state(optarg[x], RANDMMAP);
2152 solar 1.127 break;
2153     case 'x':
2154     case 'X':
2155 vapier 1.165 do_pax_state(optarg[x], RANDEXEC);
2156 solar 1.127 break;
2157     default:
2158     break;
2159     }
2160     }
2161     if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
2162     ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
2163     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
2164     ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
2165     ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
2166     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
2167     setpax = flags;
2168     break;
2169     }
2170 solar 1.190 case 'Z': show_size = 1; break;
2171 vapier 1.246 case 'g': ++g_match; break;
2172 vapier 1.240 case 'L': load_cache_config = use_ldcache = 1; break;
2173 vapier 1.37 case 'y': scan_symlink = 0; break;
2174 vapier 1.105 case 'A': scan_archives = 1; break;
2175 vapier 1.221 case 'C': color_init(true); break;
2176 solar 1.16 case 'B': show_banner = 0; break;
2177 vapier 1.240 case 'l': load_cache_config = scan_ldpath = 1; break;
2178 vapier 1.10 case 'p': scan_envpath = 1; break;
2179     case 'R': dir_recurse = 1; break;
2180 vapier 1.14 case 'm': dir_crossmount = 0; break;
2181 vapier 1.101 case 'X': ++fix_elf; break;
2182 vapier 1.10 case 'x': show_pax = 1; break;
2183 solar 1.73 case 'e': show_phdr = 1; break;
2184 vapier 1.10 case 't': show_textrel = 1; break;
2185     case 'r': show_rpath = 1; break;
2186 vapier 1.32 case 'n': show_needed = 1; break;
2187 vapier 1.38 case 'i': show_interp = 1; break;
2188 vapier 1.49 case 'b': show_bind = 1; break;
2189 vapier 1.84 case 'S': show_soname = 1; break;
2190 vapier 1.76 case 'T': show_textrels = 1; break;
2191 vapier 1.10 case 'q': be_quiet = 1; break;
2192 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
2193 solar 1.183 case 'a': show_perms = show_pax = show_phdr = show_textrel = show_rpath = show_bind = show_endian = 1; break;
2194 solar 1.181 case 'D': show_endian = 1; break;
2195 solar 1.191 case 'I': show_osabi = 1; break;
2196     case 'Y': show_eabi = 1; break;
2197 solar 1.223 case 128:
2198 vapier 1.232 root_fd = open(optarg, O_RDONLY|O_CLOEXEC);
2199     if (root_fd == -1)
2200     err("Could not open root: %s", optarg);
2201 solar 1.223 break;
2202 vapier 1.240 case 129: load_cache_config = use_ldpath = 1; break;
2203 vapier 1.10 case ':':
2204 vapier 1.113 err("Option '%c' is missing parameter", optopt);
2205 vapier 1.10 case '?':
2206 solar 1.119 err("Unknown option '%c' or argument missing", optopt);
2207 vapier 1.10 default:
2208 vapier 1.113 err("Unhandled option '%c'; please report this", i);
2209 vapier 1.10 }
2210     }
2211 vapier 1.230 if (show_textrels && be_verbose)
2212     has_objdump = bin_in_path("objdump");
2213 vapier 1.249 /* precompile all the regexes */
2214     if (g_match) {
2215     regex_t preg;
2216     const char *this_sym;
2217     size_t n;
2218     int flags = REG_EXTENDED | REG_NOSUB | (g_match > 1 ? REG_ICASE : 0);
2219    
2220     array_for_each(find_sym_arr, n, this_sym) {
2221     /* see scanelf_match_symname for logic info */
2222     switch (this_sym[0]) {
2223     case '%':
2224     while (*(this_sym++))
2225     if (*this_sym == '%') {
2226     ++this_sym;
2227     break;
2228     }
2229     break;
2230     case '+':
2231     case '-':
2232     ++this_sym;
2233     break;
2234     }
2235     if (*this_sym == '*')
2236     ++this_sym;
2237    
2238     ret = regcomp(&preg, this_sym, flags);
2239     if (ret) {
2240     char err[256];
2241     regerror(ret, &preg, err, sizeof(err));
2242     err("regcomp of %s failed: %s", this_sym, err);
2243     }
2244     xarraypush(find_sym_regex_arr, &preg, sizeof(preg));
2245     }
2246     }
2247 vapier 1.226 /* flatten arrays for display */
2248 vapier 1.248 if (array_cnt(find_sym_arr))
2249     find_sym = array_flatten_str(find_sym_arr);
2250 vapier 1.226 if (array_cnt(find_lib_arr))
2251     find_lib = array_flatten_str(find_lib_arr);
2252     if (array_cnt(find_section_arr))
2253     find_section = array_flatten_str(find_section_arr);
2254 vapier 1.39 /* let the format option override all other options */
2255     if (out_format) {
2256 solar 1.73 show_pax = show_phdr = show_textrel = show_rpath = \
2257 vapier 1.84 show_needed = show_interp = show_bind = show_soname = \
2258 solar 1.191 show_textrels = show_perms = show_endian = show_size = \
2259     show_osabi = show_eabi = 0;
2260 vapier 1.48 for (i = 0; out_format[i]; ++i) {
2261 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
2262 vapier 1.39
2263 vapier 1.48 switch (out_format[++i]) {
2264 solar 1.132 case '+': break;
2265 vapier 1.39 case '%': break;
2266 vapier 1.70 case '#': break;
2267 vapier 1.39 case 'F': break;
2268 vapier 1.66 case 'p': break;
2269     case 'f': break;
2270 solar 1.124 case 'k': break;
2271 vapier 1.39 case 's': break;
2272 vapier 1.72 case 'N': break;
2273 vapier 1.41 case 'o': break;
2274 vapier 1.166 case 'a': break;
2275 vapier 1.171 case 'M': break;
2276 solar 1.190 case 'Z': show_size = 1; break;
2277 solar 1.181 case 'D': show_endian = 1; break;
2278 solar 1.191 case 'I': show_osabi = 1; break;
2279     case 'Y': show_eabi = 1; break;
2280 solar 1.180 case 'O': show_perms = 1; break;
2281 vapier 1.39 case 'x': show_pax = 1; break;
2282 solar 1.73 case 'e': show_phdr = 1; break;
2283 vapier 1.39 case 't': show_textrel = 1; break;
2284     case 'r': show_rpath = 1; break;
2285     case 'n': show_needed = 1; break;
2286     case 'i': show_interp = 1; break;
2287 vapier 1.49 case 'b': show_bind = 1; break;
2288 vapier 1.84 case 'S': show_soname = 1; break;
2289 vapier 1.76 case 'T': show_textrels = 1; break;
2290 vapier 1.39 default:
2291 vapier 1.240 err("invalid format specifier '%c' (byte %i)",
2292 vapier 1.48 out_format[i], i+1);
2293 vapier 1.39 }
2294     }
2295 vapier 1.41
2296     /* construct our default format */
2297     } else {
2298     size_t fmt_len = 30;
2299 vapier 1.186 out_format = xmalloc(sizeof(char) * fmt_len);
2300 vapier 1.194 *out_format = '\0';
2301 vapier 1.76 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
2302     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
2303 solar 1.180 if (show_perms) xstrcat(&out_format, "%O ", &fmt_len);
2304 solar 1.190 if (show_size) xstrcat(&out_format, "%Z ", &fmt_len);
2305 solar 1.181 if (show_endian) xstrcat(&out_format, "%D ", &fmt_len);
2306 solar 1.191 if (show_osabi) xstrcat(&out_format, "%I ", &fmt_len);
2307     if (show_eabi) xstrcat(&out_format, "%Y ", &fmt_len);
2308 vapier 1.76 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
2309     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
2310     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
2311     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
2312     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
2313     if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
2314 vapier 1.84 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
2315 vapier 1.76 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
2316     if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
2317 solar 1.124 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
2318 vapier 1.76 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
2319     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
2320 vapier 1.39 }
2321 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
2322 vapier 1.39
2323     /* now lets actually do the scanning */
2324 vapier 1.240 if (load_cache_config)
2325     load_ld_cache_config(__PAX_UTILS_DEFAULT_LD_CACHE_CONFIG);
2326 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
2327     if (scan_envpath) scanelf_envpath();
2328 solar 1.153 if (!from_file && optind == argc && ttyname(0) == NULL && !scan_ldpath && !scan_envpath)
2329 vapier 1.133 from_file = "-";
2330 solar 1.45 if (from_file) {
2331     scanelf_from_file(from_file);
2332     from_file = *argv;
2333     }
2334     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
2335 vapier 1.25 err("Nothing to scan !?");
2336 vapier 1.66 while (optind < argc) {
2337     search_path = argv[optind++];
2338 solar 1.164 ret = scanelf_dir(search_path);
2339 vapier 1.66 }
2340 vapier 1.27
2341 vapier 1.250 #ifdef __PAX_UTILS_CLEANUP
2342 vapier 1.39 /* clean up */
2343 vapier 1.227 xarrayfree(ldpaths);
2344 vapier 1.248 xarrayfree(find_sym_arr);
2345 vapier 1.227 xarrayfree(find_lib_arr);
2346     xarrayfree(find_section_arr);
2347 vapier 1.248 free(find_sym);
2348 vapier 1.226 free(find_lib);
2349     free(find_section);
2350 vapier 1.249 {
2351     size_t n;
2352     regex_t *preg;
2353     array_for_each(find_sym_regex_arr, n, preg)
2354     regfree(preg);
2355     xarrayfree(find_sym_regex_arr);
2356     }
2357 solar 1.96
2358     if (ldcache != 0)
2359     munmap(ldcache, ldcache_size);
2360 vapier 1.250 #endif
2361    
2362 solar 1.164 return ret;
2363 vapier 1.10 }
2364    
2365 vapier 1.150 static char **get_split_env(const char *envvar)
2366     {
2367 vapier 1.152 const char *delims = " \t\n";
2368 solar 1.143 char **envvals = NULL;
2369 vapier 1.152 char *env, *s;
2370 kevquinn 1.142 int nentry;
2371    
2372 solar 1.143 if ((env = getenv(envvar)) == NULL)
2373     return NULL;
2374 kevquinn 1.142
2375 solar 1.143 env = xstrdup(env);
2376     if (env == NULL)
2377     return NULL;
2378 kevquinn 1.142
2379 vapier 1.152 s = strtok(env, delims);
2380     if (s == NULL) {
2381     free(env);
2382     return NULL;
2383     }
2384    
2385 solar 1.143 nentry = 0;
2386 vapier 1.152 while (s != NULL) {
2387     ++nentry;
2388     envvals = xrealloc(envvals, sizeof(*envvals) * (nentry+1));
2389     envvals[nentry-1] = s;
2390     s = strtok(NULL, delims);
2391 kevquinn 1.142 }
2392 vapier 1.152 envvals[nentry] = NULL;
2393 kevquinn 1.142
2394 vapier 1.152 /* don't want to free(env) as it contains the memory that backs
2395     * the envvals array of strings */
2396 kevquinn 1.142 return envvals;
2397     }
2398 solar 1.181
2399 vapier 1.184 static void parseenv(void)
2400 vapier 1.150 {
2401 vapier 1.221 color_init(false);
2402 vapier 1.150 qa_textrels = get_split_env("QA_TEXTRELS");
2403     qa_execstack = get_split_env("QA_EXECSTACK");
2404     qa_wx_load = get_split_env("QA_WX_LOAD");
2405 vapier 1.41 }
2406 solar 1.163
2407 vapier 1.152 #ifdef __PAX_UTILS_CLEANUP
2408 vapier 1.184 static void cleanup(void)
2409 vapier 1.152 {
2410     free(out_format);
2411     free(qa_textrels);
2412     free(qa_execstack);
2413     free(qa_wx_load);
2414     }
2415     #endif
2416 vapier 1.41
2417 vapier 1.10 int main(int argc, char *argv[])
2418 solar 1.1 {
2419 solar 1.164 int ret;
2420 vapier 1.10 if (argc < 2)
2421     usage(EXIT_FAILURE);
2422 kevquinn 1.142 parseenv();
2423 solar 1.164 ret = parseargs(argc, argv);
2424 solar 1.21 fclose(stdout);
2425 vapier 1.152 #ifdef __PAX_UTILS_CLEANUP
2426     cleanup();
2427     warn("The calls to add/delete heap should be off:\n"
2428 vapier 1.232 "\t- 1 due to the out_buffer not being freed in scanelf_fileat()\n"
2429 vapier 1.152 "\t- 1 per QA_TEXTRELS/QA_EXECSTACK/QA_WX_LOAD");
2430 solar 1.61 #endif
2431 solar 1.164 return ret;
2432 solar 1.1 }
2433 vapier 1.155
2434     /* Match filename against entries in matchlist, return TRUE
2435     * if the file is listed */
2436     static int file_matches_list(const char *filename, char **matchlist)
2437     {
2438     char **file;
2439     char *match;
2440     char buf[__PAX_UTILS_PATH_MAX];
2441    
2442     if (matchlist == NULL)
2443     return 0;
2444    
2445     for (file = matchlist; *file != NULL; file++) {
2446     if (search_path) {
2447     snprintf(buf, sizeof(buf), "%s%s", search_path, *file);
2448     match = buf;
2449     } else {
2450     match = *file;
2451     }
2452     if (fnmatch(match, filename, 0) == 0)
2453     return 1;
2454     }
2455     return 0;
2456     }

  ViewVC Help
Powered by ViewVC 1.1.20