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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.261 - (hide annotations) (download) (as text)
Thu Mar 20 07:59:27 2014 UTC (3 years, 6 months ago) by vapier
Branch: MAIN
Changes since 1.260: +9 -14 lines
File MIME type: text/x-csrc
move array_cnt check into array_for_each init

atm, if you try to use array_for_each or array_flatten_str on an array that has no members, you will get a segfault.  this is an easy rule to forget (and the current code does just that in at least one place), so move the array_cnt check into the init phase.  theres negligible code size impact so it should not be a big deal.

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