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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.64 - (hide annotations) (download) (as text)
Sun May 29 19:42:09 2005 UTC (9 years, 1 month ago) by solar
Branch: MAIN
Changes since 1.63: +8 -14 lines
File MIME type: text/x-csrc
- make parseargs checks for already alloced buffers simpler. fixed typo in -s handling

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

  ViewVC Help
Powered by ViewVC 1.1.20