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

Diff of /pax-utils/scanelf.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.1.20  
changed lines
  Added in v.1.61

  ViewVC Help
Powered by ViewVC 1.1.20