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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.166 - (hide annotations) (download) (as text)
Mon Dec 11 03:31:54 2006 UTC (7 years, 4 months ago) by vapier
Branch: MAIN
Changes since 1.165: +6 -3 lines
File MIME type: text/x-csrc
add support for displaying em_machine via %a format flag

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.166 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.165 2006/12/11 03:24:03 vapier Exp $
5 solar 1.1 *
6 vapier 1.98 * Copyright 2003-2006 Ned Ludd - <solar@gentoo.org>
7     * Copyright 2004-2006 Mike Frysinger - <vapier@gentoo.org>
8 solar 1.1 */
9    
10 vapier 1.89 #include "paxinc.h"
11 flameeyes 1.141
12 vapier 1.166 static const char *rcsid = "$Id: scanelf.c,v 1.165 2006/12/11 03:24:03 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 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.166 case 'a': prints("ARCH "); break;
1061 vapier 1.76 default: warnf("'%c' has no title ?", out_format[i]);
1062 vapier 1.39 }
1063 vapier 1.27 }
1064 vapier 1.49 if (!found_file) prints("FILE ");
1065 vapier 1.41 prints("\n");
1066 vapier 1.49 found_file = 0;
1067 vapier 1.39 show_banner = 0;
1068     }
1069    
1070     /* dump all the good stuff */
1071 vapier 1.49 for (i = 0; out_format[i]; ++i) {
1072 vapier 1.41 const char *out;
1073 vapier 1.66 const char *tmp;
1074 vapier 1.41
1075 vapier 1.70 if (!IS_MODIFIER(out_format[i])) {
1076 vapier 1.41 xchrcat(&out_buffer, out_format[i], &out_len);
1077     continue;
1078     }
1079 vapier 1.39
1080 vapier 1.41 out = NULL;
1081 solar 1.127 be_wewy_wewy_quiet = (out_format[i] == '#');
1082 solar 1.132 be_semi_verbose = (out_format[i] == '+');
1083 vapier 1.41 switch (out_format[++i]) {
1084 solar 1.132 case '+':
1085 vapier 1.70 case '%':
1086     case '#':
1087     xchrcat(&out_buffer, out_format[i], &out_len); break;
1088     case 'F':
1089 vapier 1.76 found_file = 1;
1090 solar 1.127 if (be_wewy_wewy_quiet) break;
1091 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1092 vapier 1.70 break;
1093 vapier 1.66 case 'p':
1094 vapier 1.76 found_file = 1;
1095 solar 1.127 if (be_wewy_wewy_quiet) break;
1096 vapier 1.106 tmp = elf->filename;
1097 vapier 1.66 if (search_path) {
1098     ssize_t len_search = strlen(search_path);
1099 vapier 1.106 ssize_t len_file = strlen(elf->filename);
1100     if (!strncmp(elf->filename, search_path, len_search) && \
1101 vapier 1.66 len_file > len_search)
1102     tmp += len_search;
1103     if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
1104     }
1105     xstrcat(&out_buffer, tmp, &out_len);
1106     break;
1107     case 'f':
1108 vapier 1.76 found_file = 1;
1109 solar 1.127 if (be_wewy_wewy_quiet) break;
1110 vapier 1.106 tmp = strrchr(elf->filename, '/');
1111     tmp = (tmp == NULL ? elf->filename : tmp+1);
1112 vapier 1.66 xstrcat(&out_buffer, tmp, &out_len);
1113     break;
1114 vapier 1.41 case 'o': out = get_elfetype(elf); break;
1115     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
1116 solar 1.73 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
1117 vapier 1.41 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1118 vapier 1.79 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1119 vapier 1.41 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1120 vapier 1.72 case 'n':
1121     case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1122 vapier 1.41 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1123 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1124 vapier 1.84 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1125 vapier 1.72 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1126 solar 1.124 case 'k': out = scanelf_file_sections(elf, &found_section); break;
1127 vapier 1.166 case 'a': out = get_elfemtype(elf); break;
1128 vapier 1.76 default: warnf("'%c' has no scan code?", out_format[i]);
1129 vapier 1.29 }
1130 vapier 1.95 if (out) {
1131     /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */
1132     if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL)
1133     xstrncat(&out_buffer, out, &out_len, (tmp-out));
1134     else
1135     xstrcat(&out_buffer, out, &out_len);
1136     }
1137 vapier 1.39 }
1138    
1139 vapier 1.54 #define FOUND_SOMETHING() \
1140 solar 1.73 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
1141 vapier 1.76 found_rpath || found_needed || found_interp || found_bind || \
1142 solar 1.124 found_soname || found_sym || found_lib || found_textrels || found_section )
1143 vapier 1.54
1144     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
1145     xchrcat(&out_buffer, ' ', &out_len);
1146 vapier 1.106 xstrcat(&out_buffer, elf->filename, &out_len);
1147 vapier 1.27 }
1148 vapier 1.79 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
1149 vapier 1.41 puts(out_buffer);
1150 vapier 1.79 fflush(stdout);
1151     }
1152 vapier 1.10
1153 vapier 1.105 return 0;
1154     }
1155    
1156 vapier 1.106 /* scan a single elf */
1157     static int scanelf_elf(const char *filename, int fd, size_t len)
1158     {
1159 solar 1.120 int ret = 1;
1160 vapier 1.106 elfobj *elf;
1161    
1162     /* verify this is real ELF */
1163     if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1164     if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1165 solar 1.120 return ret;
1166     }
1167     switch (match_bits) {
1168     case 32:
1169     if (elf->elf_class != ELFCLASS32)
1170     goto label_done;
1171     break;
1172     case 64:
1173     if (elf->elf_class != ELFCLASS64)
1174     goto label_done;
1175     break;
1176     default: break;
1177 vapier 1.106 }
1178 solar 1.119 if (strlen(match_etypes)) {
1179     char sbuf[126];
1180     strncpy(sbuf, match_etypes, sizeof(sbuf));
1181     if (strchr(match_etypes, ',') != NULL) {
1182     char *p;
1183     while((p = strrchr(sbuf, ',')) != NULL) {
1184     *p = 0;
1185 solar 1.129 if (etype_lookup(p+1) == get_etype(elf))
1186 solar 1.119 goto label_ret;
1187     }
1188     }
1189 solar 1.129 if (etype_lookup(sbuf) != get_etype(elf))
1190 solar 1.119 goto label_done;
1191     }
1192    
1193     label_ret:
1194 vapier 1.106 ret = scanelf_elfobj(elf);
1195 solar 1.119
1196     label_done:
1197 vapier 1.106 unreadelf(elf);
1198     return ret;
1199     }
1200 solar 1.119
1201 vapier 1.105 /* scan an archive of elfs */
1202 vapier 1.106 static int scanelf_archive(const char *filename, int fd, size_t len)
1203 vapier 1.105 {
1204 vapier 1.106 archive_handle *ar;
1205 vapier 1.105 archive_member *m;
1206 vapier 1.106 char *ar_buffer;
1207     elfobj *elf;
1208    
1209     ar = ar_open_fd(filename, fd);
1210     if (ar == NULL)
1211     return 1;
1212    
1213     ar_buffer = (char*)mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1214     while ((m=ar_next(ar)) != NULL) {
1215     elf = readelf_buffer(m->name, ar_buffer+lseek(fd,0,SEEK_CUR), m->size);
1216     if (elf) {
1217     scanelf_elfobj(elf);
1218     unreadelf(elf);
1219     }
1220     }
1221     munmap(ar_buffer, len);
1222    
1223 vapier 1.105 return 0;
1224     }
1225     /* scan a file which may be an elf or an archive or some other magical beast */
1226 solar 1.164 static int scanelf_file(const char *filename, const struct stat *st_cache)
1227 vapier 1.105 {
1228 vapier 1.161 const struct stat *st = st_cache;
1229     struct stat symlink_st;
1230 vapier 1.106 int fd;
1231 vapier 1.105
1232     /* always handle regular files and handle symlinked files if no -y */
1233 vapier 1.161 if (S_ISLNK(st->st_mode)) {
1234 solar 1.164 if (!scan_symlink) return 1;
1235 vapier 1.161 stat(filename, &symlink_st);
1236     st = &symlink_st;
1237 vapier 1.105 }
1238 vapier 1.161
1239     if (!S_ISREG(st->st_mode)) {
1240 vapier 1.105 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1241 solar 1.164 return 1;
1242 vapier 1.105 }
1243    
1244 vapier 1.106 if ((fd=open(filename, (fix_elf ? O_RDWR : O_RDONLY))) == -1)
1245 solar 1.164 return 1;
1246 vapier 1.106
1247 vapier 1.161 if (scanelf_elf(filename, fd, st->st_size) == 1 && scan_archives)
1248 vapier 1.105 /* if it isn't an ELF, maybe it's an .a archive */
1249 vapier 1.161 scanelf_archive(filename, fd, st->st_size);
1250 vapier 1.106
1251     close(fd);
1252 solar 1.164 return 0;
1253 solar 1.6 }
1254    
1255 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
1256 solar 1.164 static int scanelf_dir(const char *path)
1257 solar 1.1 {
1258 vapier 1.10 register DIR *dir;
1259     register struct dirent *dentry;
1260 vapier 1.14 struct stat st_top, st;
1261 vapier 1.104 char buf[__PAX_UTILS_PATH_MAX];
1262 vapier 1.32 size_t pathlen = 0, len = 0;
1263 solar 1.164 int ret = 0;
1264 vapier 1.10
1265     /* make sure path exists */
1266 vapier 1.39 if (lstat(path, &st_top) == -1) {
1267     if (be_verbose > 2) printf("%s: does not exist\n", path);
1268 solar 1.164 return 1;
1269 vapier 1.39 }
1270 solar 1.11
1271 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
1272 vapier 1.14 if (!S_ISDIR(st_top.st_mode)) {
1273 solar 1.164 return scanelf_file(path, &st_top);
1274 vapier 1.10 }
1275    
1276     /* now scan the dir looking for fun stuff */
1277     if ((dir = opendir(path)) == NULL) {
1278     warnf("could not opendir %s: %s", path, strerror(errno));
1279 solar 1.164 return 1;
1280 vapier 1.10 }
1281 vapier 1.114 if (be_verbose > 1) printf("%s: scanning dir\n", path);
1282 solar 1.11
1283 vapier 1.32 pathlen = strlen(path);
1284 vapier 1.10 while ((dentry = readdir(dir))) {
1285     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
1286     continue;
1287 vapier 1.32 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
1288     if (len >= sizeof(buf)) {
1289 vapier 1.76 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
1290     (unsigned long)len, (unsigned long)sizeof(buf));
1291 vapier 1.32 continue;
1292     }
1293 solar 1.143 snprintf(buf, sizeof(buf), "%s%s%s", path, (path[pathlen-1] == '/') ? "" : "/", dentry->d_name);
1294 solar 1.20 if (lstat(buf, &st) != -1) {
1295 vapier 1.10 if (S_ISREG(st.st_mode))
1296 solar 1.164 ret = scanelf_file(buf, &st);
1297 vapier 1.10 else if (dir_recurse && S_ISDIR(st.st_mode)) {
1298 vapier 1.14 if (dir_crossmount || (st_top.st_dev == st.st_dev))
1299 solar 1.164 ret = scanelf_dir(buf);
1300 vapier 1.10 }
1301     }
1302     }
1303     closedir(dir);
1304 solar 1.164 return ret;
1305 solar 1.1 }
1306    
1307 vapier 1.133 static int scanelf_from_file(const char *filename)
1308 vapier 1.47 {
1309 solar 1.45 FILE *fp = NULL;
1310     char *p;
1311 vapier 1.104 char path[__PAX_UTILS_PATH_MAX];
1312 solar 1.164 int ret = 0;
1313 solar 1.45
1314 solar 1.132 if (strcmp(filename, "-") == 0)
1315 solar 1.45 fp = stdin;
1316     else if ((fp = fopen(filename, "r")) == NULL)
1317     return 1;
1318    
1319 vapier 1.104 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1320 solar 1.45 if ((p = strchr(path, '\n')) != NULL)
1321     *p = 0;
1322 vapier 1.66 search_path = path;
1323 solar 1.164 ret = scanelf_dir(path);
1324 solar 1.45 }
1325     if (fp != stdin)
1326     fclose(fp);
1327 solar 1.164 return ret;
1328 solar 1.45 }
1329    
1330 solar 1.154 #if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
1331 vapier 1.148
1332     static int load_ld_cache_config(int i, const char *fname)
1333 vapier 1.48 {
1334     FILE *fp = NULL;
1335     char *p;
1336 vapier 1.104 char path[__PAX_UTILS_PATH_MAX];
1337 vapier 1.48
1338 vapier 1.149 if (i + 1 == ARRAY_SIZE(ldpaths))
1339 solar 1.123 return i;
1340    
1341     if ((fp = fopen(fname, "r")) == NULL)
1342     return i;
1343 vapier 1.48
1344 vapier 1.104 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1345 vapier 1.48 if ((p = strrchr(path, '\r')) != NULL)
1346     *p = 0;
1347     if ((p = strchr(path, '\n')) != NULL)
1348     *p = 0;
1349 solar 1.154 #ifdef __linux__
1350 solar 1.123 // recursive includes of the same file will make this segfault.
1351 solar 1.129 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1352 solar 1.123 glob64_t gl;
1353     size_t x;
1354     char gpath[__PAX_UTILS_PATH_MAX];
1355    
1356 solar 1.129 memset(gpath, 0, sizeof(gpath));
1357 solar 1.123
1358     if (path[8] != '/')
1359 solar 1.129 snprintf(gpath, sizeof(gpath), "/etc/%s", &path[8]);
1360 solar 1.123 else
1361 solar 1.129 strncpy(gpath, &path[8], sizeof(gpath));
1362 solar 1.123
1363     if ((glob64(gpath, 0, NULL, &gl)) == 0) {
1364     for (x = 0; x < gl.gl_pathc; ++x) {
1365     /* try to avoid direct loops */
1366     if (strcmp(gl.gl_pathv[x], fname) == 0)
1367     continue;
1368 vapier 1.148 i = load_ld_cache_config(i, gl.gl_pathv[x]);
1369 vapier 1.149 if (i + 1 >= ARRAY_SIZE(ldpaths)) {
1370 solar 1.123 globfree64(&gl);
1371     return i;
1372     }
1373     }
1374     globfree64 (&gl);
1375     continue;
1376 solar 1.160 }
1377 solar 1.123 }
1378 solar 1.154 #endif
1379 solar 1.123 if (*path != '/')
1380     continue;
1381 vapier 1.48
1382     ldpaths[i++] = xstrdup(path);
1383    
1384 vapier 1.149 if (i + 1 == ARRAY_SIZE(ldpaths))
1385 vapier 1.48 break;
1386     }
1387     ldpaths[i] = NULL;
1388    
1389     fclose(fp);
1390 solar 1.123 return i;
1391 vapier 1.48 }
1392 flameeyes 1.141
1393 vapier 1.148 #elif defined(__FreeBSD__) || (__DragonFly__)
1394    
1395     static int load_ld_cache_config(int i, const char *fname)
1396 flameeyes 1.141 {
1397     FILE *fp = NULL;
1398     char *b = NULL, *p;
1399     struct elfhints_hdr hdr;
1400 vapier 1.152
1401 vapier 1.149 if (i + 1 == ARRAY_SIZE(ldpaths))
1402 flameeyes 1.141 return i;
1403    
1404     if ((fp = fopen(fname, "r")) == NULL)
1405     return i;
1406    
1407 vapier 1.152 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
1408     hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
1409     fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
1410     {
1411 flameeyes 1.141 fclose(fp);
1412     return i;
1413     }
1414 vapier 1.152
1415 flameeyes 1.141 b = (char*)malloc(hdr.dirlistlen+1);
1416 vapier 1.152 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1417 flameeyes 1.141 fclose(fp);
1418     free(b);
1419     return i;
1420     }
1421 vapier 1.152
1422     while ((p = strsep(&b, ":"))) {
1423     if (*p == '\0') continue;
1424 flameeyes 1.141 ldpaths[i++] = xstrdup(p);
1425 vapier 1.152
1426 vapier 1.149 if (i + 1 == ARRAY_SIZE(ldpaths))
1427 flameeyes 1.141 break;
1428     }
1429     ldpaths[i] = NULL;
1430 vapier 1.157
1431 flameeyes 1.141 free(b);
1432     fclose(fp);
1433     return i;
1434     }
1435 vapier 1.148
1436     #else
1437    
1438     #warning Cache config support not implemented for your target
1439     static int load_ld_cache_config(int i, const char *fname)
1440     {
1441     memset(ldpaths, 0x00, sizeof(ldpaths));
1442     }
1443    
1444 flameeyes 1.141 #endif
1445 vapier 1.48
1446 vapier 1.10 /* scan /etc/ld.so.conf for paths */
1447     static void scanelf_ldpath()
1448     {
1449 vapier 1.17 char scan_l, scan_ul, scan_ull;
1450 vapier 1.48 int i = 0;
1451 vapier 1.10
1452 vapier 1.48 if (!ldpaths[0])
1453     err("Unable to load any paths from ld.so.conf");
1454 vapier 1.10
1455 vapier 1.17 scan_l = scan_ul = scan_ull = 0;
1456    
1457 vapier 1.48 while (ldpaths[i]) {
1458     if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
1459     if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
1460     if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
1461     scanelf_dir(ldpaths[i]);
1462     ++i;
1463     }
1464 vapier 1.10
1465 vapier 1.17 if (!scan_l) scanelf_dir("/lib");
1466     if (!scan_ul) scanelf_dir("/usr/lib");
1467     if (!scan_ull) scanelf_dir("/usr/local/lib");
1468 vapier 1.10 }
1469 solar 1.1
1470 vapier 1.10 /* scan env PATH for paths */
1471     static void scanelf_envpath()
1472 solar 1.1 {
1473 solar 1.34 char *path, *p;
1474 vapier 1.10
1475     path = getenv("PATH");
1476     if (!path)
1477     err("PATH is not set in your env !");
1478 vapier 1.41 path = xstrdup(path);
1479 vapier 1.10
1480     while ((p = strrchr(path, ':')) != NULL) {
1481     scanelf_dir(p + 1);
1482     *p = 0;
1483     }
1484 vapier 1.17
1485 solar 1.34 free(path);
1486 solar 1.1 }
1487    
1488 vapier 1.10 /* usage / invocation handling functions */
1489 solar 1.127 #define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:BhV"
1490 vapier 1.27 #define a_argument required_argument
1491 vapier 1.10 static struct option const long_opts[] = {
1492     {"path", no_argument, NULL, 'p'},
1493     {"ldpath", no_argument, NULL, 'l'},
1494     {"recursive", no_argument, NULL, 'R'},
1495 vapier 1.14 {"mount", no_argument, NULL, 'm'},
1496 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
1497 vapier 1.105 {"archives", no_argument, NULL, 'A'},
1498     {"ldcache", no_argument, NULL, 'L'},
1499 vapier 1.101 {"fix", no_argument, NULL, 'X'},
1500 solar 1.127 {"setpax", a_argument, NULL, 'z'},
1501 vapier 1.10 {"pax", no_argument, NULL, 'x'},
1502 solar 1.16 {"header", no_argument, NULL, 'e'},
1503 vapier 1.10 {"textrel", no_argument, NULL, 't'},
1504     {"rpath", no_argument, NULL, 'r'},
1505 vapier 1.32 {"needed", no_argument, NULL, 'n'},
1506 vapier 1.38 {"interp", no_argument, NULL, 'i'},
1507 vapier 1.49 {"bind", no_argument, NULL, 'b'},
1508 vapier 1.84 {"soname", no_argument, NULL, 'S'},
1509 vapier 1.76 {"symbol", a_argument, NULL, 's'},
1510 solar 1.124 {"section", a_argument, NULL, 'k'},
1511 vapier 1.76 {"lib", a_argument, NULL, 'N'},
1512 solar 1.86 {"gmatch", no_argument, NULL, 'g'},
1513 vapier 1.76 {"textrels", no_argument, NULL, 'T'},
1514 solar 1.119 {"etype", a_argument, NULL, 'E'},
1515 solar 1.120 {"bits", a_argument, NULL, 'M'},
1516 vapier 1.10 {"all", no_argument, NULL, 'a'},
1517     {"quiet", no_argument, NULL, 'q'},
1518 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
1519 vapier 1.76 {"format", a_argument, NULL, 'F'},
1520     {"from", a_argument, NULL, 'f'},
1521     {"file", a_argument, NULL, 'o'},
1522 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
1523 vapier 1.10 {"help", no_argument, NULL, 'h'},
1524     {"version", no_argument, NULL, 'V'},
1525     {NULL, no_argument, NULL, 0x0}
1526     };
1527 solar 1.57
1528 solar 1.68 static const char *opts_help[] = {
1529 vapier 1.10 "Scan all directories in PATH environment",
1530     "Scan all directories in /etc/ld.so.conf",
1531 vapier 1.14 "Scan directories recursively",
1532 vapier 1.37 "Don't recursively cross mount points",
1533 vapier 1.101 "Don't scan symlinks",
1534 vapier 1.105 "Scan archives (.a files)",
1535     "Utilize ld.so.cache information (use with -r/-n)",
1536 solar 1.127 "Try and 'fix' bad things (use with -r/-e)",
1537     "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
1538 vapier 1.10 "Print PaX markings",
1539 vapier 1.71 "Print GNU_STACK/PT_LOAD markings",
1540 vapier 1.10 "Print TEXTREL information",
1541     "Print RPATH information",
1542 vapier 1.32 "Print NEEDED information",
1543 vapier 1.38 "Print INTERP information",
1544 vapier 1.49 "Print BIND information",
1545 vapier 1.84 "Print SONAME information",
1546 vapier 1.27 "Find a specified symbol",
1547 solar 1.124 "Find a specified section",
1548 vapier 1.72 "Find a specified library",
1549 solar 1.86 "Use strncmp to match libraries. (use with -N)",
1550 vapier 1.76 "Locate cause of TEXTREL",
1551 solar 1.129 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
1552 solar 1.120 "Print only ELF files matching numeric bits",
1553 vapier 1.82 "Print all scanned info (-x -e -t -r -b)\n",
1554 vapier 1.14 "Only output 'bad' things",
1555     "Be verbose (can be specified more than once)",
1556 vapier 1.39 "Use specified format for output",
1557 solar 1.45 "Read input stream from a filename",
1558 vapier 1.24 "Write output stream to a filename",
1559 vapier 1.14 "Don't display the header",
1560 vapier 1.10 "Print this help and exit",
1561     "Print version and exit",
1562     NULL
1563     };
1564    
1565     /* display usage and exit */
1566     static void usage(int status)
1567 solar 1.1 {
1568 vapier 1.44 unsigned long i;
1569 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
1570 vapier 1.105 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
1571 solar 1.35 printf("Options: -[%s]\n", PARSE_FLAGS);
1572 vapier 1.10 for (i = 0; long_opts[i].name; ++i)
1573 vapier 1.27 if (long_opts[i].has_arg == no_argument)
1574 vapier 1.157 printf(" -%c, --%-14s* %s\n", long_opts[i].val,
1575 vapier 1.27 long_opts[i].name, opts_help[i]);
1576     else
1577 solar 1.124 printf(" -%c, --%-7s <arg> * %s\n", long_opts[i].val,
1578 vapier 1.27 long_opts[i].name, opts_help[i]);
1579 solar 1.45
1580     if (status != EXIT_SUCCESS)
1581     exit(status);
1582 vapier 1.157
1583 vapier 1.46 puts("\nThe format modifiers for the -F option are:");
1584 vapier 1.70 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1585     puts(" t TEXTREL \tr RPATH \tn NEEDED");
1586     puts(" i INTERP \tb BIND \ts symbol");
1587 vapier 1.76 puts(" N library \to Type \tT TEXTRELs");
1588 vapier 1.166 puts(" S SONAME \tk section \ta arch");
1589 vapier 1.70 puts(" p filename (with search path removed)");
1590 vapier 1.88 puts(" f filename (short name/basename)");
1591 vapier 1.70 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1592 solar 1.45
1593 solar 1.121 puts("\nELF Etypes:");
1594     print_etypes(stdout);
1595    
1596 vapier 1.10 exit(status);
1597 solar 1.1 }
1598    
1599     /* parse command line arguments and preform needed actions */
1600 vapier 1.165 #define do_pax_state(option, flag) \
1601     if (islower(option)) { \
1602     flags &= ~PF_##flag; \
1603     flags |= PF_NO##flag; \
1604     } else { \
1605     flags &= ~PF_NO##flag; \
1606     flags |= PF_##flag; \
1607     }
1608 solar 1.164 static int parseargs(int argc, char *argv[])
1609 vapier 1.10 {
1610 vapier 1.48 int i;
1611 vapier 1.133 const char *from_file = NULL;
1612 solar 1.164 int ret = 0;
1613 vapier 1.10
1614     opterr = 0;
1615 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
1616     switch (i) {
1617 vapier 1.10
1618 vapier 1.39 case 'V':
1619 vapier 1.80 printf("pax-utils-%s: %s compiled %s\n%s\n"
1620     "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
1621     VERSION, __FILE__, __DATE__, rcsid, argv0);
1622 vapier 1.10 exit(EXIT_SUCCESS);
1623     break;
1624     case 'h': usage(EXIT_SUCCESS); break;
1625 solar 1.45 case 'f':
1626 vapier 1.100 if (from_file) warn("You prob don't want to specify -f twice");
1627     from_file = optarg;
1628 solar 1.45 break;
1629 solar 1.119 case 'E':
1630     strncpy(match_etypes, optarg, sizeof(match_etypes));
1631     break;
1632 solar 1.120 case 'M':
1633     match_bits = atoi(optarg);
1634     break;
1635 vapier 1.24 case 'o': {
1636 vapier 1.146 if (freopen(optarg, "w", stdout) == NULL)
1637 vapier 1.24 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1638 solar 1.21 break;
1639     }
1640 solar 1.124 case 'k':
1641 vapier 1.126 if (find_section) warn("You prob don't want to specify -k twice");
1642 solar 1.124 find_section = optarg;
1643     break;
1644 vapier 1.39 case 's': {
1645 vapier 1.93 if (find_sym) warn("You prob don't want to specify -s twice");
1646     find_sym = optarg;
1647     versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
1648 vapier 1.39 sprintf(versioned_symname, "%s@", find_sym);
1649     break;
1650     }
1651 vapier 1.72 case 'N': {
1652 vapier 1.93 if (find_lib) warn("You prob don't want to specify -N twice");
1653     find_lib = optarg;
1654 vapier 1.72 break;
1655     }
1656 vapier 1.39
1657     case 'F': {
1658 vapier 1.93 if (out_format) warn("You prob don't want to specify -F twice");
1659     out_format = optarg;
1660 vapier 1.39 break;
1661     }
1662 solar 1.127 case 'z': {
1663 solar 1.129 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
1664 solar 1.127 size_t x;
1665 vapier 1.27
1666 solar 1.127 for (x = 0 ; x < strlen(optarg); x++) {
1667     switch(optarg[x]) {
1668     case 'p':
1669     case 'P':
1670 vapier 1.165 do_pax_state(optarg[x], PAGEEXEC);
1671 solar 1.127 break;
1672     case 's':
1673     case 'S':
1674 vapier 1.165 do_pax_state(optarg[x], SEGMEXEC);
1675 solar 1.127 break;
1676     case 'm':
1677     case 'M':
1678 vapier 1.165 do_pax_state(optarg[x], MPROTECT);
1679 solar 1.127 break;
1680     case 'e':
1681     case 'E':
1682 vapier 1.165 do_pax_state(optarg[x], EMUTRAMP);
1683 solar 1.127 break;
1684     case 'r':
1685     case 'R':
1686 vapier 1.165 do_pax_state(optarg[x], RANDMMAP);
1687 solar 1.127 break;
1688     case 'x':
1689     case 'X':
1690 vapier 1.165 do_pax_state(optarg[x], RANDEXEC);
1691 solar 1.127 break;
1692     default:
1693     break;
1694     }
1695     }
1696     if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
1697     ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
1698     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
1699     ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
1700     ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
1701     ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
1702     setpax = flags;
1703     break;
1704     }
1705 vapier 1.122 case 'g': gmatch = 1; break;
1706 vapier 1.102 case 'L': use_ldcache = 1; break;
1707 vapier 1.37 case 'y': scan_symlink = 0; break;
1708 vapier 1.105 case 'A': scan_archives = 1; break;
1709 solar 1.16 case 'B': show_banner = 0; break;
1710 vapier 1.10 case 'l': scan_ldpath = 1; break;
1711     case 'p': scan_envpath = 1; break;
1712     case 'R': dir_recurse = 1; break;
1713 vapier 1.14 case 'm': dir_crossmount = 0; break;
1714 vapier 1.101 case 'X': ++fix_elf; break;
1715 vapier 1.10 case 'x': show_pax = 1; break;
1716 solar 1.73 case 'e': show_phdr = 1; break;
1717 vapier 1.10 case 't': show_textrel = 1; break;
1718     case 'r': show_rpath = 1; break;
1719 vapier 1.32 case 'n': show_needed = 1; break;
1720 vapier 1.38 case 'i': show_interp = 1; break;
1721 vapier 1.49 case 'b': show_bind = 1; break;
1722 vapier 1.84 case 'S': show_soname = 1; break;
1723 vapier 1.76 case 'T': show_textrels = 1; break;
1724 vapier 1.10 case 'q': be_quiet = 1; break;
1725 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1726 vapier 1.82 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
1727 vapier 1.10
1728     case ':':
1729 vapier 1.113 err("Option '%c' is missing parameter", optopt);
1730 vapier 1.10 case '?':
1731 solar 1.119 err("Unknown option '%c' or argument missing", optopt);
1732 vapier 1.10 default:
1733 vapier 1.113 err("Unhandled option '%c'; please report this", i);
1734 vapier 1.10 }
1735     }
1736    
1737 vapier 1.39 /* let the format option override all other options */
1738     if (out_format) {
1739 solar 1.73 show_pax = show_phdr = show_textrel = show_rpath = \
1740 vapier 1.84 show_needed = show_interp = show_bind = show_soname = \
1741     show_textrels = 0;
1742 vapier 1.48 for (i = 0; out_format[i]; ++i) {
1743 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
1744 vapier 1.39
1745 vapier 1.48 switch (out_format[++i]) {
1746 solar 1.132 case '+': break;
1747 vapier 1.39 case '%': break;
1748 vapier 1.70 case '#': break;
1749 vapier 1.39 case 'F': break;
1750 vapier 1.66 case 'p': break;
1751     case 'f': break;
1752 solar 1.124 case 'k': break;
1753 vapier 1.39 case 's': break;
1754 vapier 1.72 case 'N': break;
1755 vapier 1.41 case 'o': break;
1756 vapier 1.166 case 'a': break;
1757 vapier 1.39 case 'x': show_pax = 1; break;
1758 solar 1.73 case 'e': show_phdr = 1; break;
1759 vapier 1.39 case 't': show_textrel = 1; break;
1760     case 'r': show_rpath = 1; break;
1761     case 'n': show_needed = 1; break;
1762     case 'i': show_interp = 1; break;
1763 vapier 1.49 case 'b': show_bind = 1; break;
1764 vapier 1.84 case 'S': show_soname = 1; break;
1765 vapier 1.76 case 'T': show_textrels = 1; break;
1766 vapier 1.39 default:
1767 vapier 1.162 err("Invalid format specifier '%c' (byte %i)",
1768 vapier 1.48 out_format[i], i+1);
1769 vapier 1.39 }
1770     }
1771 vapier 1.41
1772     /* construct our default format */
1773     } else {
1774     size_t fmt_len = 30;
1775     out_format = (char*)xmalloc(sizeof(char) * fmt_len);
1776 vapier 1.76 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1777     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1778     if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1779     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1780     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1781     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1782     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1783     if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1784 vapier 1.84 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1785 vapier 1.76 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1786     if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1787 solar 1.124 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
1788 vapier 1.76 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1789     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1790 vapier 1.39 }
1791 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
1792 vapier 1.39
1793     /* now lets actually do the scanning */
1794 vapier 1.102 if (scan_ldpath || use_ldcache)
1795 vapier 1.148 load_ld_cache_config(0, __PAX_UTILS_DEFAULT_LD_CACHE_CONFIG);
1796 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
1797     if (scan_envpath) scanelf_envpath();
1798 solar 1.153 if (!from_file && optind == argc && ttyname(0) == NULL && !scan_ldpath && !scan_envpath)
1799 vapier 1.133 from_file = "-";
1800 solar 1.45 if (from_file) {
1801     scanelf_from_file(from_file);
1802     from_file = *argv;
1803     }
1804     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
1805 vapier 1.25 err("Nothing to scan !?");
1806 vapier 1.66 while (optind < argc) {
1807     search_path = argv[optind++];
1808 solar 1.164 ret = scanelf_dir(search_path);
1809 vapier 1.66 }
1810 vapier 1.27
1811 vapier 1.39 /* clean up */
1812 vapier 1.152 free(versioned_symname);
1813 vapier 1.48 for (i = 0; ldpaths[i]; ++i)
1814     free(ldpaths[i]);
1815 solar 1.96
1816     if (ldcache != 0)
1817     munmap(ldcache, ldcache_size);
1818 solar 1.164 return ret;
1819 vapier 1.10 }
1820    
1821 vapier 1.150 static char **get_split_env(const char *envvar)
1822     {
1823 vapier 1.152 const char *delims = " \t\n";
1824 solar 1.143 char **envvals = NULL;
1825 vapier 1.152 char *env, *s;
1826 kevquinn 1.142 int nentry;
1827    
1828 solar 1.143 if ((env = getenv(envvar)) == NULL)
1829     return NULL;
1830 kevquinn 1.142
1831 solar 1.143 env = xstrdup(env);
1832     if (env == NULL)
1833     return NULL;
1834 kevquinn 1.142
1835 vapier 1.152 s = strtok(env, delims);
1836     if (s == NULL) {
1837     free(env);
1838     return NULL;
1839     }
1840    
1841 solar 1.143 nentry = 0;
1842 vapier 1.152 while (s != NULL) {
1843     ++nentry;
1844     envvals = xrealloc(envvals, sizeof(*envvals) * (nentry+1));
1845     envvals[nentry-1] = s;
1846     s = strtok(NULL, delims);
1847 kevquinn 1.142 }
1848 vapier 1.152 envvals[nentry] = NULL;
1849 kevquinn 1.142
1850 vapier 1.152 /* don't want to free(env) as it contains the memory that backs
1851     * the envvals array of strings */
1852 kevquinn 1.142 return envvals;
1853     }
1854 vapier 1.150 static void parseenv()
1855     {
1856     qa_textrels = get_split_env("QA_TEXTRELS");
1857     qa_execstack = get_split_env("QA_EXECSTACK");
1858     qa_wx_load = get_split_env("QA_WX_LOAD");
1859 vapier 1.41 }
1860 solar 1.163
1861 vapier 1.152 #ifdef __PAX_UTILS_CLEANUP
1862     static void cleanup()
1863     {
1864     free(out_format);
1865     free(qa_textrels);
1866     free(qa_execstack);
1867     free(qa_wx_load);
1868     }
1869     #endif
1870 vapier 1.41
1871    
1872 vapier 1.10 int main(int argc, char *argv[])
1873 solar 1.1 {
1874 solar 1.164 int ret;
1875 vapier 1.10 if (argc < 2)
1876     usage(EXIT_FAILURE);
1877 kevquinn 1.142 parseenv();
1878 solar 1.164 ret = parseargs(argc, argv);
1879 solar 1.21 fclose(stdout);
1880 vapier 1.152 #ifdef __PAX_UTILS_CLEANUP
1881     cleanup();
1882     warn("The calls to add/delete heap should be off:\n"
1883     "\t- 1 due to the out_buffer not being freed in scanelf_file()\n"
1884     "\t- 1 per QA_TEXTRELS/QA_EXECSTACK/QA_WX_LOAD");
1885 solar 1.61 #endif
1886 solar 1.164 return ret;
1887 solar 1.1 }
1888 vapier 1.155
1889    
1890    
1891     /* utility funcs */
1892     static char *xstrdup(const char *s)
1893     {
1894     char *ret = strdup(s);
1895     if (!ret) err("Could not strdup(): %s", strerror(errno));
1896     return ret;
1897     }
1898     static void *xmalloc(size_t size)
1899     {
1900     void *ret = malloc(size);
1901     if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1902     return ret;
1903     }
1904     static void *xrealloc(void *ptr, size_t size)
1905     {
1906     void *ret = realloc(ptr, size);
1907     if (!ret) err("Could not realloc() %li bytes", (unsigned long)size);
1908     return ret;
1909     }
1910     static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n)
1911     {
1912     size_t new_len;
1913    
1914     new_len = strlen(*dst) + strlen(src);
1915     if (*curr_len <= new_len) {
1916     *curr_len = new_len + (*curr_len / 2);
1917     *dst = realloc(*dst, *curr_len);
1918     if (!*dst)
1919     err("could not realloc() %li bytes", (unsigned long)*curr_len);
1920     }
1921    
1922     if (n)
1923     strncat(*dst, src, n);
1924     else
1925     strcat(*dst, src);
1926     }
1927     static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1928     {
1929     static char my_app[2];
1930     my_app[0] = append;
1931     my_app[1] = '\0';
1932     xstrcat(dst, my_app, curr_len);
1933     }
1934    
1935     /* Match filename against entries in matchlist, return TRUE
1936     * if the file is listed */
1937     static int file_matches_list(const char *filename, char **matchlist)
1938     {
1939     char **file;
1940     char *match;
1941     char buf[__PAX_UTILS_PATH_MAX];
1942    
1943     if (matchlist == NULL)
1944     return 0;
1945    
1946     for (file = matchlist; *file != NULL; file++) {
1947     if (search_path) {
1948     snprintf(buf, sizeof(buf), "%s%s", search_path, *file);
1949     match = buf;
1950     } else {
1951     match = *file;
1952     }
1953     if (fnmatch(match, filename, 0) == 0)
1954     return 1;
1955     }
1956     return 0;
1957     }

  ViewVC Help
Powered by ViewVC 1.1.20