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

Legend:
Removed from v.1.35  
changed lines
  Added in v.1.92

  ViewVC Help
Powered by ViewVC 1.1.20