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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.66 - (hide annotations) (download) (as text)
Wed Jun 1 22:37:38 2005 UTC (8 years, 10 months ago) by vapier
Branch: MAIN
Changes since 1.65: +36 -6 lines
File MIME type: text/x-csrc
add support for mangling of the matched filename (%p->strip search path %f->just basename)

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

  ViewVC Help
Powered by ViewVC 1.1.20