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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.62 - (hide annotations) (download) (as text)
Sun May 29 06:25:48 2005 UTC (9 years, 2 months ago) by vapier
Branch: MAIN
Changes since 1.61: +8 -2 lines
File MIME type: text/x-csrc
add some more checks to make sure people dont double specify options and make us malloc extra memory

1 solar 1.1 /*
2     * Copyright 2003 Ned Ludd <solar@gentoo.org>
3 vapier 1.8 * Copyright 1999-2005 Gentoo Foundation
4 solar 1.1 * Distributed under the terms of the GNU General Public License v2
5 vapier 1.62 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.61 2005/05/28 22:09:36 solar 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 vapier 1.60 #include <libgen.h>
28     #include <limits.h>
29 solar 1.28 #define __USE_GNU
30 solar 1.1 #include <string.h>
31 vapier 1.10 #include <errno.h>
32 solar 1.1 #include <unistd.h>
33     #include <sys/stat.h>
34     #include <dirent.h>
35     #include <getopt.h>
36 solar 1.20 #include <assert.h>
37 solar 1.1 #include "paxelf.h"
38    
39 vapier 1.62 static const char *rcsid = "$Id: scanelf.c,v 1.61 2005/05/28 22:09:36 solar Exp $";
40 vapier 1.36 #define argv0 "scanelf"
41 vapier 1.10
42    
43    
44     /* prototypes */
45     static void scanelf_file(const char *filename);
46     static void scanelf_dir(const char *path);
47     static void scanelf_ldpath();
48     static void scanelf_envpath();
49     static void usage(int status);
50     static void parseargs(int argc, char *argv[]);
51 vapier 1.60 static char *xstrdup(const char *s);
52 vapier 1.41 static void *xmalloc(size_t size);
53     static void xstrcat(char **dst, const char *src, size_t *curr_len);
54     static inline void xchrcat(char **dst, const char append, size_t *curr_len);
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 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.49 case 'F': prints("FILE "); found_file = 1; break;
491 vapier 1.41 case 'o': prints(" TYPE "); break;
492     case 'x': prints(" PAX "); break;
493     case 'e': prints("STK/REL "); break;
494     case 't': prints("TEXTREL "); break;
495     case 'r': prints("RPATH "); break;
496     case 'n': prints("NEEDED "); break;
497     case 'i': prints("INTERP "); break;
498 vapier 1.49 case 'b': prints("BIND "); break;
499 vapier 1.41 case 's': prints("SYM "); break;
500 vapier 1.39 }
501 vapier 1.27 }
502 vapier 1.49 if (!found_file) prints("FILE ");
503 vapier 1.41 prints("\n");
504 vapier 1.49 found_file = 0;
505 vapier 1.39 show_banner = 0;
506     }
507    
508     /* dump all the good stuff */
509 vapier 1.49 for (i = 0; out_format[i]; ++i) {
510 vapier 1.41 const char *out;
511    
512     /* make sure we trim leading spaces in quiet mode */
513     if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
514     *out_buffer = '\0';
515 vapier 1.39
516 vapier 1.41 if (out_format[i] != '%') {
517     xchrcat(&out_buffer, out_format[i], &out_len);
518     continue;
519     }
520 vapier 1.39
521 vapier 1.41 out = NULL;
522     switch (out_format[++i]) {
523     case '%': xchrcat(&out_buffer, '%', &out_len); break;
524     case 'F': found_file = 1; xstrcat(&out_buffer, filename, &out_len); break;
525     case 'o': out = get_elfetype(elf); break;
526     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
527     case 'e': out = scanelf_file_stack(elf, &found_stack, &found_relro); break;
528     case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
529     case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
530     case 'n': scanelf_file_needed(elf, &found_needed, &out_buffer, &out_len); break;
531     case 'i': out = scanelf_file_interp(elf, &found_interp); break;
532 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
533 vapier 1.41 case 's': out = scanelf_file_sym(elf, &found_sym, filename); break;
534 vapier 1.29 }
535 vapier 1.41 if (out) xstrcat(&out_buffer, out, &out_len);
536 vapier 1.39 }
537    
538 vapier 1.54 #define FOUND_SOMETHING() \
539     (found_pax || found_stack || found_textrel || found_rpath || \
540     found_needed || found_interp || found_bind || found_sym)
541    
542     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
543     xchrcat(&out_buffer, ' ', &out_len);
544     xstrcat(&out_buffer, filename, &out_len);
545 vapier 1.27 }
546 vapier 1.54 if (!be_quiet || (be_quiet && FOUND_SOMETHING()))
547 vapier 1.41 puts(out_buffer);
548 vapier 1.10
549     unreadelf(elf);
550 solar 1.6 }
551    
552 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
553 vapier 1.10 static void scanelf_dir(const char *path)
554 solar 1.1 {
555 vapier 1.10 register DIR *dir;
556     register struct dirent *dentry;
557 vapier 1.14 struct stat st_top, st;
558 solar 1.21 char buf[_POSIX_PATH_MAX];
559 vapier 1.32 size_t pathlen = 0, len = 0;
560 vapier 1.10
561     /* make sure path exists */
562 vapier 1.39 if (lstat(path, &st_top) == -1) {
563     if (be_verbose > 2) printf("%s: does not exist\n", path);
564 vapier 1.10 return;
565 vapier 1.39 }
566 solar 1.11
567 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
568 vapier 1.14 if (!S_ISDIR(st_top.st_mode)) {
569 vapier 1.10 scanelf_file(path);
570     return;
571     }
572    
573     /* now scan the dir looking for fun stuff */
574     if ((dir = opendir(path)) == NULL) {
575     warnf("could not opendir %s: %s", path, strerror(errno));
576     return;
577     }
578 vapier 1.15 if (be_verbose) printf("%s: scanning dir\n", path);
579 solar 1.11
580 vapier 1.32 pathlen = strlen(path);
581 vapier 1.10 while ((dentry = readdir(dir))) {
582     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
583     continue;
584 vapier 1.32 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
585     if (len >= sizeof(buf)) {
586     warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf));
587     continue;
588     }
589 solar 1.31 sprintf(buf, "%s/%s", path, dentry->d_name);
590 solar 1.20 if (lstat(buf, &st) != -1) {
591 vapier 1.10 if (S_ISREG(st.st_mode))
592 solar 1.20 scanelf_file(buf);
593 vapier 1.10 else if (dir_recurse && S_ISDIR(st.st_mode)) {
594 vapier 1.14 if (dir_crossmount || (st_top.st_dev == st.st_dev))
595 solar 1.20 scanelf_dir(buf);
596 vapier 1.10 }
597     }
598     }
599     closedir(dir);
600 solar 1.1 }
601    
602 vapier 1.47 static int scanelf_from_file(char *filename)
603     {
604 solar 1.45 FILE *fp = NULL;
605     char *p;
606     char path[_POSIX_PATH_MAX];
607    
608     if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
609     fp = stdin;
610     else if ((fp = fopen(filename, "r")) == NULL)
611     return 1;
612    
613     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
614     if ((p = strchr(path, '\n')) != NULL)
615     *p = 0;
616     scanelf_dir(path);
617     }
618     if (fp != stdin)
619     fclose(fp);
620     return 0;
621     }
622    
623 vapier 1.48 static void load_ld_so_conf()
624     {
625     FILE *fp = NULL;
626     char *p;
627     char path[_POSIX_PATH_MAX];
628     int i = 0;
629    
630     if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
631     return;
632    
633     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
634     if (*path != '/')
635     continue;
636    
637     if ((p = strrchr(path, '\r')) != NULL)
638     *p = 0;
639     if ((p = strchr(path, '\n')) != NULL)
640     *p = 0;
641    
642     ldpaths[i++] = xstrdup(path);
643    
644     if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
645     break;
646     }
647     ldpaths[i] = NULL;
648    
649     fclose(fp);
650     }
651    
652 vapier 1.10 /* scan /etc/ld.so.conf for paths */
653     static void scanelf_ldpath()
654     {
655 vapier 1.17 char scan_l, scan_ul, scan_ull;
656 vapier 1.48 int i = 0;
657 vapier 1.10
658 vapier 1.48 if (!ldpaths[0])
659     err("Unable to load any paths from ld.so.conf");
660 vapier 1.10
661 vapier 1.17 scan_l = scan_ul = scan_ull = 0;
662    
663 vapier 1.48 while (ldpaths[i]) {
664     if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
665     if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
666     if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
667     scanelf_dir(ldpaths[i]);
668     ++i;
669     }
670 vapier 1.10
671 vapier 1.17 if (!scan_l) scanelf_dir("/lib");
672     if (!scan_ul) scanelf_dir("/usr/lib");
673     if (!scan_ull) scanelf_dir("/usr/local/lib");
674 vapier 1.10 }
675 solar 1.1
676 vapier 1.10 /* scan env PATH for paths */
677     static void scanelf_envpath()
678 solar 1.1 {
679 solar 1.34 char *path, *p;
680 vapier 1.10
681     path = getenv("PATH");
682     if (!path)
683     err("PATH is not set in your env !");
684 vapier 1.41 path = xstrdup(path);
685 vapier 1.10
686     while ((p = strrchr(path, ':')) != NULL) {
687     scanelf_dir(p + 1);
688     *p = 0;
689     }
690 vapier 1.17
691 solar 1.34 free(path);
692 solar 1.1 }
693    
694    
695 vapier 1.10
696     /* usage / invocation handling functions */
697 vapier 1.49 #define PARSE_FLAGS "plRmyxetrnibs:aqvF:f:o:BhV"
698 vapier 1.27 #define a_argument required_argument
699 vapier 1.10 static struct option const long_opts[] = {
700     {"path", no_argument, NULL, 'p'},
701     {"ldpath", no_argument, NULL, 'l'},
702     {"recursive", no_argument, NULL, 'R'},
703 vapier 1.14 {"mount", no_argument, NULL, 'm'},
704 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
705 vapier 1.10 {"pax", no_argument, NULL, 'x'},
706 solar 1.16 {"header", no_argument, NULL, 'e'},
707 vapier 1.10 {"textrel", no_argument, NULL, 't'},
708     {"rpath", no_argument, NULL, 'r'},
709 vapier 1.32 {"needed", no_argument, NULL, 'n'},
710 vapier 1.38 {"interp", no_argument, NULL, 'i'},
711 vapier 1.49 {"bind", no_argument, NULL, 'b'},
712 vapier 1.27 {"symbol", a_argument, NULL, 's'},
713 vapier 1.10 {"all", no_argument, NULL, 'a'},
714     {"quiet", no_argument, NULL, 'q'},
715 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
716 vapier 1.39 {"format", a_argument, NULL, 'F'},
717 solar 1.45 {"from", a_argument, NULL, 'f'},
718 vapier 1.27 {"file", a_argument, NULL, 'o'},
719 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
720 vapier 1.10 {"help", no_argument, NULL, 'h'},
721     {"version", no_argument, NULL, 'V'},
722     {NULL, no_argument, NULL, 0x0}
723     };
724 solar 1.57
725 vapier 1.10 static char *opts_help[] = {
726     "Scan all directories in PATH environment",
727     "Scan all directories in /etc/ld.so.conf",
728 vapier 1.14 "Scan directories recursively",
729 vapier 1.37 "Don't recursively cross mount points",
730     "Don't scan symlinks\n",
731 vapier 1.10 "Print PaX markings",
732     "Print GNU_STACK markings",
733     "Print TEXTREL information",
734     "Print RPATH information",
735 vapier 1.32 "Print NEEDED information",
736 vapier 1.38 "Print INTERP information",
737 vapier 1.49 "Print BIND information",
738 vapier 1.27 "Find a specified symbol",
739 solar 1.57 "Print all scanned info (-x -e -t -r -n -i -b)\n",
740 vapier 1.14 "Only output 'bad' things",
741     "Be verbose (can be specified more than once)",
742 vapier 1.39 "Use specified format for output",
743 solar 1.45 "Read input stream from a filename",
744 vapier 1.24 "Write output stream to a filename",
745 vapier 1.14 "Don't display the header",
746 vapier 1.10 "Print this help and exit",
747     "Print version and exit",
748     NULL
749     };
750    
751     /* display usage and exit */
752     static void usage(int status)
753 solar 1.1 {
754 vapier 1.44 unsigned long i;
755 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
756 solar 1.35 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0);
757     printf("Options: -[%s]\n", PARSE_FLAGS);
758 vapier 1.10 for (i = 0; long_opts[i].name; ++i)
759 vapier 1.27 if (long_opts[i].has_arg == no_argument)
760 solar 1.52 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
761 vapier 1.27 long_opts[i].name, opts_help[i]);
762     else
763 solar 1.52 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
764 vapier 1.27 long_opts[i].name, opts_help[i]);
765 solar 1.45
766     if (status != EXIT_SUCCESS)
767     exit(status);
768    
769 vapier 1.46 puts("\nThe format modifiers for the -F option are:");
770     puts(" %F Filename \t%x PaX Flags \t%e STACK/RELRO");
771     puts(" %t TEXTREL \t%r RPATH \t%n NEEDED");
772 vapier 1.49 puts(" %i INTERP \t%b BIND \t%s symbol");
773 solar 1.45
774 vapier 1.10 exit(status);
775 solar 1.1 }
776    
777     /* parse command line arguments and preform needed actions */
778 vapier 1.10 static void parseargs(int argc, char *argv[])
779     {
780 vapier 1.48 int i;
781 solar 1.45 char *from_file = NULL;
782 vapier 1.10
783     opterr = 0;
784 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
785     switch (i) {
786 vapier 1.10
787 vapier 1.39 case 'V':
788 solar 1.19 printf("%s compiled %s\n%s\n"
789     "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n",
790     __FILE__, __DATE__, rcsid, argv0);
791 vapier 1.10 exit(EXIT_SUCCESS);
792     break;
793     case 'h': usage(EXIT_SUCCESS); break;
794 solar 1.45 case 'f':
795     if (from_file == NULL)
796     from_file = xstrdup(optarg);
797 vapier 1.62 else
798     err("Don't specify -f twice");
799 solar 1.45 break;
800 vapier 1.24 case 'o': {
801 solar 1.21 FILE *fp = NULL;
802     fp = freopen(optarg, "w", stdout);
803 vapier 1.24 if (fp == NULL)
804     err("Could not open output stream '%s': %s", optarg, strerror(errno));
805     stdout = fp;
806 solar 1.21 break;
807     }
808 vapier 1.24
809 vapier 1.39 case 's': {
810     size_t len;
811 vapier 1.62 if (!find_sym)
812     err("Don't specify -s twice");
813 vapier 1.41 find_sym = xstrdup(optarg);
814 vapier 1.39 len = strlen(find_sym) + 1;
815 vapier 1.41 versioned_symname = (char*)xmalloc(sizeof(char) * (len+1));
816 vapier 1.39 sprintf(versioned_symname, "%s@", find_sym);
817     break;
818     }
819    
820     case 'F': {
821 solar 1.61 if (!out_format)
822     out_format = xstrdup(optarg);
823 vapier 1.62 else
824     err("Don't specify -F twice");
825 vapier 1.39 break;
826     }
827 vapier 1.27
828 vapier 1.37 case 'y': scan_symlink = 0; break;
829 solar 1.16 case 'B': show_banner = 0; break;
830 vapier 1.10 case 'l': scan_ldpath = 1; break;
831     case 'p': scan_envpath = 1; break;
832     case 'R': dir_recurse = 1; break;
833 vapier 1.14 case 'm': dir_crossmount = 0; break;
834 vapier 1.10 case 'x': show_pax = 1; break;
835 solar 1.16 case 'e': show_stack = 1; break;
836 vapier 1.10 case 't': show_textrel = 1; break;
837     case 'r': show_rpath = 1; break;
838 vapier 1.32 case 'n': show_needed = 1; break;
839 vapier 1.38 case 'i': show_interp = 1; break;
840 vapier 1.49 case 'b': show_bind = 1; break;
841 vapier 1.10 case 'q': be_quiet = 1; break;
842 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
843 vapier 1.50 case 'a': show_pax = show_stack = show_textrel = show_rpath = \
844     show_needed = show_interp = show_bind = 1; break;
845 vapier 1.10
846     case ':':
847 vapier 1.49 err("Option missing parameter\n");
848 vapier 1.10 case '?':
849 vapier 1.49 err("Unknown option\n");
850 vapier 1.10 default:
851 vapier 1.48 err("Unhandled option '%c'", i);
852 vapier 1.10 }
853     }
854    
855 vapier 1.39 /* let the format option override all other options */
856     if (out_format) {
857 vapier 1.58 show_pax = show_stack = show_textrel = show_rpath = \
858     show_needed = show_interp = show_bind = 0;
859 vapier 1.48 for (i = 0; out_format[i]; ++i) {
860     if (out_format[i] != '%') continue;
861 vapier 1.39
862 vapier 1.48 switch (out_format[++i]) {
863 vapier 1.39 case '%': break;
864     case 'F': break;
865     case 's': break;
866 vapier 1.41 case 'o': break;
867 vapier 1.39 case 'x': show_pax = 1; break;
868     case 'e': show_stack = 1; break;
869     case 't': show_textrel = 1; break;
870     case 'r': show_rpath = 1; break;
871     case 'n': show_needed = 1; break;
872     case 'i': show_interp = 1; break;
873 vapier 1.49 case 'b': show_bind = 1; break;
874 vapier 1.39 default:
875     err("Invalid format specifier '%c' (byte %i)",
876 vapier 1.48 out_format[i], i+1);
877 vapier 1.39 }
878     }
879 vapier 1.41
880     /* construct our default format */
881     } else {
882     size_t fmt_len = 30;
883     out_format = (char*)xmalloc(sizeof(char) * fmt_len);
884     if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
885     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
886     if (show_stack) xstrcat(&out_format, "%e ", &fmt_len);
887     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
888     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
889     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
890     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
891 vapier 1.49 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
892 vapier 1.41 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
893     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
894 vapier 1.39 }
895 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
896 vapier 1.39
897     /* now lets actually do the scanning */
898 vapier 1.48 if (scan_ldpath || (show_rpath && be_quiet))
899     load_ld_so_conf();
900 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
901     if (scan_envpath) scanelf_envpath();
902 solar 1.45 if (from_file) {
903     scanelf_from_file(from_file);
904     free(from_file);
905     from_file = *argv;
906     }
907     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
908 vapier 1.25 err("Nothing to scan !?");
909 vapier 1.10 while (optind < argc)
910     scanelf_dir(argv[optind++]);
911 vapier 1.27
912 vapier 1.39 /* clean up */
913     if (find_sym) {
914     free(find_sym);
915     free(versioned_symname);
916     }
917     if (out_format) free(out_format);
918 vapier 1.48 for (i = 0; ldpaths[i]; ++i)
919     free(ldpaths[i]);
920 vapier 1.10 }
921    
922    
923    
924 vapier 1.41 /* utility funcs */
925 vapier 1.60 static char *xstrdup(const char *s)
926 vapier 1.41 {
927     char *ret = strdup(s);
928     if (!ret) err("Could not strdup(): %s", strerror(errno));
929     return ret;
930     }
931 solar 1.57
932 vapier 1.41 static void *xmalloc(size_t size)
933     {
934     void *ret = malloc(size);
935     if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
936     return ret;
937     }
938 solar 1.57
939 vapier 1.41 static void xstrcat(char **dst, const char *src, size_t *curr_len)
940     {
941     long new_len;
942    
943     new_len = strlen(*dst) + strlen(src);
944     if (*curr_len <= new_len) {
945     *curr_len = new_len + (*curr_len / 2);
946     *dst = realloc(*dst, *curr_len);
947     if (!*dst)
948     err("could not realloc %li bytes", (unsigned long)*curr_len);
949     }
950    
951     strcat(*dst, src);
952     }
953 solar 1.57
954 vapier 1.41 static inline void xchrcat(char **dst, const char append, size_t *curr_len)
955     {
956     static char my_app[2];
957     my_app[0] = append;
958     my_app[1] = '\0';
959     xstrcat(dst, my_app, curr_len);
960     }
961    
962    
963 vapier 1.10 int main(int argc, char *argv[])
964 solar 1.1 {
965 vapier 1.10 if (argc < 2)
966     usage(EXIT_FAILURE);
967     parseargs(argc, argv);
968 solar 1.21 fclose(stdout);
969 solar 1.61 #ifdef __BOUNDS_CHECKING_ON
970     warn("The calls to add/delete heap should be off by 1 due to the out_buffer not being freed in scanelf_file()");
971     #endif
972 vapier 1.10 return EXIT_SUCCESS;
973 solar 1.1 }

  ViewVC Help
Powered by ViewVC 1.1.20