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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.91 - (hide annotations) (download) (as text)
Wed Dec 7 01:04:52 2005 UTC (8 years, 8 months ago) by vapier
Branch: MAIN
Changes since 1.90: +59 -31 lines
File MIME type: text/x-csrc
add support for scanning .note.GNU-stack in object files

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

  ViewVC Help
Powered by ViewVC 1.1.20