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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.101 - (hide annotations) (download) (as text)
Tue Jan 10 01:40:15 2006 UTC (8 years, 7 months ago) by vapier
Branch: MAIN
Changes since 1.100: +50 -6 lines
File MIME type: text/x-csrc
add support for automatically "fixing" insecure runpaths and removing the executable bit from stack/load program headers

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

  ViewVC Help
Powered by ViewVC 1.1.20