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

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

  ViewVC Help
Powered by ViewVC 1.1.20