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

Contents of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.71 - (hide annotations) (download) (as text)
Sat Jun 4 02:50:50 2005 UTC (9 years, 3 months ago) by vapier
Branch: MAIN
Changes since 1.70: +31 -16 lines
File MIME type: text/x-csrc
add support for checking if the PT_LOAD program header is +wx

1 solar 1.1 /*
2 solar 1.63 * Copyright 2003-2005 Gentoo Foundation
3 solar 1.1 * Distributed under the terms of the GNU General Public License v2
4 vapier 1.71 * $Header: /var/cvsroot/gentoo-projects/pax-utils/scanelf.c,v 1.70 2005/06/03 23:41:59 vapier Exp $
5 solar 1.1 *
6     ********************************************************************
7     * This program is free software; you can redistribute it and/or
8     * modify it under the terms of the GNU General Public License as
9     * published by the Free Software Foundation; either version 2 of the
10     * License, or (at your option) any later version.
11     *
12     * This program is distributed in the hope that it will be useful, but
13     * WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15     * General Public License for more details.
16     *
17     * You should have received a copy of the GNU General Public License
18     * along with this program; if not, write to the Free Software
19     * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20     * MA 02111-1307, USA.
21     */
22    
23     #include <stdio.h>
24     #include <stdlib.h>
25     #include <sys/types.h>
26 vapier 1.60 #include <libgen.h>
27     #include <limits.h>
28 solar 1.28 #define __USE_GNU
29 solar 1.1 #include <string.h>
30 vapier 1.10 #include <errno.h>
31 solar 1.1 #include <unistd.h>
32     #include <sys/stat.h>
33     #include <dirent.h>
34     #include <getopt.h>
35 solar 1.20 #include <assert.h>
36 solar 1.1 #include "paxelf.h"
37    
38 vapier 1.71 static const char *rcsid = "$Id: scanelf.c,v 1.70 2005/06/03 23:41:59 vapier Exp $";
39 vapier 1.36 #define argv0 "scanelf"
40 vapier 1.10
41 vapier 1.70 #define IS_MODIFIER(c) (c == '%' || c == '#')
42    
43 vapier 1.10
44    
45     /* prototypes */
46     static void scanelf_file(const char *filename);
47     static void scanelf_dir(const char *path);
48     static void scanelf_ldpath();
49     static void scanelf_envpath();
50     static void usage(int status);
51     static void parseargs(int argc, char *argv[]);
52 vapier 1.60 static char *xstrdup(const char *s);
53 vapier 1.41 static void *xmalloc(size_t size);
54     static void xstrcat(char **dst, const char *src, size_t *curr_len);
55     static inline void xchrcat(char **dst, const char append, size_t *curr_len);
56 vapier 1.10
57     /* variables to control behavior */
58 vapier 1.48 static char *ldpaths[256];
59 vapier 1.10 static char scan_ldpath = 0;
60     static char scan_envpath = 0;
61 vapier 1.37 static char scan_symlink = 1;
62 vapier 1.10 static char dir_recurse = 0;
63 vapier 1.14 static char dir_crossmount = 1;
64 vapier 1.10 static char show_pax = 0;
65     static char show_stack = 0;
66     static char show_textrel = 0;
67     static char show_rpath = 0;
68 vapier 1.32 static char show_needed = 0;
69 vapier 1.38 static char show_interp = 0;
70 vapier 1.49 static char show_bind = 0;
71 solar 1.16 static char show_banner = 1;
72 vapier 1.10 static char be_quiet = 0;
73 vapier 1.14 static char be_verbose = 0;
74 vapier 1.70 static char be_wewy_wewy_quiet = 0;
75 vapier 1.39 static char *find_sym = NULL, *versioned_symname = NULL;
76     static char *out_format = NULL;
77 vapier 1.66 static char *search_path = NULL;
78 vapier 1.10
79 solar 1.1
80 vapier 1.70
81 vapier 1.39 /* sub-funcs for scanelf_file() */
82 vapier 1.41 static char *scanelf_file_pax(elfobj *elf, char *found_pax)
83 solar 1.6 {
84 vapier 1.41 static char *paxflags;
85 solar 1.61 static char ret[7];
86     unsigned long i, shown;
87    
88 vapier 1.41
89     if (!show_pax) return NULL;
90 vapier 1.10
91 solar 1.61 shown = 0;
92     memset(&ret, 0, sizeof(ret));
93    
94     if (elf->phdr) {
95     #define SHOW_PAX(B) \
96     if (elf->elf_class == ELFCLASS ## B) { \
97     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
98     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
99     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
100     if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
101     continue; \
102     if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \
103     continue; \
104     memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
105     *found_pax = 1; \
106     ++shown; \
107     break; \
108     } \
109     }
110     SHOW_PAX(32)
111     SHOW_PAX(64)
112     }
113    
114     /* fall back to EI_PAX if no PT_PAX was found */
115     if (!*ret) {
116     paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
117     if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
118     *found_pax = 1;
119     return paxflags;
120     }
121     strncpy(ret, paxflags, sizeof(ret));
122     // ++shown;
123 vapier 1.14 }
124 vapier 1.41
125 solar 1.61 if (be_quiet && !shown)
126     return NULL;
127     return ret;
128    
129 vapier 1.39 }
130 vapier 1.71 static char *scanelf_file_stack(elfobj *elf, char *found_stack, char *found_relro, char *found_load)
131 vapier 1.39 {
132 vapier 1.71 static char ret[12];
133 vapier 1.41 char *found;
134 vapier 1.71 unsigned long i, off, shown, check_flags;
135     unsigned char multi_stack, multi_relro, multi_load;
136 vapier 1.41
137     if (!show_stack) return NULL;
138    
139 vapier 1.71 memcpy(ret, "--- --- ---\0", 12);
140    
141 vapier 1.41 shown = 0;
142 vapier 1.71 multi_stack = multi_relro = multi_load = 0;
143 vapier 1.44
144     if (elf->phdr) {
145 vapier 1.26 #define SHOW_STACK(B) \
146 vapier 1.39 if (elf->elf_class == ELFCLASS ## B) { \
147     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
148     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
149 vapier 1.71 uint32_t flags; \
150 vapier 1.39 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
151 vapier 1.41 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
152 vapier 1.71 if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
153 vapier 1.41 found = found_stack; \
154     off = 0; \
155 vapier 1.71 check_flags = PF_X; \
156 vapier 1.41 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
157 vapier 1.71 if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
158 vapier 1.41 found = found_relro; \
159 solar 1.53 off = 4; \
160 vapier 1.71 check_flags = PF_X; \
161     } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
162     if (multi_load++ > 2) warnf("%s: more than 2 PT_LOAD's !?", elf->filename); \
163     found = found_load; \
164     off = 8; \
165     check_flags = PF_W|PF_X; \
166 vapier 1.41 } else \
167     continue; \
168 vapier 1.71 flags = EGET(phdr[i].p_flags); \
169     if (be_quiet && ((flags & check_flags) != check_flags)) \
170 vapier 1.39 continue; \
171 vapier 1.71 memcpy(ret+off, gnu_short_stack_flags(flags), 3); \
172 vapier 1.41 *found = 1; \
173     ++shown; \
174 vapier 1.39 } \
175     }
176     SHOW_STACK(32)
177     SHOW_STACK(64)
178 vapier 1.44 }
179    
180 vapier 1.41 if (be_quiet && !shown)
181     return NULL;
182     else
183     return ret;
184 vapier 1.39 }
185 vapier 1.41 static char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
186 vapier 1.39 {
187 solar 1.68 static char ret[] = "TEXTREL";
188 vapier 1.44 unsigned long i;
189 vapier 1.41
190     if (!show_textrel) return NULL;
191    
192 vapier 1.44 if (elf->phdr) {
193 vapier 1.39 #define SHOW_TEXTREL(B) \
194     if (elf->elf_class == ELFCLASS ## B) { \
195     Elf ## B ## _Dyn *dyn; \
196     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
197     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
198 vapier 1.44 Elf ## B ## _Off offset; \
199 vapier 1.39 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
200 vapier 1.59 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
201 vapier 1.44 offset = EGET(phdr[i].p_offset); \
202     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
203     dyn = DYN ## B (elf->data + offset); \
204 vapier 1.39 while (EGET(dyn->d_tag) != DT_NULL) { \
205     if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
206     *found_textrel = 1; \
207     /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
208 vapier 1.70 return (be_wewy_wewy_quiet ? NULL : ret); \
209 vapier 1.39 } \
210     ++dyn; \
211 vapier 1.26 } \
212 vapier 1.39 } }
213     SHOW_TEXTREL(32)
214     SHOW_TEXTREL(64)
215 vapier 1.44 }
216    
217 vapier 1.70 if (be_quiet || be_wewy_wewy_quiet)
218 vapier 1.41 return NULL;
219     else
220 solar 1.68 return (char *)" - ";
221 vapier 1.39 }
222 vapier 1.41 static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
223 vapier 1.39 {
224 vapier 1.48 unsigned long i, s;
225     char *rpath, *runpath, **r;
226 vapier 1.39 void *strtbl_void;
227 vapier 1.10
228 vapier 1.39 if (!show_rpath) return;
229 vapier 1.10
230 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
231     rpath = runpath = NULL;
232 vapier 1.10
233 vapier 1.44 if (elf->phdr && strtbl_void) {
234 vapier 1.26 #define SHOW_RPATH(B) \
235     if (elf->elf_class == ELFCLASS ## B) { \
236     Elf ## B ## _Dyn *dyn; \
237     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
238     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
239     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
240 vapier 1.44 Elf ## B ## _Off offset; \
241 vapier 1.60 Elf ## B ## _Xword word; \
242 vapier 1.48 /* Scan all the program headers */ \
243 vapier 1.26 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
244 vapier 1.48 /* Just scan dynamic headers */ \
245 vapier 1.26 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 vapier 1.48 /* Just scan dynamic RPATH/RUNPATH headers */ \
249 vapier 1.44 dyn = DYN ## B (elf->data + offset); \
250 vapier 1.48 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
251     if (word == DT_RPATH) { \
252     r = &rpath; \
253     } else if (word == DT_RUNPATH) { \
254     r = &runpath; \
255     } else { \
256     ++dyn; \
257     continue; \
258     } \
259     /* Verify the memory is somewhat sane */ \
260     offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
261 vapier 1.69 if (offset < (Elf ## B ## _Off)elf->len) { \
262 vapier 1.48 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
263     *r = (char*)(elf->data + offset); \
264     /* If quiet, don't output paths in ld.so.conf */ \
265 vapier 1.69 if (be_quiet) { \
266     size_t len; \
267     char *start, *end; \
268     for (s = 0; *r && ldpaths[s]; ++s) { \
269     /* scan each path in : delimited list */ \
270     start = *r; \
271     end = strchr(start, ':'); \
272     while ((start && ((end = strchr(start, ':')) != NULL)) || start) { \
273     len = (end ? abs(end - start) : strlen(start)); \
274     if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
275     *r = (end ? end + 1 : NULL); \
276     break; \
277     } \
278     start = (end ? start + len + 1 : NULL); \
279 vapier 1.48 } \
280 vapier 1.69 } \
281     } \
282 vapier 1.48 if (*r) *found_rpath = 1; \
283 vapier 1.26 } \
284     ++dyn; \
285     } \
286     } }
287     SHOW_RPATH(32)
288     SHOW_RPATH(64)
289 vapier 1.10 }
290 vapier 1.41
291 vapier 1.70 if (be_wewy_wewy_quiet) return;
292    
293 vapier 1.39 if (rpath && runpath) {
294 vapier 1.41 if (!strcmp(rpath, runpath)) {
295     xstrcat(ret, runpath, ret_len);
296     } else {
297 vapier 1.39 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
298 vapier 1.41 xchrcat(ret, '{', ret_len);
299     xstrcat(ret, rpath, ret_len);
300     xchrcat(ret, ',', ret_len);
301     xstrcat(ret, runpath, ret_len);
302     xchrcat(ret, '}', ret_len);
303 vapier 1.39 }
304     } else if (rpath || runpath)
305 vapier 1.41 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
306     else if (!be_quiet)
307     xstrcat(ret, " - ", ret_len);
308 vapier 1.39 }
309 vapier 1.41 static void scanelf_file_needed(elfobj *elf, char *found_needed, char **ret, size_t *ret_len)
310 vapier 1.39 {
311 vapier 1.44 unsigned long i;
312 vapier 1.39 char *needed;
313     void *strtbl_void;
314    
315     if (!show_needed) return;
316 vapier 1.10
317 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".dynstr");
318 vapier 1.32
319 vapier 1.44 if (elf->phdr && strtbl_void) {
320 vapier 1.32 #define SHOW_NEEDED(B) \
321     if (elf->elf_class == ELFCLASS ## B) { \
322     Elf ## B ## _Dyn *dyn; \
323     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
324     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
325     Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
326 vapier 1.44 Elf ## B ## _Off offset; \
327 vapier 1.32 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
328     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
329 vapier 1.44 offset = EGET(phdr[i].p_offset); \
330     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
331     dyn = DYN ## B (elf->data + offset); \
332 vapier 1.32 while (EGET(dyn->d_tag) != DT_NULL) { \
333     if (EGET(dyn->d_tag) == DT_NEEDED) { \
334 vapier 1.44 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
335 vapier 1.69 if (offset >= (Elf ## B ## _Off)elf->len) { \
336 vapier 1.49 ++dyn; \
337     continue; \
338     } \
339 vapier 1.44 needed = (char*)(elf->data + offset); \
340 vapier 1.41 if (*found_needed) xchrcat(ret, ',', ret_len); \
341 vapier 1.70 if (!be_wewy_wewy_quiet) xstrcat(ret, needed, ret_len); \
342 vapier 1.39 *found_needed = 1; \
343 vapier 1.32 } \
344     ++dyn; \
345     } \
346     } }
347     SHOW_NEEDED(32)
348     SHOW_NEEDED(64)
349     }
350 vapier 1.39 }
351 vapier 1.41 static char *scanelf_file_interp(elfobj *elf, char *found_interp)
352 vapier 1.39 {
353     void *strtbl_void;
354    
355 vapier 1.41 if (!show_interp) return NULL;
356 vapier 1.32
357 vapier 1.39 strtbl_void = elf_findsecbyname(elf, ".interp");
358 vapier 1.38
359 vapier 1.39 if (strtbl_void) {
360 vapier 1.38 #define SHOW_INTERP(B) \
361     if (elf->elf_class == ELFCLASS ## B) { \
362 solar 1.40 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
363     *found_interp = 1; \
364 vapier 1.70 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
365 vapier 1.38 }
366     SHOW_INTERP(32)
367     SHOW_INTERP(64)
368     }
369 vapier 1.41 return NULL;
370 vapier 1.39 }
371 vapier 1.49 static char *scanelf_file_bind(elfobj *elf, char *found_bind)
372     {
373     unsigned long i;
374     struct stat s;
375    
376     if (!show_bind) return NULL;
377 vapier 1.51 if (!elf->phdr) return NULL;
378 vapier 1.49
379     #define SHOW_BIND(B) \
380     if (elf->elf_class == ELFCLASS ## B) { \
381     Elf ## B ## _Dyn *dyn; \
382     Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
383     Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
384     Elf ## B ## _Off offset; \
385     for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
386     if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
387     offset = EGET(phdr[i].p_offset); \
388     if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
389     dyn = DYN ## B (elf->data + offset); \
390     while (EGET(dyn->d_tag) != DT_NULL) { \
391     if (EGET(dyn->d_tag) == DT_BIND_NOW || \
392     (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
393     { \
394     if (be_quiet) return NULL; \
395     *found_bind = 1; \
396 vapier 1.70 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
397 vapier 1.49 } \
398     ++dyn; \
399     } \
400     } \
401     }
402     SHOW_BIND(32)
403     SHOW_BIND(64)
404    
405 vapier 1.70 if (be_wewy_wewy_quiet) return NULL;
406    
407 vapier 1.56 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
408 vapier 1.49 return NULL;
409     } else {
410     *found_bind = 1;
411 solar 1.68 return (char *) "LAZY";
412 vapier 1.49 }
413     }
414 vapier 1.41 static char *scanelf_file_sym(elfobj *elf, char *found_sym, const char *filename)
415 vapier 1.39 {
416 vapier 1.44 unsigned long i;
417 vapier 1.39 void *symtab_void, *strtab_void;
418 vapier 1.38
419 vapier 1.41 if (!find_sym) return NULL;
420 vapier 1.32
421 vapier 1.67 /* debug sections */
422 vapier 1.39 symtab_void = elf_findsecbyname(elf, ".symtab");
423     strtab_void = elf_findsecbyname(elf, ".strtab");
424 vapier 1.67 /* fall back to runtime sections */
425     if (!symtab_void || !strtab_void) {
426     symtab_void = elf_findsecbyname(elf, ".dynsym");
427     strtab_void = elf_findsecbyname(elf, ".dynstr");
428     }
429 vapier 1.27
430 vapier 1.39 if (symtab_void && strtab_void) {
431 vapier 1.60 char *base, *basemem;
432     basemem = xstrdup(filename);
433     base = basename(basemem);
434 vapier 1.27 #define FIND_SYM(B) \
435     if (elf->elf_class == ELFCLASS ## B) { \
436     Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
437     Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
438     Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
439 vapier 1.44 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
440 vapier 1.27 char *symname; \
441     for (i = 0; i < cnt; ++i) { \
442     if (sym->st_name) { \
443     symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
444 solar 1.28 if (*find_sym == '*') { \
445 vapier 1.32 printf("%s(%s) %5lX %15s %s\n", \
446 vapier 1.39 ((*found_sym == 0) ? "\n\t" : "\t"), \
447 vapier 1.60 base, \
448 vapier 1.32 (long)sym->st_size, \
449     (char *)get_elfstttype(sym->st_info), \
450     symname); \
451 vapier 1.39 *found_sym = 1; \
452 vapier 1.32 } else if ((strcmp(find_sym, symname) == 0) || \
453 vapier 1.39 (strcmp(symname, versioned_symname) == 0)) \
454     (*found_sym)++; \
455 vapier 1.27 } \
456     ++sym; \
457     } }
458     FIND_SYM(32)
459     FIND_SYM(64)
460 vapier 1.60 free(basemem);
461 vapier 1.39 }
462 vapier 1.70
463     if (be_wewy_wewy_quiet) return NULL;
464    
465 vapier 1.41 if (*find_sym != '*' && *found_sym)
466     return find_sym;
467     if (be_quiet)
468     return NULL;
469     else
470 solar 1.68 return (char *)" - ";
471 vapier 1.39 }
472     /* scan an elf file and show all the fun stuff */
473 solar 1.57 #define prints(str) write(fileno(stdout), str, strlen(str))
474 vapier 1.39 static void scanelf_file(const char *filename)
475     {
476 vapier 1.44 unsigned long i;
477 vapier 1.71 char found_pax, found_stack, found_relro, found_load, found_textrel,
478 vapier 1.49 found_rpath, found_needed, found_interp, found_bind,
479     found_sym, found_file;
480 vapier 1.39 elfobj *elf;
481     struct stat st;
482 vapier 1.41 static char *out_buffer = NULL;
483     static size_t out_len;
484 vapier 1.39
485     /* make sure 'filename' exists */
486     if (lstat(filename, &st) == -1) {
487     if (be_verbose > 2) printf("%s: does not exist\n", filename);
488     return;
489     }
490     /* always handle regular files and handle symlinked files if no -y */
491 vapier 1.55 if (S_ISLNK(st.st_mode)) {
492     if (!scan_symlink) return;
493     stat(filename, &st);
494     }
495     if (!S_ISREG(st.st_mode)) {
496 vapier 1.39 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
497     return;
498     }
499    
500 vapier 1.71 found_pax = found_stack = found_relro = found_load = \
501     found_textrel = found_rpath = found_needed = found_interp = \
502     found_bind = found_sym = found_file = 0;
503 vapier 1.39
504     /* verify this is real ELF */
505     if ((elf = readelf(filename)) == NULL) {
506     if (be_verbose > 2) printf("%s: not an ELF\n", filename);
507     return;
508     }
509    
510     if (be_verbose > 1)
511 vapier 1.41 printf("%s: scanning file {%s,%s}\n", filename,
512 vapier 1.69 get_elfeitype(EI_CLASS, elf->elf_class),
513     get_elfeitype(EI_DATA, elf->data[EI_DATA]));
514 vapier 1.39 else if (be_verbose)
515     printf("%s: scanning file\n", filename);
516    
517 vapier 1.41 /* init output buffer */
518     if (!out_buffer) {
519     out_len = sizeof(char) * 80;
520     out_buffer = (char*)xmalloc(out_len);
521     }
522     *out_buffer = '\0';
523    
524 vapier 1.39 /* show the header */
525     if (!be_quiet && show_banner) {
526 vapier 1.49 for (i = 0; out_format[i]; ++i) {
527 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
528 vapier 1.41
529     switch (out_format[++i]) {
530     case '%': break;
531 vapier 1.70 case '#': break;
532 vapier 1.66 case 'F':
533     case 'p':
534     case 'f': prints("FILE "); found_file = 1; break;
535 vapier 1.41 case 'o': prints(" TYPE "); break;
536     case 'x': prints(" PAX "); break;
537 vapier 1.71 case 'e': prints("STK/REL/PTL "); break;
538 vapier 1.41 case 't': prints("TEXTREL "); break;
539     case 'r': prints("RPATH "); break;
540     case 'n': prints("NEEDED "); break;
541     case 'i': prints("INTERP "); break;
542 vapier 1.49 case 'b': prints("BIND "); break;
543 vapier 1.41 case 's': prints("SYM "); break;
544 vapier 1.39 }
545 vapier 1.27 }
546 vapier 1.49 if (!found_file) prints("FILE ");
547 vapier 1.41 prints("\n");
548 vapier 1.49 found_file = 0;
549 vapier 1.39 show_banner = 0;
550     }
551    
552     /* dump all the good stuff */
553 vapier 1.49 for (i = 0; out_format[i]; ++i) {
554 vapier 1.41 const char *out;
555 vapier 1.66 const char *tmp;
556 vapier 1.41
557     /* make sure we trim leading spaces in quiet mode */
558     if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
559     *out_buffer = '\0';
560 vapier 1.39
561 vapier 1.70 if (!IS_MODIFIER(out_format[i])) {
562 vapier 1.41 xchrcat(&out_buffer, out_format[i], &out_len);
563     continue;
564     }
565 vapier 1.39
566 vapier 1.41 out = NULL;
567 vapier 1.70 be_wewy_wewy_quiet = (out_format[i] == '#');
568 vapier 1.41 switch (out_format[++i]) {
569 vapier 1.70 case '%':
570     case '#':
571     xchrcat(&out_buffer, out_format[i], &out_len); break;
572     case 'F':
573     if (be_wewy_wewy_quiet) break;
574     found_file = 1;
575     xstrcat(&out_buffer, filename, &out_len);
576     break;
577 vapier 1.66 case 'p':
578 vapier 1.70 if (be_wewy_wewy_quiet) break;
579 vapier 1.66 found_file = 1;
580     tmp = filename;
581     if (search_path) {
582     ssize_t len_search = strlen(search_path);
583     ssize_t len_file = strlen(filename);
584     if (!strncmp(filename, search_path, len_search) && \
585     len_file > len_search)
586     tmp += len_search;
587     if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
588     }
589     xstrcat(&out_buffer, tmp, &out_len);
590     break;
591     case 'f':
592 vapier 1.70 if (be_wewy_wewy_quiet) break;
593 vapier 1.66 found_file = 1;
594     tmp = strrchr(filename, '/');
595     tmp = (tmp == NULL ? filename : tmp+1);
596     xstrcat(&out_buffer, tmp, &out_len);
597     break;
598 vapier 1.41 case 'o': out = get_elfetype(elf); break;
599     case 'x': out = scanelf_file_pax(elf, &found_pax); break;
600 vapier 1.71 case 'e': out = scanelf_file_stack(elf, &found_stack, &found_relro, &found_load); break;
601 vapier 1.41 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
602     case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
603     case 'n': scanelf_file_needed(elf, &found_needed, &out_buffer, &out_len); break;
604     case 'i': out = scanelf_file_interp(elf, &found_interp); break;
605 vapier 1.49 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
606 vapier 1.41 case 's': out = scanelf_file_sym(elf, &found_sym, filename); break;
607 vapier 1.29 }
608 vapier 1.41 if (out) xstrcat(&out_buffer, out, &out_len);
609 vapier 1.39 }
610    
611 vapier 1.54 #define FOUND_SOMETHING() \
612 vapier 1.71 (found_pax || found_stack || found_relro || found_load || found_textrel || \
613     found_rpath || found_needed || found_interp || found_bind || found_sym)
614 vapier 1.54
615     if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
616     xchrcat(&out_buffer, ' ', &out_len);
617     xstrcat(&out_buffer, filename, &out_len);
618 vapier 1.27 }
619 vapier 1.54 if (!be_quiet || (be_quiet && FOUND_SOMETHING()))
620 vapier 1.41 puts(out_buffer);
621 vapier 1.10
622     unreadelf(elf);
623 solar 1.6 }
624    
625 solar 1.1 /* scan a directory for ET_EXEC files and print when we find one */
626 vapier 1.10 static void scanelf_dir(const char *path)
627 solar 1.1 {
628 vapier 1.10 register DIR *dir;
629     register struct dirent *dentry;
630 vapier 1.14 struct stat st_top, st;
631 solar 1.21 char buf[_POSIX_PATH_MAX];
632 vapier 1.32 size_t pathlen = 0, len = 0;
633 vapier 1.10
634     /* make sure path exists */
635 vapier 1.39 if (lstat(path, &st_top) == -1) {
636     if (be_verbose > 2) printf("%s: does not exist\n", path);
637 vapier 1.10 return;
638 vapier 1.39 }
639 solar 1.11
640 vapier 1.10 /* ok, if it isn't a directory, assume we can open it */
641 vapier 1.14 if (!S_ISDIR(st_top.st_mode)) {
642 vapier 1.10 scanelf_file(path);
643     return;
644     }
645    
646     /* now scan the dir looking for fun stuff */
647     if ((dir = opendir(path)) == NULL) {
648     warnf("could not opendir %s: %s", path, strerror(errno));
649     return;
650     }
651 vapier 1.15 if (be_verbose) printf("%s: scanning dir\n", path);
652 solar 1.11
653 vapier 1.32 pathlen = strlen(path);
654 vapier 1.10 while ((dentry = readdir(dir))) {
655     if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
656     continue;
657 vapier 1.32 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
658     if (len >= sizeof(buf)) {
659     warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf));
660     continue;
661     }
662 solar 1.31 sprintf(buf, "%s/%s", path, dentry->d_name);
663 solar 1.20 if (lstat(buf, &st) != -1) {
664 vapier 1.10 if (S_ISREG(st.st_mode))
665 solar 1.20 scanelf_file(buf);
666 vapier 1.10 else if (dir_recurse && S_ISDIR(st.st_mode)) {
667 vapier 1.14 if (dir_crossmount || (st_top.st_dev == st.st_dev))
668 solar 1.20 scanelf_dir(buf);
669 vapier 1.10 }
670     }
671     }
672     closedir(dir);
673 solar 1.1 }
674    
675 vapier 1.47 static int scanelf_from_file(char *filename)
676     {
677 solar 1.45 FILE *fp = NULL;
678     char *p;
679     char path[_POSIX_PATH_MAX];
680    
681     if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
682     fp = stdin;
683     else if ((fp = fopen(filename, "r")) == NULL)
684     return 1;
685    
686     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
687     if ((p = strchr(path, '\n')) != NULL)
688     *p = 0;
689 vapier 1.66 search_path = path;
690 solar 1.45 scanelf_dir(path);
691     }
692     if (fp != stdin)
693     fclose(fp);
694     return 0;
695     }
696    
697 vapier 1.48 static void load_ld_so_conf()
698     {
699     FILE *fp = NULL;
700     char *p;
701     char path[_POSIX_PATH_MAX];
702     int i = 0;
703    
704     if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
705     return;
706    
707     while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
708     if (*path != '/')
709     continue;
710    
711     if ((p = strrchr(path, '\r')) != NULL)
712     *p = 0;
713     if ((p = strchr(path, '\n')) != NULL)
714     *p = 0;
715    
716     ldpaths[i++] = xstrdup(path);
717    
718     if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
719     break;
720     }
721     ldpaths[i] = NULL;
722    
723     fclose(fp);
724     }
725    
726 vapier 1.10 /* scan /etc/ld.so.conf for paths */
727     static void scanelf_ldpath()
728     {
729 vapier 1.17 char scan_l, scan_ul, scan_ull;
730 vapier 1.48 int i = 0;
731 vapier 1.10
732 vapier 1.48 if (!ldpaths[0])
733     err("Unable to load any paths from ld.so.conf");
734 vapier 1.10
735 vapier 1.17 scan_l = scan_ul = scan_ull = 0;
736    
737 vapier 1.48 while (ldpaths[i]) {
738     if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
739     if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
740     if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
741     scanelf_dir(ldpaths[i]);
742     ++i;
743     }
744 vapier 1.10
745 vapier 1.17 if (!scan_l) scanelf_dir("/lib");
746     if (!scan_ul) scanelf_dir("/usr/lib");
747     if (!scan_ull) scanelf_dir("/usr/local/lib");
748 vapier 1.10 }
749 solar 1.1
750 vapier 1.10 /* scan env PATH for paths */
751     static void scanelf_envpath()
752 solar 1.1 {
753 solar 1.34 char *path, *p;
754 vapier 1.10
755     path = getenv("PATH");
756     if (!path)
757     err("PATH is not set in your env !");
758 vapier 1.41 path = xstrdup(path);
759 vapier 1.10
760     while ((p = strrchr(path, ':')) != NULL) {
761     scanelf_dir(p + 1);
762     *p = 0;
763     }
764 vapier 1.17
765 solar 1.34 free(path);
766 solar 1.1 }
767    
768    
769 vapier 1.10
770     /* usage / invocation handling functions */
771 vapier 1.49 #define PARSE_FLAGS "plRmyxetrnibs:aqvF:f:o:BhV"
772 vapier 1.27 #define a_argument required_argument
773 vapier 1.10 static struct option const long_opts[] = {
774     {"path", no_argument, NULL, 'p'},
775     {"ldpath", no_argument, NULL, 'l'},
776     {"recursive", no_argument, NULL, 'R'},
777 vapier 1.14 {"mount", no_argument, NULL, 'm'},
778 vapier 1.37 {"symlink", no_argument, NULL, 'y'},
779 vapier 1.10 {"pax", no_argument, NULL, 'x'},
780 solar 1.16 {"header", no_argument, NULL, 'e'},
781 vapier 1.10 {"textrel", no_argument, NULL, 't'},
782     {"rpath", no_argument, NULL, 'r'},
783 vapier 1.32 {"needed", no_argument, NULL, 'n'},
784 vapier 1.38 {"interp", no_argument, NULL, 'i'},
785 vapier 1.49 {"bind", no_argument, NULL, 'b'},
786 vapier 1.27 {"symbol", a_argument, NULL, 's'},
787 vapier 1.10 {"all", no_argument, NULL, 'a'},
788     {"quiet", no_argument, NULL, 'q'},
789 vapier 1.14 {"verbose", no_argument, NULL, 'v'},
790 vapier 1.39 {"format", a_argument, NULL, 'F'},
791 solar 1.45 {"from", a_argument, NULL, 'f'},
792 vapier 1.27 {"file", a_argument, NULL, 'o'},
793 solar 1.16 {"nobanner", no_argument, NULL, 'B'},
794 vapier 1.10 {"help", no_argument, NULL, 'h'},
795     {"version", no_argument, NULL, 'V'},
796     {NULL, no_argument, NULL, 0x0}
797     };
798 solar 1.57
799 solar 1.68 static const char *opts_help[] = {
800 vapier 1.10 "Scan all directories in PATH environment",
801     "Scan all directories in /etc/ld.so.conf",
802 vapier 1.14 "Scan directories recursively",
803 vapier 1.37 "Don't recursively cross mount points",
804     "Don't scan symlinks\n",
805 vapier 1.10 "Print PaX markings",
806 vapier 1.71 "Print GNU_STACK/PT_LOAD markings",
807 vapier 1.10 "Print TEXTREL information",
808     "Print RPATH information",
809 vapier 1.32 "Print NEEDED information",
810 vapier 1.38 "Print INTERP information",
811 vapier 1.49 "Print BIND information",
812 vapier 1.27 "Find a specified symbol",
813 solar 1.57 "Print all scanned info (-x -e -t -r -n -i -b)\n",
814 vapier 1.14 "Only output 'bad' things",
815     "Be verbose (can be specified more than once)",
816 vapier 1.39 "Use specified format for output",
817 solar 1.45 "Read input stream from a filename",
818 vapier 1.24 "Write output stream to a filename",
819 vapier 1.14 "Don't display the header",
820 vapier 1.10 "Print this help and exit",
821     "Print version and exit",
822     NULL
823     };
824    
825     /* display usage and exit */
826     static void usage(int status)
827 solar 1.1 {
828 vapier 1.44 unsigned long i;
829 solar 1.52 printf("* Scan ELF binaries for stuff\n\n"
830 solar 1.35 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0);
831     printf("Options: -[%s]\n", PARSE_FLAGS);
832 vapier 1.10 for (i = 0; long_opts[i].name; ++i)
833 vapier 1.27 if (long_opts[i].has_arg == no_argument)
834 solar 1.52 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
835 vapier 1.27 long_opts[i].name, opts_help[i]);
836     else
837 solar 1.52 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
838 vapier 1.27 long_opts[i].name, opts_help[i]);
839 solar 1.45
840     if (status != EXIT_SUCCESS)
841     exit(status);
842    
843 vapier 1.46 puts("\nThe format modifiers for the -F option are:");
844 vapier 1.70 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
845     puts(" t TEXTREL \tr RPATH \tn NEEDED");
846     puts(" i INTERP \tb BIND \ts symbol");
847     puts(" p filename (with search path removed)");
848     puts(" f base filename");
849     puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
850 solar 1.45
851 vapier 1.10 exit(status);
852 solar 1.1 }
853    
854     /* parse command line arguments and preform needed actions */
855 vapier 1.10 static void parseargs(int argc, char *argv[])
856     {
857 vapier 1.48 int i;
858 solar 1.45 char *from_file = NULL;
859 vapier 1.10
860     opterr = 0;
861 vapier 1.48 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
862     switch (i) {
863 vapier 1.10
864 vapier 1.39 case 'V':
865 solar 1.19 printf("%s compiled %s\n%s\n"
866     "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n",
867     __FILE__, __DATE__, rcsid, argv0);
868 vapier 1.10 exit(EXIT_SUCCESS);
869     break;
870     case 'h': usage(EXIT_SUCCESS); break;
871 solar 1.45 case 'f':
872 solar 1.64 if (from_file) err("Don't specify -f twice");
873     from_file = xstrdup(optarg);
874 solar 1.45 break;
875 vapier 1.24 case 'o': {
876 solar 1.21 FILE *fp = NULL;
877 solar 1.64 if ((fp = freopen(optarg, "w", stdout)) == NULL)
878 vapier 1.24 err("Could not open output stream '%s': %s", optarg, strerror(errno));
879 vapier 1.65 SET_STDOUT(fp);
880 solar 1.21 break;
881     }
882 vapier 1.24
883 vapier 1.39 case 's': {
884     size_t len;
885 solar 1.64 if (find_sym) err("Don't specify -s twice");
886 vapier 1.41 find_sym = xstrdup(optarg);
887 vapier 1.39 len = strlen(find_sym) + 1;
888 vapier 1.41 versioned_symname = (char*)xmalloc(sizeof(char) * (len+1));
889 vapier 1.39 sprintf(versioned_symname, "%s@", find_sym);
890     break;
891     }
892    
893     case 'F': {
894 solar 1.64 if (out_format) err("Don't specify -F twice");
895     out_format = xstrdup(optarg);
896 vapier 1.39 break;
897     }
898 vapier 1.27
899 vapier 1.37 case 'y': scan_symlink = 0; break;
900 solar 1.16 case 'B': show_banner = 0; break;
901 vapier 1.10 case 'l': scan_ldpath = 1; break;
902     case 'p': scan_envpath = 1; break;
903     case 'R': dir_recurse = 1; break;
904 vapier 1.14 case 'm': dir_crossmount = 0; break;
905 vapier 1.10 case 'x': show_pax = 1; break;
906 solar 1.16 case 'e': show_stack = 1; break;
907 vapier 1.10 case 't': show_textrel = 1; break;
908     case 'r': show_rpath = 1; break;
909 vapier 1.32 case 'n': show_needed = 1; break;
910 vapier 1.38 case 'i': show_interp = 1; break;
911 vapier 1.49 case 'b': show_bind = 1; break;
912 vapier 1.10 case 'q': be_quiet = 1; break;
913 vapier 1.14 case 'v': be_verbose = (be_verbose % 20) + 1; break;
914 vapier 1.50 case 'a': show_pax = show_stack = show_textrel = show_rpath = \
915     show_needed = show_interp = show_bind = 1; break;
916 vapier 1.10
917     case ':':
918 vapier 1.49 err("Option missing parameter\n");
919 vapier 1.10 case '?':
920 vapier 1.49 err("Unknown option\n");
921 vapier 1.10 default:
922 vapier 1.48 err("Unhandled option '%c'", i);
923 vapier 1.10 }
924     }
925    
926 vapier 1.39 /* let the format option override all other options */
927     if (out_format) {
928 vapier 1.58 show_pax = show_stack = show_textrel = show_rpath = \
929     show_needed = show_interp = show_bind = 0;
930 vapier 1.48 for (i = 0; out_format[i]; ++i) {
931 vapier 1.70 if (!IS_MODIFIER(out_format[i])) continue;
932 vapier 1.39
933 vapier 1.48 switch (out_format[++i]) {
934 vapier 1.39 case '%': break;
935 vapier 1.70 case '#': break;
936 vapier 1.39 case 'F': break;
937 vapier 1.66 case 'p': break;
938     case 'f': break;
939 vapier 1.39 case 's': break;
940 vapier 1.41 case 'o': break;
941 vapier 1.39 case 'x': show_pax = 1; break;
942     case 'e': show_stack = 1; break;
943     case 't': show_textrel = 1; break;
944     case 'r': show_rpath = 1; break;
945     case 'n': show_needed = 1; break;
946     case 'i': show_interp = 1; break;
947 vapier 1.49 case 'b': show_bind = 1; break;
948 vapier 1.39 default:
949     err("Invalid format specifier '%c' (byte %i)",
950 vapier 1.48 out_format[i], i+1);
951 vapier 1.39 }
952     }
953 vapier 1.41
954     /* construct our default format */
955     } else {
956     size_t fmt_len = 30;
957     out_format = (char*)xmalloc(sizeof(char) * fmt_len);
958     if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
959     if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
960     if (show_stack) xstrcat(&out_format, "%e ", &fmt_len);
961     if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
962     if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
963     if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
964     if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
965 vapier 1.49 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
966 vapier 1.41 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
967     if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
968 vapier 1.39 }
969 vapier 1.41 if (be_verbose > 2) printf("Format: %s\n", out_format);
970 vapier 1.39
971     /* now lets actually do the scanning */
972 vapier 1.48 if (scan_ldpath || (show_rpath && be_quiet))
973     load_ld_so_conf();
974 vapier 1.10 if (scan_ldpath) scanelf_ldpath();
975     if (scan_envpath) scanelf_envpath();
976 solar 1.45 if (from_file) {
977     scanelf_from_file(from_file);
978     free(from_file);
979     from_file = *argv;
980     }
981     if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
982 vapier 1.25 err("Nothing to scan !?");
983 vapier 1.66 while (optind < argc) {
984     search_path = argv[optind++];
985     scanelf_dir(search_path);
986     }
987 vapier 1.27
988 vapier 1.39 /* clean up */
989     if (find_sym) {
990     free(find_sym);
991     free(versioned_symname);
992     }
993     if (out_format) free(out_format);
994 vapier 1.48 for (i = 0; ldpaths[i]; ++i)
995     free(ldpaths[i]);
996 vapier 1.10 }
997    
998    
999    
1000 vapier 1.41 /* utility funcs */
1001 vapier 1.60 static char *xstrdup(const char *s)
1002 vapier 1.41 {
1003     char *ret = strdup(s);
1004     if (!ret) err("Could not strdup(): %s", strerror(errno));
1005     return ret;
1006     }
1007 solar 1.57
1008 vapier 1.41 static void *xmalloc(size_t size)
1009     {
1010     void *ret = malloc(size);
1011     if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1012     return ret;
1013     }
1014 solar 1.57
1015 vapier 1.41 static void xstrcat(char **dst, const char *src, size_t *curr_len)
1016     {
1017 vapier 1.69 size_t new_len;
1018 vapier 1.41
1019     new_len = strlen(*dst) + strlen(src);
1020     if (*curr_len <= new_len) {
1021     *curr_len = new_len + (*curr_len / 2);
1022     *dst = realloc(*dst, *curr_len);
1023     if (!*dst)
1024     err("could not realloc %li bytes", (unsigned long)*curr_len);
1025     }
1026    
1027     strcat(*dst, src);
1028     }
1029 solar 1.57
1030 vapier 1.41 static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1031     {
1032     static char my_app[2];
1033     my_app[0] = append;
1034     my_app[1] = '\0';
1035     xstrcat(dst, my_app, curr_len);
1036     }
1037    
1038    
1039 vapier 1.10 int main(int argc, char *argv[])
1040 solar 1.1 {
1041 vapier 1.10 if (argc < 2)
1042     usage(EXIT_FAILURE);
1043     parseargs(argc, argv);
1044 solar 1.21 fclose(stdout);
1045 solar 1.61 #ifdef __BOUNDS_CHECKING_ON
1046 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()");
1047 solar 1.61 #endif
1048 vapier 1.10 return EXIT_SUCCESS;
1049 solar 1.1 }

  ViewVC Help
Powered by ViewVC 1.1.20