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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.98 - (hide annotations) (download) (as text)
Thu Jan 5 03:12:07 2006 UTC (8 years, 7 months ago) by vapier
Branch: MAIN
Changes since 1.97: +5 -19 lines
File MIME type: text/x-csrc
update copyright years and unify headers in porting.h

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

  ViewVC Help
Powered by ViewVC 1.1.20