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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.52 - (hide annotations) (download) (as text)
Wed May 18 14:37:21 2005 UTC (9 years, 4 months ago) by solar
Branch: MAIN
Changes since 1.51: +5 -5 lines
File MIME type: text/x-csrc
- remove high chars. sorry spanky but everybody hates these.

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

  ViewVC Help
Powered by ViewVC 1.1.20