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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.158 - (hide annotations) (download) (as text)
Fri Aug 25 06:32:08 2006 UTC (8 years ago) by vapier
Branch: MAIN
Changes since 1.157: +2 -0 lines
File MIME type: text/x-csrc
make sure we check QA_EXECSTACKS when .o objects are missing .note.GNU-stack #144986

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

  ViewVC Help
Powered by ViewVC 1.1.20