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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.152 - (hide annotations) (download) (as text)
Sun May 14 23:49:56 2006 UTC (8 years, 5 months ago) by vapier
Branch: MAIN
Changes since 1.151: +63 -39 lines
File MIME type: text/x-csrc
cleanup style, touchup new QA envvar code, and be better about cleaning up after ourselves

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

  ViewVC Help
Powered by ViewVC 1.1.20