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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.100 - (hide annotations) (download) (as text)
Tue Jan 10 01:38:17 2006 UTC (8 years, 6 months ago) by vapier
Branch: MAIN
Changes since 1.99: +9 -8 lines
File MIME type: text/x-csrc
make sure we warn about zero length rpaths and that we dont break out of the rpath checking too early.  also dont bother duping the arg for the -f option

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

  ViewVC Help
Powered by ViewVC 1.1.20