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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.147 - (hide annotations) (download) (as text)
Sun May 14 21:07:39 2006 UTC (8 years, 4 months ago) by vapier
Branch: MAIN
Changes since 1.146: +2 -8 lines
File MIME type: text/x-csrc
move porting-specific stuff to porting.h

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.147 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.146 2006/05/14 21:04:25 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 flameeyes 1.141
12 vapier 1.147 static const char *rcsid = "$Id: scanelf.c,v 1.146 2006/05/14 21:04:25 vapier Exp $";
13 vapier 1.36 #define argv0 "scanelf"
14 vapier 1.10
15 solar 1.132 #define IS_MODIFIER(c) (c == '%' || c == '#' || c == '+')
16 vapier 1.70
17 solar 1.132 #define do_state(option, flag) \
18     if (islower(option)) { \
19     flags &= ~PF_##flag; \
20     flags |= PF_NO##flag; \
21     } else { \
22     flags &= ~PF_NO##flag; \
23     flags |= PF_##flag; \
24     }
25    
26 vapier 1.10
27     /* prototypes */
28 kevquinn 1.142 static int file_matches_list(const char *filename, char **matchlist);
29 vapier 1.106 static int scanelf_elfobj(elfobj *elf);
30     static int scanelf_elf(const char *filename, int fd, size_t len);
31     static int scanelf_archive(const char *filename, int fd, size_t len);
32 vapier 1.10 static void scanelf_file(const char *filename);
33     static void scanelf_dir(const char *path);
34 vapier 1.106 static void scanelf_ldpath(void);
35     static void scanelf_envpath(void);
36 vapier 1.10 static void usage(int status);
37 kevquinn 1.142 static char **get_split_env(const char *envvar);
38     static void parseenv(void);
39 vapier 1.10 static void parseargs(int argc, char *argv[]);
40 vapier 1.60 static char *xstrdup(const char *s);
41 vapier 1.41 static void *xmalloc(size_t size);
42 kevquinn 1.142 static void *xrealloc(void *ptr, size_t size);
43 vapier 1.95 static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n);
44     #define xstrcat(dst,src,curr_len) xstrncat(dst,src,curr_len,0)
45 vapier 1.41 static inline void xchrcat(char **dst, const char append, size_t *curr_len);
46 vapier 1.10
47     /* variables to control behavior */
48 solar 1.119 static char match_etypes[126] = "";
49 vapier 1.48 static char *ldpaths[256];
50 vapier 1.10 static char scan_ldpath = 0;
51     static char scan_envpath = 0;
52 vapier 1.37 static char scan_symlink = 1;
53 vapier 1.105 static char scan_archives = 0;
54 vapier 1.10 static char dir_recurse = 0;
55 vapier 1.14 static char dir_crossmount = 1;
56 vapier 1.10 static char show_pax = 0;
57 solar 1.73 static char show_phdr = 0;
58 vapier 1.10 static char show_textrel = 0;
59     static char show_rpath = 0;
60 vapier 1.32 static char show_needed = 0;
61 vapier 1.38 static char show_interp = 0;
62 vapier 1.49 static char show_bind = 0;
63 vapier 1.84 static char show_soname = 0;
64 vapier 1.76 static char show_textrels = 0;
65 solar 1.16 static char show_banner = 1;
66 vapier 1.10 static char be_quiet = 0;
67 vapier 1.14 static char be_verbose = 0;
68 solar 1.127 static char be_wewy_wewy_quiet = 0;
69 solar 1.132 static char be_semi_verbose = 0;
70 vapier 1.39 static char *find_sym = NULL, *versioned_symname = NULL;
71 vapier 1.72 static char *find_lib = NULL;
72 solar 1.124 static char *find_section = NULL;
73 vapier 1.39 static char *out_format = NULL;
74 vapier 1.66 static char *search_path = NULL;
75 vapier 1.101 static char fix_elf = 0;
76 solar 1.86 static char gmatch = 0;
77 vapier 1.102 static char use_ldcache = 0;
78 solar 1.1
79 kevquinn 1.142 static char **qa_textrels = NULL;
80     static char **qa_execstack = NULL;
81 kevquinn 1.145 static char **qa_wx_load = NULL;
82 kevquinn 1.142
83 solar 1.120 int match_bits = 0;
84 solar 1.96 caddr_t ldcache = 0;
85     size_t ldcache_size = 0;
86 solar 1.127 unsigned long setpax = 0UL;
87 solar 1.96
88 kevquinn 1.142 /* utility funcs */
89     static char *xstrdup(const char *s)
90     {
91     char *ret = strdup(s);
92     if (!ret) err("Could not strdup(): %s", strerror(errno));
93     return ret;
94     }
95     static void *xmalloc(size_t size)
96     {
97     void *ret = malloc(size);
98     if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
99     return ret;
100     }
101     static void *xrealloc(void *ptr, size_t size)
102     {
103     void *ret = realloc(ptr, size);
104     if (!ret) err("Could not realloc() %li bytes", (unsigned long)size);
105     return ret;
106     }
107     static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n)
108     {
109     size_t new_len;
110    
111     new_len = strlen(*dst) + strlen(src);
112     if (*curr_len <= new_len) {
113     *curr_len = new_len + (*curr_len / 2);
114     *dst = realloc(*dst, *curr_len);
115     if (!*dst)
116     err("could not realloc() %li bytes", (unsigned long)*curr_len);
117     }
118    
119     if (n)
120     strncat(*dst, src, n);
121     else
122     strcat(*dst, src);
123     }
124     static inline void xchrcat(char **dst, const char append, size_t *curr_len)
125     {
126     static char my_app[2];
127     my_app[0] = append;
128     my_app[1] = '\0';
129     xstrcat(dst, my_app, curr_len);
130     }
131    
132     /* Match filename against entries in matchlist, return TRUE
133     * if the file is listed */
134     static int file_matches_list(const char *filename, char **matchlist) {
135     char **file;
136     char *match;
137     char buf[__PAX_UTILS_PATH_MAX];
138 solar 1.143
139     if (matchlist == NULL)
140     return 0;
141    
142     for (file = matchlist; *file != NULL; file++) {
143     if (search_path) {
144     snprintf(buf,__PAX_UTILS_PATH_MAX, "%s%s", search_path, *file);
145     match=buf;
146     } else {
147     match=*file;
148 kevquinn 1.142 }
149 solar 1.143 if (fnmatch(match, filename, 0) == 0)
150     return 1; /* TRUE */
151 kevquinn 1.142 }
152     return 0; /* FALSE */
153     }
154    
155 vapier 1.39 /* sub-funcs for scanelf_file() */
156 vapier 1.77 static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
157     {
158     /* find the best SHT_DYNSYM and SHT_STRTAB sections */
159     #define GET_SYMTABS(B) \
160     if (elf->elf_class == ELFCLASS ## B) { \
161     Elf ## B ## _Shdr *symtab, *strtab, *dynsym, *dynstr; \
162     /* debug sections */ \
163     symtab = SHDR ## B (elf_findsecbyname(elf, ".symtab")); \
164     strtab = SHDR ## B (elf_findsecbyname(elf, ".strtab")); \
165     /* runtime sections */ \
166     dynsym = SHDR ## B (elf_findsecbyname(elf, ".dynsym")); \
167     dynstr = SHDR ## B (elf_findsecbyname(elf, ".dynstr")); \
168     if (symtab && dynsym) { \
169     *sym = (void*)((EGET(symtab->sh_size) > EGET(dynsym->sh_size)) ? symtab : dynsym); \
170     } else { \
171     *sym = (void*)(symtab ? symtab : dynsym); \
172     } \
173     if (strtab && dynstr) { \
174     *tab = (void*)((EGET(strtab->sh_size) > EGET(dynstr->sh_size)) ? strtab : dynstr); \
175     } else { \
176     *tab = (void*)(strtab ? strtab : dynstr); \
177     } \
178     }
179     GET_SYMTABS(32)
180     GET_SYMTABS(64)
181     }
182 solar 1.127
183 vapier 1.41 static char *scanelf_file_pax(elfobj *elf, char *found_pax)
184 solar 1.6 {
185 solar 1.61 static char ret[7];
186     unsigned long i, shown;
187    
188 vapier 1.41 if (!show_pax) return NULL;
189 vapier 1.10
190 solar 1.61 shown = 0;
191     memset(&ret, 0, sizeof(ret));
192    
193     if (elf->phdr) {
194     #define SHOW_PAX(B) \
195     if (elf->elf_class == ELFCLASS ## B) { \
196     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
197     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
198     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
199     if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
200     continue; \
201 solar 1.127 if (fix_elf && setpax) { \
202     /* set the paxctl flags */ \
203     ESET(phdr[i].p_flags, setpax); \
204     } \
205 solar 1.129 if (be_quiet && (EGET(phdr[i].p_flags) == (PF_NOEMUTRAMP | PF_NORANDEXEC))) \
206 solar 1.61 continue; \
207     memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
208     *found_pax = 1; \
209     ++shown; \
210     break; \
211     } \
212     }
213     SHOW_PAX(32)
214     SHOW_PAX(64)
215     }
216    
217 solar 1.128
218     if (fix_elf && setpax) {
219     /* set the chpax settings */
220 solar 1.129 if (elf->elf_class == ELFCLASS32) {
221     if (EHDR32(elf->ehdr)->e_type == ET_DYN || EHDR32(elf->ehdr)->e_type == ET_EXEC)
222     ESET(EHDR32(elf->ehdr)->e_ident[EI_PAX], pax_pf2hf_flags(setpax));
223     } else {
224     if (EHDR64(elf->ehdr)->e_type == ET_DYN || EHDR64(elf->ehdr)->e_type == ET_EXEC)
225     ESET(EHDR64(elf->ehdr)->e_ident[EI_PAX], pax_pf2hf_flags(setpax));
226     }
227 solar 1.128 }
228    
229 solar 1.61 /* fall back to EI_PAX if no PT_PAX was found */
230     if (!*ret) {
231 vapier 1.90 static char *paxflags;
232 solar 1.61 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
233     if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
234     *found_pax = 1;
235 solar 1.127 return (be_wewy_wewy_quiet ? NULL : paxflags);
236 solar 1.61 }
237     strncpy(ret, paxflags, sizeof(ret));
238 vapier 1.14 }
239 vapier 1.41
240 solar 1.127 if (be_wewy_wewy_quiet || (be_quiet && !shown))
241 solar 1.61 return NULL;
242 vapier 1.90 else
243     return ret;
244     }
245 solar 1.61
246 solar 1.73 static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
247 vapier 1.39 {
248 vapier 1.71 static char ret[12];
249 vapier 1.41 char *found;
250 vapier 1.99 unsigned long i, shown, multi_stack, multi_relro, multi_load;
251     int max_pt_load;
252 vapier 1.41
253 solar 1.73 if (!show_phdr) return NULL;
254 vapier 1.41
255 vapier 1.71 memcpy(ret, "--- --- ---\0", 12);
256    
257 vapier 1.41 shown = 0;
258 vapier 1.71 multi_stack = multi_relro = multi_load = 0;
259 vapier 1.99 max_pt_load = elf_max_pt_load(elf);
260 vapier 1.44
261 vapier 1.108 #define NOTE_GNU_STACK ".note.GNU-stack"
262 solar 1.73 #define SHOW_PHDR(B) \
263 vapier 1.39 if (elf->elf_class == ELFCLASS ## B) { \
264     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
265 vapier 1.91 Elf ## B ## _Off offset; \
266     uint32_t flags, check_flags; \
267     if (elf->phdr != NULL) { \
268     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
269     for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \
270     if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
271     if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
272 kevquinn 1.142 if (!file_matches_list(elf->filename, qa_execstack)) {\
273     found = found_phdr; \
274     offset = 0; \
275     check_flags = PF_X; \
276     } else continue; \
277 vapier 1.91 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
278     if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
279     found = found_relro; \
280     offset = 4; \
281     check_flags = PF_X; \
282     } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
283 solar 1.117 if (ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC) \
284     if (multi_load++ > max_pt_load) warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
285 kevquinn 1.145 if (!file_matches_list(elf->filename, qa_wx_load)) {\
286     found = found_load; \
287     offset = 8; \
288     check_flags = PF_W|PF_X; \
289     } else continue; \
290 vapier 1.91 } else \
291     continue; \
292     flags = EGET(phdr[i].p_flags); \
293     if (be_quiet && ((flags & check_flags) != check_flags)) \
294     continue; \
295 solar 1.130 if ((EGET(phdr[i].p_type) != PT_LOAD) && (fix_elf && ((flags & PF_X) != flags))) { \
296 vapier 1.101 ESET(phdr[i].p_flags, flags & (PF_X ^ (size_t)-1)); \
297     ret[3] = ret[7] = '!'; \
298     flags = EGET(phdr[i].p_flags); \
299     } \
300 vapier 1.91 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \
301     *found = 1; \
302     ++shown; \
303     } \
304     } else if (elf->shdr != NULL) { \
305     /* no program headers which means this is prob an object file */ \
306     Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
307     Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \
308 vapier 1.108 char *str; \
309 vapier 1.118 if ((void*)strtbl > (void*)elf->data_end) \
310 vapier 1.108 goto skip_this_shdr##B; \
311 vapier 1.91 check_flags = SHF_WRITE|SHF_EXECINSTR; \
312     for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \
313     if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \
314     offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \
315 vapier 1.108 str = elf->data + offset; \
316     if (str > elf->data + offset + sizeof(NOTE_GNU_STACK)) continue; \
317     if (!strcmp(str, NOTE_GNU_STACK)) { \
318 vapier 1.91 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \
319     flags = EGET(shdr[i].sh_flags); \
320     if (be_quiet && ((flags & check_flags) != check_flags)) \
321     continue; \
322     ++*found_phdr; \
323     shown = 1; \
324     if (flags & SHF_WRITE) ret[0] = 'W'; \
325     if (flags & SHF_ALLOC) ret[1] = 'A'; \
326     if (flags & SHF_EXECINSTR) ret[2] = 'X'; \
327     if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \
328     break; \
329     } \
330     } \
331 vapier 1.108 skip_this_shdr##B: \
332 vapier 1.91 if (!multi_stack) { \
333     *found_phdr = 1; \
334     shown = 1; \
335     memcpy(ret, "!WX", 3); \
336     } \
337 vapier 1.39 } \
338     }
339 solar 1.73 SHOW_PHDR(32)
340     SHOW_PHDR(64)
341 vapier 1.44
342 solar 1.127 if (be_wewy_wewy_quiet || (be_quiet && !shown))
343 vapier 1.41 return NULL;
344     else
345     return ret;
346 vapier 1.39 }
347 kevquinn 1.142
348 vapier 1.90 static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
349 vapier 1.39 {
350 vapier 1.90 static const char *ret = "TEXTREL";
351 vapier 1.44 unsigned long i;
352 vapier 1.41
353 vapier 1.79 if (!show_textrel && !show_textrels) return NULL;
354 vapier 1.41
355 kevquinn 1.142 if (file_matches_list(elf->filename, qa_textrels)) return NULL;
356    
357 vapier 1.44 if (elf->phdr) {
358 vapier 1.39 #define SHOW_TEXTREL(B) \
359     if (elf->elf_class == ELFCLASS ## B) { \
360     Elf ## B ## _Dyn *dyn; \
361     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
362     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
363 vapier 1.44 Elf ## B ## _Off offset; \
364 vapier 1.39 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
365 vapier 1.59 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
366 vapier 1.44 offset = EGET(phdr[i].p_offset); \
367     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
368     dyn = DYN ## B (elf->data + offset); \
369 vapier 1.39 while (EGET(dyn->d_tag) != DT_NULL) { \
370     if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
371     *found_textrel = 1; \
372     /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
373 solar 1.127 return (be_wewy_wewy_quiet ? NULL : ret); \
374 vapier 1.39 } \
375     ++dyn; \
376 vapier 1.26 } \
377 vapier 1.39 } }
378     SHOW_TEXTREL(32)
379     SHOW_TEXTREL(64)
380 vapier 1.44 }
381    
382 solar 1.127 if (be_quiet || be_wewy_wewy_quiet)
383 vapier 1.41 return NULL;
384     else
385 vapier 1.90 return " - ";
386 vapier 1.39 }
387 vapier 1.79 static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
388 vapier 1.76 {
389 vapier 1.82 unsigned long s, r, rmax;
390     void *symtab_void, *strtab_void, *text_void;
391 vapier 1.76
392     if (!show_textrels) return NULL;
393    
394 vapier 1.79 /* don't search for TEXTREL's if the ELF doesn't have any */
395     if (!*found_textrel) scanelf_file_textrel(elf, found_textrel);
396     if (!*found_textrel) return NULL;
397    
398 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
399 vapier 1.82 text_void = elf_findsecbyname(elf, ".text");
400 vapier 1.76
401 vapier 1.82 if (symtab_void && strtab_void && text_void && elf->shdr) {
402 vapier 1.76 #define SHOW_TEXTRELS(B) \
403     if (elf->elf_class == ELFCLASS ## B) { \
404     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
405     Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
406     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
407     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
408 vapier 1.82 Elf ## B ## _Shdr *text = SHDR ## B (text_void); \
409     Elf ## B ## _Addr vaddr = EGET(text->sh_addr); \
410     uint ## B ## _t memsz = EGET(text->sh_size); \
411 vapier 1.76 Elf ## B ## _Rel *rel; \
412     Elf ## B ## _Rela *rela; \
413     /* search the section headers for relocations */ \
414     for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
415     uint32_t sh_type = EGET(shdr[s].sh_type); \
416     if (sh_type == SHT_REL) { \
417     rel = REL ## B (elf->data + EGET(shdr[s].sh_offset)); \
418     rela = NULL; \
419     rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
420     } else if (sh_type == SHT_RELA) { \
421     rel = NULL; \
422     rela = RELA ## B (elf->data + EGET(shdr[s].sh_offset)); \
423     rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
424     } else \
425     continue; \
426 vapier 1.82 /* now see if any of the relocs are in the .text */ \
427     for (r = 0; r < rmax; ++r) { \
428     unsigned long sym_max; \
429     Elf ## B ## _Addr offset_tmp; \
430     Elf ## B ## _Sym *func; \
431     Elf ## B ## _Sym *sym; \
432     Elf ## B ## _Addr r_offset; \
433     uint ## B ## _t r_info; \
434     if (sh_type == SHT_REL) { \
435     r_offset = EGET(rel[r].r_offset); \
436     r_info = EGET(rel[r].r_info); \
437     } else { \
438     r_offset = EGET(rela[r].r_offset); \
439     r_info = EGET(rela[r].r_info); \
440     } \
441     /* make sure this relocation is inside of the .text */ \
442     if (r_offset < vaddr || r_offset >= vaddr + memsz) { \
443     if (be_verbose <= 2) continue; \
444     } else \
445 vapier 1.78 *found_textrels = 1; \
446 vapier 1.82 /* locate this relocation symbol name */ \
447     sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
448 vapier 1.112 if ((void*)sym > (void*)elf->data_end) { \
449 vapier 1.111 warn("%s: corrupt ELF symbol", elf->filename); \
450 vapier 1.109 continue; \
451     } \
452 vapier 1.82 sym_max = ELF ## B ## _R_SYM(r_info); \
453     if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
454     sym += sym_max; \
455     else \
456     sym = NULL; \
457     sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
458     /* show the raw details about this reloc */ \
459 vapier 1.88 printf(" %s: ", elf->base_filename); \
460 vapier 1.82 if (sym && sym->st_name) \
461     printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name))); \
462     else \
463 vapier 1.88 printf("(memory/fake?)"); \
464 vapier 1.82 printf(" [0x%lX]", (unsigned long)r_offset); \
465     /* now try to find the closest symbol that this rel is probably in */ \
466     sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
467     func = NULL; \
468     offset_tmp = 0; \
469     while (sym_max--) { \
470     if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
471     func = sym; \
472     offset_tmp = EGET(sym->st_value); \
473 vapier 1.76 } \
474 vapier 1.82 ++sym; \
475 vapier 1.76 } \
476 vapier 1.82 printf(" in "); \
477     if (func && func->st_name) \
478     printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(func->st_name))); \
479     else \
480     printf("(NULL: fake?)"); \
481     printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
482 vapier 1.76 } \
483     } }
484     SHOW_TEXTRELS(32)
485     SHOW_TEXTRELS(64)
486     }
487 vapier 1.82 if (!*found_textrels)
488     warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
489 vapier 1.76
490     return NULL;
491     }
492 solar 1.83
493 vapier 1.102 static void rpath_security_checks(elfobj *, char *, const char *);
494     static void rpath_security_checks(elfobj *elf, char *item, const char *dt_type)
495 vapier 1.100 {
496 solar 1.83 struct stat st;
497 vapier 1.84 switch (*item) {
498 vapier 1.89 case '/': break;
499     case '.':
500 vapier 1.102 warnf("Security problem with relative %s '%s' in %s", dt_type, item, elf->filename);
501 vapier 1.89 break;
502 vapier 1.100 case ':':
503 vapier 1.89 case '\0':
504 vapier 1.102 warnf("Security problem NULL %s in %s", dt_type, elf->filename);
505 solar 1.83 break;
506     case '$':
507 vapier 1.84 if (fstat(elf->fd, &st) != -1)
508 solar 1.83 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
509 vapier 1.102 warnf("Security problem with %s='%s' in %s with mode set of %o",
510     dt_type, item, elf->filename, st.st_mode & 07777);
511 solar 1.83 break;
512     default:
513 vapier 1.102 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename);
514 solar 1.83 break;
515     }
516     }
517 vapier 1.41 static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
518 vapier 1.39 {
519 vapier 1.48 unsigned long i, s;
520     char *rpath, *runpath, **r;
521 vapier 1.39 void *strtbl_void;
522 vapier 1.10
523 vapier 1.39 if (!show_rpath) return;
524 vapier 1.10
525 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
526     rpath = runpath = NULL;
527 vapier 1.10
528 vapier 1.44 if (elf->phdr && strtbl_void) {
529 vapier 1.26 #define SHOW_RPATH(B) \
530     if (elf->elf_class == ELFCLASS ## B) { \
531     Elf ## B ## _Dyn *dyn; \
532     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
533     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
534     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
535 vapier 1.44 Elf ## B ## _Off offset; \
536 vapier 1.60 Elf ## B ## _Xword word; \
537 vapier 1.48 /* Scan all the program headers */ \
538 vapier 1.26 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
539 vapier 1.48 /* Just scan dynamic headers */ \
540 vapier 1.26 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
541 vapier 1.44 offset = EGET(phdr[i].p_offset); \
542     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
543 vapier 1.48 /* Just scan dynamic RPATH/RUNPATH headers */ \
544 vapier 1.44 dyn = DYN ## B (elf->data + offset); \
545 vapier 1.48 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
546     if (word == DT_RPATH) { \
547     r = &rpath; \
548     } else if (word == DT_RUNPATH) { \
549     r = &runpath; \
550     } else { \
551     ++dyn; \
552     continue; \
553     } \
554     /* Verify the memory is somewhat sane */ \
555     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
556 vapier 1.69 if (offset < (Elf ## B ## _Off)elf->len) { \
557 vapier 1.48 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
558     *r = (char*)(elf->data + offset); \
559 vapier 1.103 /* cache the length in case we need to nuke this section later on */ \
560     if (fix_elf) \
561     offset = strlen(*r); \
562 vapier 1.48 /* If quiet, don't output paths in ld.so.conf */ \
563 vapier 1.69 if (be_quiet) { \
564     size_t len; \
565     char *start, *end; \
566 vapier 1.75 /* note that we only 'chop' off leading known paths. */ \
567     /* since *r is read-only memory, we can only move the ptr forward. */ \
568     start = *r; \
569     /* scan each path in : delimited list */ \
570     while (start) { \
571 vapier 1.102 rpath_security_checks(elf, start, get_elfdtype(word)); \
572 vapier 1.69 end = strchr(start, ':'); \
573 vapier 1.75 len = (end ? abs(end - start) : strlen(start)); \
574 vapier 1.102 if (use_ldcache) \
575     for (s = 0; ldpaths[s]; ++s) \
576     if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
577     *r = end; \
578     /* corner case ... if RPATH reads "/usr/lib:", we want \
579     * to show ':' rather than '' */ \
580     if (end && end[1] != '\0') \
581     (*r)++; \
582     break; \
583     } \
584 vapier 1.100 if (!*r || !end) \
585     break; \
586 vapier 1.75 else \
587     start = start + len + 1; \
588 vapier 1.69 } \
589     } \
590 vapier 1.101 if (*r) { \
591 solar 1.107 if (fix_elf > 2 || (fix_elf && **r == '\0')) { \
592 vapier 1.101 /* just nuke it */ \
593     nuke_it##B: \
594 vapier 1.103 memset(*r, 0x00, offset); \
595 vapier 1.102 *r = NULL; \
596 vapier 1.101 ESET(dyn->d_tag, DT_DEBUG); \
597 vapier 1.103 ESET(dyn->d_un.d_ptr, 0); \
598 vapier 1.101 } else if (fix_elf) { \
599     /* try to clean "bad" paths */ \
600     size_t len, tmpdir_len; \
601     char *start, *end; \
602     const char *tmpdir; \
603     start = *r; \
604     tmpdir = (getenv("TMPDIR") ? : "."); \
605     tmpdir_len = strlen(tmpdir); \
606     while (1) { \
607     end = strchr(start, ':'); \
608     if (start == end) { \
609     eat_this_path##B: \
610     len = strlen(end); \
611     memmove(start, end+1, len); \
612     start[len-1] = '\0'; \
613     end = start - 1; \
614     } else if (tmpdir && !strncmp(start, tmpdir, tmpdir_len)) { \
615     if (!end) { \
616     if (start == *r) \
617     goto nuke_it##B; \
618     *--start = '\0'; \
619     } else \
620     goto eat_this_path##B; \
621     } \
622     if (!end) \
623     break; \
624     start = end + 1; \
625     } \
626 vapier 1.102 if (**r == '\0') \
627     goto nuke_it##B; \
628 vapier 1.101 } \
629 vapier 1.102 if (*r) \
630     *found_rpath = 1; \
631 vapier 1.101 } \
632 vapier 1.26 } \
633     ++dyn; \
634     } \
635     } }
636     SHOW_RPATH(32)
637     SHOW_RPATH(64)
638 vapier 1.10 }
639 vapier 1.41
640 solar 1.127 if (be_wewy_wewy_quiet) return;
641 vapier 1.70
642 vapier 1.39 if (rpath && runpath) {
643 vapier 1.41 if (!strcmp(rpath, runpath)) {
644     xstrcat(ret, runpath, ret_len);
645     } else {
646 vapier 1.39 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
647 vapier 1.41 xchrcat(ret, '{', ret_len);
648     xstrcat(ret, rpath, ret_len);
649     xchrcat(ret, ',', ret_len);
650     xstrcat(ret, runpath, ret_len);
651     xchrcat(ret, '}', ret_len);
652 vapier 1.39 }
653     } else if (rpath || runpath)
654 vapier 1.41 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
655     else if (!be_quiet)
656     xstrcat(ret, " - ", ret_len);
657 vapier 1.39 }
658 solar 1.96
659     #define LDSO_CACHE_MAGIC "ld.so-"
660     #define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
661     #define LDSO_CACHE_VER "1.7.0"
662     #define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
663 vapier 1.97 #define FLAG_ANY -1
664     #define FLAG_TYPE_MASK 0x00ff
665     #define FLAG_LIBC4 0x0000
666     #define FLAG_ELF 0x0001
667     #define FLAG_ELF_LIBC5 0x0002
668     #define FLAG_ELF_LIBC6 0x0003
669     #define FLAG_REQUIRED_MASK 0xff00
670     #define FLAG_SPARC_LIB64 0x0100
671     #define FLAG_IA64_LIB64 0x0200
672     #define FLAG_X8664_LIB64 0x0300
673     #define FLAG_S390_LIB64 0x0400
674     #define FLAG_POWERPC_LIB64 0x0500
675     #define FLAG_MIPS64_LIBN32 0x0600
676     #define FLAG_MIPS64_LIBN64 0x0700
677 solar 1.96
678 vapier 1.97 static char *lookup_cache_lib(elfobj *, char *);
679 flameeyes 1.141 #if defined(__GLIBC__) || defined(__UCLIBC__)
680 vapier 1.97 static char *lookup_cache_lib(elfobj *elf, char *fname)
681 solar 1.96 {
682     int fd = 0;
683     char *strs;
684 vapier 1.104 static char buf[__PAX_UTILS_PATH_MAX] = "";
685 solar 1.96 const char *cachefile = "/etc/ld.so.cache";
686     struct stat st;
687    
688     typedef struct {
689     char magic[LDSO_CACHE_MAGIC_LEN];
690     char version[LDSO_CACHE_VER_LEN];
691     int nlibs;
692     } header_t;
693 vapier 1.97 header_t *header;
694 solar 1.96
695     typedef struct {
696     int flags;
697     int sooffset;
698     int liboffset;
699     } libentry_t;
700     libentry_t *libent;
701    
702     if (fname == NULL)
703     return NULL;
704    
705     if (ldcache == 0) {
706 vapier 1.97 if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY)) == -1)
707 solar 1.96 return NULL;
708 vapier 1.97
709     /* cache these values so we only map/unmap the cache file once */
710 solar 1.96 ldcache_size = st.st_size;
711 vapier 1.97 ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
712    
713     close(fd);
714 solar 1.96
715 solar 1.116 if (ldcache == (caddr_t)-1) {
716     ldcache = 0;
717 solar 1.96 return NULL;
718 solar 1.116 }
719 solar 1.96
720     if (memcmp(((header_t *) ldcache)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN))
721     return NULL;
722     if (memcmp (((header_t *) ldcache)->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
723     return NULL;
724     }
725    
726     header = (header_t *) ldcache;
727     libent = (libentry_t *) (ldcache + sizeof(header_t));
728     strs = (char *) &libent[header->nlibs];
729    
730     for (fd = 0; fd < header->nlibs; fd++) {
731 vapier 1.97 /* this should be more fine grained, but for now we assume that
732     * diff arches will not be cached together. and we ignore the
733     * the different multilib mips cases. */
734     if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
735     continue;
736     if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
737     continue;
738    
739 solar 1.96 if (strcmp(fname, strs + libent[fd].sooffset) != 0)
740     continue;
741     strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
742     }
743     return buf;
744     }
745 flameeyes 1.141 #else
746     #warning Cache support not implemented for your current target.
747     static char *lookup_cache_lib(elfobj *elf, char *fname)
748     {
749     return NULL;
750     }
751     #endif
752 solar 1.96
753 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)
754 vapier 1.39 {
755 vapier 1.44 unsigned long i;
756 vapier 1.39 char *needed;
757     void *strtbl_void;
758 solar 1.96 char *p;
759 vapier 1.39
760 vapier 1.72 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL;
761 vapier 1.10
762 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
763 vapier 1.32
764 vapier 1.44 if (elf->phdr && strtbl_void) {
765 vapier 1.32 #define SHOW_NEEDED(B) \
766     if (elf->elf_class == ELFCLASS ## B) { \
767     Elf ## B ## _Dyn *dyn; \
768     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
769     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
770     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
771 vapier 1.44 Elf ## B ## _Off offset; \
772 vapier 1.32 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
773     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
774 vapier 1.44 offset = EGET(phdr[i].p_offset); \
775     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
776     dyn = DYN ## B (elf->data + offset); \
777 vapier 1.32 while (EGET(dyn->d_tag) != DT_NULL) { \
778     if (EGET(dyn->d_tag) == DT_NEEDED) { \
779 vapier 1.44 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
780 vapier 1.69 if (offset >= (Elf ## B ## _Off)elf->len) { \
781 vapier 1.49 ++dyn; \
782     continue; \
783     } \
784 vapier 1.44 needed = (char*)(elf->data + offset); \
785 vapier 1.72 if (op == 0) { \
786 solar 1.127 if (!be_wewy_wewy_quiet) { \
787 vapier 1.72 if (*found_needed) xchrcat(ret, ',', ret_len); \
788 vapier 1.102 if (use_ldcache) \
789 vapier 1.97 if ((p = lookup_cache_lib(elf, needed)) != NULL) \
790 solar 1.96 needed = p; \
791 vapier 1.72 xstrcat(ret, needed, ret_len); \
792     } \
793     *found_needed = 1; \
794     } else { \
795 solar 1.86 if (!strncmp(find_lib, needed, strlen( !gmatch ? needed : find_lib))) { \
796 vapier 1.81 *found_lib = 1; \
797 solar 1.127 return (be_wewy_wewy_quiet ? NULL : needed); \
798 vapier 1.81 } \
799 vapier 1.72 } \
800 vapier 1.32 } \
801     ++dyn; \
802     } \
803     } }
804     SHOW_NEEDED(32)
805     SHOW_NEEDED(64)
806 vapier 1.85 if (op == 0 && !*found_needed && be_verbose)
807 vapier 1.84 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
808 vapier 1.32 }
809 vapier 1.72
810     return NULL;
811 vapier 1.39 }
812 vapier 1.41 static char *scanelf_file_interp(elfobj *elf, char *found_interp)
813 vapier 1.39 {
814     void *strtbl_void;
815    
816 vapier 1.41 if (!show_interp) return NULL;
817 vapier 1.32
818 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".interp");
819 vapier 1.38
820 vapier 1.39 if (strtbl_void) {
821 vapier 1.38 #define SHOW_INTERP(B) \
822     if (elf->elf_class == ELFCLASS ## B) { \
823 solar 1.40 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
824     *found_interp = 1; \
825 solar 1.127 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
826 vapier 1.38 }
827     SHOW_INTERP(32)
828     SHOW_INTERP(64)
829     }
830 vapier 1.41 return NULL;
831 vapier 1.39 }
832 vapier 1.49 static char *scanelf_file_bind(elfobj *elf, char *found_bind)
833     {
834     unsigned long i;
835     struct stat s;
836 solar 1.131 char dynamic = 0;
837 vapier 1.49
838     if (!show_bind) return NULL;
839 vapier 1.51 if (!elf->phdr) return NULL;
840 vapier 1.49
841     #define SHOW_BIND(B) \
842     if (elf->elf_class == ELFCLASS ## B) { \
843     Elf ## B ## _Dyn *dyn; \
844     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
845     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
846     Elf ## B ## _Off offset; \
847     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
848     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
849 solar 1.131 dynamic = 1; \
850 vapier 1.49 offset = EGET(phdr[i].p_offset); \
851     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
852     dyn = DYN ## B (elf->data + offset); \
853     while (EGET(dyn->d_tag) != DT_NULL) { \
854     if (EGET(dyn->d_tag) == DT_BIND_NOW || \
855     (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
856     { \
857     if (be_quiet) return NULL; \
858     *found_bind = 1; \
859 solar 1.127 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
860 vapier 1.49 } \
861     ++dyn; \
862     } \
863     } \
864     }
865     SHOW_BIND(32)
866     SHOW_BIND(64)
867    
868 solar 1.127 if (be_wewy_wewy_quiet) return NULL;
869 vapier 1.70
870 vapier 1.56 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
871 vapier 1.49 return NULL;
872     } else {
873     *found_bind = 1;
874 solar 1.131 return (char *) (dynamic ? "LAZY" : "STATIC");
875 vapier 1.49 }
876     }
877 vapier 1.84 static char *scanelf_file_soname(elfobj *elf, char *found_soname)
878     {
879     unsigned long i;
880     char *soname;
881     void *strtbl_void;
882    
883     if (!show_soname) return NULL;
884    
885     strtbl_void = elf_findsecbyname(elf, ".dynstr");
886    
887     if (elf->phdr && strtbl_void) {
888     #define SHOW_SONAME(B) \
889     if (elf->elf_class == ELFCLASS ## B) { \
890     Elf ## B ## _Dyn *dyn; \
891     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
892     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
893     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
894     Elf ## B ## _Off offset; \
895     /* only look for soname in shared objects */ \
896     if (ehdr->e_type != ET_DYN) \
897     return NULL; \
898     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
899     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
900     offset = EGET(phdr[i].p_offset); \
901     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
902     dyn = DYN ## B (elf->data + offset); \
903     while (EGET(dyn->d_tag) != DT_NULL) { \
904     if (EGET(dyn->d_tag) == DT_SONAME) { \
905     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
906     if (offset >= (Elf ## B ## _Off)elf->len) { \
907     ++dyn; \
908     continue; \
909     } \
910     soname = (char*)(elf->data + offset); \
911     *found_soname = 1; \
912 solar 1.127 return (be_wewy_wewy_quiet ? NULL : soname); \
913 vapier 1.84 } \
914     ++dyn; \
915     } \
916     } }
917     SHOW_SONAME(32)
918     SHOW_SONAME(64)
919     }
920    
921     return NULL;
922     }
923 vapier 1.72 static char *scanelf_file_sym(elfobj *elf, char *found_sym)
924 vapier 1.39 {
925 vapier 1.44 unsigned long i;
926 vapier 1.95 char *ret;
927 vapier 1.39 void *symtab_void, *strtab_void;
928 vapier 1.38
929 vapier 1.41 if (!find_sym) return NULL;
930 vapier 1.95 ret = find_sym;
931 vapier 1.32
932 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
933 vapier 1.27
934 vapier 1.39 if (symtab_void && strtab_void) {
935 vapier 1.27 #define FIND_SYM(B) \
936     if (elf->elf_class == ELFCLASS ## B) { \
937     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
938     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
939     Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
940 vapier 1.115 unsigned long cnt = EGET(symtab->sh_entsize); \
941 vapier 1.27 char *symname; \
942 vapier 1.115 if (cnt) \
943     cnt = EGET(symtab->sh_size) / cnt; \
944 vapier 1.27 for (i = 0; i < cnt; ++i) { \
945     if (sym->st_name) { \
946 vapier 1.134 /* make sure the symbol name is in acceptable memory range */ \
947 vapier 1.27 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
948 vapier 1.115 if ((void*)symname > (void*)elf->data_end) { \
949     warnf("%s: corrupt ELF symbols", elf->filename); \
950 vapier 1.134 ++sym; \
951 vapier 1.111 continue; \
952     } \
953 vapier 1.134 /* debug display ... show all symbols and some extra info */ \
954     if (*ret == '*') { \
955 vapier 1.32 printf("%s(%s) %5lX %15s %s\n", \
956 vapier 1.39 ((*found_sym == 0) ? "\n\t" : "\t"), \
957 vapier 1.76 elf->base_filename, \
958 vapier 1.94 (unsigned long)sym->st_size, \
959 vapier 1.95 get_elfstttype(sym->st_info), \
960 vapier 1.32 symname); \
961 vapier 1.39 *found_sym = 1; \
962 vapier 1.95 } else { \
963 vapier 1.134 /* allow the user to specify a comma delimited list of symbols to search for */ \
964 vapier 1.95 char *this_sym, *next_sym; \
965 vapier 1.134 this_sym = ret; \
966 vapier 1.95 do { \
967     next_sym = strchr(this_sym, ','); \
968     if (next_sym == NULL) \
969     next_sym = this_sym + strlen(this_sym); \
970 vapier 1.134 /* do we want a defined symbol ? */ \
971     if (*this_sym == '+') { \
972 vapier 1.140 if (sym->st_shndx == SHN_UNDEF) \
973 vapier 1.134 goto skip_this_sym##B; \
974     ++this_sym; \
975     /* do we want an undefined symbol ? */ \
976     } else if (*this_sym == '-') { \
977 vapier 1.140 if (sym->st_shndx != SHN_UNDEF) \
978 vapier 1.134 goto skip_this_sym##B; \
979     ++this_sym; \
980     } \
981     /* ok, lets compare the name now */ \
982 vapier 1.95 if ((strncmp(this_sym, symname, (next_sym-this_sym)) == 0 && symname[next_sym-this_sym] == '\0') || \
983 solar 1.144 (strncmp(symname, versioned_symname, strlen(versioned_symname)) == 0)) { \
984 solar 1.132 if (be_semi_verbose) { \
985     char buf[126]; \
986     snprintf(buf, sizeof(buf), "%lX %s %s", \
987     (unsigned long)sym->st_size, get_elfstttype(sym->st_info), this_sym); \
988     ret = buf; \
989     } else \
990     ret = this_sym; \
991 vapier 1.95 (*found_sym)++; \
992     goto break_out; \
993     } \
994 vapier 1.134 skip_this_sym##B: this_sym = next_sym + 1; \
995 vapier 1.95 } while (*next_sym != '\0'); \
996     } \
997 vapier 1.27 } \
998     ++sym; \
999     } }
1000     FIND_SYM(32)
1001     FIND_SYM(64)
1002 vapier 1.39 }
1003 vapier 1.70
1004 vapier 1.95 break_out:
1005 solar 1.127 if (be_wewy_wewy_quiet) return NULL;
1006 vapier 1.70
1007 vapier 1.41 if (*find_sym != '*' && *found_sym)
1008 vapier 1.95 return ret;
1009 vapier 1.41 if (be_quiet)
1010     return NULL;
1011     else
1012 solar 1.68 return (char *)" - ";
1013 vapier 1.39 }
1014 solar 1.119
1015    
1016 solar 1.124 static char *scanelf_file_sections(elfobj *elf, char *found_section)
1017     {
1018     if (!find_section)
1019     return NULL;
1020    
1021     #define FIND_SECTION(B) \
1022     if (elf->elf_class == ELFCLASS ## B) { \
1023 solar 1.136 int invert; \
1024 solar 1.124 Elf ## B ## _Shdr *section; \
1025 solar 1.136 invert = (*find_section == '!' ? 1 : 0); \
1026     section = SHDR ## B (elf_findsecbyname(elf, find_section+invert)); \
1027     if ((section == NULL && invert) || (section != NULL && !invert)) \
1028 solar 1.124 *found_section = 1; \
1029     }
1030     FIND_SECTION(32)
1031     FIND_SECTION(64)
1032    
1033 vapier 1.138 if (be_wewy_wewy_quiet)
1034     return NULL;
1035 solar 1.124
1036     if (*found_section)
1037     return find_section;
1038    
1039     if (be_quiet)
1040     return NULL;
1041     else
1042     return (char *)" - ";
1043     }
1044    
1045 vapier 1.39 /* scan an elf file and show all the fun stuff */
1046 solar 1.57 #define prints(str) write(fileno(stdout), str, strlen(str))
1047 vapier 1.106 static int scanelf_elfobj(elfobj *elf)
1048 vapier 1.39 {
1049 vapier 1.44 unsigned long i;
1050 solar 1.73 char found_pax, found_phdr, found_relro, found_load, found_textrel,
1051 vapier 1.84 found_rpath, found_needed, found_interp, found_bind, found_soname,
1052 solar 1.124 found_sym, found_lib, found_file, found_textrels, found_section;
1053 vapier 1.41 static char *out_buffer = NULL;
1054     static size_t out_len;
1055 vapier 1.39
1056 vapier 1.76 found_pax = found_phdr = found_relro = found_load = found_textrel = \
1057 vapier 1.84 found_rpath = found_needed = found_interp = found_bind = found_soname = \
1058 solar 1.124 found_sym = found_lib = found_file = found_textrels = found_section = 0;
1059 vapier 1.39
1060 vapier 1.114 if (be_verbose > 2)
1061 vapier 1.106 printf("%s: scanning file {%s,%s}\n", elf->filename,
1062 vapier 1.69 get_elfeitype(EI_CLASS, elf->elf_class),
1063     get_elfeitype(EI_DATA, elf->data[EI_DATA]));
1064 vapier 1.114 else if (be_verbose > 1)
1065 vapier 1.106 printf("%s: scanning file\n", elf->filename);
1066 vapier 1.39
1067 vapier 1.41 /* init output buffer */
1068     if (!out_buffer) {
1069     out_len = sizeof(char) * 80;
1070     out_buffer = (char*)xmalloc(out_len);
1071     }
1072     *out_buffer = '\0';
1073    
1074 vapier 1.39 /* show the header */
1075     if (!be_quiet && show_banner) {
1076 vapier 1.49 for (i = 0; out_format[i]; ++i) {
1077 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
1078 vapier 1.41
1079     switch (out_format[++i]) {
1080 solar 1.132 case '+': break;
1081 vapier 1.41 case '%': break;
1082 vapier 1.70 case '#': break;
1083 vapier 1.66 case 'F':
1084     case 'p':
1085     case 'f': prints("FILE "); found_file = 1; break;
1086 vapier 1.41 case 'o': prints(" TYPE "); break;
1087     case 'x': prints(" PAX "); break;
1088 vapier 1.71 case 'e': prints("STK/REL/PTL "); break;
1089 vapier 1.41 case 't': prints("TEXTREL "); break;
1090     case 'r': prints("RPATH "); break;
1091     case 'n': prints("NEEDED "); break;
1092     case 'i': prints("INTERP "); break;
1093 vapier 1.49 case 'b': prints("BIND "); break;
1094 vapier 1.84 case 'S': prints("SONAME "); break;
1095 vapier 1.41 case 's': prints("SYM "); break;
1096 vapier 1.72 case 'N': prints("LIB "); break;
1097 vapier 1.76 case 'T': prints("TEXTRELS "); break;
1098 vapier 1.126 case 'k': prints("SECTION "); break;
1099 vapier 1.76 default: warnf("'%c' has no title ?", out_format[i]);
1100 vapier 1.39 }
1101 vapier 1.27 }
1102 vapier 1.49 if (!found_file) prints("FILE ");
1103 vapier 1.41 prints("\n");
1104 vapier 1.49 found_file = 0;
1105 vapier 1.39 show_banner = 0;
1106     }
1107    
1108     /* dump all the good stuff */
1109 vapier 1.49 for (i = 0; out_format[i]; ++i) {
1110 vapier 1.41 const char *out;
1111 vapier 1.66 const char *tmp;
1112 vapier 1.41
1113 vapier 1.70 if (!IS_MODIFIER(out_format[i])) {
1114 vapier 1.41 xchrcat(&out_buffer, out_format[i], &out_len);
1115     continue;
1116     }
1117 vapier 1.39
1118 vapier 1.41 out = NULL;
1119 solar 1.127 be_wewy_wewy_quiet = (out_format[i] == '#');
1120 solar 1.132 be_semi_verbose = (out_format[i] == '+');
1121 vapier 1.41 switch (out_format[++i]) {
1122 solar 1.132 case '+':
1123 vapier 1.70 case '%':
1124     case '#':
1125     xchrcat(&out_buffer, out_format[i], &out_len); break;
1126     case 'F':
1127 vapier 1.76 found_file = 1;
1128 solar 1.127 if (be_wewy_wewy_quiet) break;
1129 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1130 vapier 1.70 break;
1131 vapier 1.66 case 'p':
1132 vapier 1.76 found_file = 1;
1133 solar 1.127 if (be_wewy_wewy_quiet) break;
1134 vapier 1.106 tmp = elf->filename;
1135 vapier 1.66 if (search_path) {
1136     ssize_t len_search = strlen(search_path);
1137 vapier 1.106 ssize_t len_file = strlen(elf->filename);
1138     if (!strncmp(elf->filename, search_path, len_search) && \
1139 vapier 1.66 len_file > len_search)
1140     tmp += len_search;
1141     if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
1142     }
1143     xstrcat(&out_buffer, tmp, &out_len);
1144     break;
1145     case 'f':
1146 vapier 1.76 found_file = 1;
1147 solar 1.127 if (be_wewy_wewy_quiet) break;
1148 vapier 1.106 tmp = strrchr(elf->filename, '/');
1149     tmp = (tmp == NULL ? elf->filename : tmp+1);
1150 vapier 1.66 xstrcat(&out_buffer, tmp, &out_len);
1151     break;
1152 vapier 1.41 case 'o': out = get_elfetype(elf); break;
1153     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
1154 solar 1.73 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
1155 vapier 1.41 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1156 vapier 1.79 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1157 vapier 1.41 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1158 vapier 1.72 case 'n':
1159     case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1160 vapier 1.41 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1161 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1162 vapier 1.84 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1163 vapier 1.72 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1164 solar 1.124 case 'k': out = scanelf_file_sections(elf, &found_section); break;
1165 vapier 1.76 default: warnf("'%c' has no scan code?", out_format[i]);
1166 vapier 1.29 }
1167 vapier 1.95 if (out) {
1168     /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */
1169     if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL)
1170     xstrncat(&out_buffer, out, &out_len, (tmp-out));
1171     else
1172     xstrcat(&out_buffer, out, &out_len);
1173     }
1174 vapier 1.39 }
1175    
1176 vapier 1.54 #define FOUND_SOMETHING() \
1177 solar 1.73 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
1178 vapier 1.76 found_rpath || found_needed || found_interp || found_bind || \
1179 solar 1.124 found_soname || found_sym || found_lib || found_textrels || found_section )
1180 vapier 1.54
1181     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
1182     xchrcat(&out_buffer, ' ', &out_len);
1183 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1184 vapier 1.27 }
1185 vapier 1.79 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
1186 vapier 1.41 puts(out_buffer);
1187 vapier 1.79 fflush(stdout);
1188     }
1189 vapier 1.10
1190 vapier 1.105 return 0;
1191     }
1192    
1193 vapier 1.106 /* scan a single elf */
1194     static int scanelf_elf(const char *filename, int fd, size_t len)
1195     {
1196 solar 1.120 int ret = 1;
1197 vapier 1.106 elfobj *elf;
1198    
1199     /* verify this is real ELF */
1200     if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1201     if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1202 solar 1.120 return ret;
1203     }
1204     switch (match_bits) {
1205     case 32:
1206     if (elf->elf_class != ELFCLASS32)
1207     goto label_done;
1208     break;
1209     case 64:
1210     if (elf->elf_class != ELFCLASS64)
1211     goto label_done;
1212     break;
1213     default: break;
1214 vapier 1.106 }
1215 solar 1.119 if (strlen(match_etypes)) {
1216     char sbuf[126];
1217     strncpy(sbuf, match_etypes, sizeof(sbuf));
1218     if (strchr(match_etypes, ',') != NULL) {
1219     char *p;
1220     while((p = strrchr(sbuf, ',')) != NULL) {
1221     *p = 0;
1222 solar 1.129 if (etype_lookup(p+1) == get_etype(elf))
1223 solar 1.119 goto label_ret;
1224     }
1225     }
1226 solar 1.129 if (etype_lookup(sbuf) != get_etype(elf))
1227 solar 1.119 goto label_done;
1228     }
1229    
1230     label_ret:
1231 vapier 1.106 ret = scanelf_elfobj(elf);
1232 solar 1.119
1233     label_done:
1234 vapier 1.106 unreadelf(elf);
1235     return ret;
1236     }
1237 solar 1.119
1238 vapier 1.105 /* scan an archive of elfs */
1239 vapier 1.106 static int scanelf_archive(const char *filename, int fd, size_t len)
1240 vapier 1.105 {
1241 vapier 1.106 archive_handle *ar;
1242 vapier 1.105 archive_member *m;
1243 vapier 1.106 char *ar_buffer;
1244     elfobj *elf;
1245    
1246     ar = ar_open_fd(filename, fd);
1247     if (ar == NULL)
1248     return 1;
1249    
1250     ar_buffer = (char*)mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1251     while ((m=ar_next(ar)) != NULL) {
1252     elf = readelf_buffer(m->name, ar_buffer+lseek(fd,0,SEEK_CUR), m->size);
1253     if (elf) {
1254     scanelf_elfobj(elf);
1255     unreadelf(elf);
1256     }
1257     }
1258     munmap(ar_buffer, len);
1259    
1260 vapier 1.105 return 0;
1261     }
1262     /* scan a file which may be an elf or an archive or some other magical beast */
1263     static void scanelf_file(const char *filename)
1264     {
1265     struct stat st;
1266 vapier 1.106 int fd;
1267 vapier 1.105
1268     /* make sure 'filename' exists */
1269     if (lstat(filename, &st) == -1) {
1270     if (be_verbose > 2) printf("%s: does not exist\n", filename);
1271     return;
1272     }
1273    
1274     /* always handle regular files and handle symlinked files if no -y */
1275     if (S_ISLNK(st.st_mode)) {
1276     if (!scan_symlink) return;
1277     stat(filename, &st);
1278     }
1279     if (!S_ISREG(st.st_mode)) {
1280     if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1281     return;
1282     }
1283    
1284 vapier 1.106 if ((fd=open(filename, (fix_elf ? O_RDWR : O_RDONLY))) == -1)
1285     return;
1286    
1287     if (scanelf_elf(filename, fd, st.st_size) == 1 && scan_archives)
1288 vapier 1.105 /* if it isn't an ELF, maybe it's an .a archive */
1289 vapier 1.106 scanelf_archive(filename, fd, st.st_size);
1290    
1291     close(fd);
1292 solar 1.6 }
1293    
1294 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
1295 vapier 1.10 static void scanelf_dir(const char *path)
1296 solar 1.1 {
1297 vapier 1.10 register DIR *dir;
1298     register struct dirent *dentry;
1299 vapier 1.14 struct stat st_top, st;
1300 vapier 1.104 char buf[__PAX_UTILS_PATH_MAX];
1301 vapier 1.32 size_t pathlen = 0, len = 0;
1302 vapier 1.10
1303     /* make sure path exists */
1304 vapier 1.39 if (lstat(path, &st_top) == -1) {
1305     if (be_verbose > 2) printf("%s: does not exist\n", path);
1306 vapier 1.10 return;
1307 vapier 1.39 }
1308 solar 1.11
1309 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
1310 vapier 1.14 if (!S_ISDIR(st_top.st_mode)) {
1311 vapier 1.10 scanelf_file(path);
1312     return;
1313     }
1314    
1315     /* now scan the dir looking for fun stuff */
1316     if ((dir = opendir(path)) == NULL) {
1317     warnf("could not opendir %s: %s", path, strerror(errno));
1318     return;
1319     }
1320 vapier 1.114 if (be_verbose > 1) printf("%s: scanning dir\n", path);
1321 solar 1.11
1322 vapier 1.32 pathlen = strlen(path);
1323 vapier 1.10 while ((dentry = readdir(dir))) {
1324     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
1325     continue;
1326 vapier 1.32 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
1327     if (len >= sizeof(buf)) {
1328 vapier 1.76 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
1329     (unsigned long)len, (unsigned long)sizeof(buf));
1330 vapier 1.32 continue;
1331     }
1332 solar 1.143 snprintf(buf, sizeof(buf), "%s%s%s", path, (path[pathlen-1] == '/') ? "" : "/", dentry->d_name);
1333 solar 1.20 if (lstat(buf, &st) != -1) {
1334 vapier 1.10 if (S_ISREG(st.st_mode))
1335 solar 1.20 scanelf_file(buf);
1336 vapier 1.10 else if (dir_recurse && S_ISDIR(st.st_mode)) {
1337 vapier 1.14 if (dir_crossmount || (st_top.st_dev == st.st_dev))
1338 solar 1.20 scanelf_dir(buf);
1339 vapier 1.10 }
1340     }
1341     }
1342     closedir(dir);
1343 solar 1.1 }
1344    
1345 vapier 1.133 static int scanelf_from_file(const char *filename)
1346 vapier 1.47 {
1347 solar 1.45 FILE *fp = NULL;
1348     char *p;
1349 vapier 1.104 char path[__PAX_UTILS_PATH_MAX];
1350 solar 1.45
1351 solar 1.132 if (strcmp(filename, "-") == 0)
1352 solar 1.45 fp = stdin;
1353     else if ((fp = fopen(filename, "r")) == NULL)
1354     return 1;
1355    
1356 vapier 1.104 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1357 solar 1.45 if ((p = strchr(path, '\n')) != NULL)
1358     *p = 0;
1359 vapier 1.66 search_path = path;
1360 solar 1.45 scanelf_dir(path);
1361     }
1362     if (fp != stdin)
1363     fclose(fp);
1364     return 0;
1365     }
1366    
1367 flameeyes 1.141 #if defined(__GLIBC__) || defined(__UCLIBC__)
1368 solar 1.123 static int load_ld_so_conf(int i, const char *fname)
1369 vapier 1.48 {
1370     FILE *fp = NULL;
1371     char *p;
1372 vapier 1.104 char path[__PAX_UTILS_PATH_MAX];
1373 vapier 1.48
1374 solar 1.123 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
1375     return i;
1376    
1377     if ((fp = fopen(fname, "r")) == NULL)
1378     return i;
1379 vapier 1.48
1380 vapier 1.104 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1381 vapier 1.48 if ((p = strrchr(path, '\r')) != NULL)
1382     *p = 0;
1383     if ((p = strchr(path, '\n')) != NULL)
1384     *p = 0;
1385 solar 1.123 // recursive includes of the same file will make this segfault.
1386 solar 1.129 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1387 solar 1.123 glob64_t gl;
1388     size_t x;
1389     char gpath[__PAX_UTILS_PATH_MAX];
1390    
1391 solar 1.129 memset(gpath, 0, sizeof(gpath));
1392 solar 1.123
1393     if (path[8] != '/')
1394 solar 1.129 snprintf(gpath, sizeof(gpath), "/etc/%s", &path[8]);
1395 solar 1.123 else
1396 solar 1.129 strncpy(gpath, &path[8], sizeof(gpath));
1397 solar 1.123
1398     if ((glob64(gpath, 0, NULL, &gl)) == 0) {
1399     for (x = 0; x < gl.gl_pathc; ++x) {
1400     /* try to avoid direct loops */
1401     if (strcmp(gl.gl_pathv[x], fname) == 0)
1402     continue;
1403     i = load_ld_so_conf(i, gl.gl_pathv[x]);
1404     if (i + 1 >= sizeof(ldpaths) / sizeof(*ldpaths)) {
1405     globfree64(&gl);
1406     return i;
1407     }
1408     }
1409     globfree64 (&gl);
1410     continue;
1411     } else
1412     abort();
1413     }
1414     if (*path != '/')
1415     continue;
1416 vapier 1.48
1417     ldpaths[i++] = xstrdup(path);
1418    
1419     if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
1420     break;
1421     }
1422     ldpaths[i] = NULL;
1423    
1424     fclose(fp);
1425 solar 1.123 return i;
1426 vapier 1.48 }
1427 flameeyes 1.141 #endif
1428    
1429     #if defined(__FreeBSD__) || (__DragonFly__)
1430     static int load_ld_so_hints(int i, const char *fname)
1431     {
1432     FILE *fp = NULL;
1433     char *b = NULL, *p;
1434     struct elfhints_hdr hdr;
1435    
1436     if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
1437     return i;
1438    
1439     if ((fp = fopen(fname, "r")) == NULL)
1440     return i;
1441    
1442     if ( fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
1443     hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
1444     fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1
1445     ) {
1446     fclose(fp);
1447     return i;
1448     }
1449    
1450     b = (char*)malloc(hdr.dirlistlen+1);
1451     if ( fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1 ) {
1452     fclose(fp);
1453     free(b);
1454     return i;
1455     }
1456    
1457     while ( (p = strsep(&b, ":")) ) {
1458     if ( *p == '\0' ) continue;
1459     ldpaths[i++] = xstrdup(p);
1460    
1461     if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
1462     break;
1463     }
1464     ldpaths[i] = NULL;
1465    
1466     free(b);
1467     fclose(fp);
1468     return i;
1469     }
1470     #endif
1471 vapier 1.48
1472 vapier 1.10 /* scan /etc/ld.so.conf for paths */
1473     static void scanelf_ldpath()
1474     {
1475 vapier 1.17 char scan_l, scan_ul, scan_ull;
1476 vapier 1.48 int i = 0;
1477 vapier 1.10
1478 vapier 1.48 if (!ldpaths[0])
1479     err("Unable to load any paths from ld.so.conf");
1480 vapier 1.10
1481 vapier 1.17 scan_l = scan_ul = scan_ull = 0;
1482    
1483 vapier 1.48 while (ldpaths[i]) {
1484     if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
1485     if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
1486     if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
1487     scanelf_dir(ldpaths[i]);
1488     ++i;
1489     }
1490 vapier 1.10
1491 vapier 1.17 if (!scan_l) scanelf_dir("/lib");
1492     if (!scan_ul) scanelf_dir("/usr/lib");
1493     if (!scan_ull) scanelf_dir("/usr/local/lib");
1494 vapier 1.10 }
1495 solar 1.1
1496 vapier 1.10 /* scan env PATH for paths */
1497     static void scanelf_envpath()
1498 solar 1.1 {
1499 solar 1.34 char *path, *p;
1500 vapier 1.10
1501     path = getenv("PATH");
1502     if (!path)
1503     err("PATH is not set in your env !");
1504 vapier 1.41 path = xstrdup(path);
1505 vapier 1.10
1506     while ((p = strrchr(path, ':')) != NULL) {
1507     scanelf_dir(p + 1);
1508     *p = 0;
1509     }
1510 vapier 1.17
1511 solar 1.34 free(path);
1512 solar 1.1 }
1513    
1514 vapier 1.10 /* usage / invocation handling functions */
1515 solar 1.127 #define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:BhV"
1516 vapier 1.27 #define a_argument required_argument
1517 vapier 1.10 static struct option const long_opts[] = {
1518     {"path", no_argument, NULL, 'p'},
1519     {"ldpath", no_argument, NULL, 'l'},
1520     {"recursive", no_argument, NULL, 'R'},
1521 vapier 1.14 {"mount", no_argument, NULL, 'm'},
1522 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
1523 vapier 1.105 {"archives", no_argument, NULL, 'A'},
1524     {"ldcache", no_argument, NULL, 'L'},
1525 vapier 1.101 {"fix", no_argument, NULL, 'X'},
1526 solar 1.127 {"setpax", a_argument, NULL, 'z'},
1527 vapier 1.10 {"pax", no_argument, NULL, 'x'},
1528 solar 1.16 {"header", no_argument, NULL, 'e'},
1529 vapier 1.10 {"textrel", no_argument, NULL, 't'},
1530     {"rpath", no_argument, NULL, 'r'},
1531 vapier 1.32 {"needed", no_argument, NULL, 'n'},
1532 vapier 1.38 {"interp", no_argument, NULL, 'i'},
1533 vapier 1.49 {"bind", no_argument, NULL, 'b'},
1534 vapier 1.84 {"soname", no_argument, NULL, 'S'},
1535 vapier 1.76 {"symbol", a_argument, NULL, 's'},
1536 solar 1.124 {"section", a_argument, NULL, 'k'},
1537 vapier 1.76 {"lib", a_argument, NULL, 'N'},
1538 solar 1.86 {"gmatch", no_argument, NULL, 'g'},
1539 vapier 1.76 {"textrels", no_argument, NULL, 'T'},
1540 solar 1.119 {"etype", a_argument, NULL, 'E'},
1541 solar 1.120 {"bits", a_argument, NULL, 'M'},
1542 vapier 1.10 {"all", no_argument, NULL, 'a'},
1543     {"quiet", no_argument, NULL, 'q'},
1544 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
1545 vapier 1.76 {"format", a_argument, NULL, 'F'},
1546     {"from", a_argument, NULL, 'f'},
1547     {"file", a_argument, NULL, 'o'},
1548 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
1549 vapier 1.10 {"help", no_argument, NULL, 'h'},
1550     {"version", no_argument, NULL, 'V'},
1551     {NULL, no_argument, NULL, 0x0}
1552     };
1553 solar 1.57
1554 solar 1.68 static const char *opts_help[] = {
1555 vapier 1.10 "Scan all directories in PATH environment",
1556     "Scan all directories in /etc/ld.so.conf",
1557 vapier 1.14 "Scan directories recursively",
1558 vapier 1.37 "Don't recursively cross mount points",
1559 vapier 1.101 "Don't scan symlinks",
1560 vapier 1.105 "Scan archives (.a files)",
1561     "Utilize ld.so.cache information (use with -r/-n)",
1562 solar 1.127 "Try and 'fix' bad things (use with -r/-e)",
1563     "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
1564 vapier 1.10 "Print PaX markings",
1565 vapier 1.71 "Print GNU_STACK/PT_LOAD markings",
1566 vapier 1.10 "Print TEXTREL information",
1567     "Print RPATH information",
1568 vapier 1.32 "Print NEEDED information",
1569 vapier 1.38 "Print INTERP information",
1570 vapier 1.49 "Print BIND information",
1571 vapier 1.84 "Print SONAME information",
1572 vapier 1.27 "Find a specified symbol",
1573 solar 1.124 "Find a specified section",
1574 vapier 1.72 "Find a specified library",
1575 solar 1.86 "Use strncmp to match libraries. (use with -N)",
1576 vapier 1.76 "Locate cause of TEXTREL",
1577 solar 1.129 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
1578 solar 1.120 "Print only ELF files matching numeric bits",
1579 vapier 1.82 "Print all scanned info (-x -e -t -r -b)\n",
1580 vapier 1.14 "Only output 'bad' things",
1581     "Be verbose (can be specified more than once)",
1582 vapier 1.39 "Use specified format for output",
1583 solar 1.45 "Read input stream from a filename",
1584 vapier 1.24 "Write output stream to a filename",
1585 vapier 1.14 "Don't display the header",
1586 vapier 1.10 "Print this help and exit",
1587     "Print version and exit",
1588     NULL
1589     };
1590    
1591     /* display usage and exit */
1592     static void usage(int status)
1593 solar 1.1 {
1594 vapier 1.44 unsigned long i;
1595 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
1596 vapier 1.105 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
1597 solar 1.35 printf("Options: -[%s]\n", PARSE_FLAGS);
1598 vapier 1.10 for (i = 0; long_opts[i].name; ++i)
1599 vapier 1.27 if (long_opts[i].has_arg == no_argument)
1600 solar 1.124 printf(" -%c, --%-14s* %s\n", long_opts[i].val,
1601 vapier 1.27 long_opts[i].name, opts_help[i]);
1602     else
1603 solar 1.124 printf(" -%c, --%-7s <arg> * %s\n", long_opts[i].val,
1604 vapier 1.27 long_opts[i].name, opts_help[i]);
1605 solar 1.45
1606     if (status != EXIT_SUCCESS)
1607     exit(status);
1608 solar 1.125
1609 vapier 1.46 puts("\nThe format modifiers for the -F option are:");
1610 vapier 1.70 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1611     puts(" t TEXTREL \tr RPATH \tn NEEDED");
1612     puts(" i INTERP \tb BIND \ts symbol");
1613 vapier 1.76 puts(" N library \to Type \tT TEXTRELs");
1614 solar 1.125 puts(" S SONAME \tk section");
1615 vapier 1.70 puts(" p filename (with search path removed)");
1616 vapier 1.88 puts(" f filename (short name/basename)");
1617 vapier 1.70 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1618 solar 1.45
1619 solar 1.121 puts("\nELF Etypes:");
1620     print_etypes(stdout);
1621    
1622 vapier 1.10 exit(status);
1623 solar 1.1 }
1624    
1625     /* parse command line arguments and preform needed actions */
1626 vapier 1.10 static void parseargs(int argc, char *argv[])
1627     {
1628 vapier 1.48 int i;
1629 vapier 1.133 const char *from_file = NULL;
1630 vapier 1.10
1631     opterr = 0;
1632 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
1633     switch (i) {
1634 vapier 1.10
1635 vapier 1.39 case 'V':
1636 vapier 1.80 printf("pax-utils-%s: %s compiled %s\n%s\n"
1637     "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
1638     VERSION, __FILE__, __DATE__, rcsid, argv0);
1639 vapier 1.10 exit(EXIT_SUCCESS);
1640     break;
1641     case 'h': usage(EXIT_SUCCESS); break;
1642 solar 1.45 case 'f':
1643 vapier 1.100 if (from_file) warn("You prob don't want to specify -f twice");
1644     from_file = optarg;
1645 solar 1.45 break;
1646 solar 1.119 case 'E':
1647     strncpy(match_etypes, optarg, sizeof(match_etypes));
1648     break;
1649 solar 1.120 case 'M':
1650     match_bits = atoi(optarg);
1651     break;
1652 vapier 1.24 case 'o': {
1653 vapier 1.146 if (freopen(optarg, "w", stdout) == NULL)
1654 vapier 1.24 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1655 solar 1.21 break;
1656     }
1657 solar 1.124 case 'k':
1658 vapier 1.126 if (find_section) warn("You prob don't want to specify -k twice");
1659 solar 1.124 find_section = optarg;
1660     break;
1661 vapier 1.39 case 's': {
1662 vapier 1.93 if (find_sym) warn("You prob don't want to specify -s twice");
1663     find_sym = optarg;
1664     versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
1665 vapier 1.39 sprintf(versioned_symname, "%s@", find_sym);
1666     break;
1667     }
1668 vapier 1.72 case 'N': {
1669 vapier 1.93 if (find_lib) warn("You prob don't want to specify -N twice");
1670     find_lib = optarg;
1671 vapier 1.72 break;
1672     }
1673 vapier 1.39
1674     case 'F': {
1675 vapier 1.93 if (out_format) warn("You prob don't want to specify -F twice");
1676     out_format = optarg;
1677 vapier 1.39 break;
1678     }
1679 solar 1.127 case 'z': {
1680 solar 1.129 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
1681 solar 1.127 size_t x;
1682 vapier 1.27
1683 solar 1.127 for (x = 0 ; x < strlen(optarg); x++) {
1684     switch(optarg[x]) {
1685     case 'p':
1686     case 'P':
1687     do_state(optarg[x], PAGEEXEC);
1688     break;
1689     case 's':
1690     case 'S':
1691     do_state(optarg[x], SEGMEXEC);
1692     break;
1693     case 'm':
1694     case 'M':
1695     do_state(optarg[x], MPROTECT);
1696     break;
1697     case 'e':
1698     case 'E':
1699     do_state(optarg[x], EMUTRAMP);
1700     break;
1701     case 'r':
1702     case 'R':
1703     do_state(optarg[x], RANDMMAP);
1704     break;
1705     case 'x':
1706     case 'X':
1707     do_state(optarg[x], RANDEXEC);
1708     break;
1709     default:
1710     break;
1711     }
1712     }
1713     if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
1714     ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
1715     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
1716     ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
1717     ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
1718     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
1719     setpax = flags;
1720     break;
1721     }
1722 vapier 1.122 case 'g': gmatch = 1; break;
1723 vapier 1.102 case 'L': use_ldcache = 1; break;
1724 vapier 1.37 case 'y': scan_symlink = 0; break;
1725 vapier 1.105 case 'A': scan_archives = 1; break;
1726 solar 1.16 case 'B': show_banner = 0; break;
1727 vapier 1.10 case 'l': scan_ldpath = 1; break;
1728     case 'p': scan_envpath = 1; break;
1729     case 'R': dir_recurse = 1; break;
1730 vapier 1.14 case 'm': dir_crossmount = 0; break;
1731 vapier 1.101 case 'X': ++fix_elf; break;
1732 vapier 1.10 case 'x': show_pax = 1; break;
1733 solar 1.73 case 'e': show_phdr = 1; break;
1734 vapier 1.10 case 't': show_textrel = 1; break;
1735     case 'r': show_rpath = 1; break;
1736 vapier 1.32 case 'n': show_needed = 1; break;
1737 vapier 1.38 case 'i': show_interp = 1; break;
1738 vapier 1.49 case 'b': show_bind = 1; break;
1739 vapier 1.84 case 'S': show_soname = 1; break;
1740 vapier 1.76 case 'T': show_textrels = 1; break;
1741 vapier 1.10 case 'q': be_quiet = 1; break;
1742 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1743 vapier 1.82 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
1744 vapier 1.10
1745     case ':':
1746 vapier 1.113 err("Option '%c' is missing parameter", optopt);
1747 vapier 1.10 case '?':
1748 solar 1.119 err("Unknown option '%c' or argument missing", optopt);
1749 vapier 1.10 default:
1750 vapier 1.113 err("Unhandled option '%c'; please report this", i);
1751 vapier 1.10 }
1752     }
1753    
1754 vapier 1.39 /* let the format option override all other options */
1755     if (out_format) {
1756 solar 1.73 show_pax = show_phdr = show_textrel = show_rpath = \
1757 vapier 1.84 show_needed = show_interp = show_bind = show_soname = \
1758     show_textrels = 0;
1759 vapier 1.48 for (i = 0; out_format[i]; ++i) {
1760 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
1761 vapier 1.39
1762 vapier 1.48 switch (out_format[++i]) {
1763 solar 1.132 case '+': break;
1764 vapier 1.39 case '%': break;
1765 vapier 1.70 case '#': break;
1766 vapier 1.39 case 'F': break;
1767 vapier 1.66 case 'p': break;
1768     case 'f': break;
1769 solar 1.124 case 'k': break;
1770 vapier 1.39 case 's': break;
1771 vapier 1.72 case 'N': break;
1772 vapier 1.41 case 'o': break;
1773 vapier 1.39 case 'x': show_pax = 1; break;
1774 solar 1.73 case 'e': show_phdr = 1; break;
1775 vapier 1.39 case 't': show_textrel = 1; break;
1776     case 'r': show_rpath = 1; break;
1777     case 'n': show_needed = 1; break;
1778     case 'i': show_interp = 1; break;
1779 vapier 1.49 case 'b': show_bind = 1; break;
1780 vapier 1.84 case 'S': show_soname = 1; break;
1781 vapier 1.76 case 'T': show_textrels = 1; break;
1782 vapier 1.39 default:
1783     err("Invalid format specifier '%c' (byte %i)",
1784 vapier 1.48 out_format[i], i+1);
1785 vapier 1.39 }
1786     }
1787 vapier 1.41
1788     /* construct our default format */
1789     } else {
1790     size_t fmt_len = 30;
1791     out_format = (char*)xmalloc(sizeof(char) * fmt_len);
1792 vapier 1.76 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1793     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1794     if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1795     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1796     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1797     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1798     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1799     if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1800 vapier 1.84 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1801 vapier 1.76 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1802     if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1803 solar 1.124 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
1804 vapier 1.76 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1805     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1806 vapier 1.39 }
1807 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
1808 vapier 1.39
1809     /* now lets actually do the scanning */
1810 vapier 1.102 if (scan_ldpath || use_ldcache)
1811 flameeyes 1.141 #if defined(__GLIBC__) || defined(__UCLIBC__)
1812 solar 1.123 load_ld_so_conf(0, "/etc/ld.so.conf");
1813 flameeyes 1.141 #elif defined(__FreeBSD__) || defined(__DragonFly__)
1814     load_ld_so_hints(0, _PATH_ELF_HINTS);
1815     #endif
1816 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
1817     if (scan_envpath) scanelf_envpath();
1818 vapier 1.139 if (!from_file && optind == argc && ttyname(0) == NULL)
1819 vapier 1.133 from_file = "-";
1820 solar 1.45 if (from_file) {
1821     scanelf_from_file(from_file);
1822     from_file = *argv;
1823     }
1824     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
1825 vapier 1.25 err("Nothing to scan !?");
1826 vapier 1.66 while (optind < argc) {
1827     search_path = argv[optind++];
1828     scanelf_dir(search_path);
1829     }
1830 vapier 1.27
1831 vapier 1.39 /* clean up */
1832 vapier 1.93 if (versioned_symname) free(versioned_symname);
1833 vapier 1.48 for (i = 0; ldpaths[i]; ++i)
1834     free(ldpaths[i]);
1835 solar 1.96
1836     if (ldcache != 0)
1837     munmap(ldcache, ldcache_size);
1838 vapier 1.10 }
1839    
1840 kevquinn 1.142 static char **get_split_env(const char *envvar) {
1841 solar 1.143 char **envvals = NULL;
1842     char *saveptr = NULL;
1843 kevquinn 1.142 char *env;
1844     char *s;
1845     int nentry;
1846    
1847 solar 1.143 if ((env = getenv(envvar)) == NULL)
1848     return NULL;
1849 kevquinn 1.142
1850 solar 1.143 env = xstrdup(env);
1851     if (env == NULL)
1852     return NULL;
1853 kevquinn 1.142
1854 solar 1.143 nentry = 0;
1855     for (s = strtok_r(env, " \t\n", &saveptr); s != NULL; s = strtok_r(NULL, " \t\n", &saveptr)) {
1856     if ((envvals = xrealloc(envvals, sizeof(char *)*(nentry+1))) == NULL)
1857     return NULL;
1858     envvals[nentry++] = s;
1859 kevquinn 1.142 }
1860 solar 1.143 envvals[nentry] = NULL;
1861 kevquinn 1.142
1862     return envvals;
1863     }
1864    
1865     static void parseenv() {
1866     qa_textrels=get_split_env("QA_TEXTRELS");
1867     qa_execstack=get_split_env("QA_EXECSTACK");
1868 kevquinn 1.145 qa_wx_load=get_split_env("QA_WX_LOAD");
1869 vapier 1.41 }
1870    
1871    
1872 vapier 1.72
1873 vapier 1.10 int main(int argc, char *argv[])
1874 solar 1.1 {
1875 vapier 1.10 if (argc < 2)
1876     usage(EXIT_FAILURE);
1877 kevquinn 1.142 parseenv();
1878 vapier 1.10 parseargs(argc, argv);
1879 solar 1.21 fclose(stdout);
1880 solar 1.61 #ifdef __BOUNDS_CHECKING_ON
1881 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()");
1882 solar 1.61 #endif
1883 vapier 1.10 return EXIT_SUCCESS;
1884 solar 1.1 }

  ViewVC Help
Powered by ViewVC 1.1.20