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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.104 - (hide annotations) (download) (as text)
Fri Jan 13 11:31:55 2006 UTC (8 years, 11 months ago) by vapier
Branch: MAIN
Changes since 1.103: +10 -8 lines
File MIME type: text/x-csrc
define and use our own copy of PATH_MAX

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

  ViewVC Help
Powered by ViewVC 1.1.20