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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.56 - (hide annotations) (download) (as text)
Sat May 21 00:34:07 2005 UTC (9 years, 3 months ago) by vapier
Branch: MAIN
Changes since 1.55: +3 -4 lines
File MIME type: text/x-csrc
only fstat when we need to

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.56 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.55 2005/05/19 22:17:11 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.56 static const char *rcsid = "$Id: scanelf.c,v 1.55 2005/05/19 22:17:11 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 vapier 1.56 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
342 vapier 1.49 return NULL;
343     } else {
344     *found_bind = 1;
345     return "LAZY";
346     }
347     }
348 vapier 1.41 static char *scanelf_file_sym(elfobj *elf, char *found_sym, const char *filename)
349 vapier 1.39 {
350 vapier 1.44 unsigned long i;
351 vapier 1.39 void *symtab_void, *strtab_void;
352 vapier 1.38
353 vapier 1.41 if (!find_sym) return NULL;
354 vapier 1.32
355 vapier 1.39 symtab_void = elf_findsecbyname(elf, ".symtab");
356     strtab_void = elf_findsecbyname(elf, ".strtab");
357 vapier 1.27
358 vapier 1.39 if (symtab_void && strtab_void) {
359 vapier 1.27 #define FIND_SYM(B) \
360     if (elf->elf_class == ELFCLASS ## B) { \
361     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
362     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
363     Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
364 vapier 1.44 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
365 vapier 1.27 char *symname; \
366     for (i = 0; i < cnt; ++i) { \
367     if (sym->st_name) { \
368     symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
369 solar 1.28 if (*find_sym == '*') { \
370 vapier 1.32 printf("%s(%s) %5lX %15s %s\n", \
371 vapier 1.39 ((*found_sym == 0) ? "\n\t" : "\t"), \
372 vapier 1.32 (char *)basename(filename), \
373     (long)sym->st_size, \
374     (char *)get_elfstttype(sym->st_info), \
375     symname); \
376 vapier 1.39 *found_sym = 1; \
377 vapier 1.32 } else if ((strcmp(find_sym, symname) == 0) || \
378 vapier 1.39 (strcmp(symname, versioned_symname) == 0)) \
379     (*found_sym)++; \
380 vapier 1.27 } \
381     ++sym; \
382     } }
383     FIND_SYM(32)
384     FIND_SYM(64)
385 vapier 1.39 }
386 vapier 1.41 if (*find_sym != '*' && *found_sym)
387     return find_sym;
388     if (be_quiet)
389     return NULL;
390     else
391     return " - ";
392 vapier 1.39 }
393     /* scan an elf file and show all the fun stuff */
394 vapier 1.41 #define prints(str) fputs(str, stdout)
395 vapier 1.39 static void scanelf_file(const char *filename)
396     {
397 vapier 1.44 unsigned long i;
398 vapier 1.39 char found_pax, found_stack, found_relro, found_textrel,
399 vapier 1.49 found_rpath, found_needed, found_interp, found_bind,
400     found_sym, found_file;
401 vapier 1.39 elfobj *elf;
402     struct stat st;
403 vapier 1.41 static char *out_buffer = NULL;
404     static size_t out_len;
405 vapier 1.39
406     /* make sure 'filename' exists */
407     if (lstat(filename, &st) == -1) {
408     if (be_verbose > 2) printf("%s: does not exist\n", filename);
409     return;
410     }
411     /* always handle regular files and handle symlinked files if no -y */
412 vapier 1.55 if (S_ISLNK(st.st_mode)) {
413     if (!scan_symlink) return;
414     stat(filename, &st);
415     }
416     if (!S_ISREG(st.st_mode)) {
417 vapier 1.39 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
418     return;
419     }
420    
421     found_pax = found_stack = found_relro = found_textrel = \
422 vapier 1.49 found_rpath = found_needed = found_interp = found_bind = \
423     found_sym = found_file = 0;
424 vapier 1.39
425     /* verify this is real ELF */
426     if ((elf = readelf(filename)) == NULL) {
427     if (be_verbose > 2) printf("%s: not an ELF\n", filename);
428     return;
429     }
430    
431     if (be_verbose > 1)
432 vapier 1.41 printf("%s: scanning file {%s,%s}\n", filename,
433 vapier 1.39 get_elfeitype(elf, EI_CLASS, elf->elf_class),
434     get_elfeitype(elf, EI_DATA, elf->data[EI_DATA]));
435     else if (be_verbose)
436     printf("%s: scanning file\n", filename);
437    
438 vapier 1.41 /* init output buffer */
439     if (!out_buffer) {
440     out_len = sizeof(char) * 80;
441     out_buffer = (char*)xmalloc(out_len);
442     }
443     *out_buffer = '\0';
444    
445 vapier 1.39 /* show the header */
446     if (!be_quiet && show_banner) {
447 vapier 1.49 for (i = 0; out_format[i]; ++i) {
448 vapier 1.41 if (out_format[i] != '%') continue;
449    
450     switch (out_format[++i]) {
451     case '%': break;
452 vapier 1.49 case 'F': prints("FILE "); found_file = 1; break;
453 vapier 1.41 case 'o': prints(" TYPE "); break;
454     case 'x': prints(" PAX "); break;
455     case 'e': prints("STK/REL "); break;
456     case 't': prints("TEXTREL "); break;
457     case 'r': prints("RPATH "); break;
458     case 'n': prints("NEEDED "); break;
459     case 'i': prints("INTERP "); break;
460 vapier 1.49 case 'b': prints("BIND "); break;
461 vapier 1.41 case 's': prints("SYM "); break;
462 vapier 1.39 }
463 vapier 1.27 }
464 vapier 1.49 if (!found_file) prints("FILE ");
465 vapier 1.41 prints("\n");
466 vapier 1.49 found_file = 0;
467 vapier 1.39 show_banner = 0;
468     }
469    
470     /* dump all the good stuff */
471 vapier 1.49 for (i = 0; out_format[i]; ++i) {
472 vapier 1.41 const char *out;
473    
474     /* make sure we trim leading spaces in quiet mode */
475     if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
476     *out_buffer = '\0';
477 vapier 1.39
478 vapier 1.41 if (out_format[i] != '%') {
479     xchrcat(&out_buffer, out_format[i], &out_len);
480     continue;
481     }
482 vapier 1.39
483 vapier 1.41 out = NULL;
484     switch (out_format[++i]) {
485     case '%': xchrcat(&out_buffer, '%', &out_len); break;
486     case 'F': found_file = 1; xstrcat(&out_buffer, filename, &out_len); break;
487     case 'o': out = get_elfetype(elf); break;
488     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
489     case 'e': out = scanelf_file_stack(elf, &found_stack, &found_relro); break;
490     case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
491     case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
492     case 'n': scanelf_file_needed(elf, &found_needed, &out_buffer, &out_len); break;
493     case 'i': out = scanelf_file_interp(elf, &found_interp); break;
494 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
495 vapier 1.41 case 's': out = scanelf_file_sym(elf, &found_sym, filename); break;
496 vapier 1.29 }
497 vapier 1.41 if (out) xstrcat(&out_buffer, out, &out_len);
498 vapier 1.39 }
499    
500 vapier 1.54 #define FOUND_SOMETHING() \
501     (found_pax || found_stack || found_textrel || found_rpath || \
502     found_needed || found_interp || found_bind || found_sym)
503    
504     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
505     xchrcat(&out_buffer, ' ', &out_len);
506     xstrcat(&out_buffer, filename, &out_len);
507 vapier 1.27 }
508 vapier 1.54 if (!be_quiet || (be_quiet && FOUND_SOMETHING()))
509 vapier 1.41 puts(out_buffer);
510 vapier 1.10
511     unreadelf(elf);
512 solar 1.6 }
513    
514 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
515 vapier 1.10 static void scanelf_dir(const char *path)
516 solar 1.1 {
517 vapier 1.10 register DIR *dir;
518     register struct dirent *dentry;
519 vapier 1.14 struct stat st_top, st;
520 solar 1.21 char buf[_POSIX_PATH_MAX];
521 vapier 1.32 size_t pathlen = 0, len = 0;
522 vapier 1.10
523     /* make sure path exists */
524 vapier 1.39 if (lstat(path, &st_top) == -1) {
525     if (be_verbose > 2) printf("%s: does not exist\n", path);
526 vapier 1.10 return;
527 vapier 1.39 }
528 solar 1.11
529 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
530 vapier 1.14 if (!S_ISDIR(st_top.st_mode)) {
531 vapier 1.10 scanelf_file(path);
532     return;
533     }
534    
535     /* now scan the dir looking for fun stuff */
536     if ((dir = opendir(path)) == NULL) {
537     warnf("could not opendir %s: %s", path, strerror(errno));
538     return;
539     }
540 vapier 1.15 if (be_verbose) printf("%s: scanning dir\n", path);
541 solar 1.11
542 vapier 1.32 pathlen = strlen(path);
543 vapier 1.10 while ((dentry = readdir(dir))) {
544     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
545     continue;
546 vapier 1.32 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
547     if (len >= sizeof(buf)) {
548     warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf));
549     continue;
550     }
551 solar 1.31 sprintf(buf, "%s/%s", path, dentry->d_name);
552 solar 1.20 if (lstat(buf, &st) != -1) {
553 vapier 1.10 if (S_ISREG(st.st_mode))
554 solar 1.20 scanelf_file(buf);
555 vapier 1.10 else if (dir_recurse && S_ISDIR(st.st_mode)) {
556 vapier 1.14 if (dir_crossmount || (st_top.st_dev == st.st_dev))
557 solar 1.20 scanelf_dir(buf);
558 vapier 1.10 }
559     }
560     }
561     closedir(dir);
562 solar 1.1 }
563    
564 vapier 1.47 static int scanelf_from_file(char *filename)
565     {
566 solar 1.45 FILE *fp = NULL;
567     char *p;
568     char path[_POSIX_PATH_MAX];
569    
570     if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
571     fp = stdin;
572     else if ((fp = fopen(filename, "r")) == NULL)
573     return 1;
574    
575     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
576     if ((p = strchr(path, '\n')) != NULL)
577     *p = 0;
578     scanelf_dir(path);
579     }
580     if (fp != stdin)
581     fclose(fp);
582     return 0;
583     }
584    
585 vapier 1.48 static void load_ld_so_conf()
586     {
587     FILE *fp = NULL;
588     char *p;
589     char path[_POSIX_PATH_MAX];
590     int i = 0;
591    
592     if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
593     return;
594    
595     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
596     if (*path != '/')
597     continue;
598    
599     if ((p = strrchr(path, '\r')) != NULL)
600     *p = 0;
601     if ((p = strchr(path, '\n')) != NULL)
602     *p = 0;
603    
604     ldpaths[i++] = xstrdup(path);
605    
606     if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
607     break;
608     }
609     ldpaths[i] = NULL;
610    
611     fclose(fp);
612     }
613    
614 vapier 1.10 /* scan /etc/ld.so.conf for paths */
615     static void scanelf_ldpath()
616     {
617 vapier 1.17 char scan_l, scan_ul, scan_ull;
618 vapier 1.48 int i = 0;
619 vapier 1.10
620 vapier 1.48 if (!ldpaths[0])
621     err("Unable to load any paths from ld.so.conf");
622 vapier 1.10
623 vapier 1.17 scan_l = scan_ul = scan_ull = 0;
624    
625 vapier 1.48 while (ldpaths[i]) {
626     if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
627     if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
628     if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
629     scanelf_dir(ldpaths[i]);
630     ++i;
631     }
632 vapier 1.10
633 vapier 1.17 if (!scan_l) scanelf_dir("/lib");
634     if (!scan_ul) scanelf_dir("/usr/lib");
635     if (!scan_ull) scanelf_dir("/usr/local/lib");
636 vapier 1.10 }
637 solar 1.1
638 vapier 1.10 /* scan env PATH for paths */
639     static void scanelf_envpath()
640 solar 1.1 {
641 solar 1.34 char *path, *p;
642 vapier 1.10
643     path = getenv("PATH");
644     if (!path)
645     err("PATH is not set in your env !");
646 vapier 1.41 path = xstrdup(path);
647 vapier 1.10
648     while ((p = strrchr(path, ':')) != NULL) {
649     scanelf_dir(p + 1);
650     *p = 0;
651     }
652 vapier 1.17
653 solar 1.34 free(path);
654 solar 1.1 }
655    
656    
657 vapier 1.10
658     /* usage / invocation handling functions */
659 vapier 1.49 #define PARSE_FLAGS "plRmyxetrnibs:aqvF:f:o:BhV"
660 vapier 1.27 #define a_argument required_argument
661 vapier 1.10 static struct option const long_opts[] = {
662     {"path", no_argument, NULL, 'p'},
663     {"ldpath", no_argument, NULL, 'l'},
664     {"recursive", no_argument, NULL, 'R'},
665 vapier 1.14 {"mount", no_argument, NULL, 'm'},
666 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
667 vapier 1.10 {"pax", no_argument, NULL, 'x'},
668 solar 1.16 {"header", no_argument, NULL, 'e'},
669 vapier 1.10 {"textrel", no_argument, NULL, 't'},
670     {"rpath", no_argument, NULL, 'r'},
671 vapier 1.32 {"needed", no_argument, NULL, 'n'},
672 vapier 1.38 {"interp", no_argument, NULL, 'i'},
673 vapier 1.49 {"bind", no_argument, NULL, 'b'},
674 vapier 1.27 {"symbol", a_argument, NULL, 's'},
675 vapier 1.10 {"all", no_argument, NULL, 'a'},
676     {"quiet", no_argument, NULL, 'q'},
677 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
678 vapier 1.39 {"format", a_argument, NULL, 'F'},
679 solar 1.45 {"from", a_argument, NULL, 'f'},
680 vapier 1.27 {"file", a_argument, NULL, 'o'},
681 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
682 vapier 1.10 {"help", no_argument, NULL, 'h'},
683     {"version", no_argument, NULL, 'V'},
684     {NULL, no_argument, NULL, 0x0}
685     };
686     static char *opts_help[] = {
687     "Scan all directories in PATH environment",
688     "Scan all directories in /etc/ld.so.conf",
689 vapier 1.14 "Scan directories recursively",
690 vapier 1.37 "Don't recursively cross mount points",
691     "Don't scan symlinks\n",
692 vapier 1.10 "Print PaX markings",
693     "Print GNU_STACK markings",
694     "Print TEXTREL information",
695     "Print RPATH information",
696 vapier 1.32 "Print NEEDED information",
697 vapier 1.38 "Print INTERP information",
698 vapier 1.49 "Print BIND information",
699 vapier 1.27 "Find a specified symbol",
700 vapier 1.46 "Print all scanned info (-x -e -t -r -n -i)\n",
701 vapier 1.14 "Only output 'bad' things",
702     "Be verbose (can be specified more than once)",
703 vapier 1.39 "Use specified format for output",
704 solar 1.45 "Read input stream from a filename",
705 vapier 1.24 "Write output stream to a filename",
706 vapier 1.14 "Don't display the header",
707 vapier 1.10 "Print this help and exit",
708     "Print version and exit",
709     NULL
710     };
711    
712     /* display usage and exit */
713     static void usage(int status)
714 solar 1.1 {
715 vapier 1.44 unsigned long i;
716 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
717 solar 1.35 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0);
718     printf("Options: -[%s]\n", PARSE_FLAGS);
719 vapier 1.10 for (i = 0; long_opts[i].name; ++i)
720 vapier 1.27 if (long_opts[i].has_arg == no_argument)
721 solar 1.52 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
722 vapier 1.27 long_opts[i].name, opts_help[i]);
723     else
724 solar 1.52 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
725 vapier 1.27 long_opts[i].name, opts_help[i]);
726 solar 1.45
727     if (status != EXIT_SUCCESS)
728     exit(status);
729    
730 vapier 1.46 puts("\nThe format modifiers for the -F option are:");
731     puts(" %F Filename \t%x PaX Flags \t%e STACK/RELRO");
732     puts(" %t TEXTREL \t%r RPATH \t%n NEEDED");
733 vapier 1.49 puts(" %i INTERP \t%b BIND \t%s symbol");
734 solar 1.45
735 vapier 1.10 exit(status);
736 solar 1.1 }
737    
738     /* parse command line arguments and preform needed actions */
739 vapier 1.10 static void parseargs(int argc, char *argv[])
740     {
741 vapier 1.48 int i;
742 solar 1.45 char *from_file = NULL;
743 vapier 1.10
744     opterr = 0;
745 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
746     switch (i) {
747 vapier 1.10
748 vapier 1.39 case 'V':
749 solar 1.19 printf("%s compiled %s\n%s\n"
750     "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n",
751     __FILE__, __DATE__, rcsid, argv0);
752 vapier 1.10 exit(EXIT_SUCCESS);
753     break;
754     case 'h': usage(EXIT_SUCCESS); break;
755 solar 1.45 case 'f':
756     if (from_file == NULL)
757     from_file = xstrdup(optarg);
758     break;
759 vapier 1.24 case 'o': {
760 solar 1.21 FILE *fp = NULL;
761     fp = freopen(optarg, "w", stdout);
762 vapier 1.24 if (fp == NULL)
763     err("Could not open output stream '%s': %s", optarg, strerror(errno));
764     stdout = fp;
765 solar 1.21 break;
766     }
767 vapier 1.24
768 vapier 1.39 case 's': {
769     size_t len;
770 vapier 1.41 find_sym = xstrdup(optarg);
771 vapier 1.39 len = strlen(find_sym) + 1;
772 vapier 1.41 versioned_symname = (char*)xmalloc(sizeof(char) * (len+1));
773 vapier 1.39 sprintf(versioned_symname, "%s@", find_sym);
774     break;
775     }
776    
777     case 'F': {
778 solar 1.45 out_format = xstrdup(optarg);
779 vapier 1.39 break;
780     }
781 vapier 1.27
782 vapier 1.37 case 'y': scan_symlink = 0; break;
783 solar 1.16 case 'B': show_banner = 0; break;
784 vapier 1.10 case 'l': scan_ldpath = 1; break;
785     case 'p': scan_envpath = 1; break;
786     case 'R': dir_recurse = 1; break;
787 vapier 1.14 case 'm': dir_crossmount = 0; break;
788 vapier 1.10 case 'x': show_pax = 1; break;
789 solar 1.16 case 'e': show_stack = 1; break;
790 vapier 1.10 case 't': show_textrel = 1; break;
791     case 'r': show_rpath = 1; break;
792 vapier 1.32 case 'n': show_needed = 1; break;
793 vapier 1.38 case 'i': show_interp = 1; break;
794 vapier 1.49 case 'b': show_bind = 1; break;
795 vapier 1.10 case 'q': be_quiet = 1; break;
796 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
797 vapier 1.50 case 'a': show_pax = show_stack = show_textrel = show_rpath = \
798     show_needed = show_interp = show_bind = 1; break;
799 vapier 1.10
800     case ':':
801 vapier 1.49 err("Option missing parameter\n");
802 vapier 1.10 case '?':
803 vapier 1.49 err("Unknown option\n");
804 vapier 1.10 default:
805 vapier 1.48 err("Unhandled option '%c'", i);
806 vapier 1.10 }
807     }
808    
809 vapier 1.39 /* let the format option override all other options */
810     if (out_format) {
811     show_pax = show_stack = show_textrel = show_rpath = show_needed = show_interp = 0;
812 vapier 1.48 for (i = 0; out_format[i]; ++i) {
813     if (out_format[i] != '%') continue;
814 vapier 1.39
815 vapier 1.48 switch (out_format[++i]) {
816 vapier 1.39 case '%': break;
817     case 'F': break;
818     case 's': break;
819 vapier 1.41 case 'o': break;
820 vapier 1.39 case 'x': show_pax = 1; break;
821     case 'e': show_stack = 1; break;
822     case 't': show_textrel = 1; break;
823     case 'r': show_rpath = 1; break;
824     case 'n': show_needed = 1; break;
825     case 'i': show_interp = 1; break;
826 vapier 1.49 case 'b': show_bind = 1; break;
827 vapier 1.39 default:
828     err("Invalid format specifier '%c' (byte %i)",
829 vapier 1.48 out_format[i], i+1);
830 vapier 1.39 }
831     }
832 vapier 1.41
833     /* construct our default format */
834     } else {
835     size_t fmt_len = 30;
836     out_format = (char*)xmalloc(sizeof(char) * fmt_len);
837     if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
838     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
839     if (show_stack) xstrcat(&out_format, "%e ", &fmt_len);
840     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
841     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
842     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
843     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
844 vapier 1.49 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
845 vapier 1.41 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
846     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
847 vapier 1.39 }
848 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
849 vapier 1.39
850     /* now lets actually do the scanning */
851 vapier 1.48 if (scan_ldpath || (show_rpath && be_quiet))
852     load_ld_so_conf();
853 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
854     if (scan_envpath) scanelf_envpath();
855 solar 1.45 if (from_file) {
856     scanelf_from_file(from_file);
857     free(from_file);
858     from_file = *argv;
859     }
860     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
861 vapier 1.25 err("Nothing to scan !?");
862 vapier 1.10 while (optind < argc)
863     scanelf_dir(argv[optind++]);
864 vapier 1.27
865 vapier 1.39 /* clean up */
866     if (find_sym) {
867     free(find_sym);
868     free(versioned_symname);
869     }
870     if (out_format) free(out_format);
871 vapier 1.48 for (i = 0; ldpaths[i]; ++i)
872     free(ldpaths[i]);
873 vapier 1.10 }
874    
875    
876    
877 vapier 1.41 /* utility funcs */
878     static char *xstrdup(char *s)
879     {
880     char *ret = strdup(s);
881     if (!ret) err("Could not strdup(): %s", strerror(errno));
882     return ret;
883     }
884     static void *xmalloc(size_t size)
885     {
886     void *ret = malloc(size);
887     if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
888     return ret;
889     }
890     static void xstrcat(char **dst, const char *src, size_t *curr_len)
891     {
892     long new_len;
893    
894     new_len = strlen(*dst) + strlen(src);
895     if (*curr_len <= new_len) {
896     *curr_len = new_len + (*curr_len / 2);
897     *dst = realloc(*dst, *curr_len);
898     if (!*dst)
899     err("could not realloc %li bytes", (unsigned long)*curr_len);
900     }
901    
902     strcat(*dst, src);
903     }
904     static inline void xchrcat(char **dst, const char append, size_t *curr_len)
905     {
906     static char my_app[2];
907     my_app[0] = append;
908     my_app[1] = '\0';
909     xstrcat(dst, my_app, curr_len);
910     }
911    
912    
913    
914 vapier 1.10 int main(int argc, char *argv[])
915 solar 1.1 {
916 vapier 1.10 if (argc < 2)
917     usage(EXIT_FAILURE);
918     parseargs(argc, argv);
919 solar 1.21 fclose(stdout);
920 vapier 1.10 return EXIT_SUCCESS;
921 solar 1.1 }

  ViewVC Help
Powered by ViewVC 1.1.20