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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.265 - (show annotations) (download) (as text)
Fri Mar 21 05:33:33 2014 UTC (4 years, 5 months ago) by vapier
Branch: MAIN
Changes since 1.264: +4 -3 lines
File MIME type: text/x-csrc
note that which() does not handle PATH="/foo::/bar" correctly

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.264 2014/03/21 05:27:21 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.264 2014/03/21 05:27:21 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 * SHN group: SHN_UNDEF:u SHN_ABS:a SHN_COMMON:c {defined}:d
1199 * The "defined" value in the SHN group does not correspond to a SHN_xxx define.
1200 * You can search for multiple symbols at once by seperating with a comma (",").
1201 *
1202 * Some examples:
1203 * ELFs with a weak function "foo":
1204 * scanelf -s %wf%foo <ELFs>
1205 * ELFs that define the symbol "main":
1206 * scanelf -s +main <ELFs>
1207 * scanelf -s %d%main <ELFs>
1208 * ELFs that refer to the undefined symbol "brk":
1209 * scanelf -s -brk <ELFs>
1210 * scanelf -s %u%brk <ELFs>
1211 * All global defined objects in an ELF:
1212 * scanelf -s %ogd% <ELF>
1213 */
1214 static void
1215 scanelf_match_symname(elfobj *elf, char *found_sym, char **ret, size_t *ret_len, const char *symname,
1216 unsigned int stt, unsigned int stb, unsigned int shn, unsigned long size)
1217 {
1218 const char *this_sym;
1219 size_t n;
1220
1221 array_for_each(find_sym_arr, n, this_sym) {
1222 bool inc_notype, inc_object, inc_func, inc_file,
1223 inc_local, inc_global, inc_weak,
1224 inc_def, inc_undef, inc_abs, inc_common;
1225
1226 /* symbol selection! */
1227 inc_notype = inc_object = inc_func = inc_file = \
1228 inc_local = inc_global = inc_weak = \
1229 inc_def = inc_undef = inc_abs = inc_common = \
1230 (*this_sym != '%');
1231
1232 /* parse the contents of %...% */
1233 if (!inc_notype) {
1234 while (*(this_sym++)) {
1235 if (*this_sym == '%') {
1236 ++this_sym;
1237 break;
1238 }
1239 switch (*this_sym) {
1240 case 'n': inc_notype = true; break;
1241 case 'o': inc_object = true; break;
1242 case 'f': inc_func = true; break;
1243 case 'F': inc_file = true; break;
1244 case 'l': inc_local = true; break;
1245 case 'g': inc_global = true; break;
1246 case 'w': inc_weak = true; break;
1247 case 'd': inc_def = true; break;
1248 case 'u': inc_undef = true; break;
1249 case 'a': inc_abs = true; break;
1250 case 'c': inc_common = true; break;
1251 default: err("invalid symbol selector '%c'", *this_sym);
1252 }
1253 }
1254
1255 /* If no types are matched, not match all */
1256 if (!inc_notype && !inc_object && !inc_func && !inc_file)
1257 inc_notype = inc_object = inc_func = inc_file = true;
1258 if (!inc_local && !inc_global && !inc_weak)
1259 inc_local = inc_global = inc_weak = true;
1260 if (!inc_def && !inc_undef && !inc_abs && !inc_common)
1261 inc_def = inc_undef = inc_abs = inc_common = true;
1262
1263 /* backwards compat for defined/undefined short hand */
1264 } else if (*this_sym == '+') {
1265 inc_undef = false;
1266 ++this_sym;
1267 } else if (*this_sym == '-') {
1268 inc_def = inc_abs = inc_common = false;
1269 ++this_sym;
1270 }
1271
1272 /* filter symbols */
1273 if ((!inc_notype && stt == STT_NOTYPE) || \
1274 (!inc_object && stt == STT_OBJECT) || \
1275 (!inc_func && stt == STT_FUNC ) || \
1276 (!inc_file && stt == STT_FILE ) || \
1277 (!inc_local && stb == STB_LOCAL ) || \
1278 (!inc_global && stb == STB_GLOBAL) || \
1279 (!inc_weak && stb == STB_WEAK ) || \
1280 (!inc_def && shn && shn < SHN_LORESERVE) || \
1281 (!inc_undef && shn == SHN_UNDEF ) || \
1282 (!inc_abs && shn == SHN_ABS ) || \
1283 (!inc_common && shn == SHN_COMMON))
1284 continue;
1285
1286 if (*this_sym == '*') {
1287 /* a "*" symbol gets you debug output */
1288 printf("%s(%s) %5lX %15s %15s %15s %s\n",
1289 ((*found_sym == 0) ? "\n\t" : "\t"),
1290 elf->base_filename,
1291 size,
1292 get_elfstttype(stt),
1293 get_elfstbtype(stb),
1294 get_elfshntype(shn),
1295 symname);
1296 goto matched;
1297
1298 } else {
1299 if (g_match) {
1300 /* regex match the symbol */
1301 if (regexec(find_sym_regex_arr->eles[n], symname, 0, NULL, 0) == REG_NOMATCH)
1302 continue;
1303
1304 } else if (*this_sym) {
1305 /* give empty symbols a "pass", else do a normal compare */
1306 const size_t len = strlen(this_sym);
1307 if (!(strncmp(this_sym, symname, len) == 0 &&
1308 /* Accept unversioned symbol names */
1309 (symname[len] == '\0' || symname[len] == '@')))
1310 continue;
1311 }
1312
1313 if (be_semi_verbose) {
1314 char buf[1024];
1315 snprintf(buf, sizeof(buf), "%lX %s %s",
1316 size,
1317 get_elfstttype(stt),
1318 this_sym);
1319 *ret = xstrdup(buf);
1320 } else {
1321 if (*ret) xchrcat(ret, ',', ret_len);
1322 xstrcat(ret, symname, ret_len);
1323 }
1324
1325 goto matched;
1326 }
1327 }
1328
1329 return;
1330
1331 matched:
1332 *found_sym = 1;
1333 }
1334
1335 static const char *scanelf_file_sym(elfobj *elf, char *found_sym)
1336 {
1337 char *ret;
1338 void *symtab_void, *strtab_void;
1339
1340 if (!find_sym) return NULL;
1341 ret = NULL;
1342
1343 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
1344
1345 if (symtab_void && strtab_void) {
1346 #define FIND_SYM(B) \
1347 if (elf->elf_class == ELFCLASS ## B) { \
1348 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
1349 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
1350 Elf ## B ## _Sym *sym = SYM ## B (elf->vdata + EGET(symtab->sh_offset)); \
1351 Elf ## B ## _Word i, cnt = EGET(symtab->sh_entsize); \
1352 char *symname; \
1353 size_t ret_len = 0; \
1354 if (cnt) \
1355 cnt = EGET(symtab->sh_size) / cnt; \
1356 for (i = 0; i < cnt; ++i) { \
1357 if ((void*)sym > elf->data_end) { \
1358 warnf("%s: corrupt ELF symbols - aborting", elf->filename); \
1359 goto break_out; \
1360 } \
1361 if (sym->st_name) { \
1362 /* make sure the symbol name is in acceptable memory range */ \
1363 symname = elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name); \
1364 if ((void*)symname > elf->data_end) { \
1365 warnf("%s: corrupt ELF symbols", elf->filename); \
1366 ++sym; \
1367 continue; \
1368 } \
1369 scanelf_match_symname(elf, found_sym, \
1370 &ret, &ret_len, symname, \
1371 ELF##B##_ST_TYPE(EGET(sym->st_info)), \
1372 ELF##B##_ST_BIND(EGET(sym->st_info)), \
1373 EGET(sym->st_shndx), \
1374 /* st_size can be 64bit, but no one is really that big, so screw em */ \
1375 EGET(sym->st_size)); \
1376 } \
1377 ++sym; \
1378 } \
1379 }
1380 FIND_SYM(32)
1381 FIND_SYM(64)
1382 }
1383
1384 break_out:
1385 if (be_wewy_wewy_quiet) return NULL;
1386
1387 if (*find_sym != '*' && *found_sym)
1388 return ret;
1389 if (be_quiet)
1390 return NULL;
1391 else
1392 return " - ";
1393 }
1394
1395 static const char *scanelf_file_sections(elfobj *elf, char *found_section)
1396 {
1397 if (!find_section)
1398 return NULL;
1399
1400 #define FIND_SECTION(B) \
1401 if (elf->elf_class == ELFCLASS ## B) { \
1402 size_t matched, n; \
1403 int invert; \
1404 const char *section_name; \
1405 Elf ## B ## _Shdr *section; \
1406 \
1407 matched = 0; \
1408 array_for_each(find_section_arr, n, section_name) { \
1409 invert = (*section_name == '!' ? 1 : 0); \
1410 section = SHDR ## B (elf_findsecbyname(elf, section_name + invert)); \
1411 if ((section == NULL && invert) || (section != NULL && !invert)) \
1412 ++matched; \
1413 } \
1414 \
1415 if (matched == array_cnt(find_section_arr)) \
1416 *found_section = 1; \
1417 }
1418 FIND_SECTION(32)
1419 FIND_SECTION(64)
1420
1421 if (be_wewy_wewy_quiet)
1422 return NULL;
1423
1424 if (*found_section)
1425 return find_section;
1426
1427 if (be_quiet)
1428 return NULL;
1429 else
1430 return " - ";
1431 }
1432
1433 /* scan an elf file and show all the fun stuff */
1434 #define prints(str) ({ ssize_t ret = write(fileno(stdout), str, strlen(str)); ret; })
1435 static int scanelf_elfobj(elfobj *elf)
1436 {
1437 unsigned long i;
1438 char found_pax, found_phdr, found_relro, found_load, found_textrel,
1439 found_rpath, found_needed, found_interp, found_bind, found_soname,
1440 found_sym, found_lib, found_file, found_textrels, found_section;
1441 static char *out_buffer = NULL;
1442 static size_t out_len;
1443
1444 found_pax = found_phdr = found_relro = found_load = found_textrel = \
1445 found_rpath = found_needed = found_interp = found_bind = found_soname = \
1446 found_sym = found_lib = found_file = found_textrels = found_section = 0;
1447
1448 if (be_verbose > 2)
1449 printf("%s: scanning file {%s,%s}\n", elf->filename,
1450 get_elfeitype(EI_CLASS, elf->elf_class),
1451 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
1452 else if (be_verbose > 1)
1453 printf("%s: scanning file\n", elf->filename);
1454
1455 /* init output buffer */
1456 if (!out_buffer) {
1457 out_len = sizeof(char) * 80;
1458 out_buffer = xmalloc(out_len);
1459 }
1460 *out_buffer = '\0';
1461
1462 /* show the header */
1463 if (!be_quiet && show_banner) {
1464 for (i = 0; out_format[i]; ++i) {
1465 if (!IS_MODIFIER(out_format[i])) continue;
1466
1467 switch (out_format[++i]) {
1468 case '+': break;
1469 case '%': break;
1470 case '#': break;
1471 case 'F':
1472 case 'p':
1473 case 'f': prints("FILE "); found_file = 1; break;
1474 case 'o': prints(" TYPE "); break;
1475 case 'x': prints(" PAX "); break;
1476 case 'e': prints("STK/REL/PTL "); break;
1477 case 't': prints("TEXTREL "); break;
1478 case 'r': prints("RPATH "); break;
1479 case 'M': prints("CLASS "); break;
1480 case 'l':
1481 case 'n': prints("NEEDED "); break;
1482 case 'i': prints("INTERP "); break;
1483 case 'b': prints("BIND "); break;
1484 case 'Z': prints("SIZE "); break;
1485 case 'S': prints("SONAME "); break;
1486 case 's': prints("SYM "); break;
1487 case 'N': prints("LIB "); break;
1488 case 'T': prints("TEXTRELS "); break;
1489 case 'k': prints("SECTION "); break;
1490 case 'a': prints("ARCH "); break;
1491 case 'I': prints("OSABI "); break;
1492 case 'Y': prints("EABI "); break;
1493 case 'O': prints("PERM "); break;
1494 case 'D': prints("ENDIAN "); break;
1495 default: warnf("'%c' has no title ?", out_format[i]);
1496 }
1497 }
1498 if (!found_file) prints("FILE ");
1499 prints("\n");
1500 found_file = 0;
1501 show_banner = 0;
1502 }
1503
1504 /* dump all the good stuff */
1505 for (i = 0; out_format[i]; ++i) {
1506 const char *out;
1507 const char *tmp;
1508 static char ubuf[sizeof(unsigned long)*2];
1509 if (!IS_MODIFIER(out_format[i])) {
1510 xchrcat(&out_buffer, out_format[i], &out_len);
1511 continue;
1512 }
1513
1514 out = NULL;
1515 be_wewy_wewy_quiet = (out_format[i] == '#');
1516 be_semi_verbose = (out_format[i] == '+');
1517 switch (out_format[++i]) {
1518 case '+':
1519 case '%':
1520 case '#':
1521 xchrcat(&out_buffer, out_format[i], &out_len); break;
1522 case 'F':
1523 found_file = 1;
1524 if (be_wewy_wewy_quiet) break;
1525 xstrcat(&out_buffer, elf->filename, &out_len);
1526 break;
1527 case 'p':
1528 found_file = 1;
1529 if (be_wewy_wewy_quiet) break;
1530 tmp = elf->filename;
1531 if (search_path) {
1532 ssize_t len_search = strlen(search_path);
1533 ssize_t len_file = strlen(elf->filename);
1534 if (!strncmp(elf->filename, search_path, len_search) && \
1535 len_file > len_search)
1536 tmp += len_search;
1537 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
1538 }
1539 xstrcat(&out_buffer, tmp, &out_len);
1540 break;
1541 case 'f':
1542 found_file = 1;
1543 if (be_wewy_wewy_quiet) break;
1544 tmp = strrchr(elf->filename, '/');
1545 tmp = (tmp == NULL ? elf->filename : tmp+1);
1546 xstrcat(&out_buffer, tmp, &out_len);
1547 break;
1548 case 'o': out = get_elfetype(elf); break;
1549 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
1550 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
1551 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1552 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1553 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1554 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break;
1555 case 'D': out = get_endian(elf); break;
1556 case 'O': out = strfileperms(elf->filename); break;
1557 case 'n':
1558 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1559 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1560 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1561 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1562 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1563 case 'k': out = scanelf_file_sections(elf, &found_section); break;
1564 case 'a': out = get_elfemtype(elf); break;
1565 case 'I': out = get_elfosabi(elf); break;
1566 case 'Y': out = get_elf_eabi(elf); break;
1567 case 'Z': snprintf(ubuf, sizeof(ubuf), "%lu", (unsigned long)elf->len); out = ubuf; break;;
1568 default: warnf("'%c' has no scan code?", out_format[i]);
1569 }
1570 if (out)
1571 xstrcat(&out_buffer, out, &out_len);
1572 }
1573
1574 #define FOUND_SOMETHING() \
1575 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
1576 found_rpath || found_needed || found_interp || found_bind || \
1577 found_soname || found_sym || found_lib || found_textrels || found_section )
1578
1579 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
1580 xchrcat(&out_buffer, ' ', &out_len);
1581 xstrcat(&out_buffer, elf->filename, &out_len);
1582 }
1583 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
1584 puts(out_buffer);
1585 fflush(stdout);
1586 }
1587
1588 return 0;
1589 }
1590
1591 /* scan a single elf */
1592 static int scanelf_elf(const char *filename, int fd, size_t len)
1593 {
1594 int ret = 1;
1595 size_t n;
1596 const char *match_etype;
1597 elfobj *elf;
1598
1599 /* Verify this is a real ELF */
1600 if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1601 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1602 return 2;
1603 }
1604
1605 /* Possibly filter based on ELF bitness */
1606 switch (match_bits) {
1607 case 32:
1608 if (elf->elf_class != ELFCLASS32)
1609 goto done;
1610 break;
1611 case 64:
1612 if (elf->elf_class != ELFCLASS64)
1613 goto done;
1614 break;
1615 }
1616
1617 /* Possibly filter based on the ELF's e_type field */
1618 array_for_each(match_etypes, n, match_etype)
1619 if (etype_lookup(match_etype) == get_etype(elf))
1620 goto scanit;
1621 if (array_cnt(match_etypes))
1622 goto done;
1623
1624 scanit:
1625 ret = scanelf_elfobj(elf);
1626
1627 done:
1628 unreadelf(elf);
1629 return ret;
1630 }
1631
1632 /* scan an archive of elfs */
1633 static int scanelf_archive(const char *filename, int fd, size_t len)
1634 {
1635 archive_handle *ar;
1636 archive_member *m;
1637 char *ar_buffer;
1638 elfobj *elf;
1639
1640 ar = ar_open_fd(filename, fd);
1641 if (ar == NULL)
1642 return 1;
1643
1644 ar_buffer = mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1645 while ((m = ar_next(ar)) != NULL) {
1646 off_t cur_pos = lseek(fd, 0, SEEK_CUR);
1647 if (cur_pos == -1)
1648 errp("lseek() failed");
1649 elf = readelf_buffer(m->name, ar_buffer + cur_pos, m->size);
1650 if (elf) {
1651 scanelf_elfobj(elf);
1652 unreadelf(elf);
1653 }
1654 }
1655 munmap(ar_buffer, len);
1656
1657 return 0;
1658 }
1659 /* scan a file which may be an elf or an archive or some other magical beast */
1660 static int scanelf_fileat(int dir_fd, const char *filename, const struct stat *st_cache)
1661 {
1662 const struct stat *st = st_cache;
1663 struct stat symlink_st;
1664 int fd;
1665
1666 /* always handle regular files and handle symlinked files if no -y */
1667 if (S_ISLNK(st->st_mode)) {
1668 if (!scan_symlink)
1669 return 1;
1670 fstatat(dir_fd, filename, &symlink_st, 0);
1671 st = &symlink_st;
1672 }
1673
1674 if (!S_ISREG(st->st_mode)) {
1675 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1676 return 1;
1677 }
1678
1679 if (match_perms) {
1680 if ((st->st_mode | match_perms) != st->st_mode)
1681 return 1;
1682 }
1683 fd = openat(dir_fd, filename, (fix_elf ? O_RDWR : O_RDONLY) | O_CLOEXEC);
1684 if (fd == -1) {
1685 if (fix_elf && errno == ETXTBSY)
1686 warnp("%s: could not fix", filename);
1687 else if (be_verbose > 2)
1688 printf("%s: skipping file: %s\n", filename, strerror(errno));
1689 return 1;
1690 }
1691
1692 if (scanelf_elf(filename, fd, st->st_size) == 2) {
1693 /* if it isn't an ELF, maybe it's an .a archive */
1694 if (scan_archives)
1695 scanelf_archive(filename, fd, st->st_size);
1696
1697 /*
1698 * unreadelf() implicitly closes its fd, so only close it
1699 * when we are returning it in the non-ELF case
1700 */
1701 close(fd);
1702 }
1703
1704 return 0;
1705 }
1706
1707 /* scan a directory for ET_EXEC files and print when we find one */
1708 static int scanelf_dirat(int dir_fd, const char *path)
1709 {
1710 register DIR *dir;
1711 register struct dirent *dentry;
1712 struct stat st_top, st;
1713 char buf[__PAX_UTILS_PATH_MAX], *subpath;
1714 size_t pathlen = 0, len = 0;
1715 int ret = 0;
1716 int subdir_fd;
1717
1718 /* make sure path exists */
1719 if (fstatat(dir_fd, path, &st_top, AT_SYMLINK_NOFOLLOW) == -1) {
1720 if (be_verbose > 2) printf("%s: does not exist\n", path);
1721 return 1;
1722 }
1723
1724 /* ok, if it isn't a directory, assume we can open it */
1725 if (!S_ISDIR(st_top.st_mode))
1726 return scanelf_fileat(dir_fd, path, &st_top);
1727
1728 /* now scan the dir looking for fun stuff */
1729 subdir_fd = openat(dir_fd, path, O_RDONLY|O_CLOEXEC);
1730 if (subdir_fd == -1)
1731 dir = NULL;
1732 else
1733 dir = fdopendir(subdir_fd);
1734 if (dir == NULL) {
1735 if (subdir_fd != -1)
1736 close(subdir_fd);
1737 else if (be_verbose > 2)
1738 printf("%s: skipping dir: %s\n", path, strerror(errno));
1739 return 1;
1740 }
1741 if (be_verbose > 1) printf("%s: scanning dir\n", path);
1742
1743 subpath = stpcpy(buf, path);
1744 if (subpath[-1] != '/')
1745 *subpath++ = '/';
1746 pathlen = subpath - buf;
1747 while ((dentry = readdir(dir))) {
1748 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
1749 continue;
1750
1751 if (fstatat(subdir_fd, dentry->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1)
1752 continue;
1753
1754 len = strlen(dentry->d_name);
1755 if (len + pathlen + 1 >= sizeof(buf)) {
1756 warnf("Skipping '%s%s': len > sizeof(buf); %zu > %zu\n",
1757 path, dentry->d_name, len + pathlen + 1, sizeof(buf));
1758 continue;
1759 }
1760 memcpy(subpath, dentry->d_name, len);
1761 subpath[len] = '\0';
1762
1763 if (S_ISREG(st.st_mode))
1764 ret = scanelf_fileat(dir_fd, buf, &st);
1765 else if (dir_recurse && S_ISDIR(st.st_mode)) {
1766 if (dir_crossmount || (st_top.st_dev == st.st_dev))
1767 ret = scanelf_dirat(dir_fd, buf);
1768 }
1769 }
1770 closedir(dir);
1771
1772 return ret;
1773 }
1774 static int scanelf_dir(const char *path)
1775 {
1776 return scanelf_dirat(root_fd, root_rel_path(path));
1777 }
1778
1779 static int scanelf_from_file(const char *filename)
1780 {
1781 FILE *fp;
1782 char *p, *path;
1783 size_t len;
1784 int ret;
1785
1786 if (strcmp(filename, "-") == 0)
1787 fp = stdin;
1788 else if ((fp = fopen(filename, "r")) == NULL)
1789 return 1;
1790
1791 path = NULL;
1792 len = 0;
1793 ret = 0;
1794 while (getline(&path, &len, fp) != -1) {
1795 if ((p = strchr(path, '\n')) != NULL)
1796 *p = 0;
1797 search_path = path;
1798 ret = scanelf_dir(path);
1799 }
1800 free(path);
1801
1802 if (fp != stdin)
1803 fclose(fp);
1804
1805 return ret;
1806 }
1807
1808 #if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
1809
1810 static int _load_ld_cache_config(const char *fname)
1811 {
1812 FILE *fp = NULL;
1813 char *p, *path;
1814 size_t len;
1815 int curr_fd = -1;
1816
1817 fp = fopenat_r(root_fd, root_rel_path(fname));
1818 if (fp == NULL)
1819 return -1;
1820
1821 path = NULL;
1822 len = 0;
1823 while (getline(&path, &len, fp) != -1) {
1824 if ((p = strrchr(path, '\r')) != NULL)
1825 *p = 0;
1826 if ((p = strchr(path, '\n')) != NULL)
1827 *p = 0;
1828
1829 /* recursive includes of the same file will make this segfault. */
1830 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1831 glob_t gl;
1832 size_t x;
1833 const char *gpath;
1834
1835 /* re-use existing path buffer ... need to be creative */
1836 if (path[8] != '/')
1837 gpath = memcpy(path + 3, "/etc/", 5);
1838 else
1839 gpath = path + 8;
1840 if (root_fd != AT_FDCWD) {
1841 if (curr_fd == -1) {
1842 curr_fd = open(".", O_RDONLY|O_CLOEXEC);
1843 if (fchdir(root_fd))
1844 errp("unable to change to root dir");
1845 }
1846 gpath = root_rel_path(gpath);
1847 }
1848
1849 if (glob(gpath, 0, NULL, &gl) == 0) {
1850 for (x = 0; x < gl.gl_pathc; ++x) {
1851 /* try to avoid direct loops */
1852 if (strcmp(gl.gl_pathv[x], fname) == 0)
1853 continue;
1854 _load_ld_cache_config(gl.gl_pathv[x]);
1855 }
1856 globfree(&gl);
1857 }
1858
1859 /* failed globs are ignored by glibc */
1860 continue;
1861 }
1862
1863 if (*path != '/')
1864 continue;
1865
1866 xarraypush_str(ldpaths, path);
1867 }
1868 free(path);
1869
1870 fclose(fp);
1871
1872 if (curr_fd != -1) {
1873 if (fchdir(curr_fd))
1874 {/* don't care */}
1875 close(curr_fd);
1876 }
1877
1878 return 0;
1879 }
1880
1881 #elif defined(__FreeBSD__) || defined(__DragonFly__)
1882
1883 static int _load_ld_cache_config(const char *fname)
1884 {
1885 FILE *fp = NULL;
1886 char *b = NULL, *p;
1887 struct elfhints_hdr hdr;
1888
1889 fp = fopenat_r(root_fd, root_rel_path(fname));
1890 if (fp == NULL)
1891 return -1;
1892
1893 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
1894 hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
1895 fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
1896 {
1897 fclose(fp);
1898 return -1;
1899 }
1900
1901 b = xmalloc(hdr.dirlistlen + 1);
1902 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1903 fclose(fp);
1904 free(b);
1905 return -1;
1906 }
1907
1908 while ((p = strsep(&b, ":"))) {
1909 if (*p == '\0')
1910 continue;
1911 xarraypush_str(ldpaths, p);
1912 }
1913
1914 free(b);
1915 fclose(fp);
1916 return 0;
1917 }
1918
1919 #else
1920 #ifdef __ELF__
1921 #warning Cache config support not implemented for your target
1922 #endif
1923 static int _load_ld_cache_config(const char *fname)
1924 {
1925 return 0;
1926 }
1927 #endif
1928
1929 static void load_ld_cache_config(const char *fname)
1930 {
1931 bool scan_l, scan_ul, scan_ull;
1932 size_t n;
1933 const char *ldpath;
1934
1935 _load_ld_cache_config(fname);
1936
1937 scan_l = scan_ul = scan_ull = false;
1938 array_for_each(ldpaths, n, ldpath) {
1939 if (!scan_l && !strcmp(ldpath, "/lib")) scan_l = true;
1940 if (!scan_ul && !strcmp(ldpath, "/usr/lib")) scan_ul = true;
1941 if (!scan_ull && !strcmp(ldpath, "/usr/local/lib")) scan_ull = true;
1942 }
1943
1944 if (!scan_l) xarraypush_str(ldpaths, "/lib");
1945 if (!scan_ul) xarraypush_str(ldpaths, "/usr/lib");
1946 if (!scan_ull) xarraypush_str(ldpaths, "/usr/local/lib");
1947 }
1948
1949 /* scan /etc/ld.so.conf for paths */
1950 static void scanelf_ldpath(void)
1951 {
1952 size_t n;
1953 const char *ldpath;
1954
1955 array_for_each(ldpaths, n, ldpath)
1956 scanelf_dir(ldpath);
1957 }
1958
1959 /* scan env PATH for paths */
1960 static void scanelf_envpath(void)
1961 {
1962 char *path, *p;
1963
1964 path = getenv("PATH");
1965 if (!path)
1966 err("PATH is not set in your env !");
1967 path = xstrdup(path);
1968
1969 while ((p = strrchr(path, ':')) != NULL) {
1970 scanelf_dir(p + 1);
1971 *p = 0;
1972 }
1973
1974 free(path);
1975 }
1976
1977 /* usage / invocation handling functions */ /* Free Flags: c d j u w G H J K P Q U W */
1978 #define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:DIYO:ZCBhV"
1979 #define a_argument required_argument
1980 static struct option const long_opts[] = {
1981 {"path", no_argument, NULL, 'p'},
1982 {"ldpath", no_argument, NULL, 'l'},
1983 {"use-ldpath",no_argument, NULL, 129},
1984 {"root", a_argument, NULL, 128},
1985 {"recursive", no_argument, NULL, 'R'},
1986 {"mount", no_argument, NULL, 'm'},
1987 {"symlink", no_argument, NULL, 'y'},
1988 {"archives", no_argument, NULL, 'A'},
1989 {"ldcache", no_argument, NULL, 'L'},
1990 {"fix", no_argument, NULL, 'X'},
1991 {"setpax", a_argument, NULL, 'z'},
1992 {"pax", no_argument, NULL, 'x'},
1993 {"header", no_argument, NULL, 'e'},
1994 {"textrel", no_argument, NULL, 't'},
1995 {"rpath", no_argument, NULL, 'r'},
1996 {"needed", no_argument, NULL, 'n'},
1997 {"interp", no_argument, NULL, 'i'},
1998 {"bind", no_argument, NULL, 'b'},
1999 {"soname", no_argument, NULL, 'S'},
2000 {"symbol", a_argument, NULL, 's'},
2001 {"section", a_argument, NULL, 'k'},
2002 {"lib", a_argument, NULL, 'N'},
2003 {"gmatch", no_argument, NULL, 'g'},
2004 {"textrels", no_argument, NULL, 'T'},
2005 {"etype", a_argument, NULL, 'E'},
2006 {"bits", a_argument, NULL, 'M'},
2007 {"endian", no_argument, NULL, 'D'},
2008 {"osabi", no_argument, NULL, 'I'},
2009 {"eabi", no_argument, NULL, 'Y'},
2010 {"perms", a_argument, NULL, 'O'},
2011 {"size", no_argument, NULL, 'Z'},
2012 {"all", no_argument, NULL, 'a'},
2013 {"quiet", no_argument, NULL, 'q'},
2014 {"verbose", no_argument, NULL, 'v'},
2015 {"format", a_argument, NULL, 'F'},
2016 {"from", a_argument, NULL, 'f'},
2017 {"file", a_argument, NULL, 'o'},
2018 {"nocolor", no_argument, NULL, 'C'},
2019 {"nobanner", no_argument, NULL, 'B'},
2020 {"help", no_argument, NULL, 'h'},
2021 {"version", no_argument, NULL, 'V'},
2022 {NULL, no_argument, NULL, 0x0}
2023 };
2024
2025 static const char * const opts_help[] = {
2026 "Scan all directories in PATH environment",
2027 "Scan all directories in /etc/ld.so.conf",
2028 "Use ld.so.conf to show full path (use with -r/-n)",
2029 "Root directory (use with -l or -p)",
2030 "Scan directories recursively",
2031 "Don't recursively cross mount points",
2032 "Don't scan symlinks",
2033 "Scan archives (.a files)",
2034 "Utilize ld.so.cache to show full path (use with -r/-n)",
2035 "Try and 'fix' bad things (use with -r/-e)",
2036 "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
2037 "Print PaX markings",
2038 "Print GNU_STACK/PT_LOAD markings",
2039 "Print TEXTREL information",
2040 "Print RPATH information",
2041 "Print NEEDED information",
2042 "Print INTERP information",
2043 "Print BIND information",
2044 "Print SONAME information",
2045 "Find a specified symbol",
2046 "Find a specified section",
2047 "Find a specified library",
2048 "Use regex rather than string compare (with -s); specify twice for case insensitive",
2049 "Locate cause of TEXTREL",
2050 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
2051 "Print only ELF files matching numeric bits",
2052 "Print Endianness",
2053 "Print OSABI",
2054 "Print EABI (EM_ARM Only)",
2055 "Print only ELF files matching octal permissions",
2056 "Print ELF file size",
2057 "Print all useful/simple info\n",
2058 "Only output 'bad' things",
2059 "Be verbose (can be specified more than once)",
2060 "Use specified format for output",
2061 "Read input stream from a filename",
2062 "Write output stream to a filename",
2063 "Don't emit color in output",
2064 "Don't display the header",
2065 "Print this help and exit",
2066 "Print version and exit",
2067 NULL
2068 };
2069
2070 /* display usage and exit */
2071 static void usage(int status)
2072 {
2073 const char a_arg[] = "<arg>";
2074 size_t a_arg_len = strlen(a_arg) + 2;
2075 size_t i;
2076 int optlen;
2077 printf("* Scan ELF binaries for stuff\n\n"
2078 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
2079 printf("Options: -[%s]\n", PARSE_FLAGS);
2080
2081 /* prescan the --long opt length to auto-align */
2082 optlen = 0;
2083 for (i = 0; long_opts[i].name; ++i) {
2084 int l = strlen(long_opts[i].name);
2085 if (long_opts[i].has_arg == a_argument)
2086 l += a_arg_len;
2087 optlen = max(l, optlen);
2088 }
2089
2090 for (i = 0; long_opts[i].name; ++i) {
2091 /* first output the short flag if it has one */
2092 if (long_opts[i].val > '~')
2093 printf(" ");
2094 else
2095 printf(" -%c, ", long_opts[i].val);
2096
2097 /* then the long flag */
2098 if (long_opts[i].has_arg == no_argument)
2099 printf("--%-*s", optlen, long_opts[i].name);
2100 else
2101 printf("--%s %s %*s", long_opts[i].name, a_arg,
2102 (int)(optlen - strlen(long_opts[i].name) - a_arg_len), "");
2103
2104 /* finally the help text */
2105 printf("* %s\n", opts_help[i]);
2106 }
2107
2108 puts("\nFor more information, see the scanelf(1) manpage");
2109 exit(status);
2110 }
2111
2112 /* parse command line arguments and preform needed actions */
2113 #define do_pax_state(option, flag) \
2114 if (islower(option)) { \
2115 flags &= ~PF_##flag; \
2116 flags |= PF_NO##flag; \
2117 } else { \
2118 flags &= ~PF_NO##flag; \
2119 flags |= PF_##flag; \
2120 }
2121 static void parse_delimited(array_t *arr, char *arg, const char *delim)
2122 {
2123 char *ele = strtok(arg, delim);
2124 if (!ele) /* edge case: -s '' */
2125 xarraypush_str(arr, "");
2126 while (ele) {
2127 xarraypush_str(arr, ele);
2128 ele = strtok(NULL, delim);
2129 }
2130 }
2131 static int parseargs(int argc, char *argv[])
2132 {
2133 int i;
2134 const char *from_file = NULL;
2135 int ret = 0;
2136 char load_cache_config = 0;
2137
2138 opterr = 0;
2139 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
2140 switch (i) {
2141
2142 case 'V':
2143 printf("pax-utils-%s: %s compiled %s\n%s\n"
2144 "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
2145 VERSION, __FILE__, __DATE__, rcsid, argv0);
2146 exit(EXIT_SUCCESS);
2147 break;
2148 case 'h': usage(EXIT_SUCCESS); break;
2149 case 'f':
2150 if (from_file) warn("You prob don't want to specify -f twice");
2151 from_file = optarg;
2152 break;
2153 case 'E':
2154 /* historically, this was comma delimited */
2155 parse_delimited(match_etypes, optarg, ",");
2156 break;
2157 case 'M':
2158 match_bits = atoi(optarg);
2159 if (match_bits == 0) {
2160 if (strcmp(optarg, "ELFCLASS32") == 0)
2161 match_bits = 32;
2162 if (strcmp(optarg, "ELFCLASS64") == 0)
2163 match_bits = 64;
2164 }
2165 break;
2166 case 'O':
2167 if (sscanf(optarg, "%o", &match_perms) == -1)
2168 match_bits = 0;
2169 break;
2170 case 'o': {
2171 if (freopen(optarg, "w", stdout) == NULL)
2172 errp("Could not freopen(%s)", optarg);
2173 break;
2174 }
2175 case 'k':
2176 xarraypush_str(find_section_arr, optarg);
2177 break;
2178 case 's':
2179 /* historically, this was comma delimited */
2180 parse_delimited(find_sym_arr, optarg, ",");
2181 break;
2182 case 'N':
2183 xarraypush_str(find_lib_arr, optarg);
2184 break;
2185 case 'F': {
2186 if (out_format) warn("You prob don't want to specify -F twice");
2187 out_format = optarg;
2188 break;
2189 }
2190 case 'z': {
2191 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
2192 size_t x;
2193
2194 for (x = 0; x < strlen(optarg); x++) {
2195 switch (optarg[x]) {
2196 case 'p':
2197 case 'P':
2198 do_pax_state(optarg[x], PAGEEXEC);
2199 break;
2200 case 's':
2201 case 'S':
2202 do_pax_state(optarg[x], SEGMEXEC);
2203 break;
2204 case 'm':
2205 case 'M':
2206 do_pax_state(optarg[x], MPROTECT);
2207 break;
2208 case 'e':
2209 case 'E':
2210 do_pax_state(optarg[x], EMUTRAMP);
2211 break;
2212 case 'r':
2213 case 'R':
2214 do_pax_state(optarg[x], RANDMMAP);
2215 break;
2216 case 'x':
2217 case 'X':
2218 do_pax_state(optarg[x], RANDEXEC);
2219 break;
2220 default:
2221 break;
2222 }
2223 }
2224 if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
2225 ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
2226 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
2227 ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
2228 ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
2229 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
2230 setpax = flags;
2231 break;
2232 }
2233 case 'Z': show_size = 1; break;
2234 case 'g': ++g_match; break;
2235 case 'L': load_cache_config = use_ldcache = 1; break;
2236 case 'y': scan_symlink = 0; break;
2237 case 'A': scan_archives = 1; break;
2238 case 'C': color_init(true); break;
2239 case 'B': show_banner = 0; break;
2240 case 'l': load_cache_config = scan_ldpath = 1; break;
2241 case 'p': scan_envpath = 1; break;
2242 case 'R': dir_recurse = 1; break;
2243 case 'm': dir_crossmount = 0; break;
2244 case 'X': ++fix_elf; break;
2245 case 'x': show_pax = 1; break;
2246 case 'e': show_phdr = 1; break;
2247 case 't': show_textrel = 1; break;
2248 case 'r': show_rpath = 1; break;
2249 case 'n': show_needed = 1; break;
2250 case 'i': show_interp = 1; break;
2251 case 'b': show_bind = 1; break;
2252 case 'S': show_soname = 1; break;
2253 case 'T': show_textrels = 1; break;
2254 case 'q': be_quiet = min(be_quiet, 20) + 1; break;
2255 case 'v': be_verbose = min(be_verbose, 20) + 1; break;
2256 case 'a': show_perms = show_pax = show_phdr = show_textrel = show_rpath = show_bind = show_endian = 1; break;
2257 case 'D': show_endian = 1; break;
2258 case 'I': show_osabi = 1; break;
2259 case 'Y': show_eabi = 1; break;
2260 case 128:
2261 root_fd = open(optarg, O_RDONLY|O_CLOEXEC);
2262 if (root_fd == -1)
2263 err("Could not open root: %s", optarg);
2264 break;
2265 case 129: load_cache_config = use_ldpath = 1; break;
2266 case ':':
2267 err("Option '%c' is missing parameter", optopt);
2268 case '?':
2269 err("Unknown option '%c' or argument missing", optopt);
2270 default:
2271 err("Unhandled option '%c'; please report this", i);
2272 }
2273 }
2274 if (show_textrels && be_verbose)
2275 objdump = which("objdump", "OBJDUMP");
2276 /* precompile all the regexes */
2277 if (g_match) {
2278 regex_t preg;
2279 const char *this_sym;
2280 size_t n;
2281 int flags = REG_EXTENDED | REG_NOSUB | (g_match > 1 ? REG_ICASE : 0);
2282
2283 array_for_each(find_sym_arr, n, this_sym) {
2284 /* see scanelf_match_symname for logic info */
2285 switch (this_sym[0]) {
2286 case '%':
2287 while (*(this_sym++))
2288 if (*this_sym == '%') {
2289 ++this_sym;
2290 break;
2291 }
2292 break;
2293 case '+':
2294 case '-':
2295 ++this_sym;
2296 break;
2297 }
2298 if (*this_sym == '*')
2299 ++this_sym;
2300
2301 ret = regcomp(&preg, this_sym, flags);
2302 if (ret) {
2303 char err[256];
2304 regerror(ret, &preg, err, sizeof(err));
2305 err("regcomp of %s failed: %s", this_sym, err);
2306 }
2307 xarraypush(find_sym_regex_arr, &preg, sizeof(preg));
2308 }
2309 }
2310 /* flatten arrays for display */
2311 find_sym = array_flatten_str(find_sym_arr);
2312 find_lib = array_flatten_str(find_lib_arr);
2313 find_section = array_flatten_str(find_section_arr);
2314 /* let the format option override all other options */
2315 if (out_format) {
2316 show_pax = show_phdr = show_textrel = show_rpath = \
2317 show_needed = show_interp = show_bind = show_soname = \
2318 show_textrels = show_perms = show_endian = show_size = \
2319 show_osabi = show_eabi = 0;
2320 for (i = 0; out_format[i]; ++i) {
2321 if (!IS_MODIFIER(out_format[i])) continue;
2322
2323 switch (out_format[++i]) {
2324 case '+': break;
2325 case '%': break;
2326 case '#': break;
2327 case 'F': break;
2328 case 'p': break;
2329 case 'f': break;
2330 case 'k': break;
2331 case 's': break;
2332 case 'N': break;
2333 case 'o': break;
2334 case 'a': break;
2335 case 'M': break;
2336 case 'Z': show_size = 1; break;
2337 case 'D': show_endian = 1; break;
2338 case 'I': show_osabi = 1; break;
2339 case 'Y': show_eabi = 1; break;
2340 case 'O': show_perms = 1; break;
2341 case 'x': show_pax = 1; break;
2342 case 'e': show_phdr = 1; break;
2343 case 't': show_textrel = 1; break;
2344 case 'r': show_rpath = 1; break;
2345 case 'n': show_needed = 1; break;
2346 case 'i': show_interp = 1; break;
2347 case 'b': show_bind = 1; break;
2348 case 'S': show_soname = 1; break;
2349 case 'T': show_textrels = 1; break;
2350 default:
2351 err("invalid format specifier '%c' (byte %i)",
2352 out_format[i], i+1);
2353 }
2354 }
2355
2356 /* construct our default format */
2357 } else {
2358 size_t fmt_len = 30;
2359 out_format = xmalloc(sizeof(char) * fmt_len);
2360 *out_format = '\0';
2361 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
2362 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
2363 if (show_perms) xstrcat(&out_format, "%O ", &fmt_len);
2364 if (show_size) xstrcat(&out_format, "%Z ", &fmt_len);
2365 if (show_endian) xstrcat(&out_format, "%D ", &fmt_len);
2366 if (show_osabi) xstrcat(&out_format, "%I ", &fmt_len);
2367 if (show_eabi) xstrcat(&out_format, "%Y ", &fmt_len);
2368 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
2369 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
2370 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
2371 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
2372 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
2373 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
2374 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
2375 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
2376 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
2377 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
2378 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
2379 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
2380 }
2381 if (be_verbose > 2) printf("Format: %s\n", out_format);
2382
2383 /* now lets actually do the scanning */
2384 if (load_cache_config)
2385 load_ld_cache_config(__PAX_UTILS_DEFAULT_LD_CACHE_CONFIG);
2386 if (scan_ldpath) scanelf_ldpath();
2387 if (scan_envpath) scanelf_envpath();
2388 if (!from_file && optind == argc && ttyname(0) == NULL && !scan_ldpath && !scan_envpath)
2389 from_file = "-";
2390 if (from_file) {
2391 scanelf_from_file(from_file);
2392 from_file = *argv;
2393 }
2394 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
2395 err("Nothing to scan !?");
2396 while (optind < argc) {
2397 search_path = argv[optind++];
2398 ret = scanelf_dir(search_path);
2399 }
2400
2401 #ifdef __PAX_UTILS_CLEANUP
2402 /* clean up */
2403 xarrayfree(ldpaths);
2404 xarrayfree(find_sym_arr);
2405 xarrayfree(find_lib_arr);
2406 xarrayfree(find_section_arr);
2407 free(find_sym);
2408 free(find_lib);
2409 free(find_section);
2410 {
2411 size_t n;
2412 regex_t *preg;
2413 array_for_each(find_sym_regex_arr, n, preg)
2414 regfree(preg);
2415 xarrayfree(find_sym_regex_arr);
2416 }
2417
2418 if (ldcache != 0)
2419 munmap(ldcache, ldcache_size);
2420 #endif
2421
2422 return ret;
2423 }
2424
2425 static char **get_split_env(const char *envvar)
2426 {
2427 const char *delims = " \t\n";
2428 char **envvals = NULL;
2429 char *env, *s;
2430 int nentry;
2431
2432 if ((env = getenv(envvar)) == NULL)
2433 return NULL;
2434
2435 env = xstrdup(env);
2436 if (env == NULL)
2437 return NULL;
2438
2439 s = strtok(env, delims);
2440 if (s == NULL) {
2441 free(env);
2442 return NULL;
2443 }
2444
2445 nentry = 0;
2446 while (s != NULL) {
2447 ++nentry;
2448 envvals = xrealloc(envvals, sizeof(*envvals) * (nentry+1));
2449 envvals[nentry-1] = s;
2450 s = strtok(NULL, delims);
2451 }
2452 envvals[nentry] = NULL;
2453
2454 /* don't want to free(env) as it contains the memory that backs
2455 * the envvals array of strings */
2456 return envvals;
2457 }
2458
2459 static void parseenv(void)
2460 {
2461 color_init(false);
2462 qa_textrels = get_split_env("QA_TEXTRELS");
2463 qa_execstack = get_split_env("QA_EXECSTACK");
2464 qa_wx_load = get_split_env("QA_WX_LOAD");
2465 }
2466
2467 #ifdef __PAX_UTILS_CLEANUP
2468 static void cleanup(void)
2469 {
2470 free(out_format);
2471 free(qa_textrels);
2472 free(qa_execstack);
2473 free(qa_wx_load);
2474 }
2475 #endif
2476
2477 int main(int argc, char *argv[])
2478 {
2479 int ret;
2480 if (argc < 2)
2481 usage(EXIT_FAILURE);
2482 parseenv();
2483 ret = parseargs(argc, argv);
2484 fclose(stdout);
2485 #ifdef __PAX_UTILS_CLEANUP
2486 cleanup();
2487 warn("The calls to add/delete heap should be off:\n"
2488 "\t- 1 due to the out_buffer not being freed in scanelf_fileat()\n"
2489 "\t- 1 per QA_TEXTRELS/QA_EXECSTACK/QA_WX_LOAD");
2490 #endif
2491 return ret;
2492 }
2493
2494 /* Match filename against entries in matchlist, return TRUE
2495 * if the file is listed */
2496 static int file_matches_list(const char *filename, char **matchlist)
2497 {
2498 char **file;
2499 char *match;
2500 char buf[__PAX_UTILS_PATH_MAX];
2501
2502 if (matchlist == NULL)
2503 return 0;
2504
2505 for (file = matchlist; *file != NULL; file++) {
2506 if (search_path) {
2507 snprintf(buf, sizeof(buf), "%s%s", search_path, *file);
2508 match = buf;
2509 } else {
2510 match = *file;
2511 }
2512 if (fnmatch(match, filename, 0) == 0)
2513 return 1;
2514 }
2515 return 0;
2516 }

  ViewVC Help
Powered by ViewVC 1.1.20