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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.105 - (hide annotations) (download) (as text)
Fri Jan 13 12:12:52 2006 UTC (9 years, 3 months ago) by vapier
Branch: MAIN
Changes since 1.104: +50 -24 lines
File MIME type: text/x-csrc
initial support for reading archive files (*.a)

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.104 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.103 2006/01/11 23:46:21 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 solar 1.1
12 vapier 1.104 static const char *rcsid = "$Id: scanelf.c,v 1.103 2006/01/11 23:46:21 vapier Exp $";
13 vapier 1.36 #define argv0 "scanelf"
14 vapier 1.10
15 vapier 1.70 #define IS_MODIFIER(c) (c == '%' || c == '#')
16    
17 vapier 1.10
18    
19     /* prototypes */
20 vapier 1.105 static int scanelf_elf(const char *filename);
21     static int scanelf_archive(const char *filename);
22 vapier 1.10 static void scanelf_file(const char *filename);
23     static void scanelf_dir(const char *path);
24     static void scanelf_ldpath();
25     static void scanelf_envpath();
26     static void usage(int status);
27     static void parseargs(int argc, char *argv[]);
28 vapier 1.60 static char *xstrdup(const char *s);
29 vapier 1.41 static void *xmalloc(size_t size);
30 vapier 1.95 static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n);
31     #define xstrcat(dst,src,curr_len) xstrncat(dst,src,curr_len,0)
32 vapier 1.41 static inline void xchrcat(char **dst, const char append, size_t *curr_len);
33 vapier 1.10
34     /* variables to control behavior */
35 vapier 1.48 static char *ldpaths[256];
36 vapier 1.10 static char scan_ldpath = 0;
37     static char scan_envpath = 0;
38 vapier 1.37 static char scan_symlink = 1;
39 vapier 1.105 static char scan_archives = 0;
40 vapier 1.10 static char dir_recurse = 0;
41 vapier 1.14 static char dir_crossmount = 1;
42 vapier 1.10 static char show_pax = 0;
43 solar 1.73 static char show_phdr = 0;
44 vapier 1.10 static char show_textrel = 0;
45     static char show_rpath = 0;
46 vapier 1.32 static char show_needed = 0;
47 vapier 1.38 static char show_interp = 0;
48 vapier 1.49 static char show_bind = 0;
49 vapier 1.84 static char show_soname = 0;
50 vapier 1.76 static char show_textrels = 0;
51 solar 1.16 static char show_banner = 1;
52 vapier 1.10 static char be_quiet = 0;
53 vapier 1.14 static char be_verbose = 0;
54 vapier 1.70 static char be_wewy_wewy_quiet = 0;
55 vapier 1.39 static char *find_sym = NULL, *versioned_symname = NULL;
56 vapier 1.72 static char *find_lib = NULL;
57 vapier 1.39 static char *out_format = NULL;
58 vapier 1.66 static char *search_path = NULL;
59 vapier 1.101 static char fix_elf = 0;
60 solar 1.86 static char gmatch = 0;
61 vapier 1.102 static char use_ldcache = 0;
62 solar 1.1
63 vapier 1.70
64 solar 1.96 caddr_t ldcache = 0;
65     size_t ldcache_size = 0;
66    
67 vapier 1.39 /* sub-funcs for scanelf_file() */
68 vapier 1.77 static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
69     {
70     /* find the best SHT_DYNSYM and SHT_STRTAB sections */
71     #define GET_SYMTABS(B) \
72     if (elf->elf_class == ELFCLASS ## B) { \
73     Elf ## B ## _Shdr *symtab, *strtab, *dynsym, *dynstr; \
74     /* debug sections */ \
75     symtab = SHDR ## B (elf_findsecbyname(elf, ".symtab")); \
76     strtab = SHDR ## B (elf_findsecbyname(elf, ".strtab")); \
77     /* runtime sections */ \
78     dynsym = SHDR ## B (elf_findsecbyname(elf, ".dynsym")); \
79     dynstr = SHDR ## B (elf_findsecbyname(elf, ".dynstr")); \
80     if (symtab && dynsym) { \
81     *sym = (void*)((EGET(symtab->sh_size) > EGET(dynsym->sh_size)) ? symtab : dynsym); \
82     } else { \
83     *sym = (void*)(symtab ? symtab : dynsym); \
84     } \
85     if (strtab && dynstr) { \
86     *tab = (void*)((EGET(strtab->sh_size) > EGET(dynstr->sh_size)) ? strtab : dynstr); \
87     } else { \
88     *tab = (void*)(strtab ? strtab : dynstr); \
89     } \
90     }
91     GET_SYMTABS(32)
92     GET_SYMTABS(64)
93     }
94 vapier 1.41 static char *scanelf_file_pax(elfobj *elf, char *found_pax)
95 solar 1.6 {
96 solar 1.61 static char ret[7];
97     unsigned long i, shown;
98    
99 vapier 1.41 if (!show_pax) return NULL;
100 vapier 1.10
101 solar 1.61 shown = 0;
102     memset(&ret, 0, sizeof(ret));
103    
104     if (elf->phdr) {
105     #define SHOW_PAX(B) \
106     if (elf->elf_class == ELFCLASS ## B) { \
107     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
108     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
109     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
110     if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
111     continue; \
112     if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \
113     continue; \
114     memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
115     *found_pax = 1; \
116     ++shown; \
117     break; \
118     } \
119     }
120     SHOW_PAX(32)
121     SHOW_PAX(64)
122     }
123    
124     /* fall back to EI_PAX if no PT_PAX was found */
125     if (!*ret) {
126 vapier 1.90 static char *paxflags;
127 solar 1.61 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
128     if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
129     *found_pax = 1;
130 vapier 1.90 return (be_wewy_wewy_quiet ? NULL : paxflags);
131 solar 1.61 }
132     strncpy(ret, paxflags, sizeof(ret));
133 vapier 1.14 }
134 vapier 1.41
135 vapier 1.90 if (be_wewy_wewy_quiet || (be_quiet && !shown))
136 solar 1.61 return NULL;
137 vapier 1.90 else
138     return ret;
139     }
140 solar 1.61
141 solar 1.73 static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
142 vapier 1.39 {
143 vapier 1.71 static char ret[12];
144 vapier 1.41 char *found;
145 vapier 1.99 unsigned long i, shown, multi_stack, multi_relro, multi_load;
146     int max_pt_load;
147 vapier 1.41
148 solar 1.73 if (!show_phdr) return NULL;
149 vapier 1.41
150 vapier 1.71 memcpy(ret, "--- --- ---\0", 12);
151    
152 vapier 1.41 shown = 0;
153 vapier 1.71 multi_stack = multi_relro = multi_load = 0;
154 vapier 1.99 max_pt_load = elf_max_pt_load(elf);
155 vapier 1.44
156 solar 1.73 #define SHOW_PHDR(B) \
157 vapier 1.39 if (elf->elf_class == ELFCLASS ## B) { \
158     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
159 vapier 1.91 Elf ## B ## _Off offset; \
160     uint32_t flags, check_flags; \
161     if (elf->phdr != NULL) { \
162     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
163     for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \
164     if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
165     if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
166     found = found_phdr; \
167     offset = 0; \
168     check_flags = PF_X; \
169     } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
170     if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
171     found = found_relro; \
172     offset = 4; \
173     check_flags = PF_X; \
174     } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
175 vapier 1.99 if (multi_load++ > max_pt_load) warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
176 vapier 1.91 found = found_load; \
177     offset = 8; \
178     check_flags = PF_W|PF_X; \
179     } else \
180     continue; \
181     flags = EGET(phdr[i].p_flags); \
182     if (be_quiet && ((flags & check_flags) != check_flags)) \
183     continue; \
184 vapier 1.101 if (fix_elf && ((flags & PF_X) != flags)) { \
185     ESET(phdr[i].p_flags, flags & (PF_X ^ (size_t)-1)); \
186     ret[3] = ret[7] = '!'; \
187     flags = EGET(phdr[i].p_flags); \
188     } \
189 vapier 1.91 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \
190     *found = 1; \
191     ++shown; \
192     } \
193     } else if (elf->shdr != NULL) { \
194     /* no program headers which means this is prob an object file */ \
195     Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
196     Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \
197     check_flags = SHF_WRITE|SHF_EXECINSTR; \
198     for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \
199     if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \
200     offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \
201     if (!strcmp((char*)(elf->data + offset), ".note.GNU-stack")) { \
202     if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \
203     flags = EGET(shdr[i].sh_flags); \
204     if (be_quiet && ((flags & check_flags) != check_flags)) \
205     continue; \
206     ++*found_phdr; \
207     shown = 1; \
208     if (flags & SHF_WRITE) ret[0] = 'W'; \
209     if (flags & SHF_ALLOC) ret[1] = 'A'; \
210     if (flags & SHF_EXECINSTR) ret[2] = 'X'; \
211     if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \
212     break; \
213     } \
214     } \
215     if (!multi_stack) { \
216     *found_phdr = 1; \
217     shown = 1; \
218     memcpy(ret, "!WX", 3); \
219     } \
220 vapier 1.39 } \
221     }
222 solar 1.73 SHOW_PHDR(32)
223     SHOW_PHDR(64)
224 vapier 1.44
225 vapier 1.90 if (be_wewy_wewy_quiet || (be_quiet && !shown))
226 vapier 1.41 return NULL;
227     else
228     return ret;
229 vapier 1.39 }
230 vapier 1.90 static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
231 vapier 1.39 {
232 vapier 1.90 static const char *ret = "TEXTREL";
233 vapier 1.44 unsigned long i;
234 vapier 1.41
235 vapier 1.79 if (!show_textrel && !show_textrels) return NULL;
236 vapier 1.41
237 vapier 1.44 if (elf->phdr) {
238 vapier 1.39 #define SHOW_TEXTREL(B) \
239     if (elf->elf_class == ELFCLASS ## B) { \
240     Elf ## B ## _Dyn *dyn; \
241     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
242     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
243 vapier 1.44 Elf ## B ## _Off offset; \
244 vapier 1.39 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
245 vapier 1.59 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
246 vapier 1.44 offset = EGET(phdr[i].p_offset); \
247     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
248     dyn = DYN ## B (elf->data + offset); \
249 vapier 1.39 while (EGET(dyn->d_tag) != DT_NULL) { \
250     if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
251     *found_textrel = 1; \
252     /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
253 vapier 1.70 return (be_wewy_wewy_quiet ? NULL : ret); \
254 vapier 1.39 } \
255     ++dyn; \
256 vapier 1.26 } \
257 vapier 1.39 } }
258     SHOW_TEXTREL(32)
259     SHOW_TEXTREL(64)
260 vapier 1.44 }
261    
262 vapier 1.70 if (be_quiet || be_wewy_wewy_quiet)
263 vapier 1.41 return NULL;
264     else
265 vapier 1.90 return " - ";
266 vapier 1.39 }
267 vapier 1.79 static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
268 vapier 1.76 {
269 vapier 1.82 unsigned long s, r, rmax;
270     void *symtab_void, *strtab_void, *text_void;
271 vapier 1.76
272     if (!show_textrels) return NULL;
273    
274 vapier 1.79 /* don't search for TEXTREL's if the ELF doesn't have any */
275     if (!*found_textrel) scanelf_file_textrel(elf, found_textrel);
276     if (!*found_textrel) return NULL;
277    
278 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
279 vapier 1.82 text_void = elf_findsecbyname(elf, ".text");
280 vapier 1.76
281 vapier 1.82 if (symtab_void && strtab_void && text_void && elf->shdr) {
282 vapier 1.76 #define SHOW_TEXTRELS(B) \
283     if (elf->elf_class == ELFCLASS ## B) { \
284     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
285     Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
286     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
287     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
288 vapier 1.82 Elf ## B ## _Shdr *text = SHDR ## B (text_void); \
289     Elf ## B ## _Addr vaddr = EGET(text->sh_addr); \
290     uint ## B ## _t memsz = EGET(text->sh_size); \
291 vapier 1.76 Elf ## B ## _Rel *rel; \
292     Elf ## B ## _Rela *rela; \
293     /* search the section headers for relocations */ \
294     for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
295     uint32_t sh_type = EGET(shdr[s].sh_type); \
296     if (sh_type == SHT_REL) { \
297     rel = REL ## B (elf->data + EGET(shdr[s].sh_offset)); \
298     rela = NULL; \
299     rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
300     } else if (sh_type == SHT_RELA) { \
301     rel = NULL; \
302     rela = RELA ## B (elf->data + EGET(shdr[s].sh_offset)); \
303     rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
304     } else \
305     continue; \
306 vapier 1.82 /* now see if any of the relocs are in the .text */ \
307     for (r = 0; r < rmax; ++r) { \
308     unsigned long sym_max; \
309     Elf ## B ## _Addr offset_tmp; \
310     Elf ## B ## _Sym *func; \
311     Elf ## B ## _Sym *sym; \
312     Elf ## B ## _Addr r_offset; \
313     uint ## B ## _t r_info; \
314     if (sh_type == SHT_REL) { \
315     r_offset = EGET(rel[r].r_offset); \
316     r_info = EGET(rel[r].r_info); \
317     } else { \
318     r_offset = EGET(rela[r].r_offset); \
319     r_info = EGET(rela[r].r_info); \
320     } \
321     /* make sure this relocation is inside of the .text */ \
322     if (r_offset < vaddr || r_offset >= vaddr + memsz) { \
323     if (be_verbose <= 2) continue; \
324     } else \
325 vapier 1.78 *found_textrels = 1; \
326 vapier 1.82 /* locate this relocation symbol name */ \
327     sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
328     sym_max = ELF ## B ## _R_SYM(r_info); \
329     if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
330     sym += sym_max; \
331     else \
332     sym = NULL; \
333     sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
334     /* show the raw details about this reloc */ \
335 vapier 1.88 printf(" %s: ", elf->base_filename); \
336 vapier 1.82 if (sym && sym->st_name) \
337     printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name))); \
338     else \
339 vapier 1.88 printf("(memory/fake?)"); \
340 vapier 1.82 printf(" [0x%lX]", (unsigned long)r_offset); \
341     /* now try to find the closest symbol that this rel is probably in */ \
342     sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
343     func = NULL; \
344     offset_tmp = 0; \
345     while (sym_max--) { \
346     if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
347     func = sym; \
348     offset_tmp = EGET(sym->st_value); \
349 vapier 1.76 } \
350 vapier 1.82 ++sym; \
351 vapier 1.76 } \
352 vapier 1.82 printf(" in "); \
353     if (func && func->st_name) \
354     printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(func->st_name))); \
355     else \
356     printf("(NULL: fake?)"); \
357     printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
358 vapier 1.76 } \
359     } }
360     SHOW_TEXTRELS(32)
361     SHOW_TEXTRELS(64)
362     }
363 vapier 1.82 if (!*found_textrels)
364     warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
365 vapier 1.76
366     return NULL;
367     }
368 solar 1.83
369 vapier 1.102 static void rpath_security_checks(elfobj *, char *, const char *);
370     static void rpath_security_checks(elfobj *elf, char *item, const char *dt_type)
371 vapier 1.100 {
372 solar 1.83 struct stat st;
373 vapier 1.84 switch (*item) {
374 vapier 1.89 case '/': break;
375     case '.':
376 vapier 1.102 warnf("Security problem with relative %s '%s' in %s", dt_type, item, elf->filename);
377 vapier 1.89 break;
378 vapier 1.100 case ':':
379 vapier 1.89 case '\0':
380 vapier 1.102 warnf("Security problem NULL %s in %s", dt_type, elf->filename);
381 solar 1.83 break;
382     case '$':
383 vapier 1.84 if (fstat(elf->fd, &st) != -1)
384 solar 1.83 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
385 vapier 1.102 warnf("Security problem with %s='%s' in %s with mode set of %o",
386     dt_type, item, elf->filename, st.st_mode & 07777);
387 solar 1.83 break;
388     default:
389 vapier 1.102 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename);
390 solar 1.83 break;
391     }
392     }
393 vapier 1.41 static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
394 vapier 1.39 {
395 vapier 1.48 unsigned long i, s;
396     char *rpath, *runpath, **r;
397 vapier 1.39 void *strtbl_void;
398 vapier 1.10
399 vapier 1.39 if (!show_rpath) return;
400 vapier 1.10
401 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
402     rpath = runpath = NULL;
403 vapier 1.10
404 vapier 1.44 if (elf->phdr && strtbl_void) {
405 vapier 1.26 #define SHOW_RPATH(B) \
406     if (elf->elf_class == ELFCLASS ## B) { \
407     Elf ## B ## _Dyn *dyn; \
408     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
409     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
410     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
411 vapier 1.44 Elf ## B ## _Off offset; \
412 vapier 1.60 Elf ## B ## _Xword word; \
413 vapier 1.48 /* Scan all the program headers */ \
414 vapier 1.26 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
415 vapier 1.48 /* Just scan dynamic headers */ \
416 vapier 1.26 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
417 vapier 1.44 offset = EGET(phdr[i].p_offset); \
418     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
419 vapier 1.48 /* Just scan dynamic RPATH/RUNPATH headers */ \
420 vapier 1.44 dyn = DYN ## B (elf->data + offset); \
421 vapier 1.48 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
422     if (word == DT_RPATH) { \
423     r = &rpath; \
424     } else if (word == DT_RUNPATH) { \
425     r = &runpath; \
426     } else { \
427     ++dyn; \
428     continue; \
429     } \
430     /* Verify the memory is somewhat sane */ \
431     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
432 vapier 1.69 if (offset < (Elf ## B ## _Off)elf->len) { \
433 vapier 1.48 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
434     *r = (char*)(elf->data + offset); \
435 vapier 1.103 /* cache the length in case we need to nuke this section later on */ \
436     if (fix_elf) \
437     offset = strlen(*r); \
438 vapier 1.48 /* If quiet, don't output paths in ld.so.conf */ \
439 vapier 1.69 if (be_quiet) { \
440     size_t len; \
441     char *start, *end; \
442 vapier 1.75 /* note that we only 'chop' off leading known paths. */ \
443     /* since *r is read-only memory, we can only move the ptr forward. */ \
444     start = *r; \
445     /* scan each path in : delimited list */ \
446     while (start) { \
447 vapier 1.102 rpath_security_checks(elf, start, get_elfdtype(word)); \
448 vapier 1.69 end = strchr(start, ':'); \
449 vapier 1.75 len = (end ? abs(end - start) : strlen(start)); \
450 vapier 1.102 if (use_ldcache) \
451     for (s = 0; ldpaths[s]; ++s) \
452     if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
453     *r = end; \
454     /* corner case ... if RPATH reads "/usr/lib:", we want \
455     * to show ':' rather than '' */ \
456     if (end && end[1] != '\0') \
457     (*r)++; \
458     break; \
459     } \
460 vapier 1.100 if (!*r || !end) \
461     break; \
462 vapier 1.75 else \
463     start = start + len + 1; \
464 vapier 1.69 } \
465     } \
466 vapier 1.101 if (*r) { \
467 vapier 1.102 if (fix_elf > 2 || **r == '\0') { \
468 vapier 1.101 /* just nuke it */ \
469     nuke_it##B: \
470 vapier 1.103 memset(*r, 0x00, offset); \
471 vapier 1.102 *r = NULL; \
472 vapier 1.101 ESET(dyn->d_tag, DT_DEBUG); \
473 vapier 1.103 ESET(dyn->d_un.d_ptr, 0); \
474 vapier 1.101 } else if (fix_elf) { \
475     /* try to clean "bad" paths */ \
476     size_t len, tmpdir_len; \
477     char *start, *end; \
478     const char *tmpdir; \
479     start = *r; \
480     tmpdir = (getenv("TMPDIR") ? : "."); \
481     tmpdir_len = strlen(tmpdir); \
482     while (1) { \
483     end = strchr(start, ':'); \
484     if (start == end) { \
485     eat_this_path##B: \
486     len = strlen(end); \
487     memmove(start, end+1, len); \
488     start[len-1] = '\0'; \
489     end = start - 1; \
490     } else if (tmpdir && !strncmp(start, tmpdir, tmpdir_len)) { \
491     if (!end) { \
492     if (start == *r) \
493     goto nuke_it##B; \
494     *--start = '\0'; \
495     } else \
496     goto eat_this_path##B; \
497     } \
498     if (!end) \
499     break; \
500     start = end + 1; \
501     } \
502 vapier 1.102 if (**r == '\0') \
503     goto nuke_it##B; \
504 vapier 1.101 } \
505 vapier 1.102 if (*r) \
506     *found_rpath = 1; \
507 vapier 1.101 } \
508 vapier 1.26 } \
509     ++dyn; \
510     } \
511     } }
512     SHOW_RPATH(32)
513     SHOW_RPATH(64)
514 vapier 1.10 }
515 vapier 1.41
516 vapier 1.70 if (be_wewy_wewy_quiet) return;
517    
518 vapier 1.39 if (rpath && runpath) {
519 vapier 1.41 if (!strcmp(rpath, runpath)) {
520     xstrcat(ret, runpath, ret_len);
521     } else {
522 vapier 1.39 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
523 vapier 1.41 xchrcat(ret, '{', ret_len);
524     xstrcat(ret, rpath, ret_len);
525     xchrcat(ret, ',', ret_len);
526     xstrcat(ret, runpath, ret_len);
527     xchrcat(ret, '}', ret_len);
528 vapier 1.39 }
529     } else if (rpath || runpath)
530 vapier 1.41 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
531     else if (!be_quiet)
532     xstrcat(ret, " - ", ret_len);
533 vapier 1.39 }
534 solar 1.96
535     #define LDSO_CACHE_MAGIC "ld.so-"
536     #define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
537     #define LDSO_CACHE_VER "1.7.0"
538     #define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
539 vapier 1.97 #define FLAG_ANY -1
540     #define FLAG_TYPE_MASK 0x00ff
541     #define FLAG_LIBC4 0x0000
542     #define FLAG_ELF 0x0001
543     #define FLAG_ELF_LIBC5 0x0002
544     #define FLAG_ELF_LIBC6 0x0003
545     #define FLAG_REQUIRED_MASK 0xff00
546     #define FLAG_SPARC_LIB64 0x0100
547     #define FLAG_IA64_LIB64 0x0200
548     #define FLAG_X8664_LIB64 0x0300
549     #define FLAG_S390_LIB64 0x0400
550     #define FLAG_POWERPC_LIB64 0x0500
551     #define FLAG_MIPS64_LIBN32 0x0600
552     #define FLAG_MIPS64_LIBN64 0x0700
553 solar 1.96
554 vapier 1.97 static char *lookup_cache_lib(elfobj *, char *);
555     static char *lookup_cache_lib(elfobj *elf, char *fname)
556 solar 1.96 {
557     int fd = 0;
558     char *strs;
559 vapier 1.104 static char buf[__PAX_UTILS_PATH_MAX] = "";
560 solar 1.96 const char *cachefile = "/etc/ld.so.cache";
561     struct stat st;
562    
563     typedef struct {
564     char magic[LDSO_CACHE_MAGIC_LEN];
565     char version[LDSO_CACHE_VER_LEN];
566     int nlibs;
567     } header_t;
568 vapier 1.97 header_t *header;
569 solar 1.96
570     typedef struct {
571     int flags;
572     int sooffset;
573     int liboffset;
574     } libentry_t;
575     libentry_t *libent;
576    
577     if (fname == NULL)
578     return NULL;
579    
580     if (ldcache == 0) {
581 vapier 1.97 if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY)) == -1)
582 solar 1.96 return NULL;
583 vapier 1.97
584     /* cache these values so we only map/unmap the cache file once */
585 solar 1.96 ldcache_size = st.st_size;
586 vapier 1.97 ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
587    
588     close(fd);
589 solar 1.96
590 vapier 1.97 if (ldcache == (caddr_t)-1)
591 solar 1.96 return NULL;
592    
593     if (memcmp(((header_t *) ldcache)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN))
594     return NULL;
595     if (memcmp (((header_t *) ldcache)->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
596     return NULL;
597     }
598    
599     header = (header_t *) ldcache;
600     libent = (libentry_t *) (ldcache + sizeof(header_t));
601     strs = (char *) &libent[header->nlibs];
602    
603     for (fd = 0; fd < header->nlibs; fd++) {
604 vapier 1.97 /* this should be more fine grained, but for now we assume that
605     * diff arches will not be cached together. and we ignore the
606     * the different multilib mips cases. */
607     if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
608     continue;
609     if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
610     continue;
611    
612 solar 1.96 if (strcmp(fname, strs + libent[fd].sooffset) != 0)
613     continue;
614     strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
615     }
616     return buf;
617     }
618    
619    
620 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)
621 vapier 1.39 {
622 vapier 1.44 unsigned long i;
623 vapier 1.39 char *needed;
624     void *strtbl_void;
625 solar 1.96 char *p;
626 vapier 1.39
627 vapier 1.72 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL;
628 vapier 1.10
629 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
630 vapier 1.32
631 vapier 1.44 if (elf->phdr && strtbl_void) {
632 vapier 1.32 #define SHOW_NEEDED(B) \
633     if (elf->elf_class == ELFCLASS ## B) { \
634     Elf ## B ## _Dyn *dyn; \
635     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
636     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
637     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
638 vapier 1.44 Elf ## B ## _Off offset; \
639 vapier 1.32 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
640     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
641 vapier 1.44 offset = EGET(phdr[i].p_offset); \
642     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
643     dyn = DYN ## B (elf->data + offset); \
644 vapier 1.32 while (EGET(dyn->d_tag) != DT_NULL) { \
645     if (EGET(dyn->d_tag) == DT_NEEDED) { \
646 vapier 1.44 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
647 vapier 1.69 if (offset >= (Elf ## B ## _Off)elf->len) { \
648 vapier 1.49 ++dyn; \
649     continue; \
650     } \
651 vapier 1.44 needed = (char*)(elf->data + offset); \
652 vapier 1.72 if (op == 0) { \
653     if (!be_wewy_wewy_quiet) { \
654     if (*found_needed) xchrcat(ret, ',', ret_len); \
655 vapier 1.102 if (use_ldcache) \
656 vapier 1.97 if ((p = lookup_cache_lib(elf, needed)) != NULL) \
657 solar 1.96 needed = p; \
658 vapier 1.72 xstrcat(ret, needed, ret_len); \
659     } \
660     *found_needed = 1; \
661     } else { \
662 solar 1.86 if (!strncmp(find_lib, needed, strlen( !gmatch ? needed : find_lib))) { \
663 vapier 1.81 *found_lib = 1; \
664 solar 1.86 return (be_wewy_wewy_quiet ? NULL : needed); \
665 vapier 1.81 } \
666 vapier 1.72 } \
667 vapier 1.32 } \
668     ++dyn; \
669     } \
670     } }
671     SHOW_NEEDED(32)
672     SHOW_NEEDED(64)
673 vapier 1.85 if (op == 0 && !*found_needed && be_verbose)
674 vapier 1.84 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
675 vapier 1.32 }
676 vapier 1.72
677     return NULL;
678 vapier 1.39 }
679 vapier 1.41 static char *scanelf_file_interp(elfobj *elf, char *found_interp)
680 vapier 1.39 {
681     void *strtbl_void;
682    
683 vapier 1.41 if (!show_interp) return NULL;
684 vapier 1.32
685 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".interp");
686 vapier 1.38
687 vapier 1.39 if (strtbl_void) {
688 vapier 1.38 #define SHOW_INTERP(B) \
689     if (elf->elf_class == ELFCLASS ## B) { \
690 solar 1.40 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
691     *found_interp = 1; \
692 vapier 1.70 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
693 vapier 1.38 }
694     SHOW_INTERP(32)
695     SHOW_INTERP(64)
696     }
697 vapier 1.41 return NULL;
698 vapier 1.39 }
699 vapier 1.49 static char *scanelf_file_bind(elfobj *elf, char *found_bind)
700     {
701     unsigned long i;
702     struct stat s;
703    
704     if (!show_bind) return NULL;
705 vapier 1.51 if (!elf->phdr) return NULL;
706 vapier 1.49
707     #define SHOW_BIND(B) \
708     if (elf->elf_class == ELFCLASS ## B) { \
709     Elf ## B ## _Dyn *dyn; \
710     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
711     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
712     Elf ## B ## _Off offset; \
713     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
714     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
715     offset = EGET(phdr[i].p_offset); \
716     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
717     dyn = DYN ## B (elf->data + offset); \
718     while (EGET(dyn->d_tag) != DT_NULL) { \
719     if (EGET(dyn->d_tag) == DT_BIND_NOW || \
720     (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
721     { \
722     if (be_quiet) return NULL; \
723     *found_bind = 1; \
724 vapier 1.70 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
725 vapier 1.49 } \
726     ++dyn; \
727     } \
728     } \
729     }
730     SHOW_BIND(32)
731     SHOW_BIND(64)
732    
733 vapier 1.70 if (be_wewy_wewy_quiet) return NULL;
734    
735 vapier 1.56 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
736 vapier 1.49 return NULL;
737     } else {
738     *found_bind = 1;
739 solar 1.68 return (char *) "LAZY";
740 vapier 1.49 }
741     }
742 vapier 1.84 static char *scanelf_file_soname(elfobj *elf, char *found_soname)
743     {
744     unsigned long i;
745     char *soname;
746     void *strtbl_void;
747    
748     if (!show_soname) return NULL;
749    
750     strtbl_void = elf_findsecbyname(elf, ".dynstr");
751    
752     if (elf->phdr && strtbl_void) {
753     #define SHOW_SONAME(B) \
754     if (elf->elf_class == ELFCLASS ## B) { \
755     Elf ## B ## _Dyn *dyn; \
756     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
757     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
758     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
759     Elf ## B ## _Off offset; \
760     /* only look for soname in shared objects */ \
761     if (ehdr->e_type != ET_DYN) \
762     return NULL; \
763     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
764     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
765     offset = EGET(phdr[i].p_offset); \
766     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
767     dyn = DYN ## B (elf->data + offset); \
768     while (EGET(dyn->d_tag) != DT_NULL) { \
769     if (EGET(dyn->d_tag) == DT_SONAME) { \
770     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
771     if (offset >= (Elf ## B ## _Off)elf->len) { \
772     ++dyn; \
773     continue; \
774     } \
775     soname = (char*)(elf->data + offset); \
776     *found_soname = 1; \
777     return (be_wewy_wewy_quiet ? NULL : soname); \
778     } \
779     ++dyn; \
780     } \
781     } }
782     SHOW_SONAME(32)
783     SHOW_SONAME(64)
784     }
785    
786     return NULL;
787     }
788 vapier 1.72 static char *scanelf_file_sym(elfobj *elf, char *found_sym)
789 vapier 1.39 {
790 vapier 1.44 unsigned long i;
791 vapier 1.95 char *ret;
792 vapier 1.39 void *symtab_void, *strtab_void;
793 vapier 1.38
794 vapier 1.41 if (!find_sym) return NULL;
795 vapier 1.95 ret = find_sym;
796 vapier 1.32
797 vapier 1.77 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
798 vapier 1.27
799 vapier 1.39 if (symtab_void && strtab_void) {
800 vapier 1.27 #define FIND_SYM(B) \
801     if (elf->elf_class == ELFCLASS ## B) { \
802     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
803     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
804     Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
805 vapier 1.44 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
806 vapier 1.27 char *symname; \
807     for (i = 0; i < cnt; ++i) { \
808     if (sym->st_name) { \
809     symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
810 solar 1.28 if (*find_sym == '*') { \
811 vapier 1.32 printf("%s(%s) %5lX %15s %s\n", \
812 vapier 1.39 ((*found_sym == 0) ? "\n\t" : "\t"), \
813 vapier 1.76 elf->base_filename, \
814 vapier 1.94 (unsigned long)sym->st_size, \
815 vapier 1.95 get_elfstttype(sym->st_info), \
816 vapier 1.32 symname); \
817 vapier 1.39 *found_sym = 1; \
818 vapier 1.95 } else { \
819     char *this_sym, *next_sym; \
820     this_sym = find_sym; \
821     do { \
822     next_sym = strchr(this_sym, ','); \
823     if (next_sym == NULL) \
824     next_sym = this_sym + strlen(this_sym); \
825     if ((strncmp(this_sym, symname, (next_sym-this_sym)) == 0 && symname[next_sym-this_sym] == '\0') || \
826     (strcmp(symname, versioned_symname) == 0)) { \
827     ret = this_sym; \
828     (*found_sym)++; \
829     goto break_out; \
830     } \
831     this_sym = next_sym + 1; \
832     } while (*next_sym != '\0'); \
833     } \
834 vapier 1.27 } \
835     ++sym; \
836     } }
837     FIND_SYM(32)
838     FIND_SYM(64)
839 vapier 1.39 }
840 vapier 1.70
841 vapier 1.95 break_out:
842 vapier 1.70 if (be_wewy_wewy_quiet) return NULL;
843    
844 vapier 1.41 if (*find_sym != '*' && *found_sym)
845 vapier 1.95 return ret;
846 vapier 1.41 if (be_quiet)
847     return NULL;
848     else
849 solar 1.68 return (char *)" - ";
850 vapier 1.39 }
851     /* scan an elf file and show all the fun stuff */
852 solar 1.57 #define prints(str) write(fileno(stdout), str, strlen(str))
853 vapier 1.105 static int scanelf_elf(const char *filename)
854 vapier 1.39 {
855 vapier 1.44 unsigned long i;
856 solar 1.73 char found_pax, found_phdr, found_relro, found_load, found_textrel,
857 vapier 1.84 found_rpath, found_needed, found_interp, found_bind, found_soname,
858 vapier 1.76 found_sym, found_lib, found_file, found_textrels;
859 vapier 1.39 elfobj *elf;
860 vapier 1.41 static char *out_buffer = NULL;
861     static size_t out_len;
862 vapier 1.39
863 vapier 1.76 found_pax = found_phdr = found_relro = found_load = found_textrel = \
864 vapier 1.84 found_rpath = found_needed = found_interp = found_bind = found_soname = \
865 vapier 1.76 found_sym = found_lib = found_file = found_textrels = 0;
866 vapier 1.39
867     /* verify this is real ELF */
868 vapier 1.101 if ((elf = _readelf(filename, !fix_elf)) == NULL) {
869 vapier 1.39 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
870 vapier 1.105 return 1;
871 vapier 1.39 }
872    
873     if (be_verbose > 1)
874 vapier 1.41 printf("%s: scanning file {%s,%s}\n", filename,
875 vapier 1.69 get_elfeitype(EI_CLASS, elf->elf_class),
876     get_elfeitype(EI_DATA, elf->data[EI_DATA]));
877 vapier 1.39 else if (be_verbose)
878     printf("%s: scanning file\n", filename);
879    
880 vapier 1.41 /* init output buffer */
881     if (!out_buffer) {
882     out_len = sizeof(char) * 80;
883     out_buffer = (char*)xmalloc(out_len);
884     }
885     *out_buffer = '\0';
886    
887 vapier 1.39 /* show the header */
888     if (!be_quiet && show_banner) {
889 vapier 1.49 for (i = 0; out_format[i]; ++i) {
890 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
891 vapier 1.41
892     switch (out_format[++i]) {
893     case '%': break;
894 vapier 1.70 case '#': break;
895 vapier 1.66 case 'F':
896     case 'p':
897     case 'f': prints("FILE "); found_file = 1; break;
898 vapier 1.41 case 'o': prints(" TYPE "); break;
899     case 'x': prints(" PAX "); break;
900 vapier 1.71 case 'e': prints("STK/REL/PTL "); break;
901 vapier 1.41 case 't': prints("TEXTREL "); break;
902     case 'r': prints("RPATH "); break;
903     case 'n': prints("NEEDED "); break;
904     case 'i': prints("INTERP "); break;
905 vapier 1.49 case 'b': prints("BIND "); break;
906 vapier 1.84 case 'S': prints("SONAME "); break;
907 vapier 1.41 case 's': prints("SYM "); break;
908 vapier 1.72 case 'N': prints("LIB "); break;
909 vapier 1.76 case 'T': prints("TEXTRELS "); break;
910     default: warnf("'%c' has no title ?", out_format[i]);
911 vapier 1.39 }
912 vapier 1.27 }
913 vapier 1.49 if (!found_file) prints("FILE ");
914 vapier 1.41 prints("\n");
915 vapier 1.49 found_file = 0;
916 vapier 1.39 show_banner = 0;
917     }
918    
919     /* dump all the good stuff */
920 vapier 1.49 for (i = 0; out_format[i]; ++i) {
921 vapier 1.41 const char *out;
922 vapier 1.66 const char *tmp;
923 vapier 1.41
924     /* make sure we trim leading spaces in quiet mode */
925     if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
926     *out_buffer = '\0';
927 vapier 1.39
928 vapier 1.70 if (!IS_MODIFIER(out_format[i])) {
929 vapier 1.41 xchrcat(&out_buffer, out_format[i], &out_len);
930     continue;
931     }
932 vapier 1.39
933 vapier 1.41 out = NULL;
934 vapier 1.70 be_wewy_wewy_quiet = (out_format[i] == '#');
935 vapier 1.41 switch (out_format[++i]) {
936 vapier 1.70 case '%':
937     case '#':
938     xchrcat(&out_buffer, out_format[i], &out_len); break;
939     case 'F':
940 vapier 1.76 found_file = 1;
941 vapier 1.70 if (be_wewy_wewy_quiet) break;
942     xstrcat(&out_buffer, filename, &out_len);
943     break;
944 vapier 1.66 case 'p':
945 vapier 1.76 found_file = 1;
946 vapier 1.70 if (be_wewy_wewy_quiet) break;
947 vapier 1.66 tmp = filename;
948     if (search_path) {
949     ssize_t len_search = strlen(search_path);
950     ssize_t len_file = strlen(filename);
951     if (!strncmp(filename, search_path, len_search) && \
952     len_file > len_search)
953     tmp += len_search;
954     if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
955     }
956     xstrcat(&out_buffer, tmp, &out_len);
957     break;
958     case 'f':
959 vapier 1.76 found_file = 1;
960 vapier 1.70 if (be_wewy_wewy_quiet) break;
961 vapier 1.66 tmp = strrchr(filename, '/');
962     tmp = (tmp == NULL ? filename : tmp+1);
963     xstrcat(&out_buffer, tmp, &out_len);
964     break;
965 vapier 1.41 case 'o': out = get_elfetype(elf); break;
966     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
967 solar 1.73 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
968 vapier 1.41 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
969 vapier 1.79 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
970 vapier 1.41 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
971 vapier 1.72 case 'n':
972     case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
973 vapier 1.41 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
974 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
975 vapier 1.84 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
976 vapier 1.72 case 's': out = scanelf_file_sym(elf, &found_sym); break;
977 vapier 1.76 default: warnf("'%c' has no scan code?", out_format[i]);
978 vapier 1.29 }
979 vapier 1.95 if (out) {
980     /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */
981     if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL)
982     xstrncat(&out_buffer, out, &out_len, (tmp-out));
983     else
984     xstrcat(&out_buffer, out, &out_len);
985     }
986 vapier 1.39 }
987    
988 vapier 1.54 #define FOUND_SOMETHING() \
989 solar 1.73 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
990 vapier 1.76 found_rpath || found_needed || found_interp || found_bind || \
991 vapier 1.84 found_soname || found_sym || found_lib || found_textrels)
992 vapier 1.54
993     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
994     xchrcat(&out_buffer, ' ', &out_len);
995     xstrcat(&out_buffer, filename, &out_len);
996 vapier 1.27 }
997 vapier 1.79 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
998 vapier 1.41 puts(out_buffer);
999 vapier 1.79 fflush(stdout);
1000     }
1001 vapier 1.10
1002     unreadelf(elf);
1003 vapier 1.105
1004     return 0;
1005     }
1006    
1007     /* scan an archive of elfs */
1008     static int scanelf_archive(const char *filename)
1009     {
1010     archive_handle *ar = ar_open(filename);
1011     archive_member *m;
1012     while ((m=ar_next(ar)) != NULL)
1013     printf("%o %i:%i %s\n", m->mode, m->uid, m->gid, m->name);
1014     return 0;
1015     }
1016    
1017     /* scan a file which may be an elf or an archive or some other magical beast */
1018     static void scanelf_file(const char *filename)
1019     {
1020     struct stat st;
1021    
1022     /* make sure 'filename' exists */
1023     if (lstat(filename, &st) == -1) {
1024     if (be_verbose > 2) printf("%s: does not exist\n", filename);
1025     return;
1026     }
1027    
1028     /* always handle regular files and handle symlinked files if no -y */
1029     if (S_ISLNK(st.st_mode)) {
1030     if (!scan_symlink) return;
1031     stat(filename, &st);
1032     }
1033     if (!S_ISREG(st.st_mode)) {
1034     if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1035     return;
1036     }
1037    
1038     if (scanelf_elf(filename) == 1 && scan_archives)
1039     /* if it isn't an ELF, maybe it's an .a archive */
1040     scanelf_archive(filename);
1041 solar 1.6 }
1042    
1043 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
1044 vapier 1.10 static void scanelf_dir(const char *path)
1045 solar 1.1 {
1046 vapier 1.10 register DIR *dir;
1047     register struct dirent *dentry;
1048 vapier 1.14 struct stat st_top, st;
1049 vapier 1.104 char buf[__PAX_UTILS_PATH_MAX];
1050 vapier 1.32 size_t pathlen = 0, len = 0;
1051 vapier 1.10
1052     /* make sure path exists */
1053 vapier 1.39 if (lstat(path, &st_top) == -1) {
1054     if (be_verbose > 2) printf("%s: does not exist\n", path);
1055 vapier 1.10 return;
1056 vapier 1.39 }
1057 solar 1.11
1058 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
1059 vapier 1.14 if (!S_ISDIR(st_top.st_mode)) {
1060 vapier 1.10 scanelf_file(path);
1061     return;
1062     }
1063    
1064     /* now scan the dir looking for fun stuff */
1065     if ((dir = opendir(path)) == NULL) {
1066     warnf("could not opendir %s: %s", path, strerror(errno));
1067     return;
1068     }
1069 vapier 1.15 if (be_verbose) printf("%s: scanning dir\n", path);
1070 solar 1.11
1071 vapier 1.32 pathlen = strlen(path);
1072 vapier 1.10 while ((dentry = readdir(dir))) {
1073     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
1074     continue;
1075 vapier 1.32 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
1076     if (len >= sizeof(buf)) {
1077 vapier 1.76 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
1078     (unsigned long)len, (unsigned long)sizeof(buf));
1079 vapier 1.32 continue;
1080     }
1081 solar 1.31 sprintf(buf, "%s/%s", path, dentry->d_name);
1082 solar 1.20 if (lstat(buf, &st) != -1) {
1083 vapier 1.10 if (S_ISREG(st.st_mode))
1084 solar 1.20 scanelf_file(buf);
1085 vapier 1.10 else if (dir_recurse && S_ISDIR(st.st_mode)) {
1086 vapier 1.14 if (dir_crossmount || (st_top.st_dev == st.st_dev))
1087 solar 1.20 scanelf_dir(buf);
1088 vapier 1.10 }
1089     }
1090     }
1091     closedir(dir);
1092 solar 1.1 }
1093    
1094 vapier 1.47 static int scanelf_from_file(char *filename)
1095     {
1096 solar 1.45 FILE *fp = NULL;
1097     char *p;
1098 vapier 1.104 char path[__PAX_UTILS_PATH_MAX];
1099 solar 1.45
1100     if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
1101     fp = stdin;
1102     else if ((fp = fopen(filename, "r")) == NULL)
1103     return 1;
1104    
1105 vapier 1.104 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1106 solar 1.45 if ((p = strchr(path, '\n')) != NULL)
1107     *p = 0;
1108 vapier 1.66 search_path = path;
1109 solar 1.45 scanelf_dir(path);
1110     }
1111     if (fp != stdin)
1112     fclose(fp);
1113     return 0;
1114     }
1115    
1116 vapier 1.48 static void load_ld_so_conf()
1117     {
1118     FILE *fp = NULL;
1119     char *p;
1120 vapier 1.104 char path[__PAX_UTILS_PATH_MAX];
1121 vapier 1.48 int i = 0;
1122    
1123     if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
1124     return;
1125    
1126 vapier 1.104 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1127 vapier 1.48 if (*path != '/')
1128     continue;
1129    
1130     if ((p = strrchr(path, '\r')) != NULL)
1131     *p = 0;
1132     if ((p = strchr(path, '\n')) != NULL)
1133     *p = 0;
1134    
1135     ldpaths[i++] = xstrdup(path);
1136    
1137     if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
1138     break;
1139     }
1140     ldpaths[i] = NULL;
1141    
1142     fclose(fp);
1143     }
1144    
1145 vapier 1.10 /* scan /etc/ld.so.conf for paths */
1146     static void scanelf_ldpath()
1147     {
1148 vapier 1.17 char scan_l, scan_ul, scan_ull;
1149 vapier 1.48 int i = 0;
1150 vapier 1.10
1151 vapier 1.48 if (!ldpaths[0])
1152     err("Unable to load any paths from ld.so.conf");
1153 vapier 1.10
1154 vapier 1.17 scan_l = scan_ul = scan_ull = 0;
1155    
1156 vapier 1.48 while (ldpaths[i]) {
1157     if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
1158     if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
1159     if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
1160     scanelf_dir(ldpaths[i]);
1161     ++i;
1162     }
1163 vapier 1.10
1164 vapier 1.17 if (!scan_l) scanelf_dir("/lib");
1165     if (!scan_ul) scanelf_dir("/usr/lib");
1166     if (!scan_ull) scanelf_dir("/usr/local/lib");
1167 vapier 1.10 }
1168 solar 1.1
1169 vapier 1.10 /* scan env PATH for paths */
1170     static void scanelf_envpath()
1171 solar 1.1 {
1172 solar 1.34 char *path, *p;
1173 vapier 1.10
1174     path = getenv("PATH");
1175     if (!path)
1176     err("PATH is not set in your env !");
1177 vapier 1.41 path = xstrdup(path);
1178 vapier 1.10
1179     while ((p = strrchr(path, ':')) != NULL) {
1180     scanelf_dir(p + 1);
1181     *p = 0;
1182     }
1183 vapier 1.17
1184 solar 1.34 free(path);
1185 solar 1.1 }
1186    
1187    
1188 vapier 1.10 /* usage / invocation handling functions */
1189 vapier 1.105 #define PARSE_FLAGS "plRmyAXxetrnLibSs:gN:TaqvF:f:o:BhV"
1190 vapier 1.27 #define a_argument required_argument
1191 vapier 1.10 static struct option const long_opts[] = {
1192     {"path", no_argument, NULL, 'p'},
1193     {"ldpath", no_argument, NULL, 'l'},
1194     {"recursive", no_argument, NULL, 'R'},
1195 vapier 1.14 {"mount", no_argument, NULL, 'm'},
1196 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
1197 vapier 1.105 {"archives", no_argument, NULL, 'A'},
1198     {"ldcache", no_argument, NULL, 'L'},
1199 vapier 1.101 {"fix", no_argument, NULL, 'X'},
1200 vapier 1.10 {"pax", no_argument, NULL, 'x'},
1201 solar 1.16 {"header", no_argument, NULL, 'e'},
1202 vapier 1.10 {"textrel", no_argument, NULL, 't'},
1203     {"rpath", no_argument, NULL, 'r'},
1204 vapier 1.32 {"needed", no_argument, NULL, 'n'},
1205 vapier 1.38 {"interp", no_argument, NULL, 'i'},
1206 vapier 1.49 {"bind", no_argument, NULL, 'b'},
1207 vapier 1.84 {"soname", no_argument, NULL, 'S'},
1208 vapier 1.76 {"symbol", a_argument, NULL, 's'},
1209     {"lib", a_argument, NULL, 'N'},
1210 solar 1.86 {"gmatch", no_argument, NULL, 'g'},
1211 vapier 1.76 {"textrels", no_argument, NULL, 'T'},
1212 vapier 1.10 {"all", no_argument, NULL, 'a'},
1213     {"quiet", no_argument, NULL, 'q'},
1214 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
1215 vapier 1.76 {"format", a_argument, NULL, 'F'},
1216     {"from", a_argument, NULL, 'f'},
1217     {"file", a_argument, NULL, 'o'},
1218 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
1219 vapier 1.10 {"help", no_argument, NULL, 'h'},
1220     {"version", no_argument, NULL, 'V'},
1221     {NULL, no_argument, NULL, 0x0}
1222     };
1223 solar 1.57
1224 solar 1.68 static const char *opts_help[] = {
1225 vapier 1.10 "Scan all directories in PATH environment",
1226     "Scan all directories in /etc/ld.so.conf",
1227 vapier 1.14 "Scan directories recursively",
1228 vapier 1.37 "Don't recursively cross mount points",
1229 vapier 1.101 "Don't scan symlinks",
1230 vapier 1.105 "Scan archives (.a files)",
1231     "Utilize ld.so.cache information (use with -r/-n)",
1232 vapier 1.101 "Try and 'fix' bad things (use with -r/-e)\n",
1233 vapier 1.10 "Print PaX markings",
1234 vapier 1.71 "Print GNU_STACK/PT_LOAD markings",
1235 vapier 1.10 "Print TEXTREL information",
1236     "Print RPATH information",
1237 vapier 1.32 "Print NEEDED information",
1238 vapier 1.38 "Print INTERP information",
1239 vapier 1.49 "Print BIND information",
1240 vapier 1.84 "Print SONAME information",
1241 vapier 1.27 "Find a specified symbol",
1242 vapier 1.72 "Find a specified library",
1243 solar 1.86 "Use strncmp to match libraries. (use with -N)",
1244 vapier 1.76 "Locate cause of TEXTREL",
1245 vapier 1.82 "Print all scanned info (-x -e -t -r -b)\n",
1246 vapier 1.14 "Only output 'bad' things",
1247     "Be verbose (can be specified more than once)",
1248 vapier 1.39 "Use specified format for output",
1249 solar 1.45 "Read input stream from a filename",
1250 vapier 1.24 "Write output stream to a filename",
1251 vapier 1.14 "Don't display the header",
1252 vapier 1.10 "Print this help and exit",
1253     "Print version and exit",
1254     NULL
1255     };
1256    
1257     /* display usage and exit */
1258     static void usage(int status)
1259 solar 1.1 {
1260 vapier 1.44 unsigned long i;
1261 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
1262 vapier 1.105 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
1263 solar 1.35 printf("Options: -[%s]\n", PARSE_FLAGS);
1264 vapier 1.10 for (i = 0; long_opts[i].name; ++i)
1265 vapier 1.27 if (long_opts[i].has_arg == no_argument)
1266 solar 1.52 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
1267 vapier 1.27 long_opts[i].name, opts_help[i]);
1268     else
1269 solar 1.52 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
1270 vapier 1.27 long_opts[i].name, opts_help[i]);
1271 solar 1.45
1272     if (status != EXIT_SUCCESS)
1273     exit(status);
1274    
1275 vapier 1.46 puts("\nThe format modifiers for the -F option are:");
1276 vapier 1.70 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1277     puts(" t TEXTREL \tr RPATH \tn NEEDED");
1278     puts(" i INTERP \tb BIND \ts symbol");
1279 vapier 1.76 puts(" N library \to Type \tT TEXTRELs");
1280 vapier 1.88 puts(" S SONAME");
1281 vapier 1.70 puts(" p filename (with search path removed)");
1282 vapier 1.88 puts(" f filename (short name/basename)");
1283 vapier 1.70 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1284 solar 1.45
1285 vapier 1.10 exit(status);
1286 solar 1.1 }
1287    
1288     /* parse command line arguments and preform needed actions */
1289 vapier 1.10 static void parseargs(int argc, char *argv[])
1290     {
1291 vapier 1.48 int i;
1292 solar 1.45 char *from_file = NULL;
1293 vapier 1.10
1294     opterr = 0;
1295 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
1296     switch (i) {
1297 vapier 1.10
1298 vapier 1.39 case 'V':
1299 vapier 1.80 printf("pax-utils-%s: %s compiled %s\n%s\n"
1300     "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
1301     VERSION, __FILE__, __DATE__, rcsid, argv0);
1302 vapier 1.10 exit(EXIT_SUCCESS);
1303     break;
1304     case 'h': usage(EXIT_SUCCESS); break;
1305 solar 1.45 case 'f':
1306 vapier 1.100 if (from_file) warn("You prob don't want to specify -f twice");
1307     from_file = optarg;
1308 solar 1.45 break;
1309 vapier 1.24 case 'o': {
1310 solar 1.21 FILE *fp = NULL;
1311 solar 1.64 if ((fp = freopen(optarg, "w", stdout)) == NULL)
1312 vapier 1.24 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1313 vapier 1.65 SET_STDOUT(fp);
1314 solar 1.21 break;
1315     }
1316 vapier 1.24
1317 vapier 1.39 case 's': {
1318 vapier 1.93 if (find_sym) warn("You prob don't want to specify -s twice");
1319     find_sym = optarg;
1320     versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
1321 vapier 1.39 sprintf(versioned_symname, "%s@", find_sym);
1322     break;
1323     }
1324 vapier 1.72 case 'N': {
1325 vapier 1.93 if (find_lib) warn("You prob don't want to specify -N twice");
1326     find_lib = optarg;
1327 vapier 1.72 break;
1328     }
1329 vapier 1.39
1330     case 'F': {
1331 vapier 1.93 if (out_format) warn("You prob don't want to specify -F twice");
1332     out_format = optarg;
1333 vapier 1.39 break;
1334     }
1335 vapier 1.27
1336 solar 1.96 case 'g': gmatch = 1; /* break; any reason we dont breal; here ? */
1337 vapier 1.102 case 'L': use_ldcache = 1; break;
1338 vapier 1.37 case 'y': scan_symlink = 0; break;
1339 vapier 1.105 case 'A': scan_archives = 1; break;
1340 solar 1.16 case 'B': show_banner = 0; break;
1341 vapier 1.10 case 'l': scan_ldpath = 1; break;
1342     case 'p': scan_envpath = 1; break;
1343     case 'R': dir_recurse = 1; break;
1344 vapier 1.14 case 'm': dir_crossmount = 0; break;
1345 vapier 1.101 case 'X': ++fix_elf; break;
1346 vapier 1.10 case 'x': show_pax = 1; break;
1347 solar 1.73 case 'e': show_phdr = 1; break;
1348 vapier 1.10 case 't': show_textrel = 1; break;
1349     case 'r': show_rpath = 1; break;
1350 vapier 1.32 case 'n': show_needed = 1; break;
1351 vapier 1.38 case 'i': show_interp = 1; break;
1352 vapier 1.49 case 'b': show_bind = 1; break;
1353 vapier 1.84 case 'S': show_soname = 1; break;
1354 vapier 1.76 case 'T': show_textrels = 1; break;
1355 vapier 1.10 case 'q': be_quiet = 1; break;
1356 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1357 vapier 1.82 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
1358 vapier 1.10
1359     case ':':
1360 vapier 1.49 err("Option missing parameter\n");
1361 vapier 1.10 case '?':
1362 vapier 1.49 err("Unknown option\n");
1363 vapier 1.10 default:
1364 vapier 1.48 err("Unhandled option '%c'", i);
1365 vapier 1.10 }
1366     }
1367    
1368 vapier 1.39 /* let the format option override all other options */
1369     if (out_format) {
1370 solar 1.73 show_pax = show_phdr = show_textrel = show_rpath = \
1371 vapier 1.84 show_needed = show_interp = show_bind = show_soname = \
1372     show_textrels = 0;
1373 vapier 1.48 for (i = 0; out_format[i]; ++i) {
1374 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
1375 vapier 1.39
1376 vapier 1.48 switch (out_format[++i]) {
1377 vapier 1.39 case '%': break;
1378 vapier 1.70 case '#': break;
1379 vapier 1.39 case 'F': break;
1380 vapier 1.66 case 'p': break;
1381     case 'f': break;
1382 vapier 1.39 case 's': break;
1383 vapier 1.72 case 'N': break;
1384 vapier 1.41 case 'o': break;
1385 vapier 1.39 case 'x': show_pax = 1; break;
1386 solar 1.73 case 'e': show_phdr = 1; break;
1387 vapier 1.39 case 't': show_textrel = 1; break;
1388     case 'r': show_rpath = 1; break;
1389     case 'n': show_needed = 1; break;
1390     case 'i': show_interp = 1; break;
1391 vapier 1.49 case 'b': show_bind = 1; break;
1392 vapier 1.84 case 'S': show_soname = 1; break;
1393 vapier 1.76 case 'T': show_textrels = 1; break;
1394 vapier 1.39 default:
1395     err("Invalid format specifier '%c' (byte %i)",
1396 vapier 1.48 out_format[i], i+1);
1397 vapier 1.39 }
1398     }
1399 vapier 1.41
1400     /* construct our default format */
1401     } else {
1402     size_t fmt_len = 30;
1403     out_format = (char*)xmalloc(sizeof(char) * fmt_len);
1404 vapier 1.76 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1405     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1406     if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1407     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1408     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1409     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1410     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1411     if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1412 vapier 1.84 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1413 vapier 1.76 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1414     if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1415     if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1416     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1417 vapier 1.39 }
1418 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
1419 vapier 1.39
1420     /* now lets actually do the scanning */
1421 vapier 1.102 if (scan_ldpath || use_ldcache)
1422 vapier 1.48 load_ld_so_conf();
1423 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
1424     if (scan_envpath) scanelf_envpath();
1425 solar 1.45 if (from_file) {
1426     scanelf_from_file(from_file);
1427     from_file = *argv;
1428     }
1429     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
1430 vapier 1.25 err("Nothing to scan !?");
1431 vapier 1.66 while (optind < argc) {
1432     search_path = argv[optind++];
1433     scanelf_dir(search_path);
1434     }
1435 vapier 1.27
1436 vapier 1.39 /* clean up */
1437 vapier 1.93 if (versioned_symname) free(versioned_symname);
1438 vapier 1.48 for (i = 0; ldpaths[i]; ++i)
1439     free(ldpaths[i]);
1440 solar 1.96
1441     if (ldcache != 0)
1442     munmap(ldcache, ldcache_size);
1443 vapier 1.10 }
1444    
1445    
1446    
1447 vapier 1.41 /* utility funcs */
1448 vapier 1.60 static char *xstrdup(const char *s)
1449 vapier 1.41 {
1450     char *ret = strdup(s);
1451     if (!ret) err("Could not strdup(): %s", strerror(errno));
1452     return ret;
1453     }
1454     static void *xmalloc(size_t size)
1455     {
1456     void *ret = malloc(size);
1457     if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1458     return ret;
1459     }
1460 vapier 1.95 static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n)
1461 vapier 1.41 {
1462 vapier 1.69 size_t new_len;
1463 vapier 1.41
1464     new_len = strlen(*dst) + strlen(src);
1465     if (*curr_len <= new_len) {
1466     *curr_len = new_len + (*curr_len / 2);
1467     *dst = realloc(*dst, *curr_len);
1468     if (!*dst)
1469 vapier 1.95 err("could not realloc() %li bytes", (unsigned long)*curr_len);
1470 vapier 1.41 }
1471    
1472 vapier 1.95 if (n)
1473     strncat(*dst, src, n);
1474     else
1475     strcat(*dst, src);
1476 vapier 1.41 }
1477     static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1478     {
1479     static char my_app[2];
1480     my_app[0] = append;
1481     my_app[1] = '\0';
1482     xstrcat(dst, my_app, curr_len);
1483     }
1484    
1485    
1486 vapier 1.72
1487 vapier 1.10 int main(int argc, char *argv[])
1488 solar 1.1 {
1489 vapier 1.10 if (argc < 2)
1490     usage(EXIT_FAILURE);
1491     parseargs(argc, argv);
1492 solar 1.21 fclose(stdout);
1493 solar 1.61 #ifdef __BOUNDS_CHECKING_ON
1494 vapier 1.66 warn("The calls to add/delete heap should be off by 1 due to the out_buffer not being freed in scanelf_file()");
1495 solar 1.61 #endif
1496 vapier 1.10 return EXIT_SUCCESS;
1497 solar 1.1 }

  ViewVC Help
Powered by ViewVC 1.1.20