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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.102 - (hide annotations) (download) (as text)
Wed Jan 11 01:12:12 2006 UTC (8 years, 6 months ago) by vapier
Branch: MAIN
Changes since 1.101: +31 -23 lines
File MIME type: text/x-csrc
improve rpath output thanks to Ludwig Nussel and touchup the rpath fixing code to handle some corner cases

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

  ViewVC Help
Powered by ViewVC 1.1.20