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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.70 - (hide annotations) (download) (as text)
Fri Jun 3 23:41:59 2005 UTC (9 years, 1 month ago) by vapier
Branch: MAIN
Changes since 1.69: +40 -18 lines
File MIME type: text/x-csrc
add support for silent tests via # instead of %

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

  ViewVC Help
Powered by ViewVC 1.1.20