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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.163 - (hide annotations) (download) (as text)
Fri Dec 1 15:41:23 2006 UTC (7 years, 9 months ago) by solar
Branch: MAIN
Changes since 1.162: +3 -3 lines
File MIME type: text/x-csrc
- just a little whitespace cleanup

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

  ViewVC Help
Powered by ViewVC 1.1.20