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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.69 - (hide annotations) (download) (as text)
Fri Jun 3 23:18:01 2005 UTC (8 years, 10 months ago) by vapier
Branch: MAIN
Changes since 1.68: +23 -13 lines
File MIME type: text/x-csrc
fix warnings about signed compares and add support for parsing RPATH in silent mode

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

  ViewVC Help
Powered by ViewVC 1.1.20