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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.168 - (hide annotations) (download) (as text)
Mon Jan 8 22:57:01 2007 UTC (7 years, 6 months ago) by vapier
Branch: MAIN
Changes since 1.167: +5 -3 lines
File MIME type: text/x-csrc
fix warnings on 32bit host

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

  ViewVC Help
Powered by ViewVC 1.1.20