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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.156 - (hide annotations) (download) (as text)
Sun Jun 11 00:23:11 2006 UTC (8 years, 1 month ago) by vapier
Branch: MAIN
Changes since 1.155: +7 -4 lines
File MIME type: text/x-csrc
fix handling of defined/undefined versioned symbols

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

  ViewVC Help
Powered by ViewVC 1.1.20