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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.99 - (hide annotations) (download) (as text)
Tue Jan 10 01:35:06 2006 UTC (8 years, 6 months ago) by vapier
Branch: MAIN
Changes since 1.98: +6 -5 lines
File MIME type: text/x-csrc
split the pt_load check out into a sep func for easier managing in the future and to shut up openbsd

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

  ViewVC Help
Powered by ViewVC 1.1.20