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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.95 - (hide annotations) (download) (as text)
Sat Dec 10 06:08:22 2005 UTC (9 years, 4 months ago) by vapier
Branch: MAIN
Changes since 1.94: +36 -13 lines
File MIME type: text/x-csrc
add support for scanning for multiple symbols at a time

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

  ViewVC Help
Powered by ViewVC 1.1.20