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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.89 - (hide annotations) (download) (as text)
Thu Oct 13 01:53:55 2005 UTC (8 years, 9 months ago) by vapier
Branch: MAIN
Changes since 1.88: +8 -5 lines
File MIME type: text/x-csrc
split common non-elf features into paxinc.[ch]

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.89 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.88 2005/09/30 03:30:54 vapier Exp $
5 solar 1.1 *
6 vapier 1.87 * Copyright 2003-2005 Ned Ludd - <solar@gentoo.org>
7     * Copyright 2004-2005 Mike Frysinger - <vapier@gentoo.org>
8 solar 1.1 */
9    
10     #include <stdio.h>
11     #include <stdlib.h>
12     #include <sys/types.h>
13 vapier 1.60 #include <libgen.h>
14     #include <limits.h>
15 solar 1.28 #define __USE_GNU
16 solar 1.1 #include <string.h>
17 vapier 1.10 #include <errno.h>
18 solar 1.1 #include <unistd.h>
19     #include <sys/stat.h>
20     #include <dirent.h>
21     #include <getopt.h>
22 solar 1.20 #include <assert.h>
23 vapier 1.89 #include "paxinc.h"
24 solar 1.1
25 vapier 1.89 static const char *rcsid = "$Id: scanelf.c,v 1.88 2005/09/30 03:30:54 vapier Exp $";
26 vapier 1.36 #define argv0 "scanelf"
27 vapier 1.10
28 vapier 1.70 #define IS_MODIFIER(c) (c == '%' || c == '#')
29    
30 vapier 1.10
31    
32     /* prototypes */
33     static void scanelf_file(const char *filename);
34     static void scanelf_dir(const char *path);
35     static void scanelf_ldpath();
36     static void scanelf_envpath();
37     static void usage(int status);
38     static void parseargs(int argc, char *argv[]);
39 vapier 1.60 static char *xstrdup(const char *s);
40 vapier 1.41 static void *xmalloc(size_t size);
41     static void xstrcat(char **dst, const char *src, size_t *curr_len);
42     static inline void xchrcat(char **dst, const char append, size_t *curr_len);
43 vapier 1.10
44     /* variables to control behavior */
45 vapier 1.48 static char *ldpaths[256];
46 vapier 1.10 static char scan_ldpath = 0;
47     static char scan_envpath = 0;
48 vapier 1.37 static char scan_symlink = 1;
49 vapier 1.10 static char dir_recurse = 0;
50 vapier 1.14 static char dir_crossmount = 1;
51 vapier 1.10 static char show_pax = 0;
52 solar 1.73 static char show_phdr = 0;
53 vapier 1.10 static char show_textrel = 0;
54     static char show_rpath = 0;
55 vapier 1.32 static char show_needed = 0;
56 vapier 1.38 static char show_interp = 0;
57 vapier 1.49 static char show_bind = 0;
58 vapier 1.84 static char show_soname = 0;
59 vapier 1.76 static char show_textrels = 0;
60 solar 1.16 static char show_banner = 1;
61 vapier 1.10 static char be_quiet = 0;
62 vapier 1.14 static char be_verbose = 0;
63 vapier 1.70 static char be_wewy_wewy_quiet = 0;
64 vapier 1.39 static char *find_sym = NULL, *versioned_symname = NULL;
65 vapier 1.72 static char *find_lib = NULL;
66 vapier 1.39 static char *out_format = NULL;
67 vapier 1.66 static char *search_path = NULL;
68 solar 1.86 static char gmatch = 0;
69 solar 1.1
70 vapier 1.70
71 vapier 1.39 /* sub-funcs for scanelf_file() */
72 vapier 1.77 static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
73     {
74     /* find the best SHT_DYNSYM and SHT_STRTAB sections */
75     #define GET_SYMTABS(B) \
76     if (elf->elf_class == ELFCLASS ## B) { \
77     Elf ## B ## _Shdr *symtab, *strtab, *dynsym, *dynstr; \
78     /* debug sections */ \
79     symtab = SHDR ## B (elf_findsecbyname(elf, ".symtab")); \
80     strtab = SHDR ## B (elf_findsecbyname(elf, ".strtab")); \
81     /* runtime sections */ \
82     dynsym = SHDR ## B (elf_findsecbyname(elf, ".dynsym")); \
83     dynstr = SHDR ## B (elf_findsecbyname(elf, ".dynstr")); \
84     if (symtab && dynsym) { \
85     *sym = (void*)((EGET(symtab->sh_size) > EGET(dynsym->sh_size)) ? symtab : dynsym); \
86     } else { \
87     *sym = (void*)(symtab ? symtab : dynsym); \
88     } \
89     if (strtab && dynstr) { \
90     *tab = (void*)((EGET(strtab->sh_size) > EGET(dynstr->sh_size)) ? strtab : dynstr); \
91     } else { \
92     *tab = (void*)(strtab ? strtab : dynstr); \
93     } \
94     }
95     GET_SYMTABS(32)
96     GET_SYMTABS(64)
97     }
98 vapier 1.41 static char *scanelf_file_pax(elfobj *elf, char *found_pax)
99 solar 1.6 {
100 vapier 1.41 static char *paxflags;
101 solar 1.61 static char ret[7];
102     unsigned long i, shown;
103    
104 vapier 1.41 if (!show_pax) return NULL;
105 vapier 1.10
106 solar 1.61 shown = 0;
107     memset(&ret, 0, sizeof(ret));
108    
109     if (elf->phdr) {
110     #define SHOW_PAX(B) \
111     if (elf->elf_class == ELFCLASS ## B) { \
112     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
113     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
114     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
115     if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
116     continue; \
117     if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \
118     continue; \
119     memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
120     *found_pax = 1; \
121     ++shown; \
122     break; \
123     } \
124     }
125     SHOW_PAX(32)
126     SHOW_PAX(64)
127     }
128    
129     /* fall back to EI_PAX if no PT_PAX was found */
130     if (!*ret) {
131     paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
132     if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
133     *found_pax = 1;
134     return paxflags;
135     }
136     strncpy(ret, paxflags, sizeof(ret));
137 vapier 1.14 }
138 vapier 1.41
139 solar 1.61 if (be_quiet && !shown)
140     return NULL;
141     return ret;
142    
143 vapier 1.39 }
144 solar 1.73 static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
145 vapier 1.39 {
146 vapier 1.71 static char ret[12];
147 vapier 1.41 char *found;
148 vapier 1.71 unsigned long i, off, shown, check_flags;
149     unsigned char multi_stack, multi_relro, multi_load;
150 vapier 1.41
151 solar 1.73 if (!show_phdr) return NULL;
152 vapier 1.41
153 vapier 1.71 memcpy(ret, "--- --- ---\0", 12);
154    
155 vapier 1.41 shown = 0;
156 vapier 1.71 multi_stack = multi_relro = multi_load = 0;
157 vapier 1.44
158     if (elf->phdr) {
159 solar 1.73 #define SHOW_PHDR(B) \
160 vapier 1.39 if (elf->elf_class == ELFCLASS ## B) { \
161     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
162     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
163 vapier 1.71 uint32_t flags; \
164 vapier 1.39 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
165 vapier 1.41 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
166 vapier 1.71 if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
167 solar 1.73 found = found_phdr; \
168 vapier 1.41 off = 0; \
169 vapier 1.71 check_flags = PF_X; \
170 vapier 1.41 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
171 vapier 1.71 if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
172 vapier 1.41 found = found_relro; \
173 solar 1.53 off = 4; \
174 vapier 1.71 check_flags = PF_X; \
175     } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
176     if (multi_load++ > 2) warnf("%s: more than 2 PT_LOAD's !?", elf->filename); \
177     found = found_load; \
178     off = 8; \
179     check_flags = PF_W|PF_X; \
180 vapier 1.41 } else \
181     continue; \
182 vapier 1.71 flags = EGET(phdr[i].p_flags); \
183     if (be_quiet && ((flags & check_flags) != check_flags)) \
184 vapier 1.39 continue; \
185 vapier 1.71 memcpy(ret+off, gnu_short_stack_flags(flags), 3); \
186 vapier 1.41 *found = 1; \
187     ++shown; \
188 vapier 1.39 } \
189     }
190 solar 1.73 SHOW_PHDR(32)
191     SHOW_PHDR(64)
192 vapier 1.44 }
193    
194 vapier 1.41 if (be_quiet && !shown)
195     return NULL;
196     else
197     return ret;
198 vapier 1.39 }
199 vapier 1.41 static char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
200 vapier 1.39 {
201 solar 1.68 static char ret[] = "TEXTREL";
202 vapier 1.44 unsigned long i;
203 vapier 1.41
204 vapier 1.79 if (!show_textrel && !show_textrels) return NULL;
205 vapier 1.41
206 vapier 1.44 if (elf->phdr) {
207 vapier 1.39 #define SHOW_TEXTREL(B) \
208     if (elf->elf_class == ELFCLASS ## B) { \
209     Elf ## B ## _Dyn *dyn; \
210     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
211     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
212 vapier 1.44 Elf ## B ## _Off offset; \
213 vapier 1.39 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
214 vapier 1.59 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
215 vapier 1.44 offset = EGET(phdr[i].p_offset); \
216     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
217     dyn = DYN ## B (elf->data + offset); \
218 vapier 1.39 while (EGET(dyn->d_tag) != DT_NULL) { \
219     if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
220     *found_textrel = 1; \
221     /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
222 vapier 1.70 return (be_wewy_wewy_quiet ? NULL : ret); \
223 vapier 1.39 } \
224     ++dyn; \
225 vapier 1.26 } \
226 vapier 1.39 } }
227     SHOW_TEXTREL(32)
228     SHOW_TEXTREL(64)
229 vapier 1.44 }
230    
231 vapier 1.70 if (be_quiet || be_wewy_wewy_quiet)
232 vapier 1.41 return NULL;
233     else
234 solar 1.68 return (char *)" - ";
235 vapier 1.39 }
236 vapier 1.79 static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
237 vapier 1.76 {
238 vapier 1.82 unsigned long s, r, rmax;
239     void *symtab_void, *strtab_void, *text_void;
240 vapier 1.76
241     if (!show_textrels) return NULL;
242    
243 vapier 1.79 /* don't search for TEXTREL's if the ELF doesn't have any */
244     if (!*found_textrel) scanelf_file_textrel(elf, found_textrel);
245     if (!*found_textrel) return NULL;
246    
247 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
248 vapier 1.82 text_void = elf_findsecbyname(elf, ".text");
249 vapier 1.76
250 vapier 1.82 if (symtab_void && strtab_void && text_void && elf->shdr) {
251 vapier 1.76 #define SHOW_TEXTRELS(B) \
252     if (elf->elf_class == ELFCLASS ## B) { \
253     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
254     Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
255     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
256     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
257 vapier 1.82 Elf ## B ## _Shdr *text = SHDR ## B (text_void); \
258     Elf ## B ## _Addr vaddr = EGET(text->sh_addr); \
259     uint ## B ## _t memsz = EGET(text->sh_size); \
260 vapier 1.76 Elf ## B ## _Rel *rel; \
261     Elf ## B ## _Rela *rela; \
262     /* search the section headers for relocations */ \
263     for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
264     uint32_t sh_type = EGET(shdr[s].sh_type); \
265     if (sh_type == SHT_REL) { \
266     rel = REL ## B (elf->data + EGET(shdr[s].sh_offset)); \
267     rela = NULL; \
268     rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
269     } else if (sh_type == SHT_RELA) { \
270     rel = NULL; \
271     rela = RELA ## B (elf->data + EGET(shdr[s].sh_offset)); \
272     rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
273     } else \
274     continue; \
275 vapier 1.82 /* now see if any of the relocs are in the .text */ \
276     for (r = 0; r < rmax; ++r) { \
277     unsigned long sym_max; \
278     Elf ## B ## _Addr offset_tmp; \
279     Elf ## B ## _Sym *func; \
280     Elf ## B ## _Sym *sym; \
281     Elf ## B ## _Addr r_offset; \
282     uint ## B ## _t r_info; \
283     if (sh_type == SHT_REL) { \
284     r_offset = EGET(rel[r].r_offset); \
285     r_info = EGET(rel[r].r_info); \
286     } else { \
287     r_offset = EGET(rela[r].r_offset); \
288     r_info = EGET(rela[r].r_info); \
289     } \
290     /* make sure this relocation is inside of the .text */ \
291     if (r_offset < vaddr || r_offset >= vaddr + memsz) { \
292     if (be_verbose <= 2) continue; \
293     } else \
294 vapier 1.78 *found_textrels = 1; \
295 vapier 1.82 /* locate this relocation symbol name */ \
296     sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
297     sym_max = ELF ## B ## _R_SYM(r_info); \
298     if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
299     sym += sym_max; \
300     else \
301     sym = NULL; \
302     sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
303     /* show the raw details about this reloc */ \
304 vapier 1.88 printf(" %s: ", elf->base_filename); \
305 vapier 1.82 if (sym && sym->st_name) \
306     printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name))); \
307     else \
308 vapier 1.88 printf("(memory/fake?)"); \
309 vapier 1.82 printf(" [0x%lX]", (unsigned long)r_offset); \
310     /* now try to find the closest symbol that this rel is probably in */ \
311     sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
312     func = NULL; \
313     offset_tmp = 0; \
314     while (sym_max--) { \
315     if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
316     func = sym; \
317     offset_tmp = EGET(sym->st_value); \
318 vapier 1.76 } \
319 vapier 1.82 ++sym; \
320 vapier 1.76 } \
321 vapier 1.82 printf(" in "); \
322     if (func && func->st_name) \
323     printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(func->st_name))); \
324     else \
325     printf("(NULL: fake?)"); \
326     printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
327 vapier 1.76 } \
328     } }
329     SHOW_TEXTRELS(32)
330     SHOW_TEXTRELS(64)
331     }
332 vapier 1.82 if (!*found_textrels)
333     warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
334 vapier 1.76
335     return NULL;
336     }
337 solar 1.83
338     static void rpath_security_checks(elfobj *, char *);
339     static void rpath_security_checks(elfobj *elf, char *item) {
340     struct stat st;
341 vapier 1.84 switch (*item) {
342 vapier 1.89 case '/': break;
343     case '.':
344     warnf("Security problem with relative RPATH '%s' in %s", item, elf->filename);
345     break;
346     case '\0':
347 solar 1.83 warnf("Security problem NULL RPATH in %s", elf->filename);
348     break;
349     case '$':
350 vapier 1.84 if (fstat(elf->fd, &st) != -1)
351 solar 1.83 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
352     warnf("Security problem with RPATH='%s' in %s with mode set of %o",
353     item, elf->filename, st.st_mode & 07777);
354     break;
355     default:
356     warnf("Maybe? sec problem with RPATH='%s' in %s", item, elf->filename);
357     break;
358     }
359     }
360 vapier 1.41 static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
361 vapier 1.39 {
362 vapier 1.48 unsigned long i, s;
363     char *rpath, *runpath, **r;
364 vapier 1.39 void *strtbl_void;
365 vapier 1.10
366 vapier 1.39 if (!show_rpath) return;
367 vapier 1.10
368 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
369     rpath = runpath = NULL;
370 vapier 1.10
371 vapier 1.44 if (elf->phdr && strtbl_void) {
372 vapier 1.26 #define SHOW_RPATH(B) \
373     if (elf->elf_class == ELFCLASS ## B) { \
374     Elf ## B ## _Dyn *dyn; \
375     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
376     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
377     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
378 vapier 1.44 Elf ## B ## _Off offset; \
379 vapier 1.60 Elf ## B ## _Xword word; \
380 vapier 1.48 /* Scan all the program headers */ \
381 vapier 1.26 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
382 vapier 1.48 /* Just scan dynamic headers */ \
383 vapier 1.26 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
384 vapier 1.44 offset = EGET(phdr[i].p_offset); \
385     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
386 vapier 1.48 /* Just scan dynamic RPATH/RUNPATH headers */ \
387 vapier 1.44 dyn = DYN ## B (elf->data + offset); \
388 vapier 1.48 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
389     if (word == DT_RPATH) { \
390     r = &rpath; \
391     } else if (word == DT_RUNPATH) { \
392     r = &runpath; \
393     } else { \
394     ++dyn; \
395     continue; \
396     } \
397     /* Verify the memory is somewhat sane */ \
398     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
399 vapier 1.69 if (offset < (Elf ## B ## _Off)elf->len) { \
400 vapier 1.48 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
401     *r = (char*)(elf->data + offset); \
402     /* If quiet, don't output paths in ld.so.conf */ \
403 vapier 1.69 if (be_quiet) { \
404     size_t len; \
405     char *start, *end; \
406 vapier 1.75 /* note that we only 'chop' off leading known paths. */ \
407     /* since *r is read-only memory, we can only move the ptr forward. */ \
408     start = *r; \
409     /* scan each path in : delimited list */ \
410     while (start) { \
411 solar 1.83 rpath_security_checks(elf, start); \
412 vapier 1.69 end = strchr(start, ':'); \
413 vapier 1.75 len = (end ? abs(end - start) : strlen(start)); \
414     for (s = 0; ldpaths[s]; ++s) { \
415 vapier 1.69 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
416     *r = (end ? end + 1 : NULL); \
417     break; \
418     } \
419 vapier 1.48 } \
420 vapier 1.75 if (!*r || !ldpaths[s] || !end) \
421     start = NULL; \
422     else \
423     start = start + len + 1; \
424 vapier 1.69 } \
425     } \
426 vapier 1.48 if (*r) *found_rpath = 1; \
427 vapier 1.26 } \
428     ++dyn; \
429     } \
430     } }
431     SHOW_RPATH(32)
432     SHOW_RPATH(64)
433 vapier 1.10 }
434 vapier 1.41
435 vapier 1.70 if (be_wewy_wewy_quiet) return;
436    
437 vapier 1.39 if (rpath && runpath) {
438 vapier 1.41 if (!strcmp(rpath, runpath)) {
439     xstrcat(ret, runpath, ret_len);
440     } else {
441 vapier 1.39 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
442 vapier 1.41 xchrcat(ret, '{', ret_len);
443     xstrcat(ret, rpath, ret_len);
444     xchrcat(ret, ',', ret_len);
445     xstrcat(ret, runpath, ret_len);
446     xchrcat(ret, '}', ret_len);
447 vapier 1.39 }
448     } else if (rpath || runpath)
449 vapier 1.41 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
450     else if (!be_quiet)
451     xstrcat(ret, " - ", ret_len);
452 vapier 1.39 }
453 vapier 1.84 static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
454 vapier 1.39 {
455 vapier 1.44 unsigned long i;
456 vapier 1.39 char *needed;
457     void *strtbl_void;
458    
459 vapier 1.72 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL;
460 vapier 1.10
461 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
462 vapier 1.32
463 vapier 1.44 if (elf->phdr && strtbl_void) {
464 vapier 1.32 #define SHOW_NEEDED(B) \
465     if (elf->elf_class == ELFCLASS ## B) { \
466     Elf ## B ## _Dyn *dyn; \
467     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
468     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
469     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
470 vapier 1.44 Elf ## B ## _Off offset; \
471 vapier 1.32 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
472     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
473 vapier 1.44 offset = EGET(phdr[i].p_offset); \
474     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
475     dyn = DYN ## B (elf->data + offset); \
476 vapier 1.32 while (EGET(dyn->d_tag) != DT_NULL) { \
477     if (EGET(dyn->d_tag) == DT_NEEDED) { \
478 vapier 1.44 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
479 vapier 1.69 if (offset >= (Elf ## B ## _Off)elf->len) { \
480 vapier 1.49 ++dyn; \
481     continue; \
482     } \
483 vapier 1.44 needed = (char*)(elf->data + offset); \
484 vapier 1.72 if (op == 0) { \
485     if (!be_wewy_wewy_quiet) { \
486     if (*found_needed) xchrcat(ret, ',', ret_len); \
487     xstrcat(ret, needed, ret_len); \
488     } \
489     *found_needed = 1; \
490     } else { \
491 solar 1.86 if (!strncmp(find_lib, needed, strlen( !gmatch ? needed : find_lib))) { \
492 vapier 1.81 *found_lib = 1; \
493 solar 1.86 return (be_wewy_wewy_quiet ? NULL : needed); \
494 vapier 1.81 } \
495 vapier 1.72 } \
496 vapier 1.32 } \
497     ++dyn; \
498     } \
499     } }
500     SHOW_NEEDED(32)
501     SHOW_NEEDED(64)
502 vapier 1.85 if (op == 0 && !*found_needed && be_verbose)
503 vapier 1.84 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
504 vapier 1.32 }
505 vapier 1.72
506     return NULL;
507 vapier 1.39 }
508 vapier 1.41 static char *scanelf_file_interp(elfobj *elf, char *found_interp)
509 vapier 1.39 {
510     void *strtbl_void;
511    
512 vapier 1.41 if (!show_interp) return NULL;
513 vapier 1.32
514 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".interp");
515 vapier 1.38
516 vapier 1.39 if (strtbl_void) {
517 vapier 1.38 #define SHOW_INTERP(B) \
518     if (elf->elf_class == ELFCLASS ## B) { \
519 solar 1.40 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
520     *found_interp = 1; \
521 vapier 1.70 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
522 vapier 1.38 }
523     SHOW_INTERP(32)
524     SHOW_INTERP(64)
525     }
526 vapier 1.41 return NULL;
527 vapier 1.39 }
528 vapier 1.49 static char *scanelf_file_bind(elfobj *elf, char *found_bind)
529     {
530     unsigned long i;
531     struct stat s;
532    
533     if (!show_bind) return NULL;
534 vapier 1.51 if (!elf->phdr) return NULL;
535 vapier 1.49
536     #define SHOW_BIND(B) \
537     if (elf->elf_class == ELFCLASS ## B) { \
538     Elf ## B ## _Dyn *dyn; \
539     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
540     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
541     Elf ## B ## _Off offset; \
542     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
543     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
544     offset = EGET(phdr[i].p_offset); \
545     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
546     dyn = DYN ## B (elf->data + offset); \
547     while (EGET(dyn->d_tag) != DT_NULL) { \
548     if (EGET(dyn->d_tag) == DT_BIND_NOW || \
549     (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
550     { \
551     if (be_quiet) return NULL; \
552     *found_bind = 1; \
553 vapier 1.70 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
554 vapier 1.49 } \
555     ++dyn; \
556     } \
557     } \
558     }
559     SHOW_BIND(32)
560     SHOW_BIND(64)
561    
562 vapier 1.70 if (be_wewy_wewy_quiet) return NULL;
563    
564 vapier 1.56 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
565 vapier 1.49 return NULL;
566     } else {
567     *found_bind = 1;
568 solar 1.68 return (char *) "LAZY";
569 vapier 1.49 }
570     }
571 vapier 1.84 static char *scanelf_file_soname(elfobj *elf, char *found_soname)
572     {
573     unsigned long i;
574     char *soname;
575     void *strtbl_void;
576    
577     if (!show_soname) return NULL;
578    
579     strtbl_void = elf_findsecbyname(elf, ".dynstr");
580    
581     if (elf->phdr && strtbl_void) {
582     #define SHOW_SONAME(B) \
583     if (elf->elf_class == ELFCLASS ## B) { \
584     Elf ## B ## _Dyn *dyn; \
585     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
586     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
587     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
588     Elf ## B ## _Off offset; \
589     /* only look for soname in shared objects */ \
590     if (ehdr->e_type != ET_DYN) \
591     return NULL; \
592     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
593     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
594     offset = EGET(phdr[i].p_offset); \
595     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
596     dyn = DYN ## B (elf->data + offset); \
597     while (EGET(dyn->d_tag) != DT_NULL) { \
598     if (EGET(dyn->d_tag) == DT_SONAME) { \
599     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
600     if (offset >= (Elf ## B ## _Off)elf->len) { \
601     ++dyn; \
602     continue; \
603     } \
604     soname = (char*)(elf->data + offset); \
605     *found_soname = 1; \
606     return (be_wewy_wewy_quiet ? NULL : soname); \
607     } \
608     ++dyn; \
609     } \
610     } }
611     SHOW_SONAME(32)
612     SHOW_SONAME(64)
613     }
614    
615     return NULL;
616     }
617 vapier 1.72 static char *scanelf_file_sym(elfobj *elf, char *found_sym)
618 vapier 1.39 {
619 vapier 1.44 unsigned long i;
620 vapier 1.39 void *symtab_void, *strtab_void;
621 vapier 1.38
622 vapier 1.41 if (!find_sym) return NULL;
623 vapier 1.32
624 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
625 vapier 1.27
626 vapier 1.39 if (symtab_void && strtab_void) {
627 vapier 1.27 #define FIND_SYM(B) \
628     if (elf->elf_class == ELFCLASS ## B) { \
629     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
630     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
631     Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
632 vapier 1.44 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
633 vapier 1.27 char *symname; \
634     for (i = 0; i < cnt; ++i) { \
635     if (sym->st_name) { \
636     symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
637 solar 1.28 if (*find_sym == '*') { \
638 vapier 1.32 printf("%s(%s) %5lX %15s %s\n", \
639 vapier 1.39 ((*found_sym == 0) ? "\n\t" : "\t"), \
640 vapier 1.76 elf->base_filename, \
641 vapier 1.32 (long)sym->st_size, \
642     (char *)get_elfstttype(sym->st_info), \
643     symname); \
644 vapier 1.39 *found_sym = 1; \
645 vapier 1.32 } else if ((strcmp(find_sym, symname) == 0) || \
646 vapier 1.39 (strcmp(symname, versioned_symname) == 0)) \
647     (*found_sym)++; \
648 vapier 1.27 } \
649     ++sym; \
650     } }
651     FIND_SYM(32)
652     FIND_SYM(64)
653 vapier 1.39 }
654 vapier 1.70
655     if (be_wewy_wewy_quiet) return NULL;
656    
657 vapier 1.41 if (*find_sym != '*' && *found_sym)
658     return find_sym;
659     if (be_quiet)
660     return NULL;
661     else
662 solar 1.68 return (char *)" - ";
663 vapier 1.39 }
664     /* scan an elf file and show all the fun stuff */
665 solar 1.57 #define prints(str) write(fileno(stdout), str, strlen(str))
666 vapier 1.39 static void scanelf_file(const char *filename)
667     {
668 vapier 1.44 unsigned long i;
669 solar 1.73 char found_pax, found_phdr, found_relro, found_load, found_textrel,
670 vapier 1.84 found_rpath, found_needed, found_interp, found_bind, found_soname,
671 vapier 1.76 found_sym, found_lib, found_file, found_textrels;
672 vapier 1.39 elfobj *elf;
673     struct stat st;
674 vapier 1.41 static char *out_buffer = NULL;
675     static size_t out_len;
676 vapier 1.39
677     /* make sure 'filename' exists */
678     if (lstat(filename, &st) == -1) {
679     if (be_verbose > 2) printf("%s: does not exist\n", filename);
680     return;
681     }
682     /* always handle regular files and handle symlinked files if no -y */
683 vapier 1.55 if (S_ISLNK(st.st_mode)) {
684     if (!scan_symlink) return;
685     stat(filename, &st);
686     }
687     if (!S_ISREG(st.st_mode)) {
688 vapier 1.39 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
689     return;
690     }
691    
692 vapier 1.76 found_pax = found_phdr = found_relro = found_load = found_textrel = \
693 vapier 1.84 found_rpath = found_needed = found_interp = found_bind = found_soname = \
694 vapier 1.76 found_sym = found_lib = found_file = found_textrels = 0;
695 vapier 1.39
696     /* verify this is real ELF */
697     if ((elf = readelf(filename)) == NULL) {
698     if (be_verbose > 2) printf("%s: not an ELF\n", filename);
699     return;
700     }
701    
702     if (be_verbose > 1)
703 vapier 1.41 printf("%s: scanning file {%s,%s}\n", filename,
704 vapier 1.69 get_elfeitype(EI_CLASS, elf->elf_class),
705     get_elfeitype(EI_DATA, elf->data[EI_DATA]));
706 vapier 1.39 else if (be_verbose)
707     printf("%s: scanning file\n", filename);
708    
709 vapier 1.41 /* init output buffer */
710     if (!out_buffer) {
711     out_len = sizeof(char) * 80;
712     out_buffer = (char*)xmalloc(out_len);
713     }
714     *out_buffer = '\0';
715    
716 vapier 1.39 /* show the header */
717     if (!be_quiet && show_banner) {
718 vapier 1.49 for (i = 0; out_format[i]; ++i) {
719 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
720 vapier 1.41
721     switch (out_format[++i]) {
722     case '%': break;
723 vapier 1.70 case '#': break;
724 vapier 1.66 case 'F':
725     case 'p':
726     case 'f': prints("FILE "); found_file = 1; break;
727 vapier 1.41 case 'o': prints(" TYPE "); break;
728     case 'x': prints(" PAX "); break;
729 vapier 1.71 case 'e': prints("STK/REL/PTL "); break;
730 vapier 1.41 case 't': prints("TEXTREL "); break;
731     case 'r': prints("RPATH "); break;
732     case 'n': prints("NEEDED "); break;
733     case 'i': prints("INTERP "); break;
734 vapier 1.49 case 'b': prints("BIND "); break;
735 vapier 1.84 case 'S': prints("SONAME "); break;
736 vapier 1.41 case 's': prints("SYM "); break;
737 vapier 1.72 case 'N': prints("LIB "); break;
738 vapier 1.76 case 'T': prints("TEXTRELS "); break;
739     default: warnf("'%c' has no title ?", out_format[i]);
740 vapier 1.39 }
741 vapier 1.27 }
742 vapier 1.49 if (!found_file) prints("FILE ");
743 vapier 1.41 prints("\n");
744 vapier 1.49 found_file = 0;
745 vapier 1.39 show_banner = 0;
746     }
747    
748     /* dump all the good stuff */
749 vapier 1.49 for (i = 0; out_format[i]; ++i) {
750 vapier 1.41 const char *out;
751 vapier 1.66 const char *tmp;
752 vapier 1.41
753     /* make sure we trim leading spaces in quiet mode */
754     if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
755     *out_buffer = '\0';
756 vapier 1.39
757 vapier 1.70 if (!IS_MODIFIER(out_format[i])) {
758 vapier 1.41 xchrcat(&out_buffer, out_format[i], &out_len);
759     continue;
760     }
761 vapier 1.39
762 vapier 1.41 out = NULL;
763 vapier 1.70 be_wewy_wewy_quiet = (out_format[i] == '#');
764 vapier 1.41 switch (out_format[++i]) {
765 vapier 1.70 case '%':
766     case '#':
767     xchrcat(&out_buffer, out_format[i], &out_len); break;
768     case 'F':
769 vapier 1.76 found_file = 1;
770 vapier 1.70 if (be_wewy_wewy_quiet) break;
771     xstrcat(&out_buffer, filename, &out_len);
772     break;
773 vapier 1.66 case 'p':
774 vapier 1.76 found_file = 1;
775 vapier 1.70 if (be_wewy_wewy_quiet) break;
776 vapier 1.66 tmp = filename;
777     if (search_path) {
778     ssize_t len_search = strlen(search_path);
779     ssize_t len_file = strlen(filename);
780     if (!strncmp(filename, search_path, len_search) && \
781     len_file > len_search)
782     tmp += len_search;
783     if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
784     }
785     xstrcat(&out_buffer, tmp, &out_len);
786     break;
787     case 'f':
788 vapier 1.76 found_file = 1;
789 vapier 1.70 if (be_wewy_wewy_quiet) break;
790 vapier 1.66 tmp = strrchr(filename, '/');
791     tmp = (tmp == NULL ? filename : tmp+1);
792     xstrcat(&out_buffer, tmp, &out_len);
793     break;
794 vapier 1.41 case 'o': out = get_elfetype(elf); break;
795     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
796 solar 1.73 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
797 vapier 1.41 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
798 vapier 1.79 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
799 vapier 1.41 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
800 vapier 1.72 case 'n':
801     case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
802 vapier 1.41 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
803 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
804 vapier 1.84 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
805 vapier 1.72 case 's': out = scanelf_file_sym(elf, &found_sym); break;
806 vapier 1.76 default: warnf("'%c' has no scan code?", out_format[i]);
807 vapier 1.29 }
808 vapier 1.41 if (out) xstrcat(&out_buffer, out, &out_len);
809 vapier 1.39 }
810    
811 vapier 1.54 #define FOUND_SOMETHING() \
812 solar 1.73 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
813 vapier 1.76 found_rpath || found_needed || found_interp || found_bind || \
814 vapier 1.84 found_soname || found_sym || found_lib || found_textrels)
815 vapier 1.54
816     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
817     xchrcat(&out_buffer, ' ', &out_len);
818     xstrcat(&out_buffer, filename, &out_len);
819 vapier 1.27 }
820 vapier 1.79 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
821 vapier 1.41 puts(out_buffer);
822 vapier 1.79 fflush(stdout);
823     }
824 vapier 1.10
825     unreadelf(elf);
826 solar 1.6 }
827    
828 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
829 vapier 1.10 static void scanelf_dir(const char *path)
830 solar 1.1 {
831 vapier 1.10 register DIR *dir;
832     register struct dirent *dentry;
833 vapier 1.14 struct stat st_top, st;
834 solar 1.21 char buf[_POSIX_PATH_MAX];
835 vapier 1.32 size_t pathlen = 0, len = 0;
836 vapier 1.10
837     /* make sure path exists */
838 vapier 1.39 if (lstat(path, &st_top) == -1) {
839     if (be_verbose > 2) printf("%s: does not exist\n", path);
840 vapier 1.10 return;
841 vapier 1.39 }
842 solar 1.11
843 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
844 vapier 1.14 if (!S_ISDIR(st_top.st_mode)) {
845 vapier 1.10 scanelf_file(path);
846     return;
847     }
848    
849     /* now scan the dir looking for fun stuff */
850     if ((dir = opendir(path)) == NULL) {
851     warnf("could not opendir %s: %s", path, strerror(errno));
852     return;
853     }
854 vapier 1.15 if (be_verbose) printf("%s: scanning dir\n", path);
855 solar 1.11
856 vapier 1.32 pathlen = strlen(path);
857 vapier 1.10 while ((dentry = readdir(dir))) {
858     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
859     continue;
860 vapier 1.32 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
861     if (len >= sizeof(buf)) {
862 vapier 1.76 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
863     (unsigned long)len, (unsigned long)sizeof(buf));
864 vapier 1.32 continue;
865     }
866 solar 1.31 sprintf(buf, "%s/%s", path, dentry->d_name);
867 solar 1.20 if (lstat(buf, &st) != -1) {
868 vapier 1.10 if (S_ISREG(st.st_mode))
869 solar 1.20 scanelf_file(buf);
870 vapier 1.10 else if (dir_recurse && S_ISDIR(st.st_mode)) {
871 vapier 1.14 if (dir_crossmount || (st_top.st_dev == st.st_dev))
872 solar 1.20 scanelf_dir(buf);
873 vapier 1.10 }
874     }
875     }
876     closedir(dir);
877 solar 1.1 }
878    
879 vapier 1.47 static int scanelf_from_file(char *filename)
880     {
881 solar 1.45 FILE *fp = NULL;
882     char *p;
883     char path[_POSIX_PATH_MAX];
884    
885     if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
886     fp = stdin;
887     else if ((fp = fopen(filename, "r")) == NULL)
888     return 1;
889    
890     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
891     if ((p = strchr(path, '\n')) != NULL)
892     *p = 0;
893 vapier 1.66 search_path = path;
894 solar 1.45 scanelf_dir(path);
895     }
896     if (fp != stdin)
897     fclose(fp);
898     return 0;
899     }
900    
901 vapier 1.48 static void load_ld_so_conf()
902     {
903     FILE *fp = NULL;
904     char *p;
905     char path[_POSIX_PATH_MAX];
906     int i = 0;
907    
908     if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
909     return;
910    
911     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
912     if (*path != '/')
913     continue;
914    
915     if ((p = strrchr(path, '\r')) != NULL)
916     *p = 0;
917     if ((p = strchr(path, '\n')) != NULL)
918     *p = 0;
919    
920     ldpaths[i++] = xstrdup(path);
921    
922     if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
923     break;
924     }
925     ldpaths[i] = NULL;
926    
927     fclose(fp);
928     }
929    
930 vapier 1.10 /* scan /etc/ld.so.conf for paths */
931     static void scanelf_ldpath()
932     {
933 vapier 1.17 char scan_l, scan_ul, scan_ull;
934 vapier 1.48 int i = 0;
935 vapier 1.10
936 vapier 1.48 if (!ldpaths[0])
937     err("Unable to load any paths from ld.so.conf");
938 vapier 1.10
939 vapier 1.17 scan_l = scan_ul = scan_ull = 0;
940    
941 vapier 1.48 while (ldpaths[i]) {
942     if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
943     if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
944     if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
945     scanelf_dir(ldpaths[i]);
946     ++i;
947     }
948 vapier 1.10
949 vapier 1.17 if (!scan_l) scanelf_dir("/lib");
950     if (!scan_ul) scanelf_dir("/usr/lib");
951     if (!scan_ull) scanelf_dir("/usr/local/lib");
952 vapier 1.10 }
953 solar 1.1
954 vapier 1.10 /* scan env PATH for paths */
955     static void scanelf_envpath()
956 solar 1.1 {
957 solar 1.34 char *path, *p;
958 vapier 1.10
959     path = getenv("PATH");
960     if (!path)
961     err("PATH is not set in your env !");
962 vapier 1.41 path = xstrdup(path);
963 vapier 1.10
964     while ((p = strrchr(path, ':')) != NULL) {
965     scanelf_dir(p + 1);
966     *p = 0;
967     }
968 vapier 1.17
969 solar 1.34 free(path);
970 solar 1.1 }
971    
972    
973 vapier 1.10
974     /* usage / invocation handling functions */
975 solar 1.86 #define PARSE_FLAGS "plRmyxetrnibSs:gN:TaqvF:f:o:BhV"
976 vapier 1.27 #define a_argument required_argument
977 vapier 1.10 static struct option const long_opts[] = {
978     {"path", no_argument, NULL, 'p'},
979     {"ldpath", no_argument, NULL, 'l'},
980     {"recursive", no_argument, NULL, 'R'},
981 vapier 1.14 {"mount", no_argument, NULL, 'm'},
982 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
983 vapier 1.10 {"pax", no_argument, NULL, 'x'},
984 solar 1.16 {"header", no_argument, NULL, 'e'},
985 vapier 1.10 {"textrel", no_argument, NULL, 't'},
986     {"rpath", no_argument, NULL, 'r'},
987 vapier 1.32 {"needed", no_argument, NULL, 'n'},
988 vapier 1.38 {"interp", no_argument, NULL, 'i'},
989 vapier 1.49 {"bind", no_argument, NULL, 'b'},
990 vapier 1.84 {"soname", no_argument, NULL, 'S'},
991 vapier 1.76 {"symbol", a_argument, NULL, 's'},
992     {"lib", a_argument, NULL, 'N'},
993 solar 1.86 {"gmatch", no_argument, NULL, 'g'},
994 vapier 1.76 {"textrels", no_argument, NULL, 'T'},
995 vapier 1.10 {"all", no_argument, NULL, 'a'},
996     {"quiet", no_argument, NULL, 'q'},
997 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
998 vapier 1.76 {"format", a_argument, NULL, 'F'},
999     {"from", a_argument, NULL, 'f'},
1000     {"file", a_argument, NULL, 'o'},
1001 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
1002 vapier 1.10 {"help", no_argument, NULL, 'h'},
1003     {"version", no_argument, NULL, 'V'},
1004     {NULL, no_argument, NULL, 0x0}
1005     };
1006 solar 1.57
1007 solar 1.68 static const char *opts_help[] = {
1008 vapier 1.10 "Scan all directories in PATH environment",
1009     "Scan all directories in /etc/ld.so.conf",
1010 vapier 1.14 "Scan directories recursively",
1011 vapier 1.37 "Don't recursively cross mount points",
1012     "Don't scan symlinks\n",
1013 vapier 1.10 "Print PaX markings",
1014 vapier 1.71 "Print GNU_STACK/PT_LOAD markings",
1015 vapier 1.10 "Print TEXTREL information",
1016     "Print RPATH information",
1017 vapier 1.32 "Print NEEDED information",
1018 vapier 1.38 "Print INTERP information",
1019 vapier 1.49 "Print BIND information",
1020 vapier 1.84 "Print SONAME information",
1021 vapier 1.27 "Find a specified symbol",
1022 vapier 1.72 "Find a specified library",
1023 solar 1.86 "Use strncmp to match libraries. (use with -N)",
1024 vapier 1.76 "Locate cause of TEXTREL",
1025 vapier 1.82 "Print all scanned info (-x -e -t -r -b)\n",
1026 vapier 1.14 "Only output 'bad' things",
1027     "Be verbose (can be specified more than once)",
1028 vapier 1.39 "Use specified format for output",
1029 solar 1.45 "Read input stream from a filename",
1030 vapier 1.24 "Write output stream to a filename",
1031 vapier 1.14 "Don't display the header",
1032 vapier 1.10 "Print this help and exit",
1033     "Print version and exit",
1034     NULL
1035     };
1036    
1037     /* display usage and exit */
1038     static void usage(int status)
1039 solar 1.1 {
1040 vapier 1.44 unsigned long i;
1041 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
1042 solar 1.35 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0);
1043     printf("Options: -[%s]\n", PARSE_FLAGS);
1044 vapier 1.10 for (i = 0; long_opts[i].name; ++i)
1045 vapier 1.27 if (long_opts[i].has_arg == no_argument)
1046 solar 1.52 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
1047 vapier 1.27 long_opts[i].name, opts_help[i]);
1048     else
1049 solar 1.52 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
1050 vapier 1.27 long_opts[i].name, opts_help[i]);
1051 solar 1.45
1052     if (status != EXIT_SUCCESS)
1053     exit(status);
1054    
1055 vapier 1.46 puts("\nThe format modifiers for the -F option are:");
1056 vapier 1.70 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1057     puts(" t TEXTREL \tr RPATH \tn NEEDED");
1058     puts(" i INTERP \tb BIND \ts symbol");
1059 vapier 1.76 puts(" N library \to Type \tT TEXTRELs");
1060 vapier 1.88 puts(" S SONAME");
1061 vapier 1.70 puts(" p filename (with search path removed)");
1062 vapier 1.88 puts(" f filename (short name/basename)");
1063 vapier 1.70 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1064 solar 1.45
1065 vapier 1.10 exit(status);
1066 solar 1.1 }
1067    
1068     /* parse command line arguments and preform needed actions */
1069 vapier 1.10 static void parseargs(int argc, char *argv[])
1070     {
1071 vapier 1.48 int i;
1072 solar 1.45 char *from_file = NULL;
1073 vapier 1.10
1074     opterr = 0;
1075 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
1076     switch (i) {
1077 vapier 1.10
1078 vapier 1.39 case 'V':
1079 vapier 1.80 printf("pax-utils-%s: %s compiled %s\n%s\n"
1080     "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
1081     VERSION, __FILE__, __DATE__, rcsid, argv0);
1082 vapier 1.10 exit(EXIT_SUCCESS);
1083     break;
1084     case 'h': usage(EXIT_SUCCESS); break;
1085 solar 1.45 case 'f':
1086 solar 1.64 if (from_file) err("Don't specify -f twice");
1087     from_file = xstrdup(optarg);
1088 solar 1.45 break;
1089 vapier 1.24 case 'o': {
1090 solar 1.21 FILE *fp = NULL;
1091 solar 1.64 if ((fp = freopen(optarg, "w", stdout)) == NULL)
1092 vapier 1.24 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1093 vapier 1.65 SET_STDOUT(fp);
1094 solar 1.21 break;
1095     }
1096 vapier 1.24
1097 vapier 1.39 case 's': {
1098     size_t len;
1099 solar 1.64 if (find_sym) err("Don't specify -s twice");
1100 vapier 1.41 find_sym = xstrdup(optarg);
1101 vapier 1.39 len = strlen(find_sym) + 1;
1102 vapier 1.41 versioned_symname = (char*)xmalloc(sizeof(char) * (len+1));
1103 vapier 1.39 sprintf(versioned_symname, "%s@", find_sym);
1104     break;
1105     }
1106 vapier 1.72 case 'N': {
1107     if (find_lib) err("Don't specify -N twice");
1108     find_lib = xstrdup(optarg);
1109     break;
1110     }
1111 vapier 1.39
1112     case 'F': {
1113 solar 1.64 if (out_format) err("Don't specify -F twice");
1114     out_format = xstrdup(optarg);
1115 vapier 1.39 break;
1116     }
1117 vapier 1.27
1118 solar 1.86 case 'g': gmatch = 1;
1119 vapier 1.37 case 'y': scan_symlink = 0; break;
1120 solar 1.16 case 'B': show_banner = 0; break;
1121 vapier 1.10 case 'l': scan_ldpath = 1; break;
1122     case 'p': scan_envpath = 1; break;
1123     case 'R': dir_recurse = 1; break;
1124 vapier 1.14 case 'm': dir_crossmount = 0; break;
1125 vapier 1.10 case 'x': show_pax = 1; break;
1126 solar 1.73 case 'e': show_phdr = 1; break;
1127 vapier 1.10 case 't': show_textrel = 1; break;
1128     case 'r': show_rpath = 1; break;
1129 vapier 1.32 case 'n': show_needed = 1; break;
1130 vapier 1.38 case 'i': show_interp = 1; break;
1131 vapier 1.49 case 'b': show_bind = 1; break;
1132 vapier 1.84 case 'S': show_soname = 1; break;
1133 vapier 1.76 case 'T': show_textrels = 1; break;
1134 vapier 1.10 case 'q': be_quiet = 1; break;
1135 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1136 vapier 1.82 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
1137 vapier 1.10
1138     case ':':
1139 vapier 1.49 err("Option missing parameter\n");
1140 vapier 1.10 case '?':
1141 vapier 1.49 err("Unknown option\n");
1142 vapier 1.10 default:
1143 vapier 1.48 err("Unhandled option '%c'", i);
1144 vapier 1.10 }
1145     }
1146    
1147 vapier 1.39 /* let the format option override all other options */
1148     if (out_format) {
1149 solar 1.73 show_pax = show_phdr = show_textrel = show_rpath = \
1150 vapier 1.84 show_needed = show_interp = show_bind = show_soname = \
1151     show_textrels = 0;
1152 vapier 1.48 for (i = 0; out_format[i]; ++i) {
1153 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
1154 vapier 1.39
1155 vapier 1.48 switch (out_format[++i]) {
1156 vapier 1.39 case '%': break;
1157 vapier 1.70 case '#': break;
1158 vapier 1.39 case 'F': break;
1159 vapier 1.66 case 'p': break;
1160     case 'f': break;
1161 vapier 1.39 case 's': break;
1162 vapier 1.72 case 'N': break;
1163 vapier 1.41 case 'o': break;
1164 vapier 1.39 case 'x': show_pax = 1; break;
1165 solar 1.73 case 'e': show_phdr = 1; break;
1166 vapier 1.39 case 't': show_textrel = 1; break;
1167     case 'r': show_rpath = 1; break;
1168     case 'n': show_needed = 1; break;
1169     case 'i': show_interp = 1; break;
1170 vapier 1.49 case 'b': show_bind = 1; break;
1171 vapier 1.84 case 'S': show_soname = 1; break;
1172 vapier 1.76 case 'T': show_textrels = 1; break;
1173 vapier 1.39 default:
1174     err("Invalid format specifier '%c' (byte %i)",
1175 vapier 1.48 out_format[i], i+1);
1176 vapier 1.39 }
1177     }
1178 vapier 1.41
1179     /* construct our default format */
1180     } else {
1181     size_t fmt_len = 30;
1182     out_format = (char*)xmalloc(sizeof(char) * fmt_len);
1183 vapier 1.76 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1184     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1185     if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1186     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1187     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1188     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1189     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1190     if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1191 vapier 1.84 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1192 vapier 1.76 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1193     if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1194     if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1195     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1196 vapier 1.39 }
1197 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
1198 vapier 1.39
1199     /* now lets actually do the scanning */
1200 vapier 1.48 if (scan_ldpath || (show_rpath && be_quiet))
1201     load_ld_so_conf();
1202 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
1203     if (scan_envpath) scanelf_envpath();
1204 solar 1.45 if (from_file) {
1205     scanelf_from_file(from_file);
1206     free(from_file);
1207     from_file = *argv;
1208     }
1209     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
1210 vapier 1.25 err("Nothing to scan !?");
1211 vapier 1.66 while (optind < argc) {
1212     search_path = argv[optind++];
1213     scanelf_dir(search_path);
1214     }
1215 vapier 1.27
1216 vapier 1.39 /* clean up */
1217     if (find_sym) {
1218     free(find_sym);
1219     free(versioned_symname);
1220     }
1221 vapier 1.72 if (find_lib) free(find_lib);
1222 vapier 1.39 if (out_format) free(out_format);
1223 vapier 1.48 for (i = 0; ldpaths[i]; ++i)
1224     free(ldpaths[i]);
1225 vapier 1.10 }
1226    
1227    
1228    
1229 vapier 1.41 /* utility funcs */
1230 vapier 1.60 static char *xstrdup(const char *s)
1231 vapier 1.41 {
1232     char *ret = strdup(s);
1233     if (!ret) err("Could not strdup(): %s", strerror(errno));
1234     return ret;
1235     }
1236 solar 1.57
1237 vapier 1.41 static void *xmalloc(size_t size)
1238     {
1239     void *ret = malloc(size);
1240     if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1241     return ret;
1242     }
1243 solar 1.57
1244 vapier 1.41 static void xstrcat(char **dst, const char *src, size_t *curr_len)
1245     {
1246 vapier 1.69 size_t new_len;
1247 vapier 1.41
1248     new_len = strlen(*dst) + strlen(src);
1249     if (*curr_len <= new_len) {
1250     *curr_len = new_len + (*curr_len / 2);
1251     *dst = realloc(*dst, *curr_len);
1252     if (!*dst)
1253     err("could not realloc %li bytes", (unsigned long)*curr_len);
1254     }
1255    
1256     strcat(*dst, src);
1257     }
1258 solar 1.57
1259 vapier 1.41 static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1260     {
1261     static char my_app[2];
1262     my_app[0] = append;
1263     my_app[1] = '\0';
1264     xstrcat(dst, my_app, curr_len);
1265     }
1266    
1267    
1268 vapier 1.72
1269 vapier 1.10 int main(int argc, char *argv[])
1270 solar 1.1 {
1271 vapier 1.10 if (argc < 2)
1272     usage(EXIT_FAILURE);
1273     parseargs(argc, argv);
1274 solar 1.21 fclose(stdout);
1275 solar 1.61 #ifdef __BOUNDS_CHECKING_ON
1276 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()");
1277 solar 1.61 #endif
1278 vapier 1.10 return EXIT_SUCCESS;
1279 solar 1.1 }

  ViewVC Help
Powered by ViewVC 1.1.20