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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.83 - (hide annotations) (download) (as text)
Wed Jun 22 17:43:12 2005 UTC (9 years, 1 month ago) by solar
Branch: MAIN
Changes since 1.82: +24 -2 lines
File MIME type: text/x-csrc
- add a few rpath security checks

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

  ViewVC Help
Powered by ViewVC 1.1.20