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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.96 - (hide annotations) (download) (as text)
Wed Dec 28 22:26:47 2005 UTC (8 years, 11 months ago) by solar
Branch: MAIN
Changes since 1.95: +84 -5 lines
File MIME type: text/x-csrc
- initial function to do ld.so.cache lookups for DT_NEEDED entries

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

  ViewVC Help
Powered by ViewVC 1.1.20