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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.165 - (hide annotations) (download) (as text)
Mon Dec 11 03:24:03 2006 UTC (7 years, 4 months ago) by vapier
Branch: MAIN
Changes since 1.164: +16 -18 lines
File MIME type: text/x-csrc
move/rename do_state macro to a more appropriate place

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

  ViewVC Help
Powered by ViewVC 1.1.20