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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.55 - (hide annotations) (download) (as text)
Thu May 19 22:17:11 2005 UTC (9 years, 2 months ago) by vapier
Branch: MAIN
Changes since 1.54: +7 -3 lines
File MIME type: text/x-csrc
make sure we dont try to read a symlink to a directory as an ELF

1 solar 1.1 /*
2     * Copyright 2003 Ned Ludd <solar@gentoo.org>
3 vapier 1.8 * Copyright 1999-2005 Gentoo Foundation
4 solar 1.1 * Distributed under the terms of the GNU General Public License v2
5 vapier 1.55 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.54 2005/05/18 21:16:32 vapier Exp $
6 solar 1.1 *
7     ********************************************************************
8     * This program is free software; you can redistribute it and/or
9     * modify it under the terms of the GNU General Public License as
10     * published by the Free Software Foundation; either version 2 of the
11     * License, or (at your option) any later version.
12     *
13     * This program is distributed in the hope that it will be useful, but
14     * WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16     * General Public License for more details.
17     *
18     * You should have received a copy of the GNU General Public License
19     * along with this program; if not, write to the Free Software
20     * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21     * MA 02111-1307, USA.
22     */
23    
24     #include <stdio.h>
25     #include <stdlib.h>
26     #include <sys/types.h>
27 solar 1.28 #define __USE_GNU
28 solar 1.1 #include <string.h>
29 vapier 1.10 #include <errno.h>
30 solar 1.1 #include <unistd.h>
31     #include <sys/stat.h>
32     #include <dirent.h>
33     #include <getopt.h>
34 solar 1.20 #include <assert.h>
35 solar 1.21
36 solar 1.1 #include "paxelf.h"
37    
38 vapier 1.55 static const char *rcsid = "$Id: scanelf.c,v 1.54 2005/05/18 21:16:32 vapier Exp $";
39 vapier 1.36 #define argv0 "scanelf"
40 vapier 1.10
41    
42    
43     /* prototypes */
44     static void scanelf_file(const char *filename);
45     static void scanelf_dir(const char *path);
46     static void scanelf_ldpath();
47     static void scanelf_envpath();
48     static void usage(int status);
49     static void parseargs(int argc, char *argv[]);
50 vapier 1.41 static char *xstrdup(char *s);
51     static void *xmalloc(size_t size);
52     static void xstrcat(char **dst, const char *src, size_t *curr_len);
53     static inline void xchrcat(char **dst, const char append, size_t *curr_len);
54 vapier 1.10
55     /* variables to control behavior */
56 vapier 1.48 static char *ldpaths[256];
57 vapier 1.10 static char scan_ldpath = 0;
58     static char scan_envpath = 0;
59 vapier 1.37 static char scan_symlink = 1;
60 vapier 1.10 static char dir_recurse = 0;
61 vapier 1.14 static char dir_crossmount = 1;
62 vapier 1.10 static char show_pax = 0;
63     static char show_stack = 0;
64     static char show_textrel = 0;
65     static char show_rpath = 0;
66 vapier 1.32 static char show_needed = 0;
67 vapier 1.38 static char show_interp = 0;
68 vapier 1.49 static char show_bind = 0;
69 solar 1.16 static char show_banner = 1;
70 vapier 1.10 static char be_quiet = 0;
71 vapier 1.14 static char be_verbose = 0;
72 vapier 1.39 static char *find_sym = NULL, *versioned_symname = NULL;
73     static char *out_format = NULL;
74 vapier 1.10
75 solar 1.1
76    
77 vapier 1.39 /* sub-funcs for scanelf_file() */
78 vapier 1.41 static char *scanelf_file_pax(elfobj *elf, char *found_pax)
79 solar 1.6 {
80 vapier 1.41 static char *paxflags;
81    
82     if (!show_pax) return NULL;
83 vapier 1.10
84 vapier 1.39 paxflags = pax_short_hf_flags(PAX_FLAGS(elf));
85     if (!be_quiet || (be_quiet && strncmp(paxflags, "PeMRxS", 6))) {
86     *found_pax = 1;
87 vapier 1.41 return paxflags;
88 vapier 1.14 }
89 vapier 1.41
90     return NULL;
91 vapier 1.39 }
92 vapier 1.41 static char *scanelf_file_stack(elfobj *elf, char *found_stack, char *found_relro)
93 vapier 1.39 {
94 vapier 1.43 static char ret[8];
95 vapier 1.41 char *found;
96 vapier 1.44 unsigned long i, off, shown;
97 vapier 1.41
98     if (!show_stack) return NULL;
99    
100     shown = 0;
101 vapier 1.43 strcpy(ret, "--- ---");
102 vapier 1.44
103     if (elf->phdr) {
104 vapier 1.26 #define SHOW_STACK(B) \
105 vapier 1.39 if (elf->elf_class == ELFCLASS ## B) { \
106     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
107     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
108     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
109 vapier 1.41 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
110     found = found_stack; \
111     off = 0; \
112     } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
113     found = found_relro; \
114 solar 1.53 off = 4; \
115 vapier 1.41 } else \
116     continue; \
117 vapier 1.39 if (be_quiet && !(EGET(phdr[i].p_flags) & PF_X)) \
118     continue; \
119 vapier 1.41 memcpy(ret+off, gnu_short_stack_flags(EGET(phdr[i].p_flags)), 3); \
120     *found = 1; \
121     ++shown; \
122 vapier 1.39 } \
123     }
124     SHOW_STACK(32)
125     SHOW_STACK(64)
126 vapier 1.44 }
127    
128 vapier 1.41 if (be_quiet && !shown)
129     return NULL;
130     else
131     return ret;
132 vapier 1.39 }
133 vapier 1.41 static char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
134 vapier 1.39 {
135 vapier 1.41 static char *ret = "TEXTREL";
136 vapier 1.44 unsigned long i;
137 vapier 1.41
138     if (!show_textrel) return NULL;
139    
140 vapier 1.44 if (elf->phdr) {
141 vapier 1.39 #define SHOW_TEXTREL(B) \
142     if (elf->elf_class == ELFCLASS ## B) { \
143     Elf ## B ## _Dyn *dyn; \
144     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
145     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
146 vapier 1.44 Elf ## B ## _Off offset; \
147 vapier 1.39 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
148     if (phdr[i].p_type != PT_DYNAMIC) continue; \
149 vapier 1.44 offset = EGET(phdr[i].p_offset); \
150     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
151     dyn = DYN ## B (elf->data + offset); \
152 vapier 1.39 while (EGET(dyn->d_tag) != DT_NULL) { \
153     if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
154     *found_textrel = 1; \
155     /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
156 vapier 1.41 return ret; \
157 vapier 1.39 } \
158     ++dyn; \
159 vapier 1.26 } \
160 vapier 1.39 } }
161     SHOW_TEXTREL(32)
162     SHOW_TEXTREL(64)
163 vapier 1.44 }
164    
165 vapier 1.41 if (be_quiet)
166     return NULL;
167     else
168     return " - ";
169 vapier 1.39 }
170 vapier 1.41 static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
171 vapier 1.39 {
172 vapier 1.48 /* TODO: when checking RPATH entries, check each subpath (between :) in ld.so.conf */
173     unsigned long i, s;
174     char *rpath, *runpath, **r;
175 vapier 1.39 void *strtbl_void;
176 vapier 1.10
177 vapier 1.39 if (!show_rpath) return;
178 vapier 1.10
179 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
180     rpath = runpath = NULL;
181 vapier 1.10
182 vapier 1.44 if (elf->phdr && strtbl_void) {
183 vapier 1.26 #define SHOW_RPATH(B) \
184     if (elf->elf_class == ELFCLASS ## B) { \
185     Elf ## B ## _Dyn *dyn; \
186     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
187     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
188     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
189 vapier 1.44 Elf ## B ## _Off offset; \
190 vapier 1.48 Elf ## B ## _Sxword word; \
191     /* Scan all the program headers */ \
192 vapier 1.26 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
193 vapier 1.48 /* Just scan dynamic headers */ \
194 vapier 1.26 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
195 vapier 1.44 offset = EGET(phdr[i].p_offset); \
196     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
197 vapier 1.48 /* Just scan dynamic RPATH/RUNPATH headers */ \
198 vapier 1.44 dyn = DYN ## B (elf->data + offset); \
199 vapier 1.48 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
200     if (word == DT_RPATH) { \
201     r = &rpath; \
202     } else if (word == DT_RUNPATH) { \
203     r = &runpath; \
204     } else { \
205     ++dyn; \
206     continue; \
207     } \
208     /* Verify the memory is somewhat sane */ \
209     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
210     if (offset < elf->len) { \
211     if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
212     *r = (char*)(elf->data + offset); \
213     /* If quiet, don't output paths in ld.so.conf */ \
214     if (be_quiet) \
215     for (s = 0; ldpaths[s]; ++s) \
216     if (!strcmp(ldpaths[s], *r)) { \
217     *r = NULL; \
218     break; \
219     } \
220     if (*r) *found_rpath = 1; \
221 vapier 1.26 } \
222     ++dyn; \
223     } \
224     } }
225     SHOW_RPATH(32)
226     SHOW_RPATH(64)
227 vapier 1.10 }
228 vapier 1.41
229 vapier 1.39 if (rpath && runpath) {
230 vapier 1.41 if (!strcmp(rpath, runpath)) {
231     xstrcat(ret, runpath, ret_len);
232     } else {
233 vapier 1.39 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
234 vapier 1.41 xchrcat(ret, '{', ret_len);
235     xstrcat(ret, rpath, ret_len);
236     xchrcat(ret, ',', ret_len);
237     xstrcat(ret, runpath, ret_len);
238     xchrcat(ret, '}', ret_len);
239 vapier 1.39 }
240     } else if (rpath || runpath)
241 vapier 1.41 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
242     else if (!be_quiet)
243     xstrcat(ret, " - ", ret_len);
244 vapier 1.39 }
245 vapier 1.41 static void scanelf_file_needed(elfobj *elf, char *found_needed, char **ret, size_t *ret_len)
246 vapier 1.39 {
247 vapier 1.44 unsigned long i;
248 vapier 1.39 char *needed;
249     void *strtbl_void;
250    
251     if (!show_needed) return;
252 vapier 1.10
253 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
254 vapier 1.32
255 vapier 1.44 if (elf->phdr && strtbl_void) {
256 vapier 1.32 #define SHOW_NEEDED(B) \
257     if (elf->elf_class == ELFCLASS ## B) { \
258     Elf ## B ## _Dyn *dyn; \
259     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
260     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
261     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
262 vapier 1.44 Elf ## B ## _Off offset; \
263 vapier 1.32 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
264     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
265 vapier 1.44 offset = EGET(phdr[i].p_offset); \
266     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
267     dyn = DYN ## B (elf->data + offset); \
268 vapier 1.32 while (EGET(dyn->d_tag) != DT_NULL) { \
269     if (EGET(dyn->d_tag) == DT_NEEDED) { \
270 vapier 1.44 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
271 vapier 1.49 if (offset >= elf->len) { \
272     ++dyn; \
273     continue; \
274     } \
275 vapier 1.44 needed = (char*)(elf->data + offset); \
276 vapier 1.41 if (*found_needed) xchrcat(ret, ',', ret_len); \
277     xstrcat(ret, needed, ret_len); \
278 vapier 1.39 *found_needed = 1; \
279 vapier 1.32 } \
280     ++dyn; \
281     } \
282     } }
283     SHOW_NEEDED(32)
284     SHOW_NEEDED(64)
285     }
286 vapier 1.39 }
287 vapier 1.41 static char *scanelf_file_interp(elfobj *elf, char *found_interp)
288 vapier 1.39 {
289     void *strtbl_void;
290    
291 vapier 1.41 if (!show_interp) return NULL;
292 vapier 1.32
293 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".interp");
294 vapier 1.38
295 vapier 1.39 if (strtbl_void) {
296 vapier 1.38 #define SHOW_INTERP(B) \
297     if (elf->elf_class == ELFCLASS ## B) { \
298 solar 1.40 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
299     *found_interp = 1; \
300 vapier 1.41 return elf->data + EGET(strtbl->sh_offset); \
301 vapier 1.38 }
302     SHOW_INTERP(32)
303     SHOW_INTERP(64)
304     }
305 vapier 1.41 return NULL;
306 vapier 1.39 }
307 vapier 1.49 static char *scanelf_file_bind(elfobj *elf, char *found_bind)
308     {
309     unsigned long i;
310     struct stat s;
311    
312     if (!show_bind) return NULL;
313 vapier 1.51 if (!elf->phdr) return NULL;
314 vapier 1.49
315     #define SHOW_BIND(B) \
316     if (elf->elf_class == ELFCLASS ## B) { \
317     Elf ## B ## _Dyn *dyn; \
318     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
319     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
320     Elf ## B ## _Off offset; \
321     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
322     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
323     offset = EGET(phdr[i].p_offset); \
324     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
325     dyn = DYN ## B (elf->data + offset); \
326     while (EGET(dyn->d_tag) != DT_NULL) { \
327     if (EGET(dyn->d_tag) == DT_BIND_NOW || \
328     (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
329     { \
330     if (be_quiet) return NULL; \
331     *found_bind = 1; \
332     return "NOW"; \
333     } \
334     ++dyn; \
335     } \
336     } \
337     }
338     SHOW_BIND(32)
339     SHOW_BIND(64)
340    
341     fstat(elf->fd, &s);
342     if (be_quiet && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
343     return NULL;
344     } else {
345     *found_bind = 1;
346     return "LAZY";
347     }
348     }
349 vapier 1.41 static char *scanelf_file_sym(elfobj *elf, char *found_sym, const char *filename)
350 vapier 1.39 {
351 vapier 1.44 unsigned long i;
352 vapier 1.39 void *symtab_void, *strtab_void;
353 vapier 1.38
354 vapier 1.41 if (!find_sym) return NULL;
355 vapier 1.32
356 vapier 1.39 symtab_void = elf_findsecbyname(elf, ".symtab");
357     strtab_void = elf_findsecbyname(elf, ".strtab");
358 vapier 1.27
359 vapier 1.39 if (symtab_void && strtab_void) {
360 vapier 1.27 #define FIND_SYM(B) \
361     if (elf->elf_class == ELFCLASS ## B) { \
362     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
363     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
364     Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
365 vapier 1.44 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
366 vapier 1.27 char *symname; \
367     for (i = 0; i < cnt; ++i) { \
368     if (sym->st_name) { \
369     symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
370 solar 1.28 if (*find_sym == '*') { \
371 vapier 1.32 printf("%s(%s) %5lX %15s %s\n", \
372 vapier 1.39 ((*found_sym == 0) ? "\n\t" : "\t"), \
373 vapier 1.32 (char *)basename(filename), \
374     (long)sym->st_size, \
375     (char *)get_elfstttype(sym->st_info), \
376     symname); \
377 vapier 1.39 *found_sym = 1; \
378 vapier 1.32 } else if ((strcmp(find_sym, symname) == 0) || \
379 vapier 1.39 (strcmp(symname, versioned_symname) == 0)) \
380     (*found_sym)++; \
381 vapier 1.27 } \
382     ++sym; \
383     } }
384     FIND_SYM(32)
385     FIND_SYM(64)
386 vapier 1.39 }
387 vapier 1.41 if (*find_sym != '*' && *found_sym)
388     return find_sym;
389     if (be_quiet)
390     return NULL;
391     else
392     return " - ";
393 vapier 1.39 }
394     /* scan an elf file and show all the fun stuff */
395 vapier 1.41 #define prints(str) fputs(str, stdout)
396 vapier 1.39 static void scanelf_file(const char *filename)
397     {
398 vapier 1.44 unsigned long i;
399 vapier 1.39 char found_pax, found_stack, found_relro, found_textrel,
400 vapier 1.49 found_rpath, found_needed, found_interp, found_bind,
401     found_sym, found_file;
402 vapier 1.39 elfobj *elf;
403     struct stat st;
404 vapier 1.41 static char *out_buffer = NULL;
405     static size_t out_len;
406 vapier 1.39
407     /* make sure 'filename' exists */
408     if (lstat(filename, &st) == -1) {
409     if (be_verbose > 2) printf("%s: does not exist\n", filename);
410     return;
411     }
412     /* always handle regular files and handle symlinked files if no -y */
413 vapier 1.55 if (S_ISLNK(st.st_mode)) {
414     if (!scan_symlink) return;
415     stat(filename, &st);
416     }
417     if (!S_ISREG(st.st_mode)) {
418 vapier 1.39 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
419     return;
420     }
421    
422     found_pax = found_stack = found_relro = found_textrel = \
423 vapier 1.49 found_rpath = found_needed = found_interp = found_bind = \
424     found_sym = found_file = 0;
425 vapier 1.39
426     /* verify this is real ELF */
427     if ((elf = readelf(filename)) == NULL) {
428     if (be_verbose > 2) printf("%s: not an ELF\n", filename);
429     return;
430     }
431    
432     if (be_verbose > 1)
433 vapier 1.41 printf("%s: scanning file {%s,%s}\n", filename,
434 vapier 1.39 get_elfeitype(elf, EI_CLASS, elf->elf_class),
435     get_elfeitype(elf, EI_DATA, elf->data[EI_DATA]));
436     else if (be_verbose)
437     printf("%s: scanning file\n", filename);
438    
439 vapier 1.41 /* init output buffer */
440     if (!out_buffer) {
441     out_len = sizeof(char) * 80;
442     out_buffer = (char*)xmalloc(out_len);
443     }
444     *out_buffer = '\0';
445    
446 vapier 1.39 /* show the header */
447     if (!be_quiet && show_banner) {
448 vapier 1.49 for (i = 0; out_format[i]; ++i) {
449 vapier 1.41 if (out_format[i] != '%') continue;
450    
451     switch (out_format[++i]) {
452     case '%': break;
453 vapier 1.49 case 'F': prints("FILE "); found_file = 1; break;
454 vapier 1.41 case 'o': prints(" TYPE "); break;
455     case 'x': prints(" PAX "); break;
456     case 'e': prints("STK/REL "); break;
457     case 't': prints("TEXTREL "); break;
458     case 'r': prints("RPATH "); break;
459     case 'n': prints("NEEDED "); break;
460     case 'i': prints("INTERP "); break;
461 vapier 1.49 case 'b': prints("BIND "); break;
462 vapier 1.41 case 's': prints("SYM "); break;
463 vapier 1.39 }
464 vapier 1.27 }
465 vapier 1.49 if (!found_file) prints("FILE ");
466 vapier 1.41 prints("\n");
467 vapier 1.49 found_file = 0;
468 vapier 1.39 show_banner = 0;
469     }
470    
471     /* dump all the good stuff */
472 vapier 1.49 for (i = 0; out_format[i]; ++i) {
473 vapier 1.41 const char *out;
474    
475     /* make sure we trim leading spaces in quiet mode */
476     if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
477     *out_buffer = '\0';
478 vapier 1.39
479 vapier 1.41 if (out_format[i] != '%') {
480     xchrcat(&out_buffer, out_format[i], &out_len);
481     continue;
482     }
483 vapier 1.39
484 vapier 1.41 out = NULL;
485     switch (out_format[++i]) {
486     case '%': xchrcat(&out_buffer, '%', &out_len); break;
487     case 'F': found_file = 1; xstrcat(&out_buffer, filename, &out_len); break;
488     case 'o': out = get_elfetype(elf); break;
489     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
490     case 'e': out = scanelf_file_stack(elf, &found_stack, &found_relro); break;
491     case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
492     case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
493     case 'n': scanelf_file_needed(elf, &found_needed, &out_buffer, &out_len); break;
494     case 'i': out = scanelf_file_interp(elf, &found_interp); break;
495 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
496 vapier 1.41 case 's': out = scanelf_file_sym(elf, &found_sym, filename); break;
497 vapier 1.29 }
498 vapier 1.41 if (out) xstrcat(&out_buffer, out, &out_len);
499 vapier 1.39 }
500    
501 vapier 1.54 #define FOUND_SOMETHING() \
502     (found_pax || found_stack || found_textrel || found_rpath || \
503     found_needed || found_interp || found_bind || found_sym)
504    
505     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
506     xchrcat(&out_buffer, ' ', &out_len);
507     xstrcat(&out_buffer, filename, &out_len);
508 vapier 1.27 }
509 vapier 1.54 if (!be_quiet || (be_quiet && FOUND_SOMETHING()))
510 vapier 1.41 puts(out_buffer);
511 vapier 1.10
512     unreadelf(elf);
513 solar 1.6 }
514    
515 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
516 vapier 1.10 static void scanelf_dir(const char *path)
517 solar 1.1 {
518 vapier 1.10 register DIR *dir;
519     register struct dirent *dentry;
520 vapier 1.14 struct stat st_top, st;
521 solar 1.21 char buf[_POSIX_PATH_MAX];
522 vapier 1.32 size_t pathlen = 0, len = 0;
523 vapier 1.10
524     /* make sure path exists */
525 vapier 1.39 if (lstat(path, &st_top) == -1) {
526     if (be_verbose > 2) printf("%s: does not exist\n", path);
527 vapier 1.10 return;
528 vapier 1.39 }
529 solar 1.11
530 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
531 vapier 1.14 if (!S_ISDIR(st_top.st_mode)) {
532 vapier 1.10 scanelf_file(path);
533     return;
534     }
535    
536     /* now scan the dir looking for fun stuff */
537     if ((dir = opendir(path)) == NULL) {
538     warnf("could not opendir %s: %s", path, strerror(errno));
539     return;
540     }
541 vapier 1.15 if (be_verbose) printf("%s: scanning dir\n", path);
542 solar 1.11
543 vapier 1.32 pathlen = strlen(path);
544 vapier 1.10 while ((dentry = readdir(dir))) {
545     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
546     continue;
547 vapier 1.32 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
548     if (len >= sizeof(buf)) {
549     warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf));
550     continue;
551     }
552 solar 1.31 sprintf(buf, "%s/%s", path, dentry->d_name);
553 solar 1.20 if (lstat(buf, &st) != -1) {
554 vapier 1.10 if (S_ISREG(st.st_mode))
555 solar 1.20 scanelf_file(buf);
556 vapier 1.10 else if (dir_recurse && S_ISDIR(st.st_mode)) {
557 vapier 1.14 if (dir_crossmount || (st_top.st_dev == st.st_dev))
558 solar 1.20 scanelf_dir(buf);
559 vapier 1.10 }
560     }
561     }
562     closedir(dir);
563 solar 1.1 }
564    
565 vapier 1.47 static int scanelf_from_file(char *filename)
566     {
567 solar 1.45 FILE *fp = NULL;
568     char *p;
569     char path[_POSIX_PATH_MAX];
570    
571     if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
572     fp = stdin;
573     else if ((fp = fopen(filename, "r")) == NULL)
574     return 1;
575    
576     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
577     if ((p = strchr(path, '\n')) != NULL)
578     *p = 0;
579     scanelf_dir(path);
580     }
581     if (fp != stdin)
582     fclose(fp);
583     return 0;
584     }
585    
586 vapier 1.48 static void load_ld_so_conf()
587     {
588     FILE *fp = NULL;
589     char *p;
590     char path[_POSIX_PATH_MAX];
591     int i = 0;
592    
593     if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
594     return;
595    
596     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
597     if (*path != '/')
598     continue;
599    
600     if ((p = strrchr(path, '\r')) != NULL)
601     *p = 0;
602     if ((p = strchr(path, '\n')) != NULL)
603     *p = 0;
604    
605     ldpaths[i++] = xstrdup(path);
606    
607     if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
608     break;
609     }
610     ldpaths[i] = NULL;
611    
612     fclose(fp);
613     }
614    
615 vapier 1.10 /* scan /etc/ld.so.conf for paths */
616     static void scanelf_ldpath()
617     {
618 vapier 1.17 char scan_l, scan_ul, scan_ull;
619 vapier 1.48 int i = 0;
620 vapier 1.10
621 vapier 1.48 if (!ldpaths[0])
622     err("Unable to load any paths from ld.so.conf");
623 vapier 1.10
624 vapier 1.17 scan_l = scan_ul = scan_ull = 0;
625    
626 vapier 1.48 while (ldpaths[i]) {
627     if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
628     if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
629     if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
630     scanelf_dir(ldpaths[i]);
631     ++i;
632     }
633 vapier 1.10
634 vapier 1.17 if (!scan_l) scanelf_dir("/lib");
635     if (!scan_ul) scanelf_dir("/usr/lib");
636     if (!scan_ull) scanelf_dir("/usr/local/lib");
637 vapier 1.10 }
638 solar 1.1
639 vapier 1.10 /* scan env PATH for paths */
640     static void scanelf_envpath()
641 solar 1.1 {
642 solar 1.34 char *path, *p;
643 vapier 1.10
644     path = getenv("PATH");
645     if (!path)
646     err("PATH is not set in your env !");
647 vapier 1.41 path = xstrdup(path);
648 vapier 1.10
649     while ((p = strrchr(path, ':')) != NULL) {
650     scanelf_dir(p + 1);
651     *p = 0;
652     }
653 vapier 1.17
654 solar 1.34 free(path);
655 solar 1.1 }
656    
657    
658 vapier 1.10
659     /* usage / invocation handling functions */
660 vapier 1.49 #define PARSE_FLAGS "plRmyxetrnibs:aqvF:f:o:BhV"
661 vapier 1.27 #define a_argument required_argument
662 vapier 1.10 static struct option const long_opts[] = {
663     {"path", no_argument, NULL, 'p'},
664     {"ldpath", no_argument, NULL, 'l'},
665     {"recursive", no_argument, NULL, 'R'},
666 vapier 1.14 {"mount", no_argument, NULL, 'm'},
667 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
668 vapier 1.10 {"pax", no_argument, NULL, 'x'},
669 solar 1.16 {"header", no_argument, NULL, 'e'},
670 vapier 1.10 {"textrel", no_argument, NULL, 't'},
671     {"rpath", no_argument, NULL, 'r'},
672 vapier 1.32 {"needed", no_argument, NULL, 'n'},
673 vapier 1.38 {"interp", no_argument, NULL, 'i'},
674 vapier 1.49 {"bind", no_argument, NULL, 'b'},
675 vapier 1.27 {"symbol", a_argument, NULL, 's'},
676 vapier 1.10 {"all", no_argument, NULL, 'a'},
677     {"quiet", no_argument, NULL, 'q'},
678 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
679 vapier 1.39 {"format", a_argument, NULL, 'F'},
680 solar 1.45 {"from", a_argument, NULL, 'f'},
681 vapier 1.27 {"file", a_argument, NULL, 'o'},
682 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
683 vapier 1.10 {"help", no_argument, NULL, 'h'},
684     {"version", no_argument, NULL, 'V'},
685     {NULL, no_argument, NULL, 0x0}
686     };
687     static char *opts_help[] = {
688     "Scan all directories in PATH environment",
689     "Scan all directories in /etc/ld.so.conf",
690 vapier 1.14 "Scan directories recursively",
691 vapier 1.37 "Don't recursively cross mount points",
692     "Don't scan symlinks\n",
693 vapier 1.10 "Print PaX markings",
694     "Print GNU_STACK markings",
695     "Print TEXTREL information",
696     "Print RPATH information",
697 vapier 1.32 "Print NEEDED information",
698 vapier 1.38 "Print INTERP information",
699 vapier 1.49 "Print BIND information",
700 vapier 1.27 "Find a specified symbol",
701 vapier 1.46 "Print all scanned info (-x -e -t -r -n -i)\n",
702 vapier 1.14 "Only output 'bad' things",
703     "Be verbose (can be specified more than once)",
704 vapier 1.39 "Use specified format for output",
705 solar 1.45 "Read input stream from a filename",
706 vapier 1.24 "Write output stream to a filename",
707 vapier 1.14 "Don't display the header",
708 vapier 1.10 "Print this help and exit",
709     "Print version and exit",
710     NULL
711     };
712    
713     /* display usage and exit */
714     static void usage(int status)
715 solar 1.1 {
716 vapier 1.44 unsigned long i;
717 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
718 solar 1.35 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0);
719     printf("Options: -[%s]\n", PARSE_FLAGS);
720 vapier 1.10 for (i = 0; long_opts[i].name; ++i)
721 vapier 1.27 if (long_opts[i].has_arg == no_argument)
722 solar 1.52 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
723 vapier 1.27 long_opts[i].name, opts_help[i]);
724     else
725 solar 1.52 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
726 vapier 1.27 long_opts[i].name, opts_help[i]);
727 solar 1.45
728     if (status != EXIT_SUCCESS)
729     exit(status);
730    
731 vapier 1.46 puts("\nThe format modifiers for the -F option are:");
732     puts(" %F Filename \t%x PaX Flags \t%e STACK/RELRO");
733     puts(" %t TEXTREL \t%r RPATH \t%n NEEDED");
734 vapier 1.49 puts(" %i INTERP \t%b BIND \t%s symbol");
735 solar 1.45
736 vapier 1.10 exit(status);
737 solar 1.1 }
738    
739     /* parse command line arguments and preform needed actions */
740 vapier 1.10 static void parseargs(int argc, char *argv[])
741     {
742 vapier 1.48 int i;
743 solar 1.45 char *from_file = NULL;
744 vapier 1.10
745     opterr = 0;
746 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
747     switch (i) {
748 vapier 1.10
749 vapier 1.39 case 'V':
750 solar 1.19 printf("%s compiled %s\n%s\n"
751     "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n",
752     __FILE__, __DATE__, rcsid, argv0);
753 vapier 1.10 exit(EXIT_SUCCESS);
754     break;
755     case 'h': usage(EXIT_SUCCESS); break;
756 solar 1.45 case 'f':
757     if (from_file == NULL)
758     from_file = xstrdup(optarg);
759     break;
760 vapier 1.24 case 'o': {
761 solar 1.21 FILE *fp = NULL;
762     fp = freopen(optarg, "w", stdout);
763 vapier 1.24 if (fp == NULL)
764     err("Could not open output stream '%s': %s", optarg, strerror(errno));
765     stdout = fp;
766 solar 1.21 break;
767     }
768 vapier 1.24
769 vapier 1.39 case 's': {
770     size_t len;
771 vapier 1.41 find_sym = xstrdup(optarg);
772 vapier 1.39 len = strlen(find_sym) + 1;
773 vapier 1.41 versioned_symname = (char*)xmalloc(sizeof(char) * (len+1));
774 vapier 1.39 sprintf(versioned_symname, "%s@", find_sym);
775     break;
776     }
777    
778     case 'F': {
779 solar 1.45 out_format = xstrdup(optarg);
780 vapier 1.39 break;
781     }
782 vapier 1.27
783 vapier 1.37 case 'y': scan_symlink = 0; break;
784 solar 1.16 case 'B': show_banner = 0; break;
785 vapier 1.10 case 'l': scan_ldpath = 1; break;
786     case 'p': scan_envpath = 1; break;
787     case 'R': dir_recurse = 1; break;
788 vapier 1.14 case 'm': dir_crossmount = 0; break;
789 vapier 1.10 case 'x': show_pax = 1; break;
790 solar 1.16 case 'e': show_stack = 1; break;
791 vapier 1.10 case 't': show_textrel = 1; break;
792     case 'r': show_rpath = 1; break;
793 vapier 1.32 case 'n': show_needed = 1; break;
794 vapier 1.38 case 'i': show_interp = 1; break;
795 vapier 1.49 case 'b': show_bind = 1; break;
796 vapier 1.10 case 'q': be_quiet = 1; break;
797 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
798 vapier 1.50 case 'a': show_pax = show_stack = show_textrel = show_rpath = \
799     show_needed = show_interp = show_bind = 1; break;
800 vapier 1.10
801     case ':':
802 vapier 1.49 err("Option missing parameter\n");
803 vapier 1.10 case '?':
804 vapier 1.49 err("Unknown option\n");
805 vapier 1.10 default:
806 vapier 1.48 err("Unhandled option '%c'", i);
807 vapier 1.10 }
808     }
809    
810 vapier 1.39 /* let the format option override all other options */
811     if (out_format) {
812     show_pax = show_stack = show_textrel = show_rpath = show_needed = show_interp = 0;
813 vapier 1.48 for (i = 0; out_format[i]; ++i) {
814     if (out_format[i] != '%') continue;
815 vapier 1.39
816 vapier 1.48 switch (out_format[++i]) {
817 vapier 1.39 case '%': break;
818     case 'F': break;
819     case 's': break;
820 vapier 1.41 case 'o': break;
821 vapier 1.39 case 'x': show_pax = 1; break;
822     case 'e': show_stack = 1; break;
823     case 't': show_textrel = 1; break;
824     case 'r': show_rpath = 1; break;
825     case 'n': show_needed = 1; break;
826     case 'i': show_interp = 1; break;
827 vapier 1.49 case 'b': show_bind = 1; break;
828 vapier 1.39 default:
829     err("Invalid format specifier '%c' (byte %i)",
830 vapier 1.48 out_format[i], i+1);
831 vapier 1.39 }
832     }
833 vapier 1.41
834     /* construct our default format */
835     } else {
836     size_t fmt_len = 30;
837     out_format = (char*)xmalloc(sizeof(char) * fmt_len);
838     if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
839     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
840     if (show_stack) xstrcat(&out_format, "%e ", &fmt_len);
841     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
842     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
843     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
844     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
845 vapier 1.49 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
846 vapier 1.41 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
847     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
848 vapier 1.39 }
849 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
850 vapier 1.39
851     /* now lets actually do the scanning */
852 vapier 1.48 if (scan_ldpath || (show_rpath && be_quiet))
853     load_ld_so_conf();
854 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
855     if (scan_envpath) scanelf_envpath();
856 solar 1.45 if (from_file) {
857     scanelf_from_file(from_file);
858     free(from_file);
859     from_file = *argv;
860     }
861     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
862 vapier 1.25 err("Nothing to scan !?");
863 vapier 1.10 while (optind < argc)
864     scanelf_dir(argv[optind++]);
865 vapier 1.27
866 vapier 1.39 /* clean up */
867     if (find_sym) {
868     free(find_sym);
869     free(versioned_symname);
870     }
871     if (out_format) free(out_format);
872 vapier 1.48 for (i = 0; ldpaths[i]; ++i)
873     free(ldpaths[i]);
874 vapier 1.10 }
875    
876    
877    
878 vapier 1.41 /* utility funcs */
879     static char *xstrdup(char *s)
880     {
881     char *ret = strdup(s);
882     if (!ret) err("Could not strdup(): %s", strerror(errno));
883     return ret;
884     }
885     static void *xmalloc(size_t size)
886     {
887     void *ret = malloc(size);
888     if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
889     return ret;
890     }
891     static void xstrcat(char **dst, const char *src, size_t *curr_len)
892     {
893     long new_len;
894    
895     new_len = strlen(*dst) + strlen(src);
896     if (*curr_len <= new_len) {
897     *curr_len = new_len + (*curr_len / 2);
898     *dst = realloc(*dst, *curr_len);
899     if (!*dst)
900     err("could not realloc %li bytes", (unsigned long)*curr_len);
901     }
902    
903     strcat(*dst, src);
904     }
905     static inline void xchrcat(char **dst, const char append, size_t *curr_len)
906     {
907     static char my_app[2];
908     my_app[0] = append;
909     my_app[1] = '\0';
910     xstrcat(dst, my_app, curr_len);
911     }
912    
913    
914    
915 vapier 1.10 int main(int argc, char *argv[])
916 solar 1.1 {
917 vapier 1.10 if (argc < 2)
918     usage(EXIT_FAILURE);
919     parseargs(argc, argv);
920 solar 1.21 fclose(stdout);
921 vapier 1.10 return EXIT_SUCCESS;
922 solar 1.1 }

  ViewVC Help
Powered by ViewVC 1.1.20