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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.90 - (hide annotations) (download) (as text)
Sun Dec 4 18:12:44 2005 UTC (8 years, 9 months ago) by vapier
Branch: MAIN
Changes since 1.89: +12 -11 lines
File MIME type: text/x-csrc
fix #e (it used to act like %e)

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

  ViewVC Help
Powered by ViewVC 1.1.20