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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.197 - (show annotations) (download) (as text)
Mon Nov 17 18:03:38 2008 UTC (6 years, 1 month ago) by flameeyes
Branch: MAIN
Changes since 1.196: +32 -15 lines
File MIME type: text/x-csrc
Rewrite symbol matching code in scanelf.

The previous code was entirely broken when trying to match symbols in
unstripped binaries when giving multiple symbols as target.

The new code differs from the old one in quite a few ways:

- debug output when -g option is passed is disabled in the code
  (hacky, but still better than before!);

- regular expression matching is actually used when -g option is
  passed;

- by default, the symbol name is tested against the symbol name
  without version; no-version symbol name matching is possible by
  using the -g option and adding a final $;

- multiple symbols and single symbols are handled in the same way so
  that there is no more difference between them;

- the returned symbol name is the actual symbol name as found in the
  file, so includes the version when the file is not stripped.

While this means that there are some minimal differences between the
previous code, it's arguable that this code is less buggy and more
consistent than the one before.

1 /*
2 * Copyright 2003-2007 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.196 2008/10/22 17:03:17 flameeyes Exp $
5 *
6 * Copyright 2003-2007 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2004-2007 Mike Frysinger - <vapier@gentoo.org>
8 */
9
10 static const char *rcsid = "$Id: scanelf.c,v 1.196 2008/10/22 17:03:17 flameeyes Exp $";
11 const char * const 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 static int scanelf_elfobj(elfobj *elf);
20 static int scanelf_elf(const char *filename, int fd, size_t len);
21 static int scanelf_archive(const char *filename, int fd, size_t len);
22 static int scanelf_file(const char *filename, const struct stat *st_cache);
23 static int scanelf_dir(const char *path);
24 static void scanelf_ldpath(void);
25 static void scanelf_envpath(void);
26 static void usage(int status);
27 static char **get_split_env(const char *envvar);
28 static void parseenv(void);
29 static int parseargs(int argc, char *argv[]);
30 static int rematch(const char *regex, const char *match, int cflags);
31
32 /* variables to control behavior */
33 static char match_etypes[126] = "";
34 static char *ldpaths[256];
35 static char scan_ldpath = 0;
36 static char scan_envpath = 0;
37 static char scan_symlink = 1;
38 static char scan_archives = 0;
39 static char dir_recurse = 0;
40 static char dir_crossmount = 1;
41 static char show_pax = 0;
42 static char show_perms = 0;
43 static char show_size = 0;
44 static char show_phdr = 0;
45 static char show_textrel = 0;
46 static char show_rpath = 0;
47 static char show_needed = 0;
48 static char show_interp = 0;
49 static char show_bind = 0;
50 static char show_soname = 0;
51 static char show_textrels = 0;
52 static char show_banner = 1;
53 static char show_endian = 0;
54 static char show_osabi = 0;
55 static char show_eabi = 0;
56 static char be_quiet = 0;
57 static char be_verbose = 0;
58 static char be_wewy_wewy_quiet = 0;
59 static char be_semi_verbose = 0;
60 static char *find_sym = NULL, *versioned_symname = NULL;
61 static char *find_lib = NULL;
62 static char *find_section = NULL;
63 static char *out_format = NULL;
64 static char *search_path = NULL;
65 static char fix_elf = 0;
66 static char g_match = 0;
67 static char use_ldcache = 0;
68
69 static char **qa_textrels = NULL;
70 static char **qa_execstack = NULL;
71 static char **qa_wx_load = NULL;
72
73 int match_bits = 0;
74 unsigned int match_perms = 0;
75 caddr_t ldcache = 0;
76 size_t ldcache_size = 0;
77 unsigned long setpax = 0UL;
78
79 int has_objdump = 0;
80
81 static char *getstr_perms(const char *fname);
82 static char *getstr_perms(const char *fname)
83 {
84 struct stat st;
85 static char buf[8];
86
87 if ((stat(fname, &st)) == (-1))
88 return (char *) "";
89
90 snprintf(buf, sizeof(buf), "%o", st.st_mode);
91
92 return (char *) buf + 2;
93 }
94
95 /* find the path to a file by name */
96 static char *which(const char *fname)
97 {
98 static char fullpath[BUFSIZ];
99 char *path, *p;
100
101 path = getenv("PATH");
102 if (!path)
103 return NULL;
104
105 path = xstrdup(path);
106 while ((p = strrchr(path, ':')) != NULL) {
107 snprintf(fullpath, sizeof(fullpath), "%s/%s", p + 1, fname);
108 *p = 0;
109 if (access(fullpath, R_OK) != (-1)) {
110 free(path);
111 return (char *) fullpath;
112 }
113 }
114 free(path);
115 return NULL;
116 }
117
118 /* 1 on failure. 0 otherwise */
119 static int rematch(const char *regex, const char *match, int cflags)
120 {
121 regex_t preg;
122 int ret;
123
124 if ((match == NULL) || (regex == NULL))
125 return EXIT_FAILURE;
126
127 if ((ret = regcomp(&preg, regex, cflags))) {
128 char err[256];
129
130 if (regerror(ret, &preg, err, sizeof(err)))
131 fprintf(stderr, "regcomp failed: %s", err);
132 else
133 fprintf(stderr, "regcomp failed");
134
135 return EXIT_FAILURE;
136 }
137 ret = regexec(&preg, match, 0, NULL, 0);
138 regfree(&preg);
139
140 return ret;
141 }
142
143 /* sub-funcs for scanelf_file() */
144 static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
145 {
146 /* find the best SHT_DYNSYM and SHT_STRTAB sections */
147 #define GET_SYMTABS(B) \
148 if (elf->elf_class == ELFCLASS ## B) { \
149 Elf ## B ## _Shdr *symtab, *strtab, *dynsym, *dynstr; \
150 /* debug sections */ \
151 symtab = SHDR ## B (elf_findsecbyname(elf, ".symtab")); \
152 strtab = SHDR ## B (elf_findsecbyname(elf, ".strtab")); \
153 /* runtime sections */ \
154 dynsym = SHDR ## B (elf_findsecbyname(elf, ".dynsym")); \
155 dynstr = SHDR ## B (elf_findsecbyname(elf, ".dynstr")); \
156 if (symtab && dynsym) { \
157 *sym = (void*)((EGET(symtab->sh_size) > EGET(dynsym->sh_size)) ? symtab : dynsym); \
158 } else { \
159 *sym = (void*)(symtab ? symtab : dynsym); \
160 } \
161 if (strtab && dynstr) { \
162 *tab = (void*)((EGET(strtab->sh_size) > EGET(dynstr->sh_size)) ? strtab : dynstr); \
163 } else { \
164 *tab = (void*)(strtab ? strtab : dynstr); \
165 } \
166 }
167 GET_SYMTABS(32)
168 GET_SYMTABS(64)
169 }
170
171 static char *scanelf_file_pax(elfobj *elf, char *found_pax)
172 {
173 static char ret[7];
174 unsigned long i, shown;
175
176 if (!show_pax) return NULL;
177
178 shown = 0;
179 memset(&ret, 0, sizeof(ret));
180
181 if (elf->phdr) {
182 #define SHOW_PAX(B) \
183 if (elf->elf_class == ELFCLASS ## B) { \
184 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
185 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
186 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
187 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
188 continue; \
189 if (fix_elf && setpax) { \
190 /* set the paxctl flags */ \
191 ESET(phdr[i].p_flags, setpax); \
192 } \
193 if (be_quiet && (EGET(phdr[i].p_flags) == (PF_NOEMUTRAMP | PF_NORANDEXEC))) \
194 continue; \
195 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
196 *found_pax = 1; \
197 ++shown; \
198 break; \
199 } \
200 }
201 SHOW_PAX(32)
202 SHOW_PAX(64)
203 }
204
205 if (fix_elf && setpax) {
206 /* set the chpax settings */
207 if (elf->elf_class == ELFCLASS32) {
208 if (EHDR32(elf->ehdr)->e_type == ET_DYN || EHDR32(elf->ehdr)->e_type == ET_EXEC)
209 ESET(EHDR32(elf->ehdr)->e_ident[EI_PAX], pax_pf2hf_flags(setpax));
210 } else {
211 if (EHDR64(elf->ehdr)->e_type == ET_DYN || EHDR64(elf->ehdr)->e_type == ET_EXEC)
212 ESET(EHDR64(elf->ehdr)->e_ident[EI_PAX], pax_pf2hf_flags(setpax));
213 }
214 }
215
216 /* fall back to EI_PAX if no PT_PAX was found */
217 if (!*ret) {
218 static char *paxflags;
219 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
220 if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
221 *found_pax = 1;
222 return (be_wewy_wewy_quiet ? NULL : paxflags);
223 }
224 strncpy(ret, paxflags, sizeof(ret));
225 }
226
227 if (be_wewy_wewy_quiet || (be_quiet && !shown))
228 return NULL;
229 else
230 return ret;
231 }
232
233 static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
234 {
235 static char ret[12];
236 char *found;
237 unsigned long i, shown, multi_stack, multi_relro, multi_load;
238 int max_pt_load;
239
240 if (!show_phdr) return NULL;
241
242 memcpy(ret, "--- --- ---\0", 12);
243
244 shown = 0;
245 multi_stack = multi_relro = multi_load = 0;
246 max_pt_load = elf_max_pt_load(elf);
247
248 #define NOTE_GNU_STACK ".note.GNU-stack"
249 #define SHOW_PHDR(B) \
250 if (elf->elf_class == ELFCLASS ## B) { \
251 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
252 Elf ## B ## _Off offset; \
253 uint32_t flags, check_flags; \
254 if (elf->phdr != NULL) { \
255 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
256 for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \
257 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
258 if (multi_stack++) \
259 warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
260 if (file_matches_list(elf->filename, qa_execstack)) \
261 continue; \
262 found = found_phdr; \
263 offset = 0; \
264 check_flags = PF_X; \
265 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
266 if (multi_relro++) \
267 warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
268 found = found_relro; \
269 offset = 4; \
270 check_flags = PF_X; \
271 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
272 if (EGET(ehdr->e_type) == ET_DYN || EGET(ehdr->e_type) == ET_EXEC) \
273 if (multi_load++ > max_pt_load) \
274 warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
275 if (file_matches_list(elf->filename, qa_wx_load)) \
276 continue; \
277 found = found_load; \
278 offset = 8; \
279 check_flags = PF_W|PF_X; \
280 } else \
281 continue; \
282 flags = EGET(phdr[i].p_flags); \
283 if (be_quiet && ((flags & check_flags) != check_flags)) \
284 continue; \
285 if ((EGET(phdr[i].p_type) != PT_LOAD) && (fix_elf && ((flags & PF_X) != flags))) { \
286 ESET(phdr[i].p_flags, flags & (PF_X ^ (size_t)-1)); \
287 ret[3] = ret[7] = '!'; \
288 flags = EGET(phdr[i].p_flags); \
289 } \
290 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \
291 *found = 1; \
292 ++shown; \
293 } \
294 } else if (elf->shdr != NULL) { \
295 /* no program headers which means this is prob an object file */ \
296 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
297 Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \
298 char *str; \
299 if ((void*)strtbl > (void*)elf->data_end) \
300 goto skip_this_shdr##B; \
301 check_flags = SHF_WRITE|SHF_EXECINSTR; \
302 for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \
303 if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \
304 offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \
305 str = elf->data + offset; \
306 if (str > elf->data + offset + sizeof(NOTE_GNU_STACK)) continue; \
307 if (!strcmp(str, NOTE_GNU_STACK)) { \
308 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \
309 flags = EGET(shdr[i].sh_flags); \
310 if (be_quiet && ((flags & check_flags) != check_flags)) \
311 continue; \
312 ++*found_phdr; \
313 shown = 1; \
314 if (flags & SHF_WRITE) ret[0] = 'W'; \
315 if (flags & SHF_ALLOC) ret[1] = 'A'; \
316 if (flags & SHF_EXECINSTR) ret[2] = 'X'; \
317 if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \
318 break; \
319 } \
320 } \
321 skip_this_shdr##B: \
322 if (!multi_stack) { \
323 if (file_matches_list(elf->filename, qa_execstack)) \
324 return NULL; \
325 *found_phdr = 1; \
326 shown = 1; \
327 memcpy(ret, "!WX", 3); \
328 } \
329 } \
330 }
331 SHOW_PHDR(32)
332 SHOW_PHDR(64)
333
334 if (be_wewy_wewy_quiet || (be_quiet && !shown))
335 return NULL;
336 else
337 return ret;
338 }
339
340 /*
341 * See if this ELF contains a DT_TEXTREL tag in any of its
342 * PT_DYNAMIC sections.
343 */
344 static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
345 {
346 static const char *ret = "TEXTREL";
347 unsigned long i;
348
349 if (!show_textrel && !show_textrels) return NULL;
350
351 if (file_matches_list(elf->filename, qa_textrels)) return NULL;
352
353 if (elf->phdr) {
354 #define SHOW_TEXTREL(B) \
355 if (elf->elf_class == ELFCLASS ## B) { \
356 Elf ## B ## _Dyn *dyn; \
357 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
358 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
359 Elf ## B ## _Off offset; \
360 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
361 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
362 offset = EGET(phdr[i].p_offset); \
363 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
364 dyn = DYN ## B (elf->data + offset); \
365 while (EGET(dyn->d_tag) != DT_NULL) { \
366 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
367 *found_textrel = 1; \
368 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
369 return (be_wewy_wewy_quiet ? NULL : ret); \
370 } \
371 ++dyn; \
372 } \
373 } }
374 SHOW_TEXTREL(32)
375 SHOW_TEXTREL(64)
376 }
377
378 if (be_quiet || be_wewy_wewy_quiet)
379 return NULL;
380 else
381 return " - ";
382 }
383
384 /*
385 * Scan the .text section to see if there are any relocations in it.
386 * Should rewrite this to check PT_LOAD sections that are marked
387 * Executable rather than the section named '.text'.
388 */
389 static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
390 {
391 unsigned long s, r, rmax;
392 void *symtab_void, *strtab_void, *text_void;
393
394 if (!show_textrels) return NULL;
395
396 /* don't search for TEXTREL's if the ELF doesn't have any */
397 if (!*found_textrel) scanelf_file_textrel(elf, found_textrel);
398 if (!*found_textrel) return NULL;
399
400 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
401 text_void = elf_findsecbyname(elf, ".text");
402
403 if (symtab_void && strtab_void && text_void && elf->shdr) {
404 #define SHOW_TEXTRELS(B) \
405 if (elf->elf_class == ELFCLASS ## B) { \
406 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
407 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
408 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
409 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
410 Elf ## B ## _Shdr *text = SHDR ## B (text_void); \
411 Elf ## B ## _Addr vaddr = EGET(text->sh_addr); \
412 uint ## B ## _t memsz = EGET(text->sh_size); \
413 Elf ## B ## _Rel *rel; \
414 Elf ## B ## _Rela *rela; \
415 /* search the section headers for relocations */ \
416 for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
417 uint32_t sh_type = EGET(shdr[s].sh_type); \
418 if (sh_type == SHT_REL) { \
419 rel = REL ## B (elf->data + EGET(shdr[s].sh_offset)); \
420 rela = NULL; \
421 rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
422 } else if (sh_type == SHT_RELA) { \
423 rel = NULL; \
424 rela = RELA ## B (elf->data + EGET(shdr[s].sh_offset)); \
425 rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
426 } else \
427 continue; \
428 /* now see if any of the relocs are in the .text */ \
429 for (r = 0; r < rmax; ++r) { \
430 unsigned long sym_max; \
431 Elf ## B ## _Addr offset_tmp; \
432 Elf ## B ## _Sym *func; \
433 Elf ## B ## _Sym *sym; \
434 Elf ## B ## _Addr r_offset; \
435 uint ## B ## _t r_info; \
436 if (sh_type == SHT_REL) { \
437 r_offset = EGET(rel[r].r_offset); \
438 r_info = EGET(rel[r].r_info); \
439 } else { \
440 r_offset = EGET(rela[r].r_offset); \
441 r_info = EGET(rela[r].r_info); \
442 } \
443 /* make sure this relocation is inside of the .text */ \
444 if (r_offset < vaddr || r_offset >= vaddr + memsz) { \
445 if (be_verbose <= 2) continue; \
446 } else \
447 *found_textrels = 1; \
448 /* locate this relocation symbol name */ \
449 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
450 if ((void*)sym > (void*)elf->data_end) { \
451 warn("%s: corrupt ELF symbol", elf->filename); \
452 continue; \
453 } \
454 sym_max = ELF ## B ## _R_SYM(r_info); \
455 if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
456 sym += sym_max; \
457 else \
458 sym = NULL; \
459 sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
460 /* show the raw details about this reloc */ \
461 printf(" %s: ", elf->base_filename); \
462 if (sym && sym->st_name) \
463 printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name))); \
464 else \
465 printf("(memory/data?)"); \
466 printf(" [0x%lX]", (unsigned long)r_offset); \
467 /* now try to find the closest symbol that this rel is probably in */ \
468 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
469 func = NULL; \
470 offset_tmp = 0; \
471 while (sym_max--) { \
472 if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
473 func = sym; \
474 offset_tmp = EGET(sym->st_value); \
475 } \
476 ++sym; \
477 } \
478 printf(" in "); \
479 if (func && func->st_name) { \
480 const char *func_name = elf->data + EGET(strtab->sh_offset) + EGET(func->st_name); \
481 if (r_offset > EGET(func->st_size)) \
482 printf("(optimized out: previous %s)", func_name); \
483 else \
484 printf("%s", func_name); \
485 } else \
486 printf("(optimized out)"); \
487 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
488 if (be_verbose && has_objdump) { \
489 Elf ## B ## _Addr end_addr = offset_tmp + EGET(func->st_size); \
490 char *sysbuf; \
491 size_t syslen; \
492 const char sysfmt[] = "objdump -r -R -d -w -l --start-address=0x%lX --stop-address=0x%lX %s | grep --color -i -C 3 '.*[[:space:]]%lX:[[:space:]]*R_.*'\n"; \
493 syslen = sizeof(sysfmt) + strlen(elf->filename) + 3 * sizeof(unsigned long) + 1; \
494 sysbuf = xmalloc(syslen); \
495 if (end_addr < r_offset) \
496 /* not uncommon when things are optimized out */ \
497 end_addr = r_offset + 0x100; \
498 snprintf(sysbuf, syslen, sysfmt, \
499 (unsigned long)offset_tmp, \
500 (unsigned long)end_addr, \
501 elf->filename, \
502 (unsigned long)r_offset); \
503 fflush(stdout); \
504 system(sysbuf); \
505 fflush(stdout); \
506 free(sysbuf); \
507 } \
508 } \
509 } }
510 SHOW_TEXTRELS(32)
511 SHOW_TEXTRELS(64)
512 }
513 if (!*found_textrels)
514 warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
515
516 return NULL;
517 }
518
519 static void rpath_security_checks(elfobj *, char *, const char *);
520 static void rpath_security_checks(elfobj *elf, char *item, const char *dt_type)
521 {
522 struct stat st;
523 switch (*item) {
524 case '/': break;
525 case '.':
526 warnf("Security problem with relative %s '%s' in %s", dt_type, item, elf->filename);
527 break;
528 case ':':
529 case '\0':
530 warnf("Security problem NULL %s in %s", dt_type, elf->filename);
531 break;
532 case '$':
533 if (fstat(elf->fd, &st) != -1)
534 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
535 warnf("Security problem with %s='%s' in %s with mode set of %o",
536 dt_type, item, elf->filename, (unsigned int) st.st_mode & 07777);
537 break;
538 default:
539 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename);
540 break;
541 }
542 }
543 static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
544 {
545 unsigned long i, s;
546 char *rpath, *runpath, **r;
547 void *strtbl_void;
548
549 if (!show_rpath) return;
550
551 strtbl_void = elf_findsecbyname(elf, ".dynstr");
552 rpath = runpath = NULL;
553
554 if (elf->phdr && strtbl_void) {
555 #define SHOW_RPATH(B) \
556 if (elf->elf_class == ELFCLASS ## B) { \
557 Elf ## B ## _Dyn *dyn; \
558 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
559 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
560 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
561 Elf ## B ## _Off offset; \
562 Elf ## B ## _Xword word; \
563 /* Scan all the program headers */ \
564 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
565 /* Just scan dynamic headers */ \
566 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
567 offset = EGET(phdr[i].p_offset); \
568 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
569 /* Just scan dynamic RPATH/RUNPATH headers */ \
570 dyn = DYN ## B (elf->data + offset); \
571 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
572 if (word == DT_RPATH) { \
573 r = &rpath; \
574 } else if (word == DT_RUNPATH) { \
575 r = &runpath; \
576 } else { \
577 ++dyn; \
578 continue; \
579 } \
580 /* Verify the memory is somewhat sane */ \
581 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
582 if (offset < (Elf ## B ## _Off)elf->len) { \
583 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
584 *r = (char*)(elf->data + offset); \
585 /* cache the length in case we need to nuke this section later on */ \
586 if (fix_elf) \
587 offset = strlen(*r); \
588 /* If quiet, don't output paths in ld.so.conf */ \
589 if (be_quiet) { \
590 size_t len; \
591 char *start, *end; \
592 /* note that we only 'chop' off leading known paths. */ \
593 /* since *r is read-only memory, we can only move the ptr forward. */ \
594 start = *r; \
595 /* scan each path in : delimited list */ \
596 while (start) { \
597 rpath_security_checks(elf, start, get_elfdtype(word)); \
598 end = strchr(start, ':'); \
599 len = (end ? abs(end - start) : strlen(start)); \
600 if (use_ldcache) \
601 for (s = 0; ldpaths[s]; ++s) \
602 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
603 *r = end; \
604 /* corner case ... if RPATH reads "/usr/lib:", we want \
605 * to show ':' rather than '' */ \
606 if (end && end[1] != '\0') \
607 (*r)++; \
608 break; \
609 } \
610 if (!*r || !end) \
611 break; \
612 else \
613 start = start + len + 1; \
614 } \
615 } \
616 if (*r) { \
617 if (fix_elf > 2 || (fix_elf && **r == '\0')) { \
618 /* just nuke it */ \
619 nuke_it##B: \
620 memset(*r, 0x00, offset); \
621 *r = NULL; \
622 ESET(dyn->d_tag, DT_DEBUG); \
623 ESET(dyn->d_un.d_ptr, 0); \
624 } else if (fix_elf) { \
625 /* try to clean "bad" paths */ \
626 size_t len, tmpdir_len; \
627 char *start, *end; \
628 const char *tmpdir; \
629 start = *r; \
630 tmpdir = (getenv("TMPDIR") ? : "."); \
631 tmpdir_len = strlen(tmpdir); \
632 while (1) { \
633 end = strchr(start, ':'); \
634 if (start == end) { \
635 eat_this_path##B: \
636 len = strlen(end); \
637 memmove(start, end+1, len); \
638 start[len-1] = '\0'; \
639 end = start - 1; \
640 } else if (tmpdir && !strncmp(start, tmpdir, tmpdir_len)) { \
641 if (!end) { \
642 if (start == *r) \
643 goto nuke_it##B; \
644 *--start = '\0'; \
645 } else \
646 goto eat_this_path##B; \
647 } \
648 if (!end) \
649 break; \
650 start = end + 1; \
651 } \
652 if (**r == '\0') \
653 goto nuke_it##B; \
654 } \
655 if (*r) \
656 *found_rpath = 1; \
657 } \
658 } \
659 ++dyn; \
660 } \
661 } }
662 SHOW_RPATH(32)
663 SHOW_RPATH(64)
664 }
665
666 if (be_wewy_wewy_quiet) return;
667
668 if (rpath && runpath) {
669 if (!strcmp(rpath, runpath)) {
670 xstrcat(ret, runpath, ret_len);
671 } else {
672 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
673 xchrcat(ret, '{', ret_len);
674 xstrcat(ret, rpath, ret_len);
675 xchrcat(ret, ',', ret_len);
676 xstrcat(ret, runpath, ret_len);
677 xchrcat(ret, '}', ret_len);
678 }
679 } else if (rpath || runpath)
680 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
681 else if (!be_quiet)
682 xstrcat(ret, " - ", ret_len);
683 }
684
685 #define LDSO_CACHE_MAGIC "ld.so-"
686 #define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
687 #define LDSO_CACHE_VER "1.7.0"
688 #define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
689 #define FLAG_ANY -1
690 #define FLAG_TYPE_MASK 0x00ff
691 #define FLAG_LIBC4 0x0000
692 #define FLAG_ELF 0x0001
693 #define FLAG_ELF_LIBC5 0x0002
694 #define FLAG_ELF_LIBC6 0x0003
695 #define FLAG_REQUIRED_MASK 0xff00
696 #define FLAG_SPARC_LIB64 0x0100
697 #define FLAG_IA64_LIB64 0x0200
698 #define FLAG_X8664_LIB64 0x0300
699 #define FLAG_S390_LIB64 0x0400
700 #define FLAG_POWERPC_LIB64 0x0500
701 #define FLAG_MIPS64_LIBN32 0x0600
702 #define FLAG_MIPS64_LIBN64 0x0700
703
704 static char *lookup_cache_lib(elfobj *, char *);
705
706 #if defined(__GLIBC__) || defined(__UCLIBC__)
707
708 static char *lookup_cache_lib(elfobj *elf, char *fname)
709 {
710 int fd = 0;
711 char *strs;
712 static char buf[__PAX_UTILS_PATH_MAX] = "";
713 const char *cachefile = "/etc/ld.so.cache";
714 struct stat st;
715
716 typedef struct {
717 char magic[LDSO_CACHE_MAGIC_LEN];
718 char version[LDSO_CACHE_VER_LEN];
719 int nlibs;
720 } header_t;
721 header_t *header;
722
723 typedef struct {
724 int flags;
725 int sooffset;
726 int liboffset;
727 } libentry_t;
728 libentry_t *libent;
729
730 if (fname == NULL)
731 return NULL;
732
733 if (ldcache == 0) {
734 if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY)) == -1)
735 return NULL;
736
737 /* cache these values so we only map/unmap the cache file once */
738 ldcache_size = st.st_size;
739 ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
740
741 close(fd);
742
743 if (ldcache == (caddr_t)-1) {
744 ldcache = 0;
745 return NULL;
746 }
747
748 if (memcmp(((header_t *) ldcache)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN))
749 return NULL;
750 if (memcmp (((header_t *) ldcache)->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
751 return NULL;
752 }
753
754 header = (header_t *) ldcache;
755 libent = (libentry_t *) (ldcache + sizeof(header_t));
756 strs = (char *) &libent[header->nlibs];
757
758 for (fd = 0; fd < header->nlibs; fd++) {
759 /* this should be more fine grained, but for now we assume that
760 * diff arches will not be cached together. and we ignore the
761 * the different multilib mips cases. */
762 if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
763 continue;
764 if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
765 continue;
766
767 if (strcmp(fname, strs + libent[fd].sooffset) != 0)
768 continue;
769 strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
770 }
771 return buf;
772 }
773 #elif defined(__NetBSD__)
774 static char *lookup_cache_lib(elfobj *elf, char *fname)
775 {
776 static char buf[__PAX_UTILS_PATH_MAX] = "";
777 static struct stat st;
778
779 char **ldpath;
780 for (ldpath = ldpaths; *ldpath != NULL; ldpath++) {
781 if ((unsigned) snprintf(buf, sizeof(buf), "%s/%s", *ldpath, fname) >= sizeof(buf))
782 continue; /* if the pathname is too long, or something went wrong, ignore */
783
784 if (stat(buf, &st) != 0)
785 continue; /* if the lib doesn't exist in *ldpath, look further */
786
787 /* NetBSD doesn't actually do sanity checks, it just loads the file
788 * and if that doesn't work, continues looking in other directories.
789 * This cannot easily be safely emulated, unfortunately. For now,
790 * just assume that if it exists, it's a valid library. */
791
792 return buf;
793 }
794
795 /* not found in any path */
796 return NULL;
797 }
798 #else
799 #ifdef __ELF__
800 #warning Cache support not implemented for your target
801 #endif
802 static char *lookup_cache_lib(elfobj *elf, char *fname)
803 {
804 return NULL;
805 }
806 #endif
807
808 static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
809 {
810 unsigned long i;
811 char *needed;
812 void *strtbl_void;
813 char *p;
814
815 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL;
816
817 strtbl_void = elf_findsecbyname(elf, ".dynstr");
818
819 if (elf->phdr && strtbl_void) {
820 #define SHOW_NEEDED(B) \
821 if (elf->elf_class == ELFCLASS ## B) { \
822 Elf ## B ## _Dyn *dyn; \
823 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
824 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
825 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
826 Elf ## B ## _Off offset; \
827 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
828 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
829 offset = EGET(phdr[i].p_offset); \
830 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
831 dyn = DYN ## B (elf->data + offset); \
832 while (EGET(dyn->d_tag) != DT_NULL) { \
833 if (EGET(dyn->d_tag) == DT_NEEDED) { \
834 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
835 if (offset >= (Elf ## B ## _Off)elf->len) { \
836 ++dyn; \
837 continue; \
838 } \
839 needed = (char*)(elf->data + offset); \
840 if (op == 0) { \
841 if (!be_wewy_wewy_quiet) { \
842 if (*found_needed) xchrcat(ret, ',', ret_len); \
843 if (use_ldcache) \
844 if ((p = lookup_cache_lib(elf, needed)) != NULL) \
845 needed = p; \
846 xstrcat(ret, needed, ret_len); \
847 } \
848 *found_needed = 1; \
849 } else { \
850 if (!strncmp(find_lib, needed, strlen( !g_match ? needed : find_lib))) { \
851 *found_lib = 1; \
852 return (be_wewy_wewy_quiet ? NULL : needed); \
853 } \
854 } \
855 } \
856 ++dyn; \
857 } \
858 } }
859 SHOW_NEEDED(32)
860 SHOW_NEEDED(64)
861 if (op == 0 && !*found_needed && be_verbose)
862 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
863 }
864
865 return NULL;
866 }
867 static char *scanelf_file_interp(elfobj *elf, char *found_interp)
868 {
869 void *strtbl_void;
870
871 if (!show_interp) return NULL;
872
873 strtbl_void = elf_findsecbyname(elf, ".interp");
874
875 if (strtbl_void) {
876 #define SHOW_INTERP(B) \
877 if (elf->elf_class == ELFCLASS ## B) { \
878 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
879 *found_interp = 1; \
880 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
881 }
882 SHOW_INTERP(32)
883 SHOW_INTERP(64)
884 }
885 return NULL;
886 }
887 static char *scanelf_file_bind(elfobj *elf, char *found_bind)
888 {
889 unsigned long i;
890 struct stat s;
891 char dynamic = 0;
892
893 if (!show_bind) return NULL;
894 if (!elf->phdr) return NULL;
895
896 #define SHOW_BIND(B) \
897 if (elf->elf_class == ELFCLASS ## B) { \
898 Elf ## B ## _Dyn *dyn; \
899 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
900 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
901 Elf ## B ## _Off offset; \
902 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
903 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
904 dynamic = 1; \
905 offset = EGET(phdr[i].p_offset); \
906 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
907 dyn = DYN ## B (elf->data + offset); \
908 while (EGET(dyn->d_tag) != DT_NULL) { \
909 if (EGET(dyn->d_tag) == DT_BIND_NOW || \
910 (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
911 { \
912 if (be_quiet) return NULL; \
913 *found_bind = 1; \
914 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
915 } \
916 ++dyn; \
917 } \
918 } \
919 }
920 SHOW_BIND(32)
921 SHOW_BIND(64)
922
923 if (be_wewy_wewy_quiet) return NULL;
924
925 /* don't output anything if quiet mode and the ELF is static or not setuid */
926 if (be_quiet && (!dynamic || (!fstat(elf->fd, &s) && !(s.st_mode & (S_ISUID|S_ISGID))))) {
927 return NULL;
928 } else {
929 *found_bind = 1;
930 return (char *) (dynamic ? "LAZY" : "STATIC");
931 }
932 }
933 static char *scanelf_file_soname(elfobj *elf, char *found_soname)
934 {
935 unsigned long i;
936 char *soname;
937 void *strtbl_void;
938
939 if (!show_soname) return NULL;
940
941 strtbl_void = elf_findsecbyname(elf, ".dynstr");
942
943 if (elf->phdr && strtbl_void) {
944 #define SHOW_SONAME(B) \
945 if (elf->elf_class == ELFCLASS ## B) { \
946 Elf ## B ## _Dyn *dyn; \
947 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
948 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
949 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
950 Elf ## B ## _Off offset; \
951 /* only look for soname in shared objects */ \
952 if (EGET(ehdr->e_type) != ET_DYN) \
953 return NULL; \
954 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
955 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
956 offset = EGET(phdr[i].p_offset); \
957 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
958 dyn = DYN ## B (elf->data + offset); \
959 while (EGET(dyn->d_tag) != DT_NULL) { \
960 if (EGET(dyn->d_tag) == DT_SONAME) { \
961 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
962 if (offset >= (Elf ## B ## _Off)elf->len) { \
963 ++dyn; \
964 continue; \
965 } \
966 soname = (char*)(elf->data + offset); \
967 *found_soname = 1; \
968 return (be_wewy_wewy_quiet ? NULL : soname); \
969 } \
970 ++dyn; \
971 } \
972 } }
973 SHOW_SONAME(32)
974 SHOW_SONAME(64)
975 }
976
977 return NULL;
978 }
979
980 static int scanelf_match_symname(const char *compare, const char *symname, const char *symname_ver) {
981 /* We do things differently when checking with regexp */
982 if (g_match) {
983 return rematch(symname, compare, REG_EXTENDED) == 0 ||
984 rematch(symname_ver, compare, REG_EXTENDED) == 0;
985 } else {
986 const size_t symname_len = strlen(symname);
987 if (strncmp(symname, compare, symname_len) == 0 &&
988 /* Accept unversioned symbol names */
989 (compare[symname_len] == '\0' || compare[symname_len] == '@'))
990 return 1;
991
992 return strcmp(symname_ver, symname) == 0;
993 }
994 }
995
996 static char *scanelf_file_sym(elfobj *elf, char *found_sym)
997 {
998 unsigned long i;
999 char *ret;
1000 void *symtab_void, *strtab_void;
1001
1002 if (!find_sym) return NULL;
1003 ret = find_sym;
1004
1005 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
1006
1007 if (symtab_void && strtab_void) {
1008 #define FIND_SYM(B) \
1009 if (elf->elf_class == ELFCLASS ## B) { \
1010 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
1011 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
1012 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
1013 unsigned long cnt = EGET(symtab->sh_entsize); \
1014 char *symname; \
1015 if (cnt) \
1016 cnt = EGET(symtab->sh_size) / cnt; \
1017 for (i = 0; i < cnt; ++i) { \
1018 if ((void*)sym > (void*)elf->data_end) { \
1019 warnf("%s: corrupt ELF symbols - aborting", elf->filename); \
1020 goto break_out; \
1021 } \
1022 if (sym->st_name) { \
1023 /* make sure the symbol name is in acceptable memory range */ \
1024 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
1025 if ((void*)symname > (void*)elf->data_end) { \
1026 warnf("%s: corrupt ELF symbols", elf->filename); \
1027 ++sym; \
1028 continue; \
1029 } \
1030 /* debug display ... show all symbols and some extra info */ \
1031 if (0 && g_match ? rematch(ret, symname, REG_EXTENDED) == 0 : *ret == '*') { \
1032 printf("%s(%s) %5lX %15s %s %s\n", \
1033 ((*found_sym == 0) ? "\n\t" : "\t"), \
1034 elf->base_filename, \
1035 (unsigned long)sym->st_size, \
1036 get_elfstttype(sym->st_info), \
1037 sym->st_shndx == SHN_UNDEF ? "U" : "D", symname); \
1038 *found_sym = 1; \
1039 } else { \
1040 /* allow the user to specify a comma delimited list of symbols to search for */ \
1041 char *this_sym, *this_sym_ver, *next_sym; \
1042 next_sym = ret; \
1043 this_sym_ver = versioned_symname; \
1044 while (next_sym) { \
1045 this_sym = next_sym; \
1046 if ((next_sym = strchr(this_sym, ','))) \
1047 next_sym += 1; /* Skip the comma */ \
1048 /* do we want a defined symbol ? */ \
1049 if (*this_sym == '+') { \
1050 if (sym->st_shndx == SHN_UNDEF) \
1051 continue; \
1052 ++this_sym; \
1053 ++this_sym_ver; \
1054 /* do we want an undefined symbol ? */ \
1055 } else if (*this_sym == '-') { \
1056 if (sym->st_shndx != SHN_UNDEF) \
1057 continue; \
1058 ++this_sym; \
1059 ++this_sym_ver; \
1060 } \
1061 if (next_sym) /* Copy it so that we don't have to worry about the final , */ \
1062 this_sym = strndup(this_sym, next_sym-this_sym); \
1063 /* ok, lets compare the name now */ \
1064 if (scanelf_match_symname(symname, this_sym, this_sym_ver)) { \
1065 if (be_semi_verbose) { \
1066 char buf[126]; \
1067 snprintf(buf, sizeof(buf), "%lX %s %s", \
1068 (unsigned long)sym->st_size, get_elfstttype(sym->st_info), this_sym); \
1069 ret = buf; \
1070 } else \
1071 ret = symname; \
1072 (*found_sym)++; \
1073 goto break_out; \
1074 } \
1075 if (next_sym) free(this_sym); \
1076 } \
1077 } \
1078 } \
1079 ++sym; \
1080 } }
1081 FIND_SYM(32)
1082 FIND_SYM(64)
1083 }
1084
1085 break_out:
1086 if (be_wewy_wewy_quiet) return NULL;
1087
1088 if (*find_sym != '*' && *found_sym)
1089 return ret;
1090 if (be_quiet)
1091 return NULL;
1092 else
1093 return (char *)" - ";
1094 }
1095
1096 static char *scanelf_file_sections(elfobj *elf, char *found_section)
1097 {
1098 if (!find_section)
1099 return NULL;
1100
1101 #define FIND_SECTION(B) \
1102 if (elf->elf_class == ELFCLASS ## B) { \
1103 int invert; \
1104 Elf ## B ## _Shdr *section; \
1105 invert = (*find_section == '!' ? 1 : 0); \
1106 section = SHDR ## B (elf_findsecbyname(elf, find_section+invert)); \
1107 if ((section == NULL && invert) || (section != NULL && !invert)) \
1108 *found_section = 1; \
1109 }
1110 FIND_SECTION(32)
1111 FIND_SECTION(64)
1112
1113 if (be_wewy_wewy_quiet)
1114 return NULL;
1115
1116 if (*found_section)
1117 return find_section;
1118
1119 if (be_quiet)
1120 return NULL;
1121 else
1122 return (char *)" - ";
1123 }
1124
1125 /* scan an elf file and show all the fun stuff */
1126 #define prints(str) write(fileno(stdout), str, strlen(str))
1127 static int scanelf_elfobj(elfobj *elf)
1128 {
1129 unsigned long i;
1130 char found_pax, found_phdr, found_relro, found_load, found_textrel,
1131 found_rpath, found_needed, found_interp, found_bind, found_soname,
1132 found_sym, found_lib, found_file, found_textrels, found_section;
1133 static char *out_buffer = NULL;
1134 static size_t out_len;
1135
1136 found_pax = found_phdr = found_relro = found_load = found_textrel = \
1137 found_rpath = found_needed = found_interp = found_bind = found_soname = \
1138 found_sym = found_lib = found_file = found_textrels = found_section = 0;
1139
1140 if (be_verbose > 2)
1141 printf("%s: scanning file {%s,%s}\n", elf->filename,
1142 get_elfeitype(EI_CLASS, elf->elf_class),
1143 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
1144 else if (be_verbose > 1)
1145 printf("%s: scanning file\n", elf->filename);
1146
1147 /* init output buffer */
1148 if (!out_buffer) {
1149 out_len = sizeof(char) * 80;
1150 out_buffer = xmalloc(out_len);
1151 }
1152 *out_buffer = '\0';
1153
1154 /* show the header */
1155 if (!be_quiet && show_banner) {
1156 for (i = 0; out_format[i]; ++i) {
1157 if (!IS_MODIFIER(out_format[i])) continue;
1158
1159 switch (out_format[++i]) {
1160 case '+': break;
1161 case '%': break;
1162 case '#': break;
1163 case 'F':
1164 case 'p':
1165 case 'f': prints("FILE "); found_file = 1; break;
1166 case 'o': prints(" TYPE "); break;
1167 case 'x': prints(" PAX "); break;
1168 case 'e': prints("STK/REL/PTL "); break;
1169 case 't': prints("TEXTREL "); break;
1170 case 'r': prints("RPATH "); break;
1171 case 'M': prints("CLASS "); break;
1172 case 'n': prints("NEEDED "); break;
1173 case 'i': prints("INTERP "); break;
1174 case 'b': prints("BIND "); break;
1175 case 'Z': prints("SIZE "); break;
1176 case 'S': prints("SONAME "); break;
1177 case 's': prints("SYM "); break;
1178 case 'N': prints("LIB "); break;
1179 case 'T': prints("TEXTRELS "); break;
1180 case 'k': prints("SECTION "); break;
1181 case 'a': prints("ARCH "); break;
1182 case 'I': prints("OSABI "); break;
1183 case 'Y': prints("EABI "); break;
1184 case 'O': prints("PERM "); break;
1185 case 'D': prints("ENDIAN "); break;
1186 default: warnf("'%c' has no title ?", out_format[i]);
1187 }
1188 }
1189 if (!found_file) prints("FILE ");
1190 prints("\n");
1191 found_file = 0;
1192 show_banner = 0;
1193 }
1194
1195 /* dump all the good stuff */
1196 for (i = 0; out_format[i]; ++i) {
1197 const char *out;
1198 const char *tmp;
1199 static char ubuf[sizeof(unsigned long)*2];
1200 if (!IS_MODIFIER(out_format[i])) {
1201 xchrcat(&out_buffer, out_format[i], &out_len);
1202 continue;
1203 }
1204
1205 out = NULL;
1206 be_wewy_wewy_quiet = (out_format[i] == '#');
1207 be_semi_verbose = (out_format[i] == '+');
1208 switch (out_format[++i]) {
1209 case '+':
1210 case '%':
1211 case '#':
1212 xchrcat(&out_buffer, out_format[i], &out_len); break;
1213 case 'F':
1214 found_file = 1;
1215 if (be_wewy_wewy_quiet) break;
1216 xstrcat(&out_buffer, elf->filename, &out_len);
1217 break;
1218 case 'p':
1219 found_file = 1;
1220 if (be_wewy_wewy_quiet) break;
1221 tmp = elf->filename;
1222 if (search_path) {
1223 ssize_t len_search = strlen(search_path);
1224 ssize_t len_file = strlen(elf->filename);
1225 if (!strncmp(elf->filename, search_path, len_search) && \
1226 len_file > len_search)
1227 tmp += len_search;
1228 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
1229 }
1230 xstrcat(&out_buffer, tmp, &out_len);
1231 break;
1232 case 'f':
1233 found_file = 1;
1234 if (be_wewy_wewy_quiet) break;
1235 tmp = strrchr(elf->filename, '/');
1236 tmp = (tmp == NULL ? elf->filename : tmp+1);
1237 xstrcat(&out_buffer, tmp, &out_len);
1238 break;
1239 case 'o': out = get_elfetype(elf); break;
1240 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
1241 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
1242 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1243 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1244 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1245 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break;
1246 case 'D': out = get_endian(elf); break;
1247 case 'O': out = getstr_perms(elf->filename); break;
1248 case 'n':
1249 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1250 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1251 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1252 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1253 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1254 case 'k': out = scanelf_file_sections(elf, &found_section); break;
1255 case 'a': out = get_elfemtype(elf); break;
1256 case 'I': out = get_elfosabi(elf); break;
1257 case 'Y': out = get_elf_eabi(elf); break;
1258 case 'Z': snprintf(ubuf, sizeof(ubuf), "%lu", (unsigned long)elf->len); out = ubuf; break;;
1259 default: warnf("'%c' has no scan code?", out_format[i]);
1260 }
1261 if (out) {
1262 /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */
1263 if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL)
1264 xstrncat(&out_buffer, out, &out_len, (tmp-out));
1265 else
1266 xstrcat(&out_buffer, out, &out_len);
1267 }
1268 }
1269
1270 #define FOUND_SOMETHING() \
1271 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
1272 found_rpath || found_needed || found_interp || found_bind || \
1273 found_soname || found_sym || found_lib || found_textrels || found_section )
1274
1275 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
1276 xchrcat(&out_buffer, ' ', &out_len);
1277 xstrcat(&out_buffer, elf->filename, &out_len);
1278 }
1279 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
1280 puts(out_buffer);
1281 fflush(stdout);
1282 }
1283
1284 return 0;
1285 }
1286
1287 /* scan a single elf */
1288 static int scanelf_elf(const char *filename, int fd, size_t len)
1289 {
1290 int ret = 1;
1291 elfobj *elf;
1292
1293 /* verify this is real ELF */
1294 if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1295 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1296 return ret;
1297 }
1298 switch (match_bits) {
1299 case 32:
1300 if (elf->elf_class != ELFCLASS32)
1301 goto label_done;
1302 break;
1303 case 64:
1304 if (elf->elf_class != ELFCLASS64)
1305 goto label_done;
1306 break;
1307 default: break;
1308 }
1309 if (strlen(match_etypes)) {
1310 char sbuf[126];
1311 strncpy(sbuf, match_etypes, sizeof(sbuf));
1312 if (strchr(match_etypes, ',') != NULL) {
1313 char *p;
1314 while ((p = strrchr(sbuf, ',')) != NULL) {
1315 *p = 0;
1316 if (etype_lookup(p+1) == get_etype(elf))
1317 goto label_ret;
1318 }
1319 }
1320 if (etype_lookup(sbuf) != get_etype(elf))
1321 goto label_done;
1322 }
1323
1324 label_ret:
1325 ret = scanelf_elfobj(elf);
1326
1327 label_done:
1328 unreadelf(elf);
1329 return ret;
1330 }
1331
1332 /* scan an archive of elfs */
1333 static int scanelf_archive(const char *filename, int fd, size_t len)
1334 {
1335 archive_handle *ar;
1336 archive_member *m;
1337 char *ar_buffer;
1338 elfobj *elf;
1339
1340 ar = ar_open_fd(filename, fd);
1341 if (ar == NULL)
1342 return 1;
1343
1344 ar_buffer = (char*)mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1345 while ((m=ar_next(ar)) != NULL) {
1346 elf = readelf_buffer(m->name, ar_buffer+lseek(fd,0,SEEK_CUR), m->size);
1347 if (elf) {
1348 scanelf_elfobj(elf);
1349 unreadelf(elf);
1350 }
1351 }
1352 munmap(ar_buffer, len);
1353
1354 return 0;
1355 }
1356 /* scan a file which may be an elf or an archive or some other magical beast */
1357 static int scanelf_file(const char *filename, const struct stat *st_cache)
1358 {
1359 const struct stat *st = st_cache;
1360 struct stat symlink_st;
1361 int fd;
1362
1363 /* always handle regular files and handle symlinked files if no -y */
1364 if (S_ISLNK(st->st_mode)) {
1365 if (!scan_symlink) return 1;
1366 stat(filename, &symlink_st);
1367 st = &symlink_st;
1368 }
1369
1370 if (!S_ISREG(st->st_mode)) {
1371 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1372 return 1;
1373 }
1374
1375 if (match_perms) {
1376 if ((st->st_mode | match_perms) != st->st_mode)
1377 return 1;
1378 }
1379 if ((fd=open(filename, (fix_elf ? O_RDWR : O_RDONLY))) == -1)
1380 return 1;
1381
1382 if (scanelf_elf(filename, fd, st->st_size) == 1 && scan_archives)
1383 /* if it isn't an ELF, maybe it's an .a archive */
1384 scanelf_archive(filename, fd, st->st_size);
1385
1386 close(fd);
1387 return 0;
1388 }
1389
1390 /* scan a directory for ET_EXEC files and print when we find one */
1391 static int scanelf_dir(const char *path)
1392 {
1393 register DIR *dir;
1394 register struct dirent *dentry;
1395 struct stat st_top, st;
1396 char buf[__PAX_UTILS_PATH_MAX];
1397 size_t pathlen = 0, len = 0;
1398 int ret = 0;
1399
1400 /* make sure path exists */
1401 if (lstat(path, &st_top) == -1) {
1402 if (be_verbose > 2) printf("%s: does not exist\n", path);
1403 return 1;
1404 }
1405
1406 /* ok, if it isn't a directory, assume we can open it */
1407 if (!S_ISDIR(st_top.st_mode)) {
1408 return scanelf_file(path, &st_top);
1409 }
1410
1411 /* now scan the dir looking for fun stuff */
1412 if ((dir = opendir(path)) == NULL) {
1413 warnf("could not opendir %s: %s", path, strerror(errno));
1414 return 1;
1415 }
1416 if (be_verbose > 1) printf("%s: scanning dir\n", path);
1417
1418 pathlen = strlen(path);
1419 while ((dentry = readdir(dir))) {
1420 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
1421 continue;
1422 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
1423 if (len >= sizeof(buf)) {
1424 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
1425 (unsigned long)len, (unsigned long)sizeof(buf));
1426 continue;
1427 }
1428 snprintf(buf, sizeof(buf), "%s%s%s", path, (path[pathlen-1] == '/') ? "" : "/", dentry->d_name);
1429 if (lstat(buf, &st) != -1) {
1430 if (S_ISREG(st.st_mode))
1431 ret = scanelf_file(buf, &st);
1432 else if (dir_recurse && S_ISDIR(st.st_mode)) {
1433 if (dir_crossmount || (st_top.st_dev == st.st_dev))
1434 ret = scanelf_dir(buf);
1435 }
1436 }
1437 }
1438 closedir(dir);
1439 return ret;
1440 }
1441
1442 static int scanelf_from_file(const char *filename)
1443 {
1444 FILE *fp = NULL;
1445 char *p;
1446 char path[__PAX_UTILS_PATH_MAX];
1447 int ret = 0;
1448
1449 if (strcmp(filename, "-") == 0)
1450 fp = stdin;
1451 else if ((fp = fopen(filename, "r")) == NULL)
1452 return 1;
1453
1454 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1455 if ((p = strchr(path, '\n')) != NULL)
1456 *p = 0;
1457 search_path = path;
1458 ret = scanelf_dir(path);
1459 }
1460 if (fp != stdin)
1461 fclose(fp);
1462 return ret;
1463 }
1464
1465 #if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
1466
1467 static int load_ld_cache_config(int i, const char *fname)
1468 {
1469 FILE *fp = NULL;
1470 char *p;
1471 char path[__PAX_UTILS_PATH_MAX];
1472
1473 if (i + 1 == ARRAY_SIZE(ldpaths))
1474 return i;
1475
1476 if ((fp = fopen(fname, "r")) == NULL)
1477 return i;
1478
1479 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1480 if ((p = strrchr(path, '\r')) != NULL)
1481 *p = 0;
1482 if ((p = strchr(path, '\n')) != NULL)
1483 *p = 0;
1484 #ifdef __linux__
1485 /* recursive includes of the same file will make this segfault. */
1486 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1487 glob64_t gl;
1488 size_t x;
1489 char gpath[__PAX_UTILS_PATH_MAX];
1490
1491 memset(gpath, 0, sizeof(gpath));
1492
1493 if (path[8] != '/')
1494 snprintf(gpath, sizeof(gpath), "/etc/%s", &path[8]);
1495 else
1496 strncpy(gpath, &path[8], sizeof(gpath));
1497
1498 if ((glob64(gpath, 0, NULL, &gl)) == 0) {
1499 for (x = 0; x < gl.gl_pathc; ++x) {
1500 /* try to avoid direct loops */
1501 if (strcmp(gl.gl_pathv[x], fname) == 0)
1502 continue;
1503 i = load_ld_cache_config(i, gl.gl_pathv[x]);
1504 if (i + 1 >= ARRAY_SIZE(ldpaths)) {
1505 globfree64(&gl);
1506 return i;
1507 }
1508 }
1509 globfree64 (&gl);
1510 continue;
1511 }
1512 }
1513 #endif
1514 if (*path != '/')
1515 continue;
1516
1517 ldpaths[i++] = xstrdup(path);
1518
1519 if (i + 1 == ARRAY_SIZE(ldpaths))
1520 break;
1521 }
1522 ldpaths[i] = NULL;
1523
1524 fclose(fp);
1525 return i;
1526 }
1527
1528 #elif defined(__FreeBSD__) || (__DragonFly__)
1529
1530 static int load_ld_cache_config(int i, const char *fname)
1531 {
1532 FILE *fp = NULL;
1533 char *b = NULL, *p;
1534 struct elfhints_hdr hdr;
1535
1536 if (i + 1 == ARRAY_SIZE(ldpaths))
1537 return i;
1538
1539 if ((fp = fopen(fname, "r")) == NULL)
1540 return i;
1541
1542 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
1543 hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
1544 fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
1545 {
1546 fclose(fp);
1547 return i;
1548 }
1549
1550 b = xmalloc(hdr.dirlistlen + 1);
1551 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1552 fclose(fp);
1553 free(b);
1554 return i;
1555 }
1556
1557 while ((p = strsep(&b, ":"))) {
1558 if (*p == '\0') continue;
1559 ldpaths[i++] = xstrdup(p);
1560
1561 if (i + 1 == ARRAY_SIZE(ldpaths))
1562 break;
1563 }
1564 ldpaths[i] = NULL;
1565
1566 free(b);
1567 fclose(fp);
1568 return i;
1569 }
1570
1571 #else
1572 #ifdef __ELF__
1573 #warning Cache config support not implemented for your target
1574 #endif
1575 static int load_ld_cache_config(int i, const char *fname)
1576 {
1577 memset(ldpaths, 0x00, sizeof(ldpaths));
1578 return 0;
1579 }
1580 #endif
1581
1582 /* scan /etc/ld.so.conf for paths */
1583 static void scanelf_ldpath(void)
1584 {
1585 char scan_l, scan_ul, scan_ull;
1586 int i = 0;
1587
1588 if (!ldpaths[0])
1589 err("Unable to load any paths from ld.so.conf");
1590
1591 scan_l = scan_ul = scan_ull = 0;
1592
1593 while (ldpaths[i]) {
1594 if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
1595 if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
1596 if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
1597 scanelf_dir(ldpaths[i]);
1598 ++i;
1599 }
1600
1601 if (!scan_l) scanelf_dir("/lib");
1602 if (!scan_ul) scanelf_dir("/usr/lib");
1603 if (!scan_ull) scanelf_dir("/usr/local/lib");
1604 }
1605
1606 /* scan env PATH for paths */
1607 static void scanelf_envpath(void)
1608 {
1609 char *path, *p;
1610
1611 path = getenv("PATH");
1612 if (!path)
1613 err("PATH is not set in your env !");
1614 path = xstrdup(path);
1615
1616 while ((p = strrchr(path, ':')) != NULL) {
1617 scanelf_dir(p + 1);
1618 *p = 0;
1619 }
1620
1621 free(path);
1622 }
1623
1624 /* usage / invocation handling functions */ /* Free Flags: c d j u w C G H J K P Q U W */
1625 #define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:DIYO:ZBhV"
1626 #define a_argument required_argument
1627 static struct option const long_opts[] = {
1628 {"path", no_argument, NULL, 'p'},
1629 {"ldpath", no_argument, NULL, 'l'},
1630 {"recursive", no_argument, NULL, 'R'},
1631 {"mount", no_argument, NULL, 'm'},
1632 {"symlink", no_argument, NULL, 'y'},
1633 {"archives", no_argument, NULL, 'A'},
1634 {"ldcache", no_argument, NULL, 'L'},
1635 {"fix", no_argument, NULL, 'X'},
1636 {"setpax", a_argument, NULL, 'z'},
1637 {"pax", no_argument, NULL, 'x'},
1638 {"header", no_argument, NULL, 'e'},
1639 {"textrel", no_argument, NULL, 't'},
1640 {"rpath", no_argument, NULL, 'r'},
1641 {"needed", no_argument, NULL, 'n'},
1642 {"interp", no_argument, NULL, 'i'},
1643 {"bind", no_argument, NULL, 'b'},
1644 {"soname", no_argument, NULL, 'S'},
1645 {"symbol", a_argument, NULL, 's'},
1646 {"section", a_argument, NULL, 'k'},
1647 {"lib", a_argument, NULL, 'N'},
1648 {"gmatch", no_argument, NULL, 'g'},
1649 {"textrels", no_argument, NULL, 'T'},
1650 {"etype", a_argument, NULL, 'E'},
1651 {"bits", a_argument, NULL, 'M'},
1652 {"endian", no_argument, NULL, 'D'},
1653 {"osabi", no_argument, NULL, 'I'},
1654 {"eabi", no_argument, NULL, 'Y'},
1655 {"perms", a_argument, NULL, 'O'},
1656 {"size", no_argument, NULL, 'Z'},
1657 {"all", no_argument, NULL, 'a'},
1658 {"quiet", no_argument, NULL, 'q'},
1659 {"verbose", no_argument, NULL, 'v'},
1660 {"format", a_argument, NULL, 'F'},
1661 {"from", a_argument, NULL, 'f'},
1662 {"file", a_argument, NULL, 'o'},
1663 {"nobanner", no_argument, NULL, 'B'},
1664 {"help", no_argument, NULL, 'h'},
1665 {"version", no_argument, NULL, 'V'},
1666 {NULL, no_argument, NULL, 0x0}
1667 };
1668
1669 static const char *opts_help[] = {
1670 "Scan all directories in PATH environment",
1671 "Scan all directories in /etc/ld.so.conf",
1672 "Scan directories recursively",
1673 "Don't recursively cross mount points",
1674 "Don't scan symlinks",
1675 "Scan archives (.a files)",
1676 "Utilize ld.so.cache information (use with -r/-n)",
1677 "Try and 'fix' bad things (use with -r/-e)",
1678 "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
1679 "Print PaX markings",
1680 "Print GNU_STACK/PT_LOAD markings",
1681 "Print TEXTREL information",
1682 "Print RPATH information",
1683 "Print NEEDED information",
1684 "Print INTERP information",
1685 "Print BIND information",
1686 "Print SONAME information",
1687 "Find a specified symbol",
1688 "Find a specified section",
1689 "Find a specified library",
1690 "Use strncmp to match libraries. (use with -N)",
1691 "Locate cause of TEXTREL",
1692 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
1693 "Print only ELF files matching numeric bits",
1694 "Print Endianness",
1695 "Print OSABI",
1696 "Print EABI (EM_ARM Only)",
1697 "Print only ELF files matching octal permissions",
1698 "Print ELF file size",
1699 "Print all scanned info (-x -e -t -r -b)\n",
1700 "Only output 'bad' things",
1701 "Be verbose (can be specified more than once)",
1702 "Use specified format for output",
1703 "Read input stream from a filename",
1704 "Write output stream to a filename",
1705 "Don't display the header",
1706 "Print this help and exit",
1707 "Print version and exit",
1708 NULL
1709 };
1710
1711 /* display usage and exit */
1712 static void usage(int status)
1713 {
1714 unsigned long i;
1715 printf("* Scan ELF binaries for stuff\n\n"
1716 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
1717 printf("Options: -[%s]\n", PARSE_FLAGS);
1718 for (i = 0; long_opts[i].name; ++i)
1719 if (long_opts[i].has_arg == no_argument)
1720 printf(" -%c, --%-14s* %s\n", long_opts[i].val,
1721 long_opts[i].name, opts_help[i]);
1722 else
1723 printf(" -%c, --%-7s <arg> * %s\n", long_opts[i].val,
1724 long_opts[i].name, opts_help[i]);
1725
1726 puts("\nFor more information, see the scanelf(1) manpage");
1727 exit(status);
1728 }
1729
1730 /* parse command line arguments and preform needed actions */
1731 #define do_pax_state(option, flag) \
1732 if (islower(option)) { \
1733 flags &= ~PF_##flag; \
1734 flags |= PF_NO##flag; \
1735 } else { \
1736 flags &= ~PF_NO##flag; \
1737 flags |= PF_##flag; \
1738 }
1739 static int parseargs(int argc, char *argv[])
1740 {
1741 int i;
1742 const char *from_file = NULL;
1743 int ret = 0;
1744
1745 opterr = 0;
1746 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
1747 switch (i) {
1748
1749 case 'V':
1750 printf("pax-utils-%s: %s compiled %s\n%s\n"
1751 "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
1752 VERSION, __FILE__, __DATE__, rcsid, argv0);
1753 exit(EXIT_SUCCESS);
1754 break;
1755 case 'h': usage(EXIT_SUCCESS); break;
1756 case 'f':
1757 if (from_file) warn("You prob don't want to specify -f twice");
1758 from_file = optarg;
1759 break;
1760 case 'E':
1761 strncpy(match_etypes, optarg, sizeof(match_etypes));
1762 break;
1763 case 'M':
1764 match_bits = atoi(optarg);
1765 if (match_bits == 0) {
1766 if (strcmp(optarg, "ELFCLASS32") == 0)
1767 match_bits = 32;
1768 if (strcmp(optarg, "ELFCLASS64") == 0)
1769 match_bits = 64;
1770 }
1771 break;
1772 case 'O':
1773 if (sscanf(optarg, "%o", &match_perms) == (-1))
1774 match_bits = 0;
1775 break;
1776 case 'o': {
1777 if (freopen(optarg, "w", stdout) == NULL)
1778 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1779 break;
1780 }
1781 case 'k':
1782 if (find_section) warn("You prob don't want to specify -k twice");
1783 find_section = optarg;
1784 break;
1785 case 's': {
1786 if (find_sym) warn("You prob don't want to specify -s twice");
1787 find_sym = optarg;
1788 versioned_symname = xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
1789 sprintf(versioned_symname, "%s@", find_sym);
1790 break;
1791 }
1792 case 'N': {
1793 if (find_lib) warn("You prob don't want to specify -N twice");
1794 find_lib = optarg;
1795 break;
1796 }
1797
1798 case 'F': {
1799 if (out_format) warn("You prob don't want to specify -F twice");
1800 out_format = optarg;
1801 break;
1802 }
1803 case 'z': {
1804 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
1805 size_t x;
1806
1807 for (x = 0; x < strlen(optarg); x++) {
1808 switch (optarg[x]) {
1809 case 'p':
1810 case 'P':
1811 do_pax_state(optarg[x], PAGEEXEC);
1812 break;
1813 case 's':
1814 case 'S':
1815 do_pax_state(optarg[x], SEGMEXEC);
1816 break;
1817 case 'm':
1818 case 'M':
1819 do_pax_state(optarg[x], MPROTECT);
1820 break;
1821 case 'e':
1822 case 'E':
1823 do_pax_state(optarg[x], EMUTRAMP);
1824 break;
1825 case 'r':
1826 case 'R':
1827 do_pax_state(optarg[x], RANDMMAP);
1828 break;
1829 case 'x':
1830 case 'X':
1831 do_pax_state(optarg[x], RANDEXEC);
1832 break;
1833 default:
1834 break;
1835 }
1836 }
1837 if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
1838 ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
1839 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
1840 ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
1841 ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
1842 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
1843 setpax = flags;
1844 break;
1845 }
1846 case 'Z': show_size = 1; break;
1847 case 'g': g_match = 1; break;
1848 case 'L': use_ldcache = 1; break;
1849 case 'y': scan_symlink = 0; break;
1850 case 'A': scan_archives = 1; break;
1851 case 'B': show_banner = 0; break;
1852 case 'l': scan_ldpath = 1; break;
1853 case 'p': scan_envpath = 1; break;
1854 case 'R': dir_recurse = 1; break;
1855 case 'm': dir_crossmount = 0; break;
1856 case 'X': ++fix_elf; break;
1857 case 'x': show_pax = 1; break;
1858 case 'e': show_phdr = 1; break;
1859 case 't': show_textrel = 1; break;
1860 case 'r': show_rpath = 1; break;
1861 case 'n': show_needed = 1; break;
1862 case 'i': show_interp = 1; break;
1863 case 'b': show_bind = 1; break;
1864 case 'S': show_soname = 1; break;
1865 case 'T': show_textrels = 1; break;
1866 case 'q': be_quiet = 1; break;
1867 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1868 case 'a': show_perms = show_pax = show_phdr = show_textrel = show_rpath = show_bind = show_endian = 1; break;
1869 case 'D': show_endian = 1; break;
1870 case 'I': show_osabi = 1; break;
1871 case 'Y': show_eabi = 1; break;
1872 case ':':
1873 err("Option '%c' is missing parameter", optopt);
1874 case '?':
1875 err("Unknown option '%c' or argument missing", optopt);
1876 default:
1877 err("Unhandled option '%c'; please report this", i);
1878 }
1879 }
1880 if (show_textrels && be_verbose) {
1881 if (which("objdump") != NULL)
1882 has_objdump = 1;
1883 }
1884 /* let the format option override all other options */
1885 if (out_format) {
1886 show_pax = show_phdr = show_textrel = show_rpath = \
1887 show_needed = show_interp = show_bind = show_soname = \
1888 show_textrels = show_perms = show_endian = show_size = \
1889 show_osabi = show_eabi = 0;
1890 for (i = 0; out_format[i]; ++i) {
1891 if (!IS_MODIFIER(out_format[i])) continue;
1892
1893 switch (out_format[++i]) {
1894 case '+': break;
1895 case '%': break;
1896 case '#': break;
1897 case 'F': break;
1898 case 'p': break;
1899 case 'f': break;
1900 case 'k': break;
1901 case 's': break;
1902 case 'N': break;
1903 case 'o': break;
1904 case 'a': break;
1905 case 'M': break;
1906 case 'Z': show_size = 1; break;
1907 case 'D': show_endian = 1; break;
1908 case 'I': show_osabi = 1; break;
1909 case 'Y': show_eabi = 1; break;
1910 case 'O': show_perms = 1; break;
1911 case 'x': show_pax = 1; break;
1912 case 'e': show_phdr = 1; break;
1913 case 't': show_textrel = 1; break;
1914 case 'r': show_rpath = 1; break;
1915 case 'n': show_needed = 1; break;
1916 case 'i': show_interp = 1; break;
1917 case 'b': show_bind = 1; break;
1918 case 'S': show_soname = 1; break;
1919 case 'T': show_textrels = 1; break;
1920 default:
1921 err("Invalid format specifier '%c' (byte %i)",
1922 out_format[i], i+1);
1923 }
1924 }
1925
1926 /* construct our default format */
1927 } else {
1928 size_t fmt_len = 30;
1929 out_format = xmalloc(sizeof(char) * fmt_len);
1930 *out_format = '\0';
1931 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1932 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1933 if (show_perms) xstrcat(&out_format, "%O ", &fmt_len);
1934 if (show_size) xstrcat(&out_format, "%Z ", &fmt_len);
1935 if (show_endian) xstrcat(&out_format, "%D ", &fmt_len);
1936 if (show_osabi) xstrcat(&out_format, "%I ", &fmt_len);
1937 if (show_eabi) xstrcat(&out_format, "%Y ", &fmt_len);
1938 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1939 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1940 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1941 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1942 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1943 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1944 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1945 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1946 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1947 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
1948 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1949 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1950 }
1951 if (be_verbose > 2) printf("Format: %s\n", out_format);
1952
1953 /* now lets actually do the scanning */
1954 if (scan_ldpath || use_ldcache)
1955 load_ld_cache_config(0, __PAX_UTILS_DEFAULT_LD_CACHE_CONFIG);
1956 if (scan_ldpath) scanelf_ldpath();
1957 if (scan_envpath) scanelf_envpath();
1958 if (!from_file && optind == argc && ttyname(0) == NULL && !scan_ldpath && !scan_envpath)
1959 from_file = "-";
1960 if (from_file) {
1961 scanelf_from_file(from_file);
1962 from_file = *argv;
1963 }
1964 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
1965 err("Nothing to scan !?");
1966 while (optind < argc) {
1967 search_path = argv[optind++];
1968 ret = scanelf_dir(search_path);
1969 }
1970
1971 /* clean up */
1972 free(versioned_symname);
1973 for (i = 0; ldpaths[i]; ++i)
1974 free(ldpaths[i]);
1975
1976 if (ldcache != 0)
1977 munmap(ldcache, ldcache_size);
1978 return ret;
1979 }
1980
1981 static char **get_split_env(const char *envvar)
1982 {
1983 const char *delims = " \t\n";
1984 char **envvals = NULL;
1985 char *env, *s;
1986 int nentry;
1987
1988 if ((env = getenv(envvar)) == NULL)
1989 return NULL;
1990
1991 env = xstrdup(env);
1992 if (env == NULL)
1993 return NULL;
1994
1995 s = strtok(env, delims);
1996 if (s == NULL) {
1997 free(env);
1998 return NULL;
1999 }
2000
2001 nentry = 0;
2002 while (s != NULL) {
2003 ++nentry;
2004 envvals = xrealloc(envvals, sizeof(*envvals) * (nentry+1));
2005 envvals[nentry-1] = s;
2006 s = strtok(NULL, delims);
2007 }
2008 envvals[nentry] = NULL;
2009
2010 /* don't want to free(env) as it contains the memory that backs
2011 * the envvals array of strings */
2012 return envvals;
2013 }
2014
2015 static void parseenv(void)
2016 {
2017 qa_textrels = get_split_env("QA_TEXTRELS");
2018 qa_execstack = get_split_env("QA_EXECSTACK");
2019 qa_wx_load = get_split_env("QA_WX_LOAD");
2020 }
2021
2022 #ifdef __PAX_UTILS_CLEANUP
2023 static void cleanup(void)
2024 {
2025 free(out_format);
2026 free(qa_textrels);
2027 free(qa_execstack);
2028 free(qa_wx_load);
2029 }
2030 #endif
2031
2032 int main(int argc, char *argv[])
2033 {
2034 int ret;
2035 if (argc < 2)
2036 usage(EXIT_FAILURE);
2037 parseenv();
2038 ret = parseargs(argc, argv);
2039 fclose(stdout);
2040 #ifdef __PAX_UTILS_CLEANUP
2041 cleanup();
2042 warn("The calls to add/delete heap should be off:\n"
2043 "\t- 1 due to the out_buffer not being freed in scanelf_file()\n"
2044 "\t- 1 per QA_TEXTRELS/QA_EXECSTACK/QA_WX_LOAD");
2045 #endif
2046 return ret;
2047 }
2048
2049 /* Match filename against entries in matchlist, return TRUE
2050 * if the file is listed */
2051 static int file_matches_list(const char *filename, char **matchlist)
2052 {
2053 char **file;
2054 char *match;
2055 char buf[__PAX_UTILS_PATH_MAX];
2056
2057 if (matchlist == NULL)
2058 return 0;
2059
2060 for (file = matchlist; *file != NULL; file++) {
2061 if (search_path) {
2062 snprintf(buf, sizeof(buf), "%s%s", search_path, *file);
2063 match = buf;
2064 } else {
2065 match = *file;
2066 }
2067 if (fnmatch(match, filename, 0) == 0)
2068 return 1;
2069 }
2070 return 0;
2071 }

  ViewVC Help
Powered by ViewVC 1.1.20