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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.84 - (hide annotations) (download) (as text)
Fri Jul 22 00:10:51 2005 UTC (9 years, 1 month ago) by vapier
Branch: MAIN
Changes since 1.83: +69 -11 lines
File MIME type: text/x-csrc
add support for showing SONAME (-S)

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

  ViewVC Help
Powered by ViewVC 1.1.20