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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.266 - (show annotations) (download) (as text)
Wed Jun 18 03:16:52 2014 UTC (2 months, 1 week ago) by vapier
Branch: MAIN
CVS Tags: HEAD
Changes since 1.265: +32 -17 lines
File MIME type: text/x-csrc
add support for symbol visibility

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

  ViewVC Help
Powered by ViewVC 1.1.20