/[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.34 Revision 1.91
1/* 1/*
2 * Copyright 2003 Ned Ludd <solar@gentoo.org>
3 * Copyright 1999-2005 Gentoo Foundation 2 * Copyright 2003-2005 Gentoo Foundation
4 * Distributed under the terms of the GNU General Public License v2 3 * Distributed under the terms of the GNU General Public License v2
5 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.34 2005/04/12 19:11:32 solar Exp $ 4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.91 2005/12/07 01:04:52 vapier Exp $
6 * 5 *
7 ******************************************************************** 6 * Copyright 2003-2005 Ned Ludd - <solar@gentoo.org>
8 * This program is free software; you can redistribute it and/or 7 * Copyright 2004-2005 Mike Frysinger - <vapier@gentoo.org>
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
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21 * MA 02111-1307, USA.
22 */ 8 */
23 9
24#include <stdio.h> 10#include <stdio.h>
25#include <stdlib.h> 11#include <stdlib.h>
26#include <sys/types.h> 12#include <sys/types.h>
13#include <libgen.h>
14#include <limits.h>
27#define __USE_GNU 15#define __USE_GNU
28#include <string.h> 16#include <string.h>
29#include <errno.h> 17#include <errno.h>
30#include <unistd.h> 18#include <unistd.h>
31#include <sys/stat.h> 19#include <sys/stat.h>
32#include <dirent.h> 20#include <dirent.h>
33#include <getopt.h> 21#include <getopt.h>
34#include <assert.h> 22#include <assert.h>
35
36#include "paxelf.h" 23#include "paxinc.h"
37 24
38static const char *rcsid = "$Id: scanelf.c,v 1.34 2005/04/12 19:11:32 solar Exp $"; 25static const char *rcsid = "$Id: scanelf.c,v 1.91 2005/12/07 01:04:52 vapier Exp $";
26#define argv0 "scanelf"
39 27
40 28#define IS_MODIFIER(c) (c == '%' || c == '#')
41/* helper functions for showing errors */
42#define argv0 "scanelf" /*((*argv != NULL) ? argv[0] : __FILE__ "\b\b")*/
43#define warn(fmt, args...) \
44 fprintf(stderr, "%s: " fmt "\n", argv0, ## args)
45#define warnf(fmt, args...) warn("%s(): " fmt, __FUNCTION__, ## args)
46#define err(fmt, args...) \
47 do { \
48 warn(fmt, ## args); \
49 exit(EXIT_FAILURE); \
50 } while (0)
51 29
52 30
53 31
54/* prototypes */ 32/* prototypes */
55static void scanelf_file(const char *filename); 33static void scanelf_file(const char *filename);
56static void scanelf_dir(const char *path); 34static void scanelf_dir(const char *path);
57static void scanelf_ldpath(); 35static void scanelf_ldpath();
58static void scanelf_envpath(); 36static void scanelf_envpath();
59static void usage(int status); 37static void usage(int status);
60static void parseargs(int argc, char *argv[]); 38static void parseargs(int argc, char *argv[]);
39static char *xstrdup(const char *s);
40static void *xmalloc(size_t size);
41static void xstrcat(char **dst, const char *src, size_t *curr_len);
42static inline void xchrcat(char **dst, const char append, size_t *curr_len);
61 43
62/* variables to control behavior */ 44/* variables to control behavior */
45static char *ldpaths[256];
63static char scan_ldpath = 0; 46static char scan_ldpath = 0;
64static char scan_envpath = 0; 47static char scan_envpath = 0;
48static char scan_symlink = 1;
65static char dir_recurse = 0; 49static char dir_recurse = 0;
66static char dir_crossmount = 1; 50static char dir_crossmount = 1;
67static char show_pax = 0; 51static char show_pax = 0;
68static char show_stack = 0; 52static char show_phdr = 0;
69static char show_textrel = 0; 53static char show_textrel = 0;
70static char show_rpath = 0; 54static char show_rpath = 0;
71static char show_needed = 0; 55static char show_needed = 0;
56static char show_interp = 0;
57static char show_bind = 0;
58static char show_soname = 0;
59static char show_textrels = 0;
72static char show_banner = 1; 60static char show_banner = 1;
73static char be_quiet = 0; 61static char be_quiet = 0;
74static char be_verbose = 0; 62static char be_verbose = 0;
63static char be_wewy_wewy_quiet = 0;
64static char *find_sym = NULL, *versioned_symname = NULL;
75static char *find_sym = NULL; 65static char *find_lib = NULL;
66static char *out_format = NULL;
67static char *search_path = NULL;
68static char gmatch = 0;
76 69
77 70
78 71/* sub-funcs for scanelf_file() */
79/* scan an elf file and show all the fun stuff */ 72static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
80static void scanelf_file(const char *filename)
81{ 73{
82 int i; 74 /* find the best SHT_DYNSYM and SHT_STRTAB sections */
83 char found_pax, found_stack, found_relro, found_textrel, 75#define GET_SYMTABS(B) \
84 found_rpath, found_needed, found_sym; 76 if (elf->elf_class == ELFCLASS ## B) { \
85 elfobj *elf; 77 Elf ## B ## _Shdr *symtab, *strtab, *dynsym, *dynstr; \
78 /* debug sections */ \
79 symtab = SHDR ## B (elf_findsecbyname(elf, ".symtab")); \
80 strtab = SHDR ## B (elf_findsecbyname(elf, ".strtab")); \
81 /* runtime sections */ \
82 dynsym = SHDR ## B (elf_findsecbyname(elf, ".dynsym")); \
83 dynstr = SHDR ## B (elf_findsecbyname(elf, ".dynstr")); \
84 if (symtab && dynsym) { \
85 *sym = (void*)((EGET(symtab->sh_size) > EGET(dynsym->sh_size)) ? symtab : dynsym); \
86 } else { \
87 *sym = (void*)(symtab ? symtab : dynsym); \
88 } \
89 if (strtab && dynstr) { \
90 *tab = (void*)((EGET(strtab->sh_size) > EGET(dynstr->sh_size)) ? strtab : dynstr); \
91 } else { \
92 *tab = (void*)(strtab ? strtab : dynstr); \
93 } \
94 }
95 GET_SYMTABS(32)
96 GET_SYMTABS(64)
97}
98static char *scanelf_file_pax(elfobj *elf, char *found_pax)
99{
100 static char ret[7];
101 unsigned long i, shown;
102
103 if (!show_pax) return NULL;
104
105 shown = 0;
106 memset(&ret, 0, sizeof(ret));
107
108 if (elf->phdr) {
109#define SHOW_PAX(B) \
110 if (elf->elf_class == ELFCLASS ## B) { \
111 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
112 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
113 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
114 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
115 continue; \
116 if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \
117 continue; \
118 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
119 *found_pax = 1; \
120 ++shown; \
121 break; \
122 } \
123 }
124 SHOW_PAX(32)
125 SHOW_PAX(64)
126 }
127
128 /* fall back to EI_PAX if no PT_PAX was found */
129 if (!*ret) {
130 static char *paxflags;
131 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
132 if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
133 *found_pax = 1;
134 return (be_wewy_wewy_quiet ? NULL : paxflags);
135 }
136 strncpy(ret, paxflags, sizeof(ret));
137 }
138
139 if (be_wewy_wewy_quiet || (be_quiet && !shown))
140 return NULL;
141 else
142 return ret;
143}
144
145static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
146{
147 static char ret[12];
148 char *found;
149 unsigned long i, shown;
150 unsigned char multi_stack, multi_relro, multi_load;
151
152 if (!show_phdr) return NULL;
153
154 memcpy(ret, "--- --- ---\0", 12);
155
156 shown = 0;
157 multi_stack = multi_relro = multi_load = 0;
158
159#define SHOW_PHDR(B) \
160 if (elf->elf_class == ELFCLASS ## B) { \
161 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
162 Elf ## B ## _Off offset; \
163 uint32_t flags, check_flags; \
164 if (elf->phdr != NULL) { \
165 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
166 for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \
167 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
168 if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
169 found = found_phdr; \
170 offset = 0; \
171 check_flags = PF_X; \
172 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
173 if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
174 found = found_relro; \
175 offset = 4; \
176 check_flags = PF_X; \
177 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
178 if (multi_load++ > 2) warnf("%s: more than 2 PT_LOAD's !?", elf->filename); \
179 found = found_load; \
180 offset = 8; \
181 check_flags = PF_W|PF_X; \
182 } else \
183 continue; \
184 flags = EGET(phdr[i].p_flags); \
185 if (be_quiet && ((flags & check_flags) != check_flags)) \
186 continue; \
187 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \
188 *found = 1; \
189 ++shown; \
190 } \
191 } else if (elf->shdr != NULL) { \
192 /* no program headers which means this is prob an object file */ \
193 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
194 Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \
195 check_flags = SHF_WRITE|SHF_EXECINSTR; \
196 for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \
197 if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \
198 offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \
199 if (!strcmp((char*)(elf->data + offset), ".note.GNU-stack")) { \
200 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \
201 flags = EGET(shdr[i].sh_flags); \
202 if (be_quiet && ((flags & check_flags) != check_flags)) \
203 continue; \
204 ++*found_phdr; \
205 shown = 1; \
206 if (flags & SHF_WRITE) ret[0] = 'W'; \
207 if (flags & SHF_ALLOC) ret[1] = 'A'; \
208 if (flags & SHF_EXECINSTR) ret[2] = 'X'; \
209 if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \
210 break; \
211 } \
212 } \
213 if (!multi_stack) { \
214 *found_phdr = 1; \
215 shown = 1; \
216 memcpy(ret, "!WX", 3); \
217 } \
218 } \
219 }
220 SHOW_PHDR(32)
221 SHOW_PHDR(64)
222
223 if (be_wewy_wewy_quiet || (be_quiet && !shown))
224 return NULL;
225 else
226 return ret;
227}
228static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
229{
230 static const char *ret = "TEXTREL";
231 unsigned long i;
232
233 if (!show_textrel && !show_textrels) return NULL;
234
235 if (elf->phdr) {
236#define SHOW_TEXTREL(B) \
237 if (elf->elf_class == ELFCLASS ## B) { \
238 Elf ## B ## _Dyn *dyn; \
239 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
240 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
241 Elf ## B ## _Off offset; \
242 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
243 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
244 offset = EGET(phdr[i].p_offset); \
245 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
246 dyn = DYN ## B (elf->data + offset); \
247 while (EGET(dyn->d_tag) != DT_NULL) { \
248 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
249 *found_textrel = 1; \
250 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
251 return (be_wewy_wewy_quiet ? NULL : ret); \
252 } \
253 ++dyn; \
254 } \
255 } }
256 SHOW_TEXTREL(32)
257 SHOW_TEXTREL(64)
258 }
259
260 if (be_quiet || be_wewy_wewy_quiet)
261 return NULL;
262 else
263 return " - ";
264}
265static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
266{
267 unsigned long s, r, rmax;
268 void *symtab_void, *strtab_void, *text_void;
269
270 if (!show_textrels) return NULL;
271
272 /* don't search for TEXTREL's if the ELF doesn't have any */
273 if (!*found_textrel) scanelf_file_textrel(elf, found_textrel);
274 if (!*found_textrel) return NULL;
275
276 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
277 text_void = elf_findsecbyname(elf, ".text");
278
279 if (symtab_void && strtab_void && text_void && elf->shdr) {
280#define SHOW_TEXTRELS(B) \
281 if (elf->elf_class == ELFCLASS ## B) { \
282 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
283 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
284 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
285 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
286 Elf ## B ## _Shdr *text = SHDR ## B (text_void); \
287 Elf ## B ## _Addr vaddr = EGET(text->sh_addr); \
288 uint ## B ## _t memsz = EGET(text->sh_size); \
289 Elf ## B ## _Rel *rel; \
290 Elf ## B ## _Rela *rela; \
291 /* search the section headers for relocations */ \
292 for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
293 uint32_t sh_type = EGET(shdr[s].sh_type); \
294 if (sh_type == SHT_REL) { \
295 rel = REL ## B (elf->data + EGET(shdr[s].sh_offset)); \
296 rela = NULL; \
297 rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
298 } else if (sh_type == SHT_RELA) { \
299 rel = NULL; \
300 rela = RELA ## B (elf->data + EGET(shdr[s].sh_offset)); \
301 rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
302 } else \
303 continue; \
304 /* now see if any of the relocs are in the .text */ \
305 for (r = 0; r < rmax; ++r) { \
306 unsigned long sym_max; \
307 Elf ## B ## _Addr offset_tmp; \
308 Elf ## B ## _Sym *func; \
309 Elf ## B ## _Sym *sym; \
310 Elf ## B ## _Addr r_offset; \
311 uint ## B ## _t r_info; \
312 if (sh_type == SHT_REL) { \
313 r_offset = EGET(rel[r].r_offset); \
314 r_info = EGET(rel[r].r_info); \
315 } else { \
316 r_offset = EGET(rela[r].r_offset); \
317 r_info = EGET(rela[r].r_info); \
318 } \
319 /* make sure this relocation is inside of the .text */ \
320 if (r_offset < vaddr || r_offset >= vaddr + memsz) { \
321 if (be_verbose <= 2) continue; \
322 } else \
323 *found_textrels = 1; \
324 /* locate this relocation symbol name */ \
325 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
326 sym_max = ELF ## B ## _R_SYM(r_info); \
327 if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
328 sym += sym_max; \
329 else \
330 sym = NULL; \
331 sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
332 /* show the raw details about this reloc */ \
333 printf(" %s: ", elf->base_filename); \
334 if (sym && sym->st_name) \
335 printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name))); \
336 else \
337 printf("(memory/fake?)"); \
338 printf(" [0x%lX]", (unsigned long)r_offset); \
339 /* now try to find the closest symbol that this rel is probably in */ \
340 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
341 func = NULL; \
342 offset_tmp = 0; \
343 while (sym_max--) { \
344 if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
345 func = sym; \
346 offset_tmp = EGET(sym->st_value); \
347 } \
348 ++sym; \
349 } \
350 printf(" in "); \
351 if (func && func->st_name) \
352 printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(func->st_name))); \
353 else \
354 printf("(NULL: fake?)"); \
355 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
356 } \
357 } }
358 SHOW_TEXTRELS(32)
359 SHOW_TEXTRELS(64)
360 }
361 if (!*found_textrels)
362 warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
363
364 return NULL;
365}
366
367static void rpath_security_checks(elfobj *, char *);
368static void rpath_security_checks(elfobj *elf, char *item) {
86 struct stat st; 369 struct stat st;
87 370 switch (*item) {
88 /* make sure path exists */ 371 case '/': break;
89 if (lstat(filename, &st) == -1) 372 case '.':
90 return; 373 warnf("Security problem with relative RPATH '%s' in %s", item, elf->filename);
91 if (!S_ISREG(st.st_mode)) 374 break;
92 return; 375 case '\0':
93 found_pax = found_stack = found_relro = found_textrel = \ 376 warnf("Security problem NULL RPATH in %s", elf->filename);
94 found_rpath = found_needed = found_sym = 0; 377 break;
95 378 case '$':
96 /* verify this is real ELF */ 379 if (fstat(elf->fd, &st) != -1)
97 if ((elf = readelf(filename)) == NULL) { 380 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
98 if (be_verbose > 2) printf("%s: not an ELF\n", filename); 381 warnf("Security problem with RPATH='%s' in %s with mode set of %o",
99 return; 382 item, elf->filename, st.st_mode & 07777);
100 } 383 break;
101 384 default:
102 if (be_verbose > 1) 385 warnf("Maybe? sec problem with RPATH='%s' in %s", item, elf->filename);
103 printf("%s: {%s,%s} scanning file\n", filename, 386 break;
104 get_elfeitype(elf, EI_CLASS, elf->elf_class),
105 get_elfeitype(elf, EI_DATA, elf->data[EI_DATA]));
106 else if (be_verbose)
107 printf("%s: scanning file\n", filename);
108
109 /* show the header */
110 if (!be_quiet && show_banner) {
111 printf(" TYPE ");
112 if (show_pax) printf(" PAX ");
113 if (show_stack) printf("STK/REL ");
114 if (show_textrel) printf("TEXTREL ");
115 if (show_rpath) printf("RPATH ");
116 if (show_needed) printf("NEEDED ");
117 printf(" FILE\n");
118 show_banner = 0;
119 }
120
121 /* dump all the good stuff */
122 if (!be_quiet)
123 printf("%-7s ", get_elfetype(elf));
124
125 if (show_pax) {
126 char *paxflags = pax_short_hf_flags(PAX_FLAGS(elf));
127 if (!be_quiet || (be_quiet && strncmp(paxflags, "PeMRxS", 6))) {
128 found_pax = 1;
129 printf("%s ", pax_short_hf_flags(PAX_FLAGS(elf)));
130 } 387 }
131 } 388}
132 389static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
133 /* stack fun */ 390{
134 if (show_stack) { 391 unsigned long i, s;
135#define SHOW_STACK(B) \
136 if (elf->elf_class == ELFCLASS ## B) { \
137 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
138 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
139 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
140 if (EGET(phdr[i].p_type) != PT_GNU_STACK && \
141 EGET(phdr[i].p_type) != PT_GNU_RELRO) continue; \
142 if (be_quiet && !(EGET(phdr[i].p_flags) & PF_X)) \
143 continue; \
144 if (EGET(phdr[i].p_type) == PT_GNU_STACK) \
145 found_stack = 1; \
146 if (EGET(phdr[i].p_type) == PT_GNU_RELRO) \
147 found_relro = 1; \
148 printf("%s ", gnu_short_stack_flags(EGET(phdr[i].p_flags))); \
149 } \
150 }
151 SHOW_STACK(32)
152 SHOW_STACK(64)
153 if (!be_quiet && !found_stack) printf("--- ");
154 if (!be_quiet && !found_relro) printf("--- ");
155 }
156
157 /* textrel fun */
158 if (show_textrel) {
159#define SHOW_TEXTREL(B) \
160 if (elf->elf_class == ELFCLASS ## B) { \
161 Elf ## B ## _Dyn *dyn; \
162 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
163 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
164 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
165 if (phdr[i].p_type != PT_DYNAMIC) continue; \
166 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \
167 while (EGET(dyn->d_tag) != DT_NULL) { \
168 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
169 found_textrel = 1; \
170 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
171 fputs("TEXTREL ", stdout); \
172 } \
173 ++dyn; \
174 } \
175 } }
176 SHOW_TEXTREL(32)
177 SHOW_TEXTREL(64)
178 if (!be_quiet && !found_textrel) fputs("------- ", stdout);
179 }
180
181 /* rpath fun */
182 /* TODO: if be_quiet, only output RPATH's which aren't in /etc/ld.so.conf */
183 if (show_rpath) {
184 char *rpath, *runpath; 392 char *rpath, *runpath, **r;
393 void *strtbl_void;
394
395 if (!show_rpath) return;
396
185 void *strtbl_void = elf_findsecbyname(elf, ".dynstr"); 397 strtbl_void = elf_findsecbyname(elf, ".dynstr");
186 rpath = runpath = NULL; 398 rpath = runpath = NULL;
187 399
188 if (strtbl_void) { 400 if (elf->phdr && strtbl_void) {
189#define SHOW_RPATH(B) \ 401#define SHOW_RPATH(B) \
190 if (elf->elf_class == ELFCLASS ## B) { \ 402 if (elf->elf_class == ELFCLASS ## B) { \
191 Elf ## B ## _Dyn *dyn; \ 403 Elf ## B ## _Dyn *dyn; \
192 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 404 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
193 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 405 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
194 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 406 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
407 Elf ## B ## _Off offset; \
408 Elf ## B ## _Xword word; \
409 /* Scan all the program headers */ \
195 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 410 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
411 /* Just scan dynamic headers */ \
196 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 412 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
413 offset = EGET(phdr[i].p_offset); \
414 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
415 /* Just scan dynamic RPATH/RUNPATH headers */ \
197 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \ 416 dyn = DYN ## B (elf->data + offset); \
198 while (EGET(dyn->d_tag) != DT_NULL) { \ 417 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
199 if (EGET(dyn->d_tag) == DT_RPATH) { \ 418 if (word == DT_RPATH) { \
419 r = &rpath; \
420 } else if (word == DT_RUNPATH) { \
421 r = &runpath; \
422 } else { \
423 ++dyn; \
424 continue; \
425 } \
426 /* Verify the memory is somewhat sane */ \
200 rpath = elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 427 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
428 if (offset < (Elf ## B ## _Off)elf->len) { \
429 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
430 *r = (char*)(elf->data + offset); \
431 /* If quiet, don't output paths in ld.so.conf */ \
432 if (be_quiet) { \
433 size_t len; \
434 char *start, *end; \
435 /* note that we only 'chop' off leading known paths. */ \
436 /* since *r is read-only memory, we can only move the ptr forward. */ \
437 start = *r; \
438 /* scan each path in : delimited list */ \
439 while (start) { \
440 rpath_security_checks(elf, start); \
441 end = strchr(start, ':'); \
442 len = (end ? abs(end - start) : strlen(start)); \
443 for (s = 0; ldpaths[s]; ++s) { \
444 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
445 *r = (end ? end + 1 : NULL); \
446 break; \
447 } \
448 } \
449 if (!*r || !ldpaths[s] || !end) \
450 start = NULL; \
451 else \
452 start = start + len + 1; \
453 } \
454 } \
201 found_rpath = 1; \ 455 if (*r) *found_rpath = 1; \
202 } else if (EGET(dyn->d_tag) == DT_RUNPATH) { \
203 runpath = elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
204 found_rpath = 1; \
205 } \ 456 } \
206 ++dyn; \ 457 ++dyn; \
207 } \ 458 } \
208 } } 459 } }
209 SHOW_RPATH(32) 460 SHOW_RPATH(32)
210 SHOW_RPATH(64) 461 SHOW_RPATH(64)
462 }
463
464 if (be_wewy_wewy_quiet) return;
465
466 if (rpath && runpath) {
467 if (!strcmp(rpath, runpath)) {
468 xstrcat(ret, runpath, ret_len);
469 } else {
470 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
471 xchrcat(ret, '{', ret_len);
472 xstrcat(ret, rpath, ret_len);
473 xchrcat(ret, ',', ret_len);
474 xstrcat(ret, runpath, ret_len);
475 xchrcat(ret, '}', ret_len);
211 } 476 }
212 if (rpath && runpath) {
213 if (!strcmp(rpath, runpath))
214 printf("%-5s ", runpath);
215 else {
216 fprintf(stderr, "%s's RPATH [%s] != RUNPATH [%s]\n", filename, rpath, runpath);
217 printf("{%s,%s} ", rpath, runpath);
218 }
219 } else if (rpath || runpath) 477 } else if (rpath || runpath)
220 printf("%-5s ", (runpath ? runpath : rpath)); 478 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
221 else if (!be_quiet && !found_rpath) 479 else if (!be_quiet)
222 printf(" - "); 480 xstrcat(ret, " - ", ret_len);
223 } 481}
224 482static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
225 /* print out all the NEEDED entries */ 483{
226 if (show_needed) { 484 unsigned long i;
227 char *needed; 485 char *needed;
486 void *strtbl_void;
487
488 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL;
489
228 void *strtbl_void = elf_findsecbyname(elf, ".dynstr"); 490 strtbl_void = elf_findsecbyname(elf, ".dynstr");
229 491
230 if (strtbl_void) { 492 if (elf->phdr && strtbl_void) {
231#define SHOW_NEEDED(B) \ 493#define SHOW_NEEDED(B) \
232 if (elf->elf_class == ELFCLASS ## B) { \ 494 if (elf->elf_class == ELFCLASS ## B) { \
233 Elf ## B ## _Dyn *dyn; \ 495 Elf ## B ## _Dyn *dyn; \
234 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 496 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
235 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 497 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
236 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 498 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
499 Elf ## B ## _Off offset; \
237 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 500 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
238 if (be_verbose && EGET(phdr[i].p_type) == PT_INTERP) { \
239 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \
240 printf("%s\n", elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr)); \
241 exit(0); \
242 } \
243 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 501 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
502 offset = EGET(phdr[i].p_offset); \
503 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
244 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \ 504 dyn = DYN ## B (elf->data + offset); \
245 while (EGET(dyn->d_tag) != DT_NULL) { \ 505 while (EGET(dyn->d_tag) != DT_NULL) { \
246 if (EGET(dyn->d_tag) == DT_NEEDED) { \ 506 if (EGET(dyn->d_tag) == DT_NEEDED) { \
247 needed = elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 507 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
248 if (found_needed) printf(","); \ 508 if (offset >= (Elf ## B ## _Off)elf->len) { \
249 printf("%s", needed); \ 509 ++dyn; \
510 continue; \
511 } \
512 needed = (char*)(elf->data + offset); \
513 if (op == 0) { \
514 if (!be_wewy_wewy_quiet) { \
515 if (*found_needed) xchrcat(ret, ',', ret_len); \
516 xstrcat(ret, needed, ret_len); \
517 } \
250 found_needed = 1; \ 518 *found_needed = 1; \
519 } else { \
520 if (!strncmp(find_lib, needed, strlen( !gmatch ? needed : find_lib))) { \
521 *found_lib = 1; \
522 return (be_wewy_wewy_quiet ? NULL : needed); \
523 } \
524 } \
251 } \ 525 } \
252 ++dyn; \ 526 ++dyn; \
253 } \ 527 } \
254 } } 528 } }
255 SHOW_NEEDED(32) 529 SHOW_NEEDED(32)
256 SHOW_NEEDED(64) 530 SHOW_NEEDED(64)
531 if (op == 0 && !*found_needed && be_verbose)
532 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
533 }
534
535 return NULL;
536}
537static char *scanelf_file_interp(elfobj *elf, char *found_interp)
538{
539 void *strtbl_void;
540
541 if (!show_interp) return NULL;
542
543 strtbl_void = elf_findsecbyname(elf, ".interp");
544
545 if (strtbl_void) {
546#define SHOW_INTERP(B) \
547 if (elf->elf_class == ELFCLASS ## B) { \
548 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
549 *found_interp = 1; \
550 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
257 } 551 }
258 if (!be_quiet && !found_needed) 552 SHOW_INTERP(32)
259 printf(" - "); 553 SHOW_INTERP(64)
260 else if (found_needed)
261 printf(" ");
262 } 554 }
555 return NULL;
556}
557static char *scanelf_file_bind(elfobj *elf, char *found_bind)
558{
559 unsigned long i;
560 struct stat s;
263 561
264 /* search the symbol table for a specified symbol */ 562 if (!show_bind) return NULL;
265 if (find_sym) { 563 if (!elf->phdr) return NULL;
564
565#define SHOW_BIND(B) \
566 if (elf->elf_class == ELFCLASS ## B) { \
567 Elf ## B ## _Dyn *dyn; \
568 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
569 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
570 Elf ## B ## _Off offset; \
571 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
572 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
573 offset = EGET(phdr[i].p_offset); \
574 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
575 dyn = DYN ## B (elf->data + offset); \
576 while (EGET(dyn->d_tag) != DT_NULL) { \
577 if (EGET(dyn->d_tag) == DT_BIND_NOW || \
578 (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
579 { \
580 if (be_quiet) return NULL; \
581 *found_bind = 1; \
582 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
583 } \
584 ++dyn; \
585 } \
586 } \
587 }
588 SHOW_BIND(32)
589 SHOW_BIND(64)
590
591 if (be_wewy_wewy_quiet) return NULL;
592
593 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
594 return NULL;
595 } else {
596 *found_bind = 1;
597 return (char *) "LAZY";
598 }
599}
600static char *scanelf_file_soname(elfobj *elf, char *found_soname)
601{
602 unsigned long i;
603 char *soname;
604 void *strtbl_void;
605
606 if (!show_soname) return NULL;
607
608 strtbl_void = elf_findsecbyname(elf, ".dynstr");
609
610 if (elf->phdr && strtbl_void) {
611#define SHOW_SONAME(B) \
612 if (elf->elf_class == ELFCLASS ## B) { \
613 Elf ## B ## _Dyn *dyn; \
614 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
615 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
616 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
617 Elf ## B ## _Off offset; \
618 /* only look for soname in shared objects */ \
619 if (ehdr->e_type != ET_DYN) \
620 return NULL; \
621 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
622 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
623 offset = EGET(phdr[i].p_offset); \
624 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
625 dyn = DYN ## B (elf->data + offset); \
626 while (EGET(dyn->d_tag) != DT_NULL) { \
627 if (EGET(dyn->d_tag) == DT_SONAME) { \
628 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
629 if (offset >= (Elf ## B ## _Off)elf->len) { \
630 ++dyn; \
631 continue; \
632 } \
633 soname = (char*)(elf->data + offset); \
634 *found_soname = 1; \
635 return (be_wewy_wewy_quiet ? NULL : soname); \
636 } \
637 ++dyn; \
638 } \
639 } }
640 SHOW_SONAME(32)
641 SHOW_SONAME(64)
642 }
643
644 return NULL;
645}
646static char *scanelf_file_sym(elfobj *elf, char *found_sym)
647{
648 unsigned long i;
266 void *symtab_void, *strtab_void; 649 void *symtab_void, *strtab_void;
267 char *versioned_symname;
268 size_t len;
269 650
270 len = strlen(find_sym) + 1; 651 if (!find_sym) return NULL;
271 versioned_symname = (char *)malloc(sizeof(char) * (len+1));
272 if (!versioned_symname) {
273 warnf("Could not malloc() mem for sym scan");
274 return;
275 }
276 sprintf(versioned_symname, "%s@", find_sym);
277 652
278 symtab_void = elf_findsecbyname(elf, ".symtab"); 653 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
279 strtab_void = elf_findsecbyname(elf, ".strtab");
280 654
281 if (symtab_void && strtab_void) { 655 if (symtab_void && strtab_void) {
282#define FIND_SYM(B) \ 656#define FIND_SYM(B) \
283 if (elf->elf_class == ELFCLASS ## B) { \ 657 if (elf->elf_class == ELFCLASS ## B) { \
284 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ 658 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
285 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \ 659 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
286 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ 660 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
287 int cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \ 661 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
288 char *symname; \ 662 char *symname; \
289 for (i = 0; i < cnt; ++i) { \ 663 for (i = 0; i < cnt; ++i) { \
290 if (sym->st_name) { \ 664 if (sym->st_name) { \
291 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \ 665 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
292 if (*find_sym == '*') { \ 666 if (*find_sym == '*') { \
293 printf("%s(%s) %5lX %15s %s\n", \ 667 printf("%s(%s) %5lX %15s %s\n", \
294 ((found_sym == 0) ? "\n\t" : "\t"), \ 668 ((*found_sym == 0) ? "\n\t" : "\t"), \
295 (char *)basename(filename), \ 669 elf->base_filename, \
296 (long)sym->st_size, \ 670 (long)sym->st_size, \
297 (char *)get_elfstttype(sym->st_info), \ 671 (char *)get_elfstttype(sym->st_info), \
298 symname); \ 672 symname); \
299 found_sym = 1; \ 673 *found_sym = 1; \
300 } else if ((strcmp(find_sym, symname) == 0) || \ 674 } else if ((strcmp(find_sym, symname) == 0) || \
301 (strncmp(symname, versioned_symname, len) == 0)) \ 675 (strcmp(symname, versioned_symname) == 0)) \
302 found_sym++; \ 676 (*found_sym)++; \
303 } \ 677 } \
304 ++sym; \ 678 ++sym; \
305 } } 679 } }
306 FIND_SYM(32) 680 FIND_SYM(32)
307 FIND_SYM(64) 681 FIND_SYM(64)
682 }
683
684 if (be_wewy_wewy_quiet) return NULL;
685
686 if (*find_sym != '*' && *found_sym)
687 return find_sym;
688 if (be_quiet)
689 return NULL;
690 else
691 return (char *)" - ";
692}
693/* scan an elf file and show all the fun stuff */
694#define prints(str) write(fileno(stdout), str, strlen(str))
695static void scanelf_file(const char *filename)
696{
697 unsigned long i;
698 char found_pax, found_phdr, found_relro, found_load, found_textrel,
699 found_rpath, found_needed, found_interp, found_bind, found_soname,
700 found_sym, found_lib, found_file, found_textrels;
701 elfobj *elf;
702 struct stat st;
703 static char *out_buffer = NULL;
704 static size_t out_len;
705
706 /* make sure 'filename' exists */
707 if (lstat(filename, &st) == -1) {
708 if (be_verbose > 2) printf("%s: does not exist\n", filename);
709 return;
710 }
711 /* always handle regular files and handle symlinked files if no -y */
712 if (S_ISLNK(st.st_mode)) {
713 if (!scan_symlink) return;
714 stat(filename, &st);
715 }
716 if (!S_ISREG(st.st_mode)) {
717 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
718 return;
719 }
720
721 found_pax = found_phdr = found_relro = found_load = found_textrel = \
722 found_rpath = found_needed = found_interp = found_bind = found_soname = \
723 found_sym = found_lib = found_file = found_textrels = 0;
724
725 /* verify this is real ELF */
726 if ((elf = readelf(filename)) == NULL) {
727 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
728 return;
729 }
730
731 if (be_verbose > 1)
732 printf("%s: scanning file {%s,%s}\n", filename,
733 get_elfeitype(EI_CLASS, elf->elf_class),
734 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
735 else if (be_verbose)
736 printf("%s: scanning file\n", filename);
737
738 /* init output buffer */
739 if (!out_buffer) {
740 out_len = sizeof(char) * 80;
741 out_buffer = (char*)xmalloc(out_len);
742 }
743 *out_buffer = '\0';
744
745 /* show the header */
746 if (!be_quiet && show_banner) {
747 for (i = 0; out_format[i]; ++i) {
748 if (!IS_MODIFIER(out_format[i])) continue;
749
750 switch (out_format[++i]) {
751 case '%': break;
752 case '#': break;
753 case 'F':
754 case 'p':
755 case 'f': prints("FILE "); found_file = 1; break;
756 case 'o': prints(" TYPE "); break;
757 case 'x': prints(" PAX "); break;
758 case 'e': prints("STK/REL/PTL "); break;
759 case 't': prints("TEXTREL "); break;
760 case 'r': prints("RPATH "); break;
761 case 'n': prints("NEEDED "); break;
762 case 'i': prints("INTERP "); break;
763 case 'b': prints("BIND "); break;
764 case 'S': prints("SONAME "); break;
765 case 's': prints("SYM "); break;
766 case 'N': prints("LIB "); break;
767 case 'T': prints("TEXTRELS "); break;
768 default: warnf("'%c' has no title ?", out_format[i]);
769 }
308 } 770 }
309 free(versioned_symname); 771 if (!found_file) prints("FILE ");
310 if (*find_sym != '*') { 772 prints("\n");
311 if (found_sym) 773 found_file = 0;
312 printf(" %s ", find_sym); 774 show_banner = 0;
313 else if (!be_quiet) 775 }
314 fputs(" - ", stdout); 776
777 /* dump all the good stuff */
778 for (i = 0; out_format[i]; ++i) {
779 const char *out;
780 const char *tmp;
781
782 /* make sure we trim leading spaces in quiet mode */
783 if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
784 *out_buffer = '\0';
785
786 if (!IS_MODIFIER(out_format[i])) {
787 xchrcat(&out_buffer, out_format[i], &out_len);
788 continue;
315 } 789 }
790
791 out = NULL;
792 be_wewy_wewy_quiet = (out_format[i] == '#');
793 switch (out_format[++i]) {
794 case '%':
795 case '#':
796 xchrcat(&out_buffer, out_format[i], &out_len); break;
797 case 'F':
798 found_file = 1;
799 if (be_wewy_wewy_quiet) break;
800 xstrcat(&out_buffer, filename, &out_len);
801 break;
802 case 'p':
803 found_file = 1;
804 if (be_wewy_wewy_quiet) break;
805 tmp = filename;
806 if (search_path) {
807 ssize_t len_search = strlen(search_path);
808 ssize_t len_file = strlen(filename);
809 if (!strncmp(filename, search_path, len_search) && \
810 len_file > len_search)
811 tmp += len_search;
812 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
813 }
814 xstrcat(&out_buffer, tmp, &out_len);
815 break;
816 case 'f':
817 found_file = 1;
818 if (be_wewy_wewy_quiet) break;
819 tmp = strrchr(filename, '/');
820 tmp = (tmp == NULL ? filename : tmp+1);
821 xstrcat(&out_buffer, tmp, &out_len);
822 break;
823 case 'o': out = get_elfetype(elf); break;
824 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
825 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
826 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
827 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
828 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
829 case 'n':
830 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
831 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
832 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
833 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
834 case 's': out = scanelf_file_sym(elf, &found_sym); break;
835 default: warnf("'%c' has no scan code?", out_format[i]);
316 } 836 }
837 if (out) xstrcat(&out_buffer, out, &out_len);
838 }
317 839
318 if (!be_quiet || found_pax || found_stack || found_textrel || \ 840#define FOUND_SOMETHING() \
319 found_rpath || found_needed || found_sym) 841 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
320 puts(filename); 842 found_rpath || found_needed || found_interp || found_bind || \
843 found_soname || found_sym || found_lib || found_textrels)
844
845 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
846 xchrcat(&out_buffer, ' ', &out_len);
847 xstrcat(&out_buffer, filename, &out_len);
848 }
849 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
850 puts(out_buffer);
851 fflush(stdout);
852 }
321 853
322 unreadelf(elf); 854 unreadelf(elf);
323} 855}
324 856
325/* scan a directory for ET_EXEC files and print when we find one */ 857/* scan a directory for ET_EXEC files and print when we find one */
330 struct stat st_top, st; 862 struct stat st_top, st;
331 char buf[_POSIX_PATH_MAX]; 863 char buf[_POSIX_PATH_MAX];
332 size_t pathlen = 0, len = 0; 864 size_t pathlen = 0, len = 0;
333 865
334 /* make sure path exists */ 866 /* make sure path exists */
335 if (lstat(path, &st_top) == -1) 867 if (lstat(path, &st_top) == -1) {
868 if (be_verbose > 2) printf("%s: does not exist\n", path);
336 return; 869 return;
870 }
337 871
338 /* ok, if it isn't a directory, assume we can open it */ 872 /* ok, if it isn't a directory, assume we can open it */
339 if (!S_ISDIR(st_top.st_mode)) { 873 if (!S_ISDIR(st_top.st_mode)) {
340 scanelf_file(path); 874 scanelf_file(path);
341 return; 875 return;
352 while ((dentry = readdir(dir))) { 886 while ((dentry = readdir(dir))) {
353 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) 887 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
354 continue; 888 continue;
355 len = (pathlen + 1 + strlen(dentry->d_name) + 1); 889 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
356 if (len >= sizeof(buf)) { 890 if (len >= sizeof(buf)) {
357 warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf)); 891 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
892 (unsigned long)len, (unsigned long)sizeof(buf));
358 continue; 893 continue;
359 } 894 }
360 sprintf(buf, "%s/%s", path, dentry->d_name); 895 sprintf(buf, "%s/%s", path, dentry->d_name);
361 if (lstat(buf, &st) != -1) { 896 if (lstat(buf, &st) != -1) {
362 if (S_ISREG(st.st_mode)) 897 if (S_ISREG(st.st_mode))
368 } 903 }
369 } 904 }
370 closedir(dir); 905 closedir(dir);
371} 906}
372 907
908static int scanelf_from_file(char *filename)
909{
910 FILE *fp = NULL;
911 char *p;
912 char path[_POSIX_PATH_MAX];
913
914 if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
915 fp = stdin;
916 else if ((fp = fopen(filename, "r")) == NULL)
917 return 1;
918
919 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
920 if ((p = strchr(path, '\n')) != NULL)
921 *p = 0;
922 search_path = path;
923 scanelf_dir(path);
924 }
925 if (fp != stdin)
926 fclose(fp);
927 return 0;
928}
929
930static void load_ld_so_conf()
931{
932 FILE *fp = NULL;
933 char *p;
934 char path[_POSIX_PATH_MAX];
935 int i = 0;
936
937 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
938 return;
939
940 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
941 if (*path != '/')
942 continue;
943
944 if ((p = strrchr(path, '\r')) != NULL)
945 *p = 0;
946 if ((p = strchr(path, '\n')) != NULL)
947 *p = 0;
948
949 ldpaths[i++] = xstrdup(path);
950
951 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
952 break;
953 }
954 ldpaths[i] = NULL;
955
956 fclose(fp);
957}
958
373/* scan /etc/ld.so.conf for paths */ 959/* scan /etc/ld.so.conf for paths */
374static void scanelf_ldpath() 960static void scanelf_ldpath()
375{ 961{
376 char scan_l, scan_ul, scan_ull; 962 char scan_l, scan_ul, scan_ull;
377 char *path, *p; 963 int i = 0;
378 FILE *fp;
379 964
380 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL) 965 if (!ldpaths[0])
381 err("Unable to open ld.so.conf: %s", strerror(errno)); 966 err("Unable to load any paths from ld.so.conf");
382 967
383 scan_l = scan_ul = scan_ull = 0; 968 scan_l = scan_ul = scan_ull = 0;
384 969
385 if ((path = malloc(_POSIX_PATH_MAX)) == NULL) { 970 while (ldpaths[i]) {
386 warn("Can not malloc() memory for ldpath scanning");
387 return;
388 }
389 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL)
390 if (*path == '/') {
391 if ((p = strrchr(path, '\r')) != NULL)
392 *p = 0;
393 if ((p = strrchr(path, '\n')) != NULL)
394 *p = 0;
395 if (!scan_l && !strcmp(path, "/lib")) scan_l = 1; 971 if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
396 if (!scan_ul && !strcmp(path, "/usr/lib")) scan_ul = 1; 972 if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
397 if (!scan_ull && !strcmp(path, "/usr/local/lib")) scan_ull = 1; 973 if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
398 scanelf_dir(path); 974 scanelf_dir(ldpaths[i]);
975 ++i;
399 } 976 }
400 free(path);
401 fclose(fp);
402 977
403 if (!scan_l) scanelf_dir("/lib"); 978 if (!scan_l) scanelf_dir("/lib");
404 if (!scan_ul) scanelf_dir("/usr/lib"); 979 if (!scan_ul) scanelf_dir("/usr/lib");
405 if (!scan_ull) scanelf_dir("/usr/local/lib"); 980 if (!scan_ull) scanelf_dir("/usr/local/lib");
406} 981}
411 char *path, *p; 986 char *path, *p;
412 987
413 path = getenv("PATH"); 988 path = getenv("PATH");
414 if (!path) 989 if (!path)
415 err("PATH is not set in your env !"); 990 err("PATH is not set in your env !");
416 991 path = xstrdup(path);
417 if ((path = strdup(path)) == NULL)
418 err("strdup failed: %s", strerror(errno));
419 992
420 while ((p = strrchr(path, ':')) != NULL) { 993 while ((p = strrchr(path, ':')) != NULL) {
421 scanelf_dir(p + 1); 994 scanelf_dir(p + 1);
422 *p = 0; 995 *p = 0;
423 } 996 }
426} 999}
427 1000
428 1001
429 1002
430/* usage / invocation handling functions */ 1003/* usage / invocation handling functions */
431#define PARSE_FLAGS "plRmxetrns:aqvo:BhV" 1004#define PARSE_FLAGS "plRmyxetrnibSs:gN:TaqvF:f:o:BhV"
432#define a_argument required_argument 1005#define a_argument required_argument
433static struct option const long_opts[] = { 1006static struct option const long_opts[] = {
434 {"path", no_argument, NULL, 'p'}, 1007 {"path", no_argument, NULL, 'p'},
435 {"ldpath", no_argument, NULL, 'l'}, 1008 {"ldpath", no_argument, NULL, 'l'},
436 {"recursive", no_argument, NULL, 'R'}, 1009 {"recursive", no_argument, NULL, 'R'},
437 {"mount", no_argument, NULL, 'm'}, 1010 {"mount", no_argument, NULL, 'm'},
1011 {"symlink", no_argument, NULL, 'y'},
438 {"pax", no_argument, NULL, 'x'}, 1012 {"pax", no_argument, NULL, 'x'},
439 {"header", no_argument, NULL, 'e'}, 1013 {"header", no_argument, NULL, 'e'},
440 {"textrel", no_argument, NULL, 't'}, 1014 {"textrel", no_argument, NULL, 't'},
441 {"rpath", no_argument, NULL, 'r'}, 1015 {"rpath", no_argument, NULL, 'r'},
442 {"needed", no_argument, NULL, 'n'}, 1016 {"needed", no_argument, NULL, 'n'},
1017 {"interp", no_argument, NULL, 'i'},
1018 {"bind", no_argument, NULL, 'b'},
1019 {"soname", no_argument, NULL, 'S'},
443 {"symbol", a_argument, NULL, 's'}, 1020 {"symbol", a_argument, NULL, 's'},
1021 {"lib", a_argument, NULL, 'N'},
1022 {"gmatch", no_argument, NULL, 'g'},
1023 {"textrels", no_argument, NULL, 'T'},
444 {"all", no_argument, NULL, 'a'}, 1024 {"all", no_argument, NULL, 'a'},
445 {"quiet", no_argument, NULL, 'q'}, 1025 {"quiet", no_argument, NULL, 'q'},
446 {"verbose", no_argument, NULL, 'v'}, 1026 {"verbose", no_argument, NULL, 'v'},
1027 {"format", a_argument, NULL, 'F'},
1028 {"from", a_argument, NULL, 'f'},
447 {"file", a_argument, NULL, 'o'}, 1029 {"file", a_argument, NULL, 'o'},
448 {"nobanner", no_argument, NULL, 'B'}, 1030 {"nobanner", no_argument, NULL, 'B'},
449 {"help", no_argument, NULL, 'h'}, 1031 {"help", no_argument, NULL, 'h'},
450 {"version", no_argument, NULL, 'V'}, 1032 {"version", no_argument, NULL, 'V'},
451 {NULL, no_argument, NULL, 0x0} 1033 {NULL, no_argument, NULL, 0x0}
452}; 1034};
1035
453static char *opts_help[] = { 1036static const char *opts_help[] = {
454 "Scan all directories in PATH environment", 1037 "Scan all directories in PATH environment",
455 "Scan all directories in /etc/ld.so.conf", 1038 "Scan all directories in /etc/ld.so.conf",
456 "Scan directories recursively", 1039 "Scan directories recursively",
457 "Don't recursively cross mount points\n", 1040 "Don't recursively cross mount points",
1041 "Don't scan symlinks\n",
458 "Print PaX markings", 1042 "Print PaX markings",
459 "Print GNU_STACK markings", 1043 "Print GNU_STACK/PT_LOAD markings",
460 "Print TEXTREL information", 1044 "Print TEXTREL information",
461 "Print RPATH information", 1045 "Print RPATH information",
462 "Print NEEDED information", 1046 "Print NEEDED information",
1047 "Print INTERP information",
1048 "Print BIND information",
1049 "Print SONAME information",
463 "Find a specified symbol", 1050 "Find a specified symbol",
1051 "Find a specified library",
1052 "Use strncmp to match libraries. (use with -N)",
1053 "Locate cause of TEXTREL",
464 "Print all scanned info (-x -e -t -r)\n", 1054 "Print all scanned info (-x -e -t -r -b)\n",
465 "Only output 'bad' things", 1055 "Only output 'bad' things",
466 "Be verbose (can be specified more than once)", 1056 "Be verbose (can be specified more than once)",
1057 "Use specified format for output",
1058 "Read input stream from a filename",
467 "Write output stream to a filename", 1059 "Write output stream to a filename",
468 "Don't display the header", 1060 "Don't display the header",
469 "Print this help and exit", 1061 "Print this help and exit",
470 "Print version and exit", 1062 "Print version and exit",
471 NULL 1063 NULL
472}; 1064};
473 1065
474/* display usage and exit */ 1066/* display usage and exit */
475static void usage(int status) 1067static void usage(int status)
476{ 1068{
477 int i; 1069 unsigned long i;
478 printf(" Scan ELF binaries for stuff\n" 1070 printf("* Scan ELF binaries for stuff\n\n"
479 "Usage: %s [options] <dir1> [dir2 dirN ...]\n\n", argv0); 1071 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0);
480 printf("Options:\n"); 1072 printf("Options: -[%s]\n", PARSE_FLAGS);
481 for (i = 0; long_opts[i].name; ++i) 1073 for (i = 0; long_opts[i].name; ++i)
482 if (long_opts[i].has_arg == no_argument) 1074 if (long_opts[i].has_arg == no_argument)
483 printf(" -%c, --%-13s %s\n", long_opts[i].val, 1075 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
484 long_opts[i].name, opts_help[i]); 1076 long_opts[i].name, opts_help[i]);
485 else 1077 else
486 printf(" -%c, --%-6s <arg> %s\n", long_opts[i].val, 1078 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
487 long_opts[i].name, opts_help[i]); 1079 long_opts[i].name, opts_help[i]);
1080
1081 if (status != EXIT_SUCCESS)
1082 exit(status);
1083
1084 puts("\nThe format modifiers for the -F option are:");
1085 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1086 puts(" t TEXTREL \tr RPATH \tn NEEDED");
1087 puts(" i INTERP \tb BIND \ts symbol");
1088 puts(" N library \to Type \tT TEXTRELs");
1089 puts(" S SONAME");
1090 puts(" p filename (with search path removed)");
1091 puts(" f filename (short name/basename)");
1092 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1093
488 exit(status); 1094 exit(status);
489} 1095}
490 1096
491/* parse command line arguments and preform needed actions */ 1097/* parse command line arguments and preform needed actions */
492static void parseargs(int argc, char *argv[]) 1098static void parseargs(int argc, char *argv[])
493{ 1099{
494 int flag; 1100 int i;
1101 char *from_file = NULL;
495 1102
496 opterr = 0; 1103 opterr = 0;
497 while ((flag=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { 1104 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
498 switch (flag) { 1105 switch (i) {
499 1106
500 case 'V': /* version info */ 1107 case 'V':
501 printf("%s compiled %s\n%s\n" 1108 printf("pax-utils-%s: %s compiled %s\n%s\n"
502 "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n", 1109 "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
503 __FILE__, __DATE__, rcsid, argv0); 1110 VERSION, __FILE__, __DATE__, rcsid, argv0);
504 exit(EXIT_SUCCESS); 1111 exit(EXIT_SUCCESS);
505 break; 1112 break;
506 case 'h': usage(EXIT_SUCCESS); break; 1113 case 'h': usage(EXIT_SUCCESS); break;
507 1114 case 'f':
1115 if (from_file) err("Don't specify -f twice");
1116 from_file = xstrdup(optarg);
1117 break;
508 case 'o': { 1118 case 'o': {
509 FILE *fp = NULL; 1119 FILE *fp = NULL;
510 fp = freopen(optarg, "w", stdout); 1120 if ((fp = freopen(optarg, "w", stdout)) == NULL)
511 if (fp == NULL)
512 err("Could not open output stream '%s': %s", optarg, strerror(errno)); 1121 err("Could not open output stream '%s': %s", optarg, strerror(errno));
513 stdout = fp; 1122 SET_STDOUT(fp);
514 break; 1123 break;
515 } 1124 }
516 1125
517 case 's': find_sym = strdup(optarg); break; 1126 case 's': {
1127 size_t len;
1128 if (find_sym) err("Don't specify -s twice");
1129 find_sym = xstrdup(optarg);
1130 len = strlen(find_sym) + 1;
1131 versioned_symname = (char*)xmalloc(sizeof(char) * (len+1));
1132 sprintf(versioned_symname, "%s@", find_sym);
1133 break;
1134 }
1135 case 'N': {
1136 if (find_lib) err("Don't specify -N twice");
1137 find_lib = xstrdup(optarg);
1138 break;
1139 }
518 1140
1141 case 'F': {
1142 if (out_format) err("Don't specify -F twice");
1143 out_format = xstrdup(optarg);
1144 break;
1145 }
1146
1147 case 'g': gmatch = 1;
1148 case 'y': scan_symlink = 0; break;
519 case 'B': show_banner = 0; break; 1149 case 'B': show_banner = 0; break;
520 case 'l': scan_ldpath = 1; break; 1150 case 'l': scan_ldpath = 1; break;
521 case 'p': scan_envpath = 1; break; 1151 case 'p': scan_envpath = 1; break;
522 case 'R': dir_recurse = 1; break; 1152 case 'R': dir_recurse = 1; break;
523 case 'm': dir_crossmount = 0; break; 1153 case 'm': dir_crossmount = 0; break;
524 case 'x': show_pax = 1; break; 1154 case 'x': show_pax = 1; break;
525 case 'e': show_stack = 1; break; 1155 case 'e': show_phdr = 1; break;
526 case 't': show_textrel = 1; break; 1156 case 't': show_textrel = 1; break;
527 case 'r': show_rpath = 1; break; 1157 case 'r': show_rpath = 1; break;
528 case 'n': show_needed = 1; break; 1158 case 'n': show_needed = 1; break;
1159 case 'i': show_interp = 1; break;
1160 case 'b': show_bind = 1; break;
1161 case 'S': show_soname = 1; break;
1162 case 'T': show_textrels = 1; break;
529 case 'q': be_quiet = 1; break; 1163 case 'q': be_quiet = 1; break;
530 case 'v': be_verbose = (be_verbose % 20) + 1; break; 1164 case 'v': be_verbose = (be_verbose % 20) + 1; break;
531 case 'a': show_pax = show_stack = show_textrel = show_needed = show_rpath = 1; break; 1165 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
532 1166
533 case ':': 1167 case ':':
534 warn("Option missing parameter"); 1168 err("Option missing parameter\n");
535 usage(EXIT_FAILURE);
536 break;
537 case '?': 1169 case '?':
538 warn("Unknown option"); 1170 err("Unknown option\n");
539 usage(EXIT_FAILURE);
540 break;
541 default: 1171 default:
542 err("Unhandled option '%c'", flag); 1172 err("Unhandled option '%c'", i);
543 break;
544 } 1173 }
545 } 1174 }
546 1175
547 if (be_quiet && be_verbose) 1176 /* let the format option override all other options */
548 err("You can be quiet or you can be verbose, not both, stupid"); 1177 if (out_format) {
1178 show_pax = show_phdr = show_textrel = show_rpath = \
1179 show_needed = show_interp = show_bind = show_soname = \
1180 show_textrels = 0;
1181 for (i = 0; out_format[i]; ++i) {
1182 if (!IS_MODIFIER(out_format[i])) continue;
549 1183
1184 switch (out_format[++i]) {
1185 case '%': break;
1186 case '#': break;
1187 case 'F': break;
1188 case 'p': break;
1189 case 'f': break;
1190 case 's': break;
1191 case 'N': break;
1192 case 'o': break;
1193 case 'x': show_pax = 1; break;
1194 case 'e': show_phdr = 1; break;
1195 case 't': show_textrel = 1; break;
1196 case 'r': show_rpath = 1; break;
1197 case 'n': show_needed = 1; break;
1198 case 'i': show_interp = 1; break;
1199 case 'b': show_bind = 1; break;
1200 case 'S': show_soname = 1; break;
1201 case 'T': show_textrels = 1; break;
1202 default:
1203 err("Invalid format specifier '%c' (byte %i)",
1204 out_format[i], i+1);
1205 }
1206 }
1207
1208 /* construct our default format */
1209 } else {
1210 size_t fmt_len = 30;
1211 out_format = (char*)xmalloc(sizeof(char) * fmt_len);
1212 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1213 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1214 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1215 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1216 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1217 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1218 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1219 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1220 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1221 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1222 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1223 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1224 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1225 }
1226 if (be_verbose > 2) printf("Format: %s\n", out_format);
1227
1228 /* now lets actually do the scanning */
1229 if (scan_ldpath || (show_rpath && be_quiet))
1230 load_ld_so_conf();
550 if (scan_ldpath) scanelf_ldpath(); 1231 if (scan_ldpath) scanelf_ldpath();
551 if (scan_envpath) scanelf_envpath(); 1232 if (scan_envpath) scanelf_envpath();
1233 if (from_file) {
1234 scanelf_from_file(from_file);
1235 free(from_file);
1236 from_file = *argv;
1237 }
552 if (optind == argc && !scan_ldpath && !scan_envpath) 1238 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
553 err("Nothing to scan !?"); 1239 err("Nothing to scan !?");
554 while (optind < argc) 1240 while (optind < argc) {
555 scanelf_dir(argv[optind++]); 1241 search_path = argv[optind++];
1242 scanelf_dir(search_path);
1243 }
556 1244
1245 /* clean up */
1246 if (find_sym) {
1247 free(find_sym);
1248 free(versioned_symname);
1249 }
557 if (find_sym) free(find_sym); 1250 if (find_lib) free(find_lib);
1251 if (out_format) free(out_format);
1252 for (i = 0; ldpaths[i]; ++i)
1253 free(ldpaths[i]);
1254}
1255
1256
1257
1258/* utility funcs */
1259static char *xstrdup(const char *s)
1260{
1261 char *ret = strdup(s);
1262 if (!ret) err("Could not strdup(): %s", strerror(errno));
1263 return ret;
1264}
1265
1266static void *xmalloc(size_t size)
1267{
1268 void *ret = malloc(size);
1269 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1270 return ret;
1271}
1272
1273static void xstrcat(char **dst, const char *src, size_t *curr_len)
1274{
1275 size_t new_len;
1276
1277 new_len = strlen(*dst) + strlen(src);
1278 if (*curr_len <= new_len) {
1279 *curr_len = new_len + (*curr_len / 2);
1280 *dst = realloc(*dst, *curr_len);
1281 if (!*dst)
1282 err("could not realloc %li bytes", (unsigned long)*curr_len);
1283 }
1284
1285 strcat(*dst, src);
1286}
1287
1288static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1289{
1290 static char my_app[2];
1291 my_app[0] = append;
1292 my_app[1] = '\0';
1293 xstrcat(dst, my_app, curr_len);
558} 1294}
559 1295
560 1296
561 1297
562int main(int argc, char *argv[]) 1298int main(int argc, char *argv[])
563{ 1299{
564 if (argc < 2) 1300 if (argc < 2)
565 usage(EXIT_FAILURE); 1301 usage(EXIT_FAILURE);
566 parseargs(argc, argv); 1302 parseargs(argc, argv);
567 fclose(stdout); 1303 fclose(stdout);
1304#ifdef __BOUNDS_CHECKING_ON
1305 warn("The calls to add/delete heap should be off by 1 due to the out_buffer not being freed in scanelf_file()");
1306#endif
568 return EXIT_SUCCESS; 1307 return EXIT_SUCCESS;
569} 1308}

Legend:
Removed from v.1.34  
changed lines
  Added in v.1.91

  ViewVC Help
Powered by ViewVC 1.1.20