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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.97 - (hide annotations) (download) (as text)
Thu Dec 29 14:03:25 2005 UTC (8 years, 7 months ago) by vapier
Branch: MAIN
Changes since 1.96: +35 -13 lines
File MIME type: text/x-csrc
touchup lookup_cache_lib() func a bit by plugging some memleaks, adding some comments, and handling 64/32 bit multilib systems

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

  ViewVC Help
Powered by ViewVC 1.1.20