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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.76 - (show annotations) (download) (as text)
Wed Jun 8 04:24:19 2005 UTC (9 years, 3 months ago) by vapier
Branch: MAIN
Changes since 1.75: +152 -35 lines
File MIME type: text/x-csrc
add support by kev quinn for showing textrels

1 /*
2 * Copyright 2003-2005 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.75 2005/06/06 23:32:38 vapier Exp $
5 *
6 ********************************************************************
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20 * MA 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <libgen.h>
27 #include <limits.h>
28 #define __USE_GNU
29 #include <string.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <dirent.h>
34 #include <getopt.h>
35 #include <assert.h>
36 #include "paxelf.h"
37
38 static const char *rcsid = "$Id: scanelf.c,v 1.75 2005/06/06 23:32:38 vapier Exp $";
39 #define argv0 "scanelf"
40
41 #define IS_MODIFIER(c) (c == '%' || c == '#')
42
43
44
45 /* prototypes */
46 static void scanelf_file(const char *filename);
47 static void scanelf_dir(const char *path);
48 static void scanelf_ldpath();
49 static void scanelf_envpath();
50 static void usage(int status);
51 static void parseargs(int argc, char *argv[]);
52 static char *xstrdup(const char *s);
53 static void *xmalloc(size_t size);
54 static void xstrcat(char **dst, const char *src, size_t *curr_len);
55 static inline void xchrcat(char **dst, const char append, size_t *curr_len);
56
57 /* variables to control behavior */
58 static char *ldpaths[256];
59 static char scan_ldpath = 0;
60 static char scan_envpath = 0;
61 static char scan_symlink = 1;
62 static char dir_recurse = 0;
63 static char dir_crossmount = 1;
64 static char show_pax = 0;
65 static char show_phdr = 0;
66 static char show_textrel = 0;
67 static char show_rpath = 0;
68 static char show_needed = 0;
69 static char show_interp = 0;
70 static char show_bind = 0;
71 static char show_textrels = 0;
72 static char show_banner = 1;
73 static char be_quiet = 0;
74 static char be_verbose = 0;
75 static char be_wewy_wewy_quiet = 0;
76 static char *find_sym = NULL, *versioned_symname = NULL;
77 static char *find_lib = NULL;
78 static char *out_format = NULL;
79 static char *search_path = NULL;
80
81
82
83 /* sub-funcs for scanelf_file() */
84 static char *scanelf_file_pax(elfobj *elf, char *found_pax)
85 {
86 static char *paxflags;
87 static char ret[7];
88 unsigned long i, shown;
89
90
91 if (!show_pax) return NULL;
92
93 shown = 0;
94 memset(&ret, 0, sizeof(ret));
95
96 if (elf->phdr) {
97 #define SHOW_PAX(B) \
98 if (elf->elf_class == ELFCLASS ## B) { \
99 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
100 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
101 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
102 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
103 continue; \
104 if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \
105 continue; \
106 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
107 *found_pax = 1; \
108 ++shown; \
109 break; \
110 } \
111 }
112 SHOW_PAX(32)
113 SHOW_PAX(64)
114 }
115
116 /* fall back to EI_PAX if no PT_PAX was found */
117 if (!*ret) {
118 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
119 if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
120 *found_pax = 1;
121 return paxflags;
122 }
123 strncpy(ret, paxflags, sizeof(ret));
124 // ++shown;
125 }
126
127 if (be_quiet && !shown)
128 return NULL;
129 return ret;
130
131 }
132 static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
133 {
134 static char ret[12];
135 char *found;
136 unsigned long i, off, shown, check_flags;
137 unsigned char multi_stack, multi_relro, multi_load;
138
139 if (!show_phdr) return NULL;
140
141 memcpy(ret, "--- --- ---\0", 12);
142
143 shown = 0;
144 multi_stack = multi_relro = multi_load = 0;
145
146 if (elf->phdr) {
147 #define SHOW_PHDR(B) \
148 if (elf->elf_class == ELFCLASS ## B) { \
149 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
150 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
151 uint32_t flags; \
152 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
153 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
154 if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
155 found = found_phdr; \
156 off = 0; \
157 check_flags = PF_X; \
158 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
159 if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
160 found = found_relro; \
161 off = 4; \
162 check_flags = PF_X; \
163 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
164 if (multi_load++ > 2) warnf("%s: more than 2 PT_LOAD's !?", elf->filename); \
165 found = found_load; \
166 off = 8; \
167 check_flags = PF_W|PF_X; \
168 } else \
169 continue; \
170 flags = EGET(phdr[i].p_flags); \
171 if (be_quiet && ((flags & check_flags) != check_flags)) \
172 continue; \
173 memcpy(ret+off, gnu_short_stack_flags(flags), 3); \
174 *found = 1; \
175 ++shown; \
176 } \
177 }
178 SHOW_PHDR(32)
179 SHOW_PHDR(64)
180 }
181
182 if (be_quiet && !shown)
183 return NULL;
184 else
185 return ret;
186 }
187 static char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
188 {
189 static char ret[] = "TEXTREL";
190 unsigned long i;
191
192 if (!show_textrel) return NULL;
193
194 if (elf->phdr) {
195 #define SHOW_TEXTREL(B) \
196 if (elf->elf_class == ELFCLASS ## B) { \
197 Elf ## B ## _Dyn *dyn; \
198 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
199 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
200 Elf ## B ## _Off offset; \
201 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
202 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
203 offset = EGET(phdr[i].p_offset); \
204 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
205 dyn = DYN ## B (elf->data + offset); \
206 while (EGET(dyn->d_tag) != DT_NULL) { \
207 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
208 *found_textrel = 1; \
209 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
210 return (be_wewy_wewy_quiet ? NULL : ret); \
211 } \
212 ++dyn; \
213 } \
214 } }
215 SHOW_TEXTREL(32)
216 SHOW_TEXTREL(64)
217 }
218
219 if (be_quiet || be_wewy_wewy_quiet)
220 return NULL;
221 else
222 return (char *)" - ";
223 }
224 static char *scanelf_file_textrels(elfobj *elf, char *found_textrels)
225 {
226 /* To locate TEXTREL symbols:
227 * for each shdr of type SHT_REL:
228 * for each phdr of type PT_LOAD:
229 * if phdr is not writable (why?)
230 * if shdr offset is inside of load (shdr->offset, phdr->{vaddr,memsz}
231 * look up shdr's symbol name
232 */
233 unsigned long p, s, r, rmax;
234 char *symtab_void, *strtab_void;
235
236 if (!show_textrels) return NULL;
237
238 /* debug sections */
239 symtab_void = elf_findsecbyname(elf, ".symtab");
240 strtab_void = elf_findsecbyname(elf, ".strtab");
241 /* fall back to runtime sections */
242 if (!symtab_void || !strtab_void) {
243 symtab_void = elf_findsecbyname(elf, ".dynsym");
244 strtab_void = elf_findsecbyname(elf, ".dynstr");
245 }
246
247 if (elf->phdr && elf->shdr) {
248 #define SHOW_TEXTRELS(B) \
249 if (elf->elf_class == ELFCLASS ## B) { \
250 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
251 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
252 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
253 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
254 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
255 Elf ## B ## _Rel *rel; \
256 Elf ## B ## _Rela *rela; \
257 /* search the section headers for relocations */ \
258 for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
259 uint32_t sh_type = EGET(shdr[s].sh_type); \
260 if (sh_type == SHT_REL) { \
261 rel = REL ## B (elf->data + EGET(shdr[s].sh_offset)); \
262 rela = NULL; \
263 rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
264 } else if (sh_type == SHT_RELA) { \
265 rel = NULL; \
266 rela = RELA ## B (elf->data + EGET(shdr[s].sh_offset)); \
267 rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
268 } else \
269 continue; \
270 /* search the program headers for PT_LOAD headers */ \
271 for (p = 0; p < EGET(ehdr->e_phnum); ++p) { \
272 Elf ## B ## _Addr vaddr; \
273 uint ## B ## _t memsz; \
274 if (EGET(phdr[p].p_type) != PT_LOAD) continue; \
275 if (EGET(phdr[p].p_flags) & PF_W) continue; \
276 vaddr = EGET(phdr[p].p_vaddr); \
277 memsz = EGET(phdr[p].p_memsz); \
278 *found_textrels = 1; \
279 /* now see if any of the relocs are in the PT_LOAD */ \
280 for (r = 0; r < rmax; ++r) { \
281 unsigned long sym_max; \
282 Elf ## B ## _Addr offset_tmp; \
283 Elf ## B ## _Sym *func; \
284 Elf ## B ## _Sym *sym; \
285 Elf ## B ## _Addr r_offset; \
286 uint ## B ## _t r_info; \
287 if (sh_type == SHT_REL) { \
288 r_offset = EGET(rel[r].r_offset); \
289 r_info = EGET(rel[r].r_info); \
290 } else { \
291 r_offset = EGET(rela[r].r_offset); \
292 r_info = EGET(rela[r].r_info); \
293 } \
294 /* make sure this relocation is inside of the .text */ \
295 if (r_offset < vaddr || r_offset >= vaddr + memsz) continue; \
296 sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
297 /* locate this relocation symbol name */ \
298 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
299 sym += ELF ## B ## _R_SYM(r_info); \
300 /* show the raw details about this reloc */ \
301 printf("\tTEXTREL %s: ", elf->base_filename); \
302 if (sym->st_name) \
303 printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name))); \
304 else \
305 printf("(NULL: fake?)"); \
306 printf(" [0x%lX]", (unsigned long)r_offset); \
307 /* now try to find the closest symbol that this rel is probably in */ \
308 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
309 func = NULL; \
310 offset_tmp = 0; \
311 while (sym_max--) { \
312 if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
313 func = sym; \
314 offset_tmp = EGET(sym->st_value); \
315 } \
316 ++sym; \
317 } \
318 printf(" in "); \
319 if (func && func->st_name) \
320 printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(func->st_name))); \
321 else \
322 printf("(NULL: fake?)"); \
323 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
324 } \
325 } \
326 } }
327 SHOW_TEXTRELS(32)
328 SHOW_TEXTRELS(64)
329 }
330
331 return NULL;
332 }
333 static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
334 {
335 unsigned long i, s;
336 char *rpath, *runpath, **r;
337 void *strtbl_void;
338
339 if (!show_rpath) return;
340
341 strtbl_void = elf_findsecbyname(elf, ".dynstr");
342 rpath = runpath = NULL;
343
344 if (elf->phdr && strtbl_void) {
345 #define SHOW_RPATH(B) \
346 if (elf->elf_class == ELFCLASS ## B) { \
347 Elf ## B ## _Dyn *dyn; \
348 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
349 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
350 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
351 Elf ## B ## _Off offset; \
352 Elf ## B ## _Xword word; \
353 /* Scan all the program headers */ \
354 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
355 /* Just scan dynamic headers */ \
356 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
357 offset = EGET(phdr[i].p_offset); \
358 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
359 /* Just scan dynamic RPATH/RUNPATH headers */ \
360 dyn = DYN ## B (elf->data + offset); \
361 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
362 if (word == DT_RPATH) { \
363 r = &rpath; \
364 } else if (word == DT_RUNPATH) { \
365 r = &runpath; \
366 } else { \
367 ++dyn; \
368 continue; \
369 } \
370 /* Verify the memory is somewhat sane */ \
371 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
372 if (offset < (Elf ## B ## _Off)elf->len) { \
373 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
374 *r = (char*)(elf->data + offset); \
375 /* If quiet, don't output paths in ld.so.conf */ \
376 if (be_quiet) { \
377 size_t len; \
378 char *start, *end; \
379 /* note that we only 'chop' off leading known paths. */ \
380 /* since *r is read-only memory, we can only move the ptr forward. */ \
381 start = *r; \
382 /* scan each path in : delimited list */ \
383 while (start) { \
384 end = strchr(start, ':'); \
385 len = (end ? abs(end - start) : strlen(start)); \
386 for (s = 0; ldpaths[s]; ++s) { \
387 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
388 *r = (end ? end + 1 : NULL); \
389 break; \
390 } \
391 } \
392 if (!*r || !ldpaths[s] || !end) \
393 start = NULL; \
394 else \
395 start = start + len + 1; \
396 } \
397 } \
398 if (*r) *found_rpath = 1; \
399 } \
400 ++dyn; \
401 } \
402 } }
403 SHOW_RPATH(32)
404 SHOW_RPATH(64)
405 }
406
407 if (be_wewy_wewy_quiet) return;
408
409 if (rpath && runpath) {
410 if (!strcmp(rpath, runpath)) {
411 xstrcat(ret, runpath, ret_len);
412 } else {
413 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
414 xchrcat(ret, '{', ret_len);
415 xstrcat(ret, rpath, ret_len);
416 xchrcat(ret, ',', ret_len);
417 xstrcat(ret, runpath, ret_len);
418 xchrcat(ret, '}', ret_len);
419 }
420 } else if (rpath || runpath)
421 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
422 else if (!be_quiet)
423 xstrcat(ret, " - ", ret_len);
424 }
425 static char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
426 {
427 unsigned long i;
428 char *needed;
429 void *strtbl_void;
430
431 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL;
432
433 strtbl_void = elf_findsecbyname(elf, ".dynstr");
434
435 if (elf->phdr && strtbl_void) {
436 #define SHOW_NEEDED(B) \
437 if (elf->elf_class == ELFCLASS ## B) { \
438 Elf ## B ## _Dyn *dyn; \
439 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
440 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
441 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
442 Elf ## B ## _Off offset; \
443 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
444 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
445 offset = EGET(phdr[i].p_offset); \
446 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
447 dyn = DYN ## B (elf->data + offset); \
448 while (EGET(dyn->d_tag) != DT_NULL) { \
449 if (EGET(dyn->d_tag) == DT_NEEDED) { \
450 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
451 if (offset >= (Elf ## B ## _Off)elf->len) { \
452 ++dyn; \
453 continue; \
454 } \
455 needed = (char*)(elf->data + offset); \
456 if (op == 0) { \
457 if (!be_wewy_wewy_quiet) { \
458 if (*found_needed) xchrcat(ret, ',', ret_len); \
459 xstrcat(ret, needed, ret_len); \
460 } \
461 *found_needed = 1; \
462 } else { \
463 if (strcmp(find_lib, needed)) return NULL; \
464 *found_lib = 1; \
465 return (be_wewy_wewy_quiet ? NULL : find_lib); \
466 } \
467 } \
468 ++dyn; \
469 } \
470 } }
471 SHOW_NEEDED(32)
472 SHOW_NEEDED(64)
473 }
474
475 return NULL;
476 }
477 static char *scanelf_file_interp(elfobj *elf, char *found_interp)
478 {
479 void *strtbl_void;
480
481 if (!show_interp) return NULL;
482
483 strtbl_void = elf_findsecbyname(elf, ".interp");
484
485 if (strtbl_void) {
486 #define SHOW_INTERP(B) \
487 if (elf->elf_class == ELFCLASS ## B) { \
488 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
489 *found_interp = 1; \
490 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
491 }
492 SHOW_INTERP(32)
493 SHOW_INTERP(64)
494 }
495 return NULL;
496 }
497 static char *scanelf_file_bind(elfobj *elf, char *found_bind)
498 {
499 unsigned long i;
500 struct stat s;
501
502 if (!show_bind) return NULL;
503 if (!elf->phdr) return NULL;
504
505 #define SHOW_BIND(B) \
506 if (elf->elf_class == ELFCLASS ## B) { \
507 Elf ## B ## _Dyn *dyn; \
508 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
509 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
510 Elf ## B ## _Off offset; \
511 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
512 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
513 offset = EGET(phdr[i].p_offset); \
514 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
515 dyn = DYN ## B (elf->data + offset); \
516 while (EGET(dyn->d_tag) != DT_NULL) { \
517 if (EGET(dyn->d_tag) == DT_BIND_NOW || \
518 (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
519 { \
520 if (be_quiet) return NULL; \
521 *found_bind = 1; \
522 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
523 } \
524 ++dyn; \
525 } \
526 } \
527 }
528 SHOW_BIND(32)
529 SHOW_BIND(64)
530
531 if (be_wewy_wewy_quiet) return NULL;
532
533 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
534 return NULL;
535 } else {
536 *found_bind = 1;
537 return (char *) "LAZY";
538 }
539 }
540 static char *scanelf_file_sym(elfobj *elf, char *found_sym)
541 {
542 unsigned long i;
543 void *symtab_void, *strtab_void;
544
545 if (!find_sym) return NULL;
546
547 /* debug sections */
548 symtab_void = elf_findsecbyname(elf, ".symtab");
549 strtab_void = elf_findsecbyname(elf, ".strtab");
550 /* fall back to runtime sections */
551 if (!symtab_void || !strtab_void) {
552 symtab_void = elf_findsecbyname(elf, ".dynsym");
553 strtab_void = elf_findsecbyname(elf, ".dynstr");
554 }
555
556 if (symtab_void && strtab_void) {
557 #define FIND_SYM(B) \
558 if (elf->elf_class == ELFCLASS ## B) { \
559 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
560 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
561 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
562 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
563 char *symname; \
564 for (i = 0; i < cnt; ++i) { \
565 if (sym->st_name) { \
566 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
567 if (*find_sym == '*') { \
568 printf("%s(%s) %5lX %15s %s\n", \
569 ((*found_sym == 0) ? "\n\t" : "\t"), \
570 elf->base_filename, \
571 (long)sym->st_size, \
572 (char *)get_elfstttype(sym->st_info), \
573 symname); \
574 *found_sym = 1; \
575 } else if ((strcmp(find_sym, symname) == 0) || \
576 (strcmp(symname, versioned_symname) == 0)) \
577 (*found_sym)++; \
578 } \
579 ++sym; \
580 } }
581 FIND_SYM(32)
582 FIND_SYM(64)
583 }
584
585 if (be_wewy_wewy_quiet) return NULL;
586
587 if (*find_sym != '*' && *found_sym)
588 return find_sym;
589 if (be_quiet)
590 return NULL;
591 else
592 return (char *)" - ";
593 }
594 /* scan an elf file and show all the fun stuff */
595 #define prints(str) write(fileno(stdout), str, strlen(str))
596 static void scanelf_file(const char *filename)
597 {
598 unsigned long i;
599 char found_pax, found_phdr, found_relro, found_load, found_textrel,
600 found_rpath, found_needed, found_interp, found_bind,
601 found_sym, found_lib, found_file, found_textrels;
602 elfobj *elf;
603 struct stat st;
604 static char *out_buffer = NULL;
605 static size_t out_len;
606
607 /* make sure 'filename' exists */
608 if (lstat(filename, &st) == -1) {
609 if (be_verbose > 2) printf("%s: does not exist\n", filename);
610 return;
611 }
612 /* always handle regular files and handle symlinked files if no -y */
613 if (S_ISLNK(st.st_mode)) {
614 if (!scan_symlink) return;
615 stat(filename, &st);
616 }
617 if (!S_ISREG(st.st_mode)) {
618 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
619 return;
620 }
621
622 found_pax = found_phdr = found_relro = found_load = found_textrel = \
623 found_rpath = found_needed = found_interp = found_bind = \
624 found_sym = found_lib = found_file = found_textrels = 0;
625
626 /* verify this is real ELF */
627 if ((elf = readelf(filename)) == NULL) {
628 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
629 return;
630 }
631
632 if (be_verbose > 1)
633 printf("%s: scanning file {%s,%s}\n", filename,
634 get_elfeitype(EI_CLASS, elf->elf_class),
635 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
636 else if (be_verbose)
637 printf("%s: scanning file\n", filename);
638
639 /* init output buffer */
640 if (!out_buffer) {
641 out_len = sizeof(char) * 80;
642 out_buffer = (char*)xmalloc(out_len);
643 }
644 *out_buffer = '\0';
645
646 /* show the header */
647 if (!be_quiet && show_banner) {
648 for (i = 0; out_format[i]; ++i) {
649 if (!IS_MODIFIER(out_format[i])) continue;
650
651 switch (out_format[++i]) {
652 case '%': break;
653 case '#': break;
654 case 'F':
655 case 'p':
656 case 'f': prints("FILE "); found_file = 1; break;
657 case 'o': prints(" TYPE "); break;
658 case 'x': prints(" PAX "); break;
659 case 'e': prints("STK/REL/PTL "); break;
660 case 't': prints("TEXTREL "); break;
661 case 'r': prints("RPATH "); break;
662 case 'n': prints("NEEDED "); break;
663 case 'i': prints("INTERP "); break;
664 case 'b': prints("BIND "); break;
665 case 's': prints("SYM "); break;
666 case 'N': prints("LIB "); break;
667 case 'T': prints("TEXTRELS "); break;
668 default: warnf("'%c' has no title ?", out_format[i]);
669 }
670 }
671 if (!found_file) prints("FILE ");
672 prints("\n");
673 found_file = 0;
674 show_banner = 0;
675 }
676
677 /* dump all the good stuff */
678 for (i = 0; out_format[i]; ++i) {
679 const char *out;
680 const char *tmp;
681
682 /* make sure we trim leading spaces in quiet mode */
683 if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
684 *out_buffer = '\0';
685
686 if (!IS_MODIFIER(out_format[i])) {
687 xchrcat(&out_buffer, out_format[i], &out_len);
688 continue;
689 }
690
691 out = NULL;
692 be_wewy_wewy_quiet = (out_format[i] == '#');
693 switch (out_format[++i]) {
694 case '%':
695 case '#':
696 xchrcat(&out_buffer, out_format[i], &out_len); break;
697 case 'F':
698 found_file = 1;
699 if (be_wewy_wewy_quiet) break;
700 xstrcat(&out_buffer, filename, &out_len);
701 break;
702 case 'p':
703 found_file = 1;
704 if (be_wewy_wewy_quiet) break;
705 tmp = filename;
706 if (search_path) {
707 ssize_t len_search = strlen(search_path);
708 ssize_t len_file = strlen(filename);
709 if (!strncmp(filename, search_path, len_search) && \
710 len_file > len_search)
711 tmp += len_search;
712 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
713 }
714 xstrcat(&out_buffer, tmp, &out_len);
715 break;
716 case 'f':
717 found_file = 1;
718 if (be_wewy_wewy_quiet) break;
719 tmp = strrchr(filename, '/');
720 tmp = (tmp == NULL ? filename : tmp+1);
721 xstrcat(&out_buffer, tmp, &out_len);
722 break;
723 case 'o': out = get_elfetype(elf); break;
724 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
725 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
726 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
727 case 'T': out = scanelf_file_textrels(elf, &found_textrels); break;
728 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
729 case 'n':
730 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
731 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
732 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
733 case 's': out = scanelf_file_sym(elf, &found_sym); break;
734 default: warnf("'%c' has no scan code?", out_format[i]);
735 }
736 if (out) xstrcat(&out_buffer, out, &out_len);
737 }
738
739 #define FOUND_SOMETHING() \
740 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
741 found_rpath || found_needed || found_interp || found_bind || \
742 found_sym || found_lib || found_textrels)
743
744 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
745 xchrcat(&out_buffer, ' ', &out_len);
746 xstrcat(&out_buffer, filename, &out_len);
747 }
748 if (!be_quiet || (be_quiet && FOUND_SOMETHING()))
749 puts(out_buffer);
750
751 unreadelf(elf);
752 }
753
754 /* scan a directory for ET_EXEC files and print when we find one */
755 static void scanelf_dir(const char *path)
756 {
757 register DIR *dir;
758 register struct dirent *dentry;
759 struct stat st_top, st;
760 char buf[_POSIX_PATH_MAX];
761 size_t pathlen = 0, len = 0;
762
763 /* make sure path exists */
764 if (lstat(path, &st_top) == -1) {
765 if (be_verbose > 2) printf("%s: does not exist\n", path);
766 return;
767 }
768
769 /* ok, if it isn't a directory, assume we can open it */
770 if (!S_ISDIR(st_top.st_mode)) {
771 scanelf_file(path);
772 return;
773 }
774
775 /* now scan the dir looking for fun stuff */
776 if ((dir = opendir(path)) == NULL) {
777 warnf("could not opendir %s: %s", path, strerror(errno));
778 return;
779 }
780 if (be_verbose) printf("%s: scanning dir\n", path);
781
782 pathlen = strlen(path);
783 while ((dentry = readdir(dir))) {
784 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
785 continue;
786 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
787 if (len >= sizeof(buf)) {
788 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
789 (unsigned long)len, (unsigned long)sizeof(buf));
790 continue;
791 }
792 sprintf(buf, "%s/%s", path, dentry->d_name);
793 if (lstat(buf, &st) != -1) {
794 if (S_ISREG(st.st_mode))
795 scanelf_file(buf);
796 else if (dir_recurse && S_ISDIR(st.st_mode)) {
797 if (dir_crossmount || (st_top.st_dev == st.st_dev))
798 scanelf_dir(buf);
799 }
800 }
801 }
802 closedir(dir);
803 }
804
805 static int scanelf_from_file(char *filename)
806 {
807 FILE *fp = NULL;
808 char *p;
809 char path[_POSIX_PATH_MAX];
810
811 if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
812 fp = stdin;
813 else if ((fp = fopen(filename, "r")) == NULL)
814 return 1;
815
816 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
817 if ((p = strchr(path, '\n')) != NULL)
818 *p = 0;
819 search_path = path;
820 scanelf_dir(path);
821 }
822 if (fp != stdin)
823 fclose(fp);
824 return 0;
825 }
826
827 static void load_ld_so_conf()
828 {
829 FILE *fp = NULL;
830 char *p;
831 char path[_POSIX_PATH_MAX];
832 int i = 0;
833
834 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
835 return;
836
837 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
838 if (*path != '/')
839 continue;
840
841 if ((p = strrchr(path, '\r')) != NULL)
842 *p = 0;
843 if ((p = strchr(path, '\n')) != NULL)
844 *p = 0;
845
846 ldpaths[i++] = xstrdup(path);
847
848 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
849 break;
850 }
851 ldpaths[i] = NULL;
852
853 fclose(fp);
854 }
855
856 /* scan /etc/ld.so.conf for paths */
857 static void scanelf_ldpath()
858 {
859 char scan_l, scan_ul, scan_ull;
860 int i = 0;
861
862 if (!ldpaths[0])
863 err("Unable to load any paths from ld.so.conf");
864
865 scan_l = scan_ul = scan_ull = 0;
866
867 while (ldpaths[i]) {
868 if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
869 if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
870 if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
871 scanelf_dir(ldpaths[i]);
872 ++i;
873 }
874
875 if (!scan_l) scanelf_dir("/lib");
876 if (!scan_ul) scanelf_dir("/usr/lib");
877 if (!scan_ull) scanelf_dir("/usr/local/lib");
878 }
879
880 /* scan env PATH for paths */
881 static void scanelf_envpath()
882 {
883 char *path, *p;
884
885 path = getenv("PATH");
886 if (!path)
887 err("PATH is not set in your env !");
888 path = xstrdup(path);
889
890 while ((p = strrchr(path, ':')) != NULL) {
891 scanelf_dir(p + 1);
892 *p = 0;
893 }
894
895 free(path);
896 }
897
898
899
900 /* usage / invocation handling functions */
901 #define PARSE_FLAGS "plRmyxetrnibs:N:TaqvF:f:o:BhV"
902 #define a_argument required_argument
903 static struct option const long_opts[] = {
904 {"path", no_argument, NULL, 'p'},
905 {"ldpath", no_argument, NULL, 'l'},
906 {"recursive", no_argument, NULL, 'R'},
907 {"mount", no_argument, NULL, 'm'},
908 {"symlink", no_argument, NULL, 'y'},
909 {"pax", no_argument, NULL, 'x'},
910 {"header", no_argument, NULL, 'e'},
911 {"textrel", no_argument, NULL, 't'},
912 {"rpath", no_argument, NULL, 'r'},
913 {"needed", no_argument, NULL, 'n'},
914 {"interp", no_argument, NULL, 'i'},
915 {"bind", no_argument, NULL, 'b'},
916 {"symbol", a_argument, NULL, 's'},
917 {"lib", a_argument, NULL, 'N'},
918 {"textrels", no_argument, NULL, 'T'},
919 {"all", no_argument, NULL, 'a'},
920 {"quiet", no_argument, NULL, 'q'},
921 {"verbose", no_argument, NULL, 'v'},
922 {"format", a_argument, NULL, 'F'},
923 {"from", a_argument, NULL, 'f'},
924 {"file", a_argument, NULL, 'o'},
925 {"nobanner", no_argument, NULL, 'B'},
926 {"help", no_argument, NULL, 'h'},
927 {"version", no_argument, NULL, 'V'},
928 {NULL, no_argument, NULL, 0x0}
929 };
930
931 static const char *opts_help[] = {
932 "Scan all directories in PATH environment",
933 "Scan all directories in /etc/ld.so.conf",
934 "Scan directories recursively",
935 "Don't recursively cross mount points",
936 "Don't scan symlinks\n",
937 "Print PaX markings",
938 "Print GNU_STACK/PT_LOAD markings",
939 "Print TEXTREL information",
940 "Print RPATH information",
941 "Print NEEDED information",
942 "Print INTERP information",
943 "Print BIND information",
944 "Find a specified symbol",
945 "Find a specified library",
946 "Locate cause of TEXTREL",
947 "Print all scanned info (-x -e -t -r -n -i -b)\n",
948 "Only output 'bad' things",
949 "Be verbose (can be specified more than once)",
950 "Use specified format for output",
951 "Read input stream from a filename",
952 "Write output stream to a filename",
953 "Don't display the header",
954 "Print this help and exit",
955 "Print version and exit",
956 NULL
957 };
958
959 /* display usage and exit */
960 static void usage(int status)
961 {
962 unsigned long i;
963 printf("* Scan ELF binaries for stuff\n\n"
964 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0);
965 printf("Options: -[%s]\n", PARSE_FLAGS);
966 for (i = 0; long_opts[i].name; ++i)
967 if (long_opts[i].has_arg == no_argument)
968 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
969 long_opts[i].name, opts_help[i]);
970 else
971 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
972 long_opts[i].name, opts_help[i]);
973
974 if (status != EXIT_SUCCESS)
975 exit(status);
976
977 puts("\nThe format modifiers for the -F option are:");
978 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
979 puts(" t TEXTREL \tr RPATH \tn NEEDED");
980 puts(" i INTERP \tb BIND \ts symbol");
981 puts(" N library \to Type \tT TEXTRELs");
982 puts(" p filename (with search path removed)");
983 puts(" f base filename");
984 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
985
986 exit(status);
987 }
988
989 /* parse command line arguments and preform needed actions */
990 static void parseargs(int argc, char *argv[])
991 {
992 int i;
993 char *from_file = NULL;
994
995 opterr = 0;
996 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
997 switch (i) {
998
999 case 'V':
1000 printf("%s compiled %s\n%s\n"
1001 "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n",
1002 __FILE__, __DATE__, rcsid, argv0);
1003 exit(EXIT_SUCCESS);
1004 break;
1005 case 'h': usage(EXIT_SUCCESS); break;
1006 case 'f':
1007 if (from_file) err("Don't specify -f twice");
1008 from_file = xstrdup(optarg);
1009 break;
1010 case 'o': {
1011 FILE *fp = NULL;
1012 if ((fp = freopen(optarg, "w", stdout)) == NULL)
1013 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1014 SET_STDOUT(fp);
1015 break;
1016 }
1017
1018 case 's': {
1019 size_t len;
1020 if (find_sym) err("Don't specify -s twice");
1021 find_sym = xstrdup(optarg);
1022 len = strlen(find_sym) + 1;
1023 versioned_symname = (char*)xmalloc(sizeof(char) * (len+1));
1024 sprintf(versioned_symname, "%s@", find_sym);
1025 break;
1026 }
1027 case 'N': {
1028 if (find_lib) err("Don't specify -N twice");
1029 find_lib = xstrdup(optarg);
1030 break;
1031 }
1032
1033 case 'F': {
1034 if (out_format) err("Don't specify -F twice");
1035 out_format = xstrdup(optarg);
1036 break;
1037 }
1038
1039 case 'y': scan_symlink = 0; break;
1040 case 'B': show_banner = 0; break;
1041 case 'l': scan_ldpath = 1; break;
1042 case 'p': scan_envpath = 1; break;
1043 case 'R': dir_recurse = 1; break;
1044 case 'm': dir_crossmount = 0; break;
1045 case 'x': show_pax = 1; break;
1046 case 'e': show_phdr = 1; break;
1047 case 't': show_textrel = 1; break;
1048 case 'r': show_rpath = 1; break;
1049 case 'n': show_needed = 1; break;
1050 case 'i': show_interp = 1; break;
1051 case 'b': show_bind = 1; break;
1052 case 'T': show_textrels = 1; break;
1053 case 'q': be_quiet = 1; break;
1054 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1055 case 'a': show_pax = show_phdr = show_textrel = show_rpath = \
1056 show_needed = show_interp = show_bind = 1; break;
1057
1058 case ':':
1059 err("Option missing parameter\n");
1060 case '?':
1061 err("Unknown option\n");
1062 default:
1063 err("Unhandled option '%c'", i);
1064 }
1065 }
1066
1067 /* let the format option override all other options */
1068 if (out_format) {
1069 show_pax = show_phdr = show_textrel = show_rpath = \
1070 show_needed = show_interp = show_bind = show_textrels = 0;
1071 for (i = 0; out_format[i]; ++i) {
1072 if (!IS_MODIFIER(out_format[i])) continue;
1073
1074 switch (out_format[++i]) {
1075 case '%': break;
1076 case '#': break;
1077 case 'F': break;
1078 case 'p': break;
1079 case 'f': break;
1080 case 's': break;
1081 case 'N': break;
1082 case 'o': break;
1083 case 'x': show_pax = 1; break;
1084 case 'e': show_phdr = 1; break;
1085 case 't': show_textrel = 1; break;
1086 case 'r': show_rpath = 1; break;
1087 case 'n': show_needed = 1; break;
1088 case 'i': show_interp = 1; break;
1089 case 'b': show_bind = 1; break;
1090 case 'T': show_textrels = 1; break;
1091 default:
1092 err("Invalid format specifier '%c' (byte %i)",
1093 out_format[i], i+1);
1094 }
1095 }
1096
1097 /* construct our default format */
1098 } else {
1099 size_t fmt_len = 30;
1100 out_format = (char*)xmalloc(sizeof(char) * fmt_len);
1101 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1102 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1103 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1104 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1105 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1106 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1107 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1108 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1109 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1110 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1111 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1112 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1113 }
1114 if (be_verbose > 2) printf("Format: %s\n", out_format);
1115
1116 /* now lets actually do the scanning */
1117 if (scan_ldpath || (show_rpath && be_quiet))
1118 load_ld_so_conf();
1119 if (scan_ldpath) scanelf_ldpath();
1120 if (scan_envpath) scanelf_envpath();
1121 if (from_file) {
1122 scanelf_from_file(from_file);
1123 free(from_file);
1124 from_file = *argv;
1125 }
1126 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
1127 err("Nothing to scan !?");
1128 while (optind < argc) {
1129 search_path = argv[optind++];
1130 scanelf_dir(search_path);
1131 }
1132
1133 /* clean up */
1134 if (find_sym) {
1135 free(find_sym);
1136 free(versioned_symname);
1137 }
1138 if (find_lib) free(find_lib);
1139 if (out_format) free(out_format);
1140 for (i = 0; ldpaths[i]; ++i)
1141 free(ldpaths[i]);
1142 }
1143
1144
1145
1146 /* utility funcs */
1147 static char *xstrdup(const char *s)
1148 {
1149 char *ret = strdup(s);
1150 if (!ret) err("Could not strdup(): %s", strerror(errno));
1151 return ret;
1152 }
1153
1154 static void *xmalloc(size_t size)
1155 {
1156 void *ret = malloc(size);
1157 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1158 return ret;
1159 }
1160
1161 static void xstrcat(char **dst, const char *src, size_t *curr_len)
1162 {
1163 size_t new_len;
1164
1165 new_len = strlen(*dst) + strlen(src);
1166 if (*curr_len <= new_len) {
1167 *curr_len = new_len + (*curr_len / 2);
1168 *dst = realloc(*dst, *curr_len);
1169 if (!*dst)
1170 err("could not realloc %li bytes", (unsigned long)*curr_len);
1171 }
1172
1173 strcat(*dst, src);
1174 }
1175
1176 static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1177 {
1178 static char my_app[2];
1179 my_app[0] = append;
1180 my_app[1] = '\0';
1181 xstrcat(dst, my_app, curr_len);
1182 }
1183
1184
1185
1186 int main(int argc, char *argv[])
1187 {
1188 if (argc < 2)
1189 usage(EXIT_FAILURE);
1190 parseargs(argc, argv);
1191 fclose(stdout);
1192 #ifdef __BOUNDS_CHECKING_ON
1193 warn("The calls to add/delete heap should be off by 1 due to the out_buffer not being freed in scanelf_file()");
1194 #endif
1195 return EXIT_SUCCESS;
1196 }

  ViewVC Help
Powered by ViewVC 1.1.20