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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.167 - (hide annotations) (download) (as text)
Mon Jan 8 07:55:11 2007 UTC (7 years, 11 months ago) by vapier
Branch: MAIN
Changes since 1.166: +18 -2 lines
File MIME type: text/x-csrc
add support for running objdump when using -T -v to get automatic host disassemble

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

  ViewVC Help
Powered by ViewVC 1.1.20