/[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.95
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.95 2005/12/10 06:08:22 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.95 2005/12/10 06:08:22 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 xstrncat(char **dst, const char *src, size_t *curr_len, size_t n);
41#define xstrcat(dst,src,curr_len) xstrncat(dst,src,curr_len,0)
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)
211 } 462 }
463
464 if (be_wewy_wewy_quiet) return;
465
212 if (rpath && runpath) { 466 if (rpath && runpath) {
213 if (!strcmp(rpath, runpath)) 467 if (!strcmp(rpath, runpath)) {
214 printf("%-5s ", runpath); 468 xstrcat(ret, runpath, ret_len);
215 else { 469 } else {
216 fprintf(stderr, "%s's RPATH [%s] != RUNPATH [%s]\n", filename, rpath, runpath); 470 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
217 printf("{%s,%s} ", 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);
218 } 476 }
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);
257 } 533 }
258 if (!be_quiet && !found_needed) 534
259 printf(" - "); 535 return NULL;
260 else if (found_needed) 536}
261 printf(" "); 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)); \
262 } 551 }
552 SHOW_INTERP(32)
553 SHOW_INTERP(64)
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;
649 char *ret;
266 void *symtab_void, *strtab_void; 650 void *symtab_void, *strtab_void;
267 char *versioned_symname;
268 size_t len;
269 651
270 len = strlen(find_sym) + 1; 652 if (!find_sym) return NULL;
271 versioned_symname = (char *)malloc(sizeof(char) * (len+1)); 653 ret = find_sym;
272 if (!versioned_symname) {
273 warnf("Could not malloc() mem for sym scan");
274 return;
275 }
276 sprintf(versioned_symname, "%s@", find_sym);
277 654
278 symtab_void = elf_findsecbyname(elf, ".symtab"); 655 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
279 strtab_void = elf_findsecbyname(elf, ".strtab");
280 656
281 if (symtab_void && strtab_void) { 657 if (symtab_void && strtab_void) {
282#define FIND_SYM(B) \ 658#define FIND_SYM(B) \
283 if (elf->elf_class == ELFCLASS ## B) { \ 659 if (elf->elf_class == ELFCLASS ## B) { \
284 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ 660 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
285 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \ 661 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
286 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ 662 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
287 int cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \ 663 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
288 char *symname; \ 664 char *symname; \
289 for (i = 0; i < cnt; ++i) { \ 665 for (i = 0; i < cnt; ++i) { \
290 if (sym->st_name) { \ 666 if (sym->st_name) { \
291 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \ 667 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
292 if (*find_sym == '*') { \ 668 if (*find_sym == '*') { \
293 printf("%s(%s) %5lX %15s %s\n", \ 669 printf("%s(%s) %5lX %15s %s\n", \
294 ((found_sym == 0) ? "\n\t" : "\t"), \ 670 ((*found_sym == 0) ? "\n\t" : "\t"), \
295 (char *)basename(filename), \ 671 elf->base_filename, \
296 (long)sym->st_size, \ 672 (unsigned long)sym->st_size, \
297 (char *)get_elfstttype(sym->st_info), \ 673 get_elfstttype(sym->st_info), \
298 symname); \ 674 symname); \
299 found_sym = 1; \ 675 *found_sym = 1; \
300 } else if ((strcmp(find_sym, symname) == 0) || \ 676 } else { \
677 char *this_sym, *next_sym; \
678 this_sym = find_sym; \
679 do { \
680 next_sym = strchr(this_sym, ','); \
681 if (next_sym == NULL) \
682 next_sym = this_sym + strlen(this_sym); \
683 if ((strncmp(this_sym, symname, (next_sym-this_sym)) == 0 && symname[next_sym-this_sym] == '\0') || \
301 (strncmp(symname, versioned_symname, len) == 0)) \ 684 (strcmp(symname, versioned_symname) == 0)) { \
685 ret = this_sym; \
302 found_sym++; \ 686 (*found_sym)++; \
687 goto break_out; \
688 } \
689 this_sym = next_sym + 1; \
690 } while (*next_sym != '\0'); \
691 } \
303 } \ 692 } \
304 ++sym; \ 693 ++sym; \
305 } } 694 } }
306 FIND_SYM(32) 695 FIND_SYM(32)
307 FIND_SYM(64) 696 FIND_SYM(64)
697 }
698
699break_out:
700 if (be_wewy_wewy_quiet) return NULL;
701
702 if (*find_sym != '*' && *found_sym)
703 return ret;
704 if (be_quiet)
705 return NULL;
706 else
707 return (char *)" - ";
708}
709/* scan an elf file and show all the fun stuff */
710#define prints(str) write(fileno(stdout), str, strlen(str))
711static void scanelf_file(const char *filename)
712{
713 unsigned long i;
714 char found_pax, found_phdr, found_relro, found_load, found_textrel,
715 found_rpath, found_needed, found_interp, found_bind, found_soname,
716 found_sym, found_lib, found_file, found_textrels;
717 elfobj *elf;
718 struct stat st;
719 static char *out_buffer = NULL;
720 static size_t out_len;
721
722 /* make sure 'filename' exists */
723 if (lstat(filename, &st) == -1) {
724 if (be_verbose > 2) printf("%s: does not exist\n", filename);
725 return;
726 }
727 /* always handle regular files and handle symlinked files if no -y */
728 if (S_ISLNK(st.st_mode)) {
729 if (!scan_symlink) return;
730 stat(filename, &st);
731 }
732 if (!S_ISREG(st.st_mode)) {
733 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
734 return;
735 }
736
737 found_pax = found_phdr = found_relro = found_load = found_textrel = \
738 found_rpath = found_needed = found_interp = found_bind = found_soname = \
739 found_sym = found_lib = found_file = found_textrels = 0;
740
741 /* verify this is real ELF */
742 if ((elf = readelf(filename)) == NULL) {
743 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
744 return;
745 }
746
747 if (be_verbose > 1)
748 printf("%s: scanning file {%s,%s}\n", filename,
749 get_elfeitype(EI_CLASS, elf->elf_class),
750 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
751 else if (be_verbose)
752 printf("%s: scanning file\n", filename);
753
754 /* init output buffer */
755 if (!out_buffer) {
756 out_len = sizeof(char) * 80;
757 out_buffer = (char*)xmalloc(out_len);
758 }
759 *out_buffer = '\0';
760
761 /* show the header */
762 if (!be_quiet && show_banner) {
763 for (i = 0; out_format[i]; ++i) {
764 if (!IS_MODIFIER(out_format[i])) continue;
765
766 switch (out_format[++i]) {
767 case '%': break;
768 case '#': break;
769 case 'F':
770 case 'p':
771 case 'f': prints("FILE "); found_file = 1; break;
772 case 'o': prints(" TYPE "); break;
773 case 'x': prints(" PAX "); break;
774 case 'e': prints("STK/REL/PTL "); break;
775 case 't': prints("TEXTREL "); break;
776 case 'r': prints("RPATH "); break;
777 case 'n': prints("NEEDED "); break;
778 case 'i': prints("INTERP "); break;
779 case 'b': prints("BIND "); break;
780 case 'S': prints("SONAME "); break;
781 case 's': prints("SYM "); break;
782 case 'N': prints("LIB "); break;
783 case 'T': prints("TEXTRELS "); break;
784 default: warnf("'%c' has no title ?", out_format[i]);
308 } 785 }
309 free(versioned_symname); 786 }
310 if (*find_sym != '*') { 787 if (!found_file) prints("FILE ");
311 if (found_sym) 788 prints("\n");
312 printf(" %s ", find_sym); 789 found_file = 0;
313 else if (!be_quiet) 790 show_banner = 0;
314 fputs(" - ", stdout); 791 }
792
793 /* dump all the good stuff */
794 for (i = 0; out_format[i]; ++i) {
795 const char *out;
796 const char *tmp;
797
798 /* make sure we trim leading spaces in quiet mode */
799 if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
800 *out_buffer = '\0';
801
802 if (!IS_MODIFIER(out_format[i])) {
803 xchrcat(&out_buffer, out_format[i], &out_len);
804 continue;
805 }
806
807 out = NULL;
808 be_wewy_wewy_quiet = (out_format[i] == '#');
809 switch (out_format[++i]) {
810 case '%':
811 case '#':
812 xchrcat(&out_buffer, out_format[i], &out_len); break;
813 case 'F':
814 found_file = 1;
815 if (be_wewy_wewy_quiet) break;
816 xstrcat(&out_buffer, filename, &out_len);
817 break;
818 case 'p':
819 found_file = 1;
820 if (be_wewy_wewy_quiet) break;
821 tmp = filename;
822 if (search_path) {
823 ssize_t len_search = strlen(search_path);
824 ssize_t len_file = strlen(filename);
825 if (!strncmp(filename, search_path, len_search) && \
826 len_file > len_search)
827 tmp += len_search;
828 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
315 } 829 }
830 xstrcat(&out_buffer, tmp, &out_len);
831 break;
832 case 'f':
833 found_file = 1;
834 if (be_wewy_wewy_quiet) break;
835 tmp = strrchr(filename, '/');
836 tmp = (tmp == NULL ? filename : tmp+1);
837 xstrcat(&out_buffer, tmp, &out_len);
838 break;
839 case 'o': out = get_elfetype(elf); break;
840 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
841 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
842 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
843 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
844 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
845 case 'n':
846 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
847 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
848 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
849 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
850 case 's': out = scanelf_file_sym(elf, &found_sym); break;
851 default: warnf("'%c' has no scan code?", out_format[i]);
316 } 852 }
853 if (out) {
854 /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */
855 if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL)
856 xstrncat(&out_buffer, out, &out_len, (tmp-out));
857 else
858 xstrcat(&out_buffer, out, &out_len);
859 }
860 }
317 861
318 if (!be_quiet || found_pax || found_stack || found_textrel || \ 862#define FOUND_SOMETHING() \
319 found_rpath || found_needed || found_sym) 863 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
320 puts(filename); 864 found_rpath || found_needed || found_interp || found_bind || \
865 found_soname || found_sym || found_lib || found_textrels)
866
867 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
868 xchrcat(&out_buffer, ' ', &out_len);
869 xstrcat(&out_buffer, filename, &out_len);
870 }
871 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
872 puts(out_buffer);
873 fflush(stdout);
874 }
321 875
322 unreadelf(elf); 876 unreadelf(elf);
323} 877}
324 878
325/* scan a directory for ET_EXEC files and print when we find one */ 879/* scan a directory for ET_EXEC files and print when we find one */
330 struct stat st_top, st; 884 struct stat st_top, st;
331 char buf[_POSIX_PATH_MAX]; 885 char buf[_POSIX_PATH_MAX];
332 size_t pathlen = 0, len = 0; 886 size_t pathlen = 0, len = 0;
333 887
334 /* make sure path exists */ 888 /* make sure path exists */
335 if (lstat(path, &st_top) == -1) 889 if (lstat(path, &st_top) == -1) {
890 if (be_verbose > 2) printf("%s: does not exist\n", path);
336 return; 891 return;
892 }
337 893
338 /* ok, if it isn't a directory, assume we can open it */ 894 /* ok, if it isn't a directory, assume we can open it */
339 if (!S_ISDIR(st_top.st_mode)) { 895 if (!S_ISDIR(st_top.st_mode)) {
340 scanelf_file(path); 896 scanelf_file(path);
341 return; 897 return;
352 while ((dentry = readdir(dir))) { 908 while ((dentry = readdir(dir))) {
353 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) 909 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
354 continue; 910 continue;
355 len = (pathlen + 1 + strlen(dentry->d_name) + 1); 911 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
356 if (len >= sizeof(buf)) { 912 if (len >= sizeof(buf)) {
357 warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf)); 913 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
914 (unsigned long)len, (unsigned long)sizeof(buf));
358 continue; 915 continue;
359 } 916 }
360 sprintf(buf, "%s/%s", path, dentry->d_name); 917 sprintf(buf, "%s/%s", path, dentry->d_name);
361 if (lstat(buf, &st) != -1) { 918 if (lstat(buf, &st) != -1) {
362 if (S_ISREG(st.st_mode)) 919 if (S_ISREG(st.st_mode))
368 } 925 }
369 } 926 }
370 closedir(dir); 927 closedir(dir);
371} 928}
372 929
930static int scanelf_from_file(char *filename)
931{
932 FILE *fp = NULL;
933 char *p;
934 char path[_POSIX_PATH_MAX];
935
936 if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
937 fp = stdin;
938 else if ((fp = fopen(filename, "r")) == NULL)
939 return 1;
940
941 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
942 if ((p = strchr(path, '\n')) != NULL)
943 *p = 0;
944 search_path = path;
945 scanelf_dir(path);
946 }
947 if (fp != stdin)
948 fclose(fp);
949 return 0;
950}
951
952static void load_ld_so_conf()
953{
954 FILE *fp = NULL;
955 char *p;
956 char path[_POSIX_PATH_MAX];
957 int i = 0;
958
959 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
960 return;
961
962 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
963 if (*path != '/')
964 continue;
965
966 if ((p = strrchr(path, '\r')) != NULL)
967 *p = 0;
968 if ((p = strchr(path, '\n')) != NULL)
969 *p = 0;
970
971 ldpaths[i++] = xstrdup(path);
972
973 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
974 break;
975 }
976 ldpaths[i] = NULL;
977
978 fclose(fp);
979}
980
373/* scan /etc/ld.so.conf for paths */ 981/* scan /etc/ld.so.conf for paths */
374static void scanelf_ldpath() 982static void scanelf_ldpath()
375{ 983{
376 char scan_l, scan_ul, scan_ull; 984 char scan_l, scan_ul, scan_ull;
377 char *path, *p; 985 int i = 0;
378 FILE *fp;
379 986
380 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL) 987 if (!ldpaths[0])
381 err("Unable to open ld.so.conf: %s", strerror(errno)); 988 err("Unable to load any paths from ld.so.conf");
382 989
383 scan_l = scan_ul = scan_ull = 0; 990 scan_l = scan_ul = scan_ull = 0;
384 991
385 if ((path = malloc(_POSIX_PATH_MAX)) == NULL) { 992 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; 993 if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
396 if (!scan_ul && !strcmp(path, "/usr/lib")) scan_ul = 1; 994 if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
397 if (!scan_ull && !strcmp(path, "/usr/local/lib")) scan_ull = 1; 995 if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
398 scanelf_dir(path); 996 scanelf_dir(ldpaths[i]);
997 ++i;
399 } 998 }
400 free(path);
401 fclose(fp);
402 999
403 if (!scan_l) scanelf_dir("/lib"); 1000 if (!scan_l) scanelf_dir("/lib");
404 if (!scan_ul) scanelf_dir("/usr/lib"); 1001 if (!scan_ul) scanelf_dir("/usr/lib");
405 if (!scan_ull) scanelf_dir("/usr/local/lib"); 1002 if (!scan_ull) scanelf_dir("/usr/local/lib");
406} 1003}
411 char *path, *p; 1008 char *path, *p;
412 1009
413 path = getenv("PATH"); 1010 path = getenv("PATH");
414 if (!path) 1011 if (!path)
415 err("PATH is not set in your env !"); 1012 err("PATH is not set in your env !");
416 1013 path = xstrdup(path);
417 if ((path = strdup(path)) == NULL)
418 err("strdup failed: %s", strerror(errno));
419 1014
420 while ((p = strrchr(path, ':')) != NULL) { 1015 while ((p = strrchr(path, ':')) != NULL) {
421 scanelf_dir(p + 1); 1016 scanelf_dir(p + 1);
422 *p = 0; 1017 *p = 0;
423 } 1018 }
426} 1021}
427 1022
428 1023
429 1024
430/* usage / invocation handling functions */ 1025/* usage / invocation handling functions */
431#define PARSE_FLAGS "plRmxetrns:aqvo:BhV" 1026#define PARSE_FLAGS "plRmyxetrnibSs:gN:TaqvF:f:o:BhV"
432#define a_argument required_argument 1027#define a_argument required_argument
433static struct option const long_opts[] = { 1028static struct option const long_opts[] = {
434 {"path", no_argument, NULL, 'p'}, 1029 {"path", no_argument, NULL, 'p'},
435 {"ldpath", no_argument, NULL, 'l'}, 1030 {"ldpath", no_argument, NULL, 'l'},
436 {"recursive", no_argument, NULL, 'R'}, 1031 {"recursive", no_argument, NULL, 'R'},
437 {"mount", no_argument, NULL, 'm'}, 1032 {"mount", no_argument, NULL, 'm'},
1033 {"symlink", no_argument, NULL, 'y'},
438 {"pax", no_argument, NULL, 'x'}, 1034 {"pax", no_argument, NULL, 'x'},
439 {"header", no_argument, NULL, 'e'}, 1035 {"header", no_argument, NULL, 'e'},
440 {"textrel", no_argument, NULL, 't'}, 1036 {"textrel", no_argument, NULL, 't'},
441 {"rpath", no_argument, NULL, 'r'}, 1037 {"rpath", no_argument, NULL, 'r'},
442 {"needed", no_argument, NULL, 'n'}, 1038 {"needed", no_argument, NULL, 'n'},
1039 {"interp", no_argument, NULL, 'i'},
1040 {"bind", no_argument, NULL, 'b'},
1041 {"soname", no_argument, NULL, 'S'},
443 {"symbol", a_argument, NULL, 's'}, 1042 {"symbol", a_argument, NULL, 's'},
1043 {"lib", a_argument, NULL, 'N'},
1044 {"gmatch", no_argument, NULL, 'g'},
1045 {"textrels", no_argument, NULL, 'T'},
444 {"all", no_argument, NULL, 'a'}, 1046 {"all", no_argument, NULL, 'a'},
445 {"quiet", no_argument, NULL, 'q'}, 1047 {"quiet", no_argument, NULL, 'q'},
446 {"verbose", no_argument, NULL, 'v'}, 1048 {"verbose", no_argument, NULL, 'v'},
1049 {"format", a_argument, NULL, 'F'},
1050 {"from", a_argument, NULL, 'f'},
447 {"file", a_argument, NULL, 'o'}, 1051 {"file", a_argument, NULL, 'o'},
448 {"nobanner", no_argument, NULL, 'B'}, 1052 {"nobanner", no_argument, NULL, 'B'},
449 {"help", no_argument, NULL, 'h'}, 1053 {"help", no_argument, NULL, 'h'},
450 {"version", no_argument, NULL, 'V'}, 1054 {"version", no_argument, NULL, 'V'},
451 {NULL, no_argument, NULL, 0x0} 1055 {NULL, no_argument, NULL, 0x0}
452}; 1056};
1057
453static char *opts_help[] = { 1058static const char *opts_help[] = {
454 "Scan all directories in PATH environment", 1059 "Scan all directories in PATH environment",
455 "Scan all directories in /etc/ld.so.conf", 1060 "Scan all directories in /etc/ld.so.conf",
456 "Scan directories recursively", 1061 "Scan directories recursively",
457 "Don't recursively cross mount points\n", 1062 "Don't recursively cross mount points",
1063 "Don't scan symlinks\n",
458 "Print PaX markings", 1064 "Print PaX markings",
459 "Print GNU_STACK markings", 1065 "Print GNU_STACK/PT_LOAD markings",
460 "Print TEXTREL information", 1066 "Print TEXTREL information",
461 "Print RPATH information", 1067 "Print RPATH information",
462 "Print NEEDED information", 1068 "Print NEEDED information",
1069 "Print INTERP information",
1070 "Print BIND information",
1071 "Print SONAME information",
463 "Find a specified symbol", 1072 "Find a specified symbol",
1073 "Find a specified library",
1074 "Use strncmp to match libraries. (use with -N)",
1075 "Locate cause of TEXTREL",
464 "Print all scanned info (-x -e -t -r)\n", 1076 "Print all scanned info (-x -e -t -r -b)\n",
465 "Only output 'bad' things", 1077 "Only output 'bad' things",
466 "Be verbose (can be specified more than once)", 1078 "Be verbose (can be specified more than once)",
1079 "Use specified format for output",
1080 "Read input stream from a filename",
467 "Write output stream to a filename", 1081 "Write output stream to a filename",
468 "Don't display the header", 1082 "Don't display the header",
469 "Print this help and exit", 1083 "Print this help and exit",
470 "Print version and exit", 1084 "Print version and exit",
471 NULL 1085 NULL
472}; 1086};
473 1087
474/* display usage and exit */ 1088/* display usage and exit */
475static void usage(int status) 1089static void usage(int status)
476{ 1090{
477 int i; 1091 unsigned long i;
478 printf(" Scan ELF binaries for stuff\n" 1092 printf("* Scan ELF binaries for stuff\n\n"
479 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0); 1093 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0);
480 printf("Options: -[%s]\n", PARSE_FLAGS); 1094 printf("Options: -[%s]\n", PARSE_FLAGS);
481 for (i = 0; long_opts[i].name; ++i) 1095 for (i = 0; long_opts[i].name; ++i)
482 if (long_opts[i].has_arg == no_argument) 1096 if (long_opts[i].has_arg == no_argument)
483 printf(" -%c, --%-13s %s\n", long_opts[i].val, 1097 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
484 long_opts[i].name, opts_help[i]); 1098 long_opts[i].name, opts_help[i]);
485 else 1099 else
486 printf(" -%c, --%-6s <arg> %s\n", long_opts[i].val, 1100 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
487 long_opts[i].name, opts_help[i]); 1101 long_opts[i].name, opts_help[i]);
1102
1103 if (status != EXIT_SUCCESS)
1104 exit(status);
1105
1106 puts("\nThe format modifiers for the -F option are:");
1107 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1108 puts(" t TEXTREL \tr RPATH \tn NEEDED");
1109 puts(" i INTERP \tb BIND \ts symbol");
1110 puts(" N library \to Type \tT TEXTRELs");
1111 puts(" S SONAME");
1112 puts(" p filename (with search path removed)");
1113 puts(" f filename (short name/basename)");
1114 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1115
488 exit(status); 1116 exit(status);
489} 1117}
490 1118
491/* parse command line arguments and preform needed actions */ 1119/* parse command line arguments and preform needed actions */
492static void parseargs(int argc, char *argv[]) 1120static void parseargs(int argc, char *argv[])
493{ 1121{
494 int flag; 1122 int i;
1123 char *from_file = NULL;
495 1124
496 opterr = 0; 1125 opterr = 0;
497 while ((flag=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { 1126 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
498 switch (flag) { 1127 switch (i) {
499 1128
500 case 'V': /* version info */ 1129 case 'V':
501 printf("%s compiled %s\n%s\n" 1130 printf("pax-utils-%s: %s compiled %s\n%s\n"
502 "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n", 1131 "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
503 __FILE__, __DATE__, rcsid, argv0); 1132 VERSION, __FILE__, __DATE__, rcsid, argv0);
504 exit(EXIT_SUCCESS); 1133 exit(EXIT_SUCCESS);
505 break; 1134 break;
506 case 'h': usage(EXIT_SUCCESS); break; 1135 case 'h': usage(EXIT_SUCCESS); break;
507 1136 case 'f':
1137 if (from_file) err("Don't specify -f twice");
1138 from_file = xstrdup(optarg);
1139 break;
508 case 'o': { 1140 case 'o': {
509 FILE *fp = NULL; 1141 FILE *fp = NULL;
510 fp = freopen(optarg, "w", stdout); 1142 if ((fp = freopen(optarg, "w", stdout)) == NULL)
511 if (fp == NULL)
512 err("Could not open output stream '%s': %s", optarg, strerror(errno)); 1143 err("Could not open output stream '%s': %s", optarg, strerror(errno));
513 stdout = fp; 1144 SET_STDOUT(fp);
514 break; 1145 break;
515 } 1146 }
516 1147
517 case 's': find_sym = strdup(optarg); break; 1148 case 's': {
1149 if (find_sym) warn("You prob don't want to specify -s twice");
1150 find_sym = optarg;
1151 versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
1152 sprintf(versioned_symname, "%s@", find_sym);
1153 break;
1154 }
1155 case 'N': {
1156 if (find_lib) warn("You prob don't want to specify -N twice");
1157 find_lib = optarg;
1158 break;
1159 }
518 1160
1161 case 'F': {
1162 if (out_format) warn("You prob don't want to specify -F twice");
1163 out_format = optarg;
1164 break;
1165 }
1166
1167 case 'g': gmatch = 1;
1168 case 'y': scan_symlink = 0; break;
519 case 'B': show_banner = 0; break; 1169 case 'B': show_banner = 0; break;
520 case 'l': scan_ldpath = 1; break; 1170 case 'l': scan_ldpath = 1; break;
521 case 'p': scan_envpath = 1; break; 1171 case 'p': scan_envpath = 1; break;
522 case 'R': dir_recurse = 1; break; 1172 case 'R': dir_recurse = 1; break;
523 case 'm': dir_crossmount = 0; break; 1173 case 'm': dir_crossmount = 0; break;
524 case 'x': show_pax = 1; break; 1174 case 'x': show_pax = 1; break;
525 case 'e': show_stack = 1; break; 1175 case 'e': show_phdr = 1; break;
526 case 't': show_textrel = 1; break; 1176 case 't': show_textrel = 1; break;
527 case 'r': show_rpath = 1; break; 1177 case 'r': show_rpath = 1; break;
528 case 'n': show_needed = 1; break; 1178 case 'n': show_needed = 1; break;
1179 case 'i': show_interp = 1; break;
1180 case 'b': show_bind = 1; break;
1181 case 'S': show_soname = 1; break;
1182 case 'T': show_textrels = 1; break;
529 case 'q': be_quiet = 1; break; 1183 case 'q': be_quiet = 1; break;
530 case 'v': be_verbose = (be_verbose % 20) + 1; break; 1184 case 'v': be_verbose = (be_verbose % 20) + 1; break;
531 case 'a': show_pax = show_stack = show_textrel = show_needed = show_rpath = 1; break; 1185 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
532 1186
533 case ':': 1187 case ':':
534 warn("Option missing parameter"); 1188 err("Option missing parameter\n");
535 usage(EXIT_FAILURE);
536 break;
537 case '?': 1189 case '?':
538 warn("Unknown option"); 1190 err("Unknown option\n");
539 usage(EXIT_FAILURE);
540 break;
541 default: 1191 default:
542 err("Unhandled option '%c'", flag); 1192 err("Unhandled option '%c'", i);
543 break; 1193 }
1194 }
1195
1196 /* let the format option override all other options */
1197 if (out_format) {
1198 show_pax = show_phdr = show_textrel = show_rpath = \
1199 show_needed = show_interp = show_bind = show_soname = \
1200 show_textrels = 0;
1201 for (i = 0; out_format[i]; ++i) {
1202 if (!IS_MODIFIER(out_format[i])) continue;
1203
1204 switch (out_format[++i]) {
1205 case '%': break;
1206 case '#': break;
1207 case 'F': break;
1208 case 'p': break;
1209 case 'f': break;
1210 case 's': break;
1211 case 'N': break;
1212 case 'o': break;
1213 case 'x': show_pax = 1; break;
1214 case 'e': show_phdr = 1; break;
1215 case 't': show_textrel = 1; break;
1216 case 'r': show_rpath = 1; break;
1217 case 'n': show_needed = 1; break;
1218 case 'i': show_interp = 1; break;
1219 case 'b': show_bind = 1; break;
1220 case 'S': show_soname = 1; break;
1221 case 'T': show_textrels = 1; break;
1222 default:
1223 err("Invalid format specifier '%c' (byte %i)",
1224 out_format[i], i+1);
544 } 1225 }
545 } 1226 }
546 1227
547 if (be_quiet && be_verbose) 1228 /* construct our default format */
548 err("You can be quiet or you can be verbose, not both, stupid"); 1229 } else {
1230 size_t fmt_len = 30;
1231 out_format = (char*)xmalloc(sizeof(char) * fmt_len);
1232 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1233 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1234 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1235 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1236 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1237 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1238 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1239 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1240 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1241 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1242 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1243 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1244 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1245 }
1246 if (be_verbose > 2) printf("Format: %s\n", out_format);
549 1247
1248 /* now lets actually do the scanning */
1249 if (scan_ldpath || (show_rpath && be_quiet))
1250 load_ld_so_conf();
550 if (scan_ldpath) scanelf_ldpath(); 1251 if (scan_ldpath) scanelf_ldpath();
551 if (scan_envpath) scanelf_envpath(); 1252 if (scan_envpath) scanelf_envpath();
1253 if (from_file) {
1254 scanelf_from_file(from_file);
1255 free(from_file);
1256 from_file = *argv;
1257 }
552 if (optind == argc && !scan_ldpath && !scan_envpath) 1258 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
553 err("Nothing to scan !?"); 1259 err("Nothing to scan !?");
554 while (optind < argc) 1260 while (optind < argc) {
555 scanelf_dir(argv[optind++]); 1261 search_path = argv[optind++];
1262 scanelf_dir(search_path);
1263 }
556 1264
557 if (find_sym) free(find_sym); 1265 /* clean up */
1266 if (versioned_symname) free(versioned_symname);
1267 for (i = 0; ldpaths[i]; ++i)
1268 free(ldpaths[i]);
1269}
1270
1271
1272
1273/* utility funcs */
1274static char *xstrdup(const char *s)
1275{
1276 char *ret = strdup(s);
1277 if (!ret) err("Could not strdup(): %s", strerror(errno));
1278 return ret;
1279}
1280static void *xmalloc(size_t size)
1281{
1282 void *ret = malloc(size);
1283 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1284 return ret;
1285}
1286static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n)
1287{
1288 size_t new_len;
1289
1290 new_len = strlen(*dst) + strlen(src);
1291 if (*curr_len <= new_len) {
1292 *curr_len = new_len + (*curr_len / 2);
1293 *dst = realloc(*dst, *curr_len);
1294 if (!*dst)
1295 err("could not realloc() %li bytes", (unsigned long)*curr_len);
1296 }
1297
1298 if (n)
1299 strncat(*dst, src, n);
1300 else
1301 strcat(*dst, src);
1302}
1303static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1304{
1305 static char my_app[2];
1306 my_app[0] = append;
1307 my_app[1] = '\0';
1308 xstrcat(dst, my_app, curr_len);
558} 1309}
559 1310
560 1311
561 1312
562int main(int argc, char *argv[]) 1313int main(int argc, char *argv[])
563{ 1314{
564 if (argc < 2) 1315 if (argc < 2)
565 usage(EXIT_FAILURE); 1316 usage(EXIT_FAILURE);
566 parseargs(argc, argv); 1317 parseargs(argc, argv);
567 fclose(stdout); 1318 fclose(stdout);
1319#ifdef __BOUNDS_CHECKING_ON
1320 warn("The calls to add/delete heap should be off by 1 due to the out_buffer not being freed in scanelf_file()");
1321#endif
568 return EXIT_SUCCESS; 1322 return EXIT_SUCCESS;
569} 1323}

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

  ViewVC Help
Powered by ViewVC 1.1.20