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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.48 - (hide annotations) (download) (as text)
Wed May 18 02:51:02 2005 UTC (11 years, 2 months ago) by vapier
Branch: MAIN
Changes since 1.47: +83 -44 lines
File MIME type: text/x-csrc
cache /etc/ld.so.conf so we can check it while scanning rpath

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

  ViewVC Help
Powered by ViewVC 1.1.20