/[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.44 Revision 1.122
1/* 1/*
2 * Copyright 2003 Ned Ludd <solar@gentoo.org>
3 * Copyright 1999-2005 Gentoo Foundation 2 * Copyright 2003-2006 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.44 2005/05/10 22:54:25 vapier Exp $ 4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.122 2006/02/11 04:11:44 vapier Exp $
6 * 5 *
7 ******************************************************************** 6 * Copyright 2003-2006 Ned Ludd - <solar@gentoo.org>
8 * This program is free software; you can redistribute it and/or 7 * Copyright 2004-2006 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>
25#include <stdlib.h>
26#include <sys/types.h>
27#define __USE_GNU
28#include <string.h>
29#include <errno.h>
30#include <unistd.h>
31#include <sys/stat.h>
32#include <dirent.h>
33#include <getopt.h>
34#include <assert.h>
35
36#include "paxelf.h" 10#include "paxinc.h"
37 11
38static const char *rcsid = "$Id: scanelf.c,v 1.44 2005/05/10 22:54:25 vapier Exp $"; 12static const char *rcsid = "$Id: scanelf.c,v 1.122 2006/02/11 04:11:44 vapier Exp $";
39#define argv0 "scanelf" 13#define argv0 "scanelf"
40 14
15#define IS_MODIFIER(c) (c == '%' || c == '#')
16
41 17
42 18
43/* prototypes */ 19/* prototypes */
20static int scanelf_elfobj(elfobj *elf);
21static int scanelf_elf(const char *filename, int fd, size_t len);
22static int scanelf_archive(const char *filename, int fd, size_t len);
44static void scanelf_file(const char *filename); 23static void scanelf_file(const char *filename);
45static void scanelf_dir(const char *path); 24static void scanelf_dir(const char *path);
46static void scanelf_ldpath(); 25static void scanelf_ldpath(void);
47static void scanelf_envpath(); 26static void scanelf_envpath(void);
48static void usage(int status); 27static void usage(int status);
49static void parseargs(int argc, char *argv[]); 28static void parseargs(int argc, char *argv[]);
50static char *xstrdup(char *s); 29static char *xstrdup(const char *s);
51static void *xmalloc(size_t size); 30static void *xmalloc(size_t size);
52static void xstrcat(char **dst, const char *src, size_t *curr_len); 31static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n);
32#define xstrcat(dst,src,curr_len) xstrncat(dst,src,curr_len,0)
53static inline void xchrcat(char **dst, const char append, size_t *curr_len); 33static inline void xchrcat(char **dst, const char append, size_t *curr_len);
54static int xemptybuffer(const char *buff);
55 34
56/* variables to control behavior */ 35/* variables to control behavior */
36static char match_etypes[126] = "";
37static char *ldpaths[256];
57static char scan_ldpath = 0; 38static char scan_ldpath = 0;
58static char scan_envpath = 0; 39static char scan_envpath = 0;
59static char scan_symlink = 1; 40static char scan_symlink = 1;
41static char scan_archives = 0;
60static char dir_recurse = 0; 42static char dir_recurse = 0;
61static char dir_crossmount = 1; 43static char dir_crossmount = 1;
62static char show_pax = 0; 44static char show_pax = 0;
63static char show_stack = 0; 45static char show_phdr = 0;
64static char show_textrel = 0; 46static char show_textrel = 0;
65static char show_rpath = 0; 47static char show_rpath = 0;
66static char show_needed = 0; 48static char show_needed = 0;
67static char show_interp = 0; 49static char show_interp = 0;
50static char show_bind = 0;
51static char show_soname = 0;
52static char show_textrels = 0;
68static char show_banner = 1; 53static char show_banner = 1;
69static char be_quiet = 0; 54static char be_quiet = 0;
70static char be_verbose = 0; 55static char be_verbose = 0;
56static char be_wewy_wewy_quiet = 0;
71static char *find_sym = NULL, *versioned_symname = NULL; 57static char *find_sym = NULL, *versioned_symname = NULL;
58static char *find_lib = NULL;
72static char *out_format = NULL; 59static char *out_format = NULL;
60static char *search_path = NULL;
61static char fix_elf = 0;
62static char gmatch = 0;
63static char use_ldcache = 0;
73 64
74 65int match_bits = 0;
66caddr_t ldcache = 0;
67size_t ldcache_size = 0;
75 68
76/* sub-funcs for scanelf_file() */ 69/* sub-funcs for scanelf_file() */
70static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
71{
72 /* find the best SHT_DYNSYM and SHT_STRTAB sections */
73#define GET_SYMTABS(B) \
74 if (elf->elf_class == ELFCLASS ## B) { \
75 Elf ## B ## _Shdr *symtab, *strtab, *dynsym, *dynstr; \
76 /* debug sections */ \
77 symtab = SHDR ## B (elf_findsecbyname(elf, ".symtab")); \
78 strtab = SHDR ## B (elf_findsecbyname(elf, ".strtab")); \
79 /* runtime sections */ \
80 dynsym = SHDR ## B (elf_findsecbyname(elf, ".dynsym")); \
81 dynstr = SHDR ## B (elf_findsecbyname(elf, ".dynstr")); \
82 if (symtab && dynsym) { \
83 *sym = (void*)((EGET(symtab->sh_size) > EGET(dynsym->sh_size)) ? symtab : dynsym); \
84 } else { \
85 *sym = (void*)(symtab ? symtab : dynsym); \
86 } \
87 if (strtab && dynstr) { \
88 *tab = (void*)((EGET(strtab->sh_size) > EGET(dynstr->sh_size)) ? strtab : dynstr); \
89 } else { \
90 *tab = (void*)(strtab ? strtab : dynstr); \
91 } \
92 }
93 GET_SYMTABS(32)
94 GET_SYMTABS(64)
95}
77static char *scanelf_file_pax(elfobj *elf, char *found_pax) 96static char *scanelf_file_pax(elfobj *elf, char *found_pax)
78{ 97{
79 static char *paxflags; 98 static char ret[7];
99 unsigned long i, shown;
80 100
81 if (!show_pax) return NULL; 101 if (!show_pax) return NULL;
82 102
83 paxflags = pax_short_hf_flags(PAX_FLAGS(elf));
84 if (!be_quiet || (be_quiet && strncmp(paxflags, "PeMRxS", 6))) {
85 *found_pax = 1;
86 return paxflags;
87 }
88
89 return NULL;
90}
91static char *scanelf_file_stack(elfobj *elf, char *found_stack, char *found_relro)
92{
93 static char ret[8];
94 char *found;
95 unsigned long i, off, shown;
96
97 if (!show_stack) return NULL;
98
99 shown = 0; 103 shown = 0;
100 strcpy(ret, "--- ---"); 104 memset(&ret, 0, sizeof(ret));
101 105
102 if (elf->phdr) { 106 if (elf->phdr) {
103#define SHOW_STACK(B) \ 107#define SHOW_PAX(B) \
104 if (elf->elf_class == ELFCLASS ## B) { \ 108 if (elf->elf_class == ELFCLASS ## B) { \
105 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 109 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
106 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 110 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
107 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 111 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
108 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \ 112 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
109 found = found_stack; \
110 off = 0; \
111 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
112 found = found_relro; \
113 off = 3; \
114 } else \
115 continue; \ 113 continue; \
116 if (be_quiet && !(EGET(phdr[i].p_flags) & PF_X)) \ 114 if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \
117 continue; \ 115 continue; \
118 memcpy(ret+off, gnu_short_stack_flags(EGET(phdr[i].p_flags)), 3); \ 116 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
119 *found = 1; \ 117 *found_pax = 1; \
120 ++shown; \ 118 ++shown; \
119 break; \
121 } \ 120 } \
122 } 121 }
123 SHOW_STACK(32) 122 SHOW_PAX(32)
124 SHOW_STACK(64) 123 SHOW_PAX(64)
124 }
125
126 /* fall back to EI_PAX if no PT_PAX was found */
127 if (!*ret) {
128 static char *paxflags;
129 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
130 if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
131 *found_pax = 1;
132 return (be_wewy_wewy_quiet ? NULL : paxflags);
125 } 133 }
134 strncpy(ret, paxflags, sizeof(ret));
135 }
126 136
127 if (be_quiet && !shown) 137 if (be_wewy_wewy_quiet || (be_quiet && !shown))
128 return NULL; 138 return NULL;
129 else 139 else
130 return ret; 140 return ret;
131} 141}
142
143static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
144{
145 static char ret[12];
146 char *found;
147 unsigned long i, shown, multi_stack, multi_relro, multi_load;
148 int max_pt_load;
149
150 if (!show_phdr) return NULL;
151
152 memcpy(ret, "--- --- ---\0", 12);
153
154 shown = 0;
155 multi_stack = multi_relro = multi_load = 0;
156 max_pt_load = elf_max_pt_load(elf);
157
158#define NOTE_GNU_STACK ".note.GNU-stack"
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 (ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC) \
179 if (multi_load++ > max_pt_load) warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
180 found = found_load; \
181 offset = 8; \
182 check_flags = PF_W|PF_X; \
183 } else \
184 continue; \
185 flags = EGET(phdr[i].p_flags); \
186 if (be_quiet && ((flags & check_flags) != check_flags)) \
187 continue; \
188 if (fix_elf && ((flags & PF_X) != flags)) { \
189 ESET(phdr[i].p_flags, flags & (PF_X ^ (size_t)-1)); \
190 ret[3] = ret[7] = '!'; \
191 flags = EGET(phdr[i].p_flags); \
192 } \
193 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \
194 *found = 1; \
195 ++shown; \
196 } \
197 } else if (elf->shdr != NULL) { \
198 /* no program headers which means this is prob an object file */ \
199 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
200 Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \
201 char *str; \
202 if ((void*)strtbl > (void*)elf->data_end) \
203 goto skip_this_shdr##B; \
204 check_flags = SHF_WRITE|SHF_EXECINSTR; \
205 for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \
206 if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \
207 offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \
208 str = elf->data + offset; \
209 if (str > elf->data + offset + sizeof(NOTE_GNU_STACK)) continue; \
210 if (!strcmp(str, NOTE_GNU_STACK)) { \
211 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \
212 flags = EGET(shdr[i].sh_flags); \
213 if (be_quiet && ((flags & check_flags) != check_flags)) \
214 continue; \
215 ++*found_phdr; \
216 shown = 1; \
217 if (flags & SHF_WRITE) ret[0] = 'W'; \
218 if (flags & SHF_ALLOC) ret[1] = 'A'; \
219 if (flags & SHF_EXECINSTR) ret[2] = 'X'; \
220 if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \
221 break; \
222 } \
223 } \
224 skip_this_shdr##B: \
225 if (!multi_stack) { \
226 *found_phdr = 1; \
227 shown = 1; \
228 memcpy(ret, "!WX", 3); \
229 } \
230 } \
231 }
232 SHOW_PHDR(32)
233 SHOW_PHDR(64)
234
235 if (be_wewy_wewy_quiet || (be_quiet && !shown))
236 return NULL;
237 else
238 return ret;
239}
132static char *scanelf_file_textrel(elfobj *elf, char *found_textrel) 240static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
133{ 241{
134 static char *ret = "TEXTREL"; 242 static const char *ret = "TEXTREL";
135 unsigned long i; 243 unsigned long i;
136 244
137 if (!show_textrel) return NULL; 245 if (!show_textrel && !show_textrels) return NULL;
138 246
139 if (elf->phdr) { 247 if (elf->phdr) {
140#define SHOW_TEXTREL(B) \ 248#define SHOW_TEXTREL(B) \
141 if (elf->elf_class == ELFCLASS ## B) { \ 249 if (elf->elf_class == ELFCLASS ## B) { \
142 Elf ## B ## _Dyn *dyn; \ 250 Elf ## B ## _Dyn *dyn; \
143 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 251 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
144 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 252 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
145 Elf ## B ## _Off offset; \ 253 Elf ## B ## _Off offset; \
146 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 254 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
147 if (phdr[i].p_type != PT_DYNAMIC) continue; \ 255 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
148 offset = EGET(phdr[i].p_offset); \ 256 offset = EGET(phdr[i].p_offset); \
149 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 257 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
150 dyn = DYN ## B (elf->data + offset); \ 258 dyn = DYN ## B (elf->data + offset); \
151 while (EGET(dyn->d_tag) != DT_NULL) { \ 259 while (EGET(dyn->d_tag) != DT_NULL) { \
152 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \ 260 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
153 *found_textrel = 1; \ 261 *found_textrel = 1; \
154 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \ 262 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
155 return ret; \ 263 return (be_wewy_wewy_quiet ? NULL : ret); \
156 } \ 264 } \
157 ++dyn; \ 265 ++dyn; \
158 } \ 266 } \
159 } } 267 } }
160 SHOW_TEXTREL(32) 268 SHOW_TEXTREL(32)
161 SHOW_TEXTREL(64) 269 SHOW_TEXTREL(64)
162 } 270 }
163 271
164 if (be_quiet) 272 if (be_quiet || be_wewy_wewy_quiet)
165 return NULL; 273 return NULL;
166 else 274 else
167 return " - "; 275 return " - ";
168} 276}
277static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
278{
279 unsigned long s, r, rmax;
280 void *symtab_void, *strtab_void, *text_void;
281
282 if (!show_textrels) return NULL;
283
284 /* don't search for TEXTREL's if the ELF doesn't have any */
285 if (!*found_textrel) scanelf_file_textrel(elf, found_textrel);
286 if (!*found_textrel) return NULL;
287
288 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
289 text_void = elf_findsecbyname(elf, ".text");
290
291 if (symtab_void && strtab_void && text_void && elf->shdr) {
292#define SHOW_TEXTRELS(B) \
293 if (elf->elf_class == ELFCLASS ## B) { \
294 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
295 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
296 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
297 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
298 Elf ## B ## _Shdr *text = SHDR ## B (text_void); \
299 Elf ## B ## _Addr vaddr = EGET(text->sh_addr); \
300 uint ## B ## _t memsz = EGET(text->sh_size); \
301 Elf ## B ## _Rel *rel; \
302 Elf ## B ## _Rela *rela; \
303 /* search the section headers for relocations */ \
304 for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
305 uint32_t sh_type = EGET(shdr[s].sh_type); \
306 if (sh_type == SHT_REL) { \
307 rel = REL ## B (elf->data + EGET(shdr[s].sh_offset)); \
308 rela = NULL; \
309 rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
310 } else if (sh_type == SHT_RELA) { \
311 rel = NULL; \
312 rela = RELA ## B (elf->data + EGET(shdr[s].sh_offset)); \
313 rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
314 } else \
315 continue; \
316 /* now see if any of the relocs are in the .text */ \
317 for (r = 0; r < rmax; ++r) { \
318 unsigned long sym_max; \
319 Elf ## B ## _Addr offset_tmp; \
320 Elf ## B ## _Sym *func; \
321 Elf ## B ## _Sym *sym; \
322 Elf ## B ## _Addr r_offset; \
323 uint ## B ## _t r_info; \
324 if (sh_type == SHT_REL) { \
325 r_offset = EGET(rel[r].r_offset); \
326 r_info = EGET(rel[r].r_info); \
327 } else { \
328 r_offset = EGET(rela[r].r_offset); \
329 r_info = EGET(rela[r].r_info); \
330 } \
331 /* make sure this relocation is inside of the .text */ \
332 if (r_offset < vaddr || r_offset >= vaddr + memsz) { \
333 if (be_verbose <= 2) continue; \
334 } else \
335 *found_textrels = 1; \
336 /* locate this relocation symbol name */ \
337 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
338 if ((void*)sym > (void*)elf->data_end) { \
339 warn("%s: corrupt ELF symbol", elf->filename); \
340 continue; \
341 } \
342 sym_max = ELF ## B ## _R_SYM(r_info); \
343 if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
344 sym += sym_max; \
345 else \
346 sym = NULL; \
347 sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
348 /* show the raw details about this reloc */ \
349 printf(" %s: ", elf->base_filename); \
350 if (sym && sym->st_name) \
351 printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name))); \
352 else \
353 printf("(memory/fake?)"); \
354 printf(" [0x%lX]", (unsigned long)r_offset); \
355 /* now try to find the closest symbol that this rel is probably in */ \
356 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
357 func = NULL; \
358 offset_tmp = 0; \
359 while (sym_max--) { \
360 if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
361 func = sym; \
362 offset_tmp = EGET(sym->st_value); \
363 } \
364 ++sym; \
365 } \
366 printf(" in "); \
367 if (func && func->st_name) \
368 printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(func->st_name))); \
369 else \
370 printf("(NULL: fake?)"); \
371 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
372 } \
373 } }
374 SHOW_TEXTRELS(32)
375 SHOW_TEXTRELS(64)
376 }
377 if (!*found_textrels)
378 warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
379
380 return NULL;
381}
382
383static void rpath_security_checks(elfobj *, char *, const char *);
384static void rpath_security_checks(elfobj *elf, char *item, const char *dt_type)
385{
386 struct stat st;
387 switch (*item) {
388 case '/': break;
389 case '.':
390 warnf("Security problem with relative %s '%s' in %s", dt_type, item, elf->filename);
391 break;
392 case ':':
393 case '\0':
394 warnf("Security problem NULL %s in %s", dt_type, elf->filename);
395 break;
396 case '$':
397 if (fstat(elf->fd, &st) != -1)
398 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
399 warnf("Security problem with %s='%s' in %s with mode set of %o",
400 dt_type, item, elf->filename, st.st_mode & 07777);
401 break;
402 default:
403 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename);
404 break;
405 }
406}
169static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len) 407static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
170{ 408{
171 /* TODO: if be_quiet, only output RPATH's which aren't in /etc/ld.so.conf */
172 unsigned long i; 409 unsigned long i, s;
173 char *rpath, *runpath; 410 char *rpath, *runpath, **r;
174 void *strtbl_void; 411 void *strtbl_void;
175 412
176 if (!show_rpath) return; 413 if (!show_rpath) return;
177 414
178 strtbl_void = elf_findsecbyname(elf, ".dynstr"); 415 strtbl_void = elf_findsecbyname(elf, ".dynstr");
184 Elf ## B ## _Dyn *dyn; \ 421 Elf ## B ## _Dyn *dyn; \
185 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 422 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
186 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 423 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
187 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 424 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
188 Elf ## B ## _Off offset; \ 425 Elf ## B ## _Off offset; \
426 Elf ## B ## _Xword word; \
427 /* Scan all the program headers */ \
189 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 428 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
429 /* Just scan dynamic headers */ \
190 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 430 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
191 offset = EGET(phdr[i].p_offset); \ 431 offset = EGET(phdr[i].p_offset); \
192 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 432 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
433 /* Just scan dynamic RPATH/RUNPATH headers */ \
193 dyn = DYN ## B (elf->data + offset); \ 434 dyn = DYN ## B (elf->data + offset); \
194 while (EGET(dyn->d_tag) != DT_NULL) { \ 435 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
195 if (EGET(dyn->d_tag) == DT_RPATH) { \ 436 if (word == DT_RPATH) { \
196 if (rpath) warn("ELF has multiple DT_RPATH's !?"); \ 437 r = &rpath; \
438 } else if (word == DT_RUNPATH) { \
439 r = &runpath; \
440 } else { \
441 ++dyn; \
442 continue; \
443 } \
444 /* Verify the memory is somewhat sane */ \
197 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 445 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
198 if (offset >= elf->len) continue; \ 446 if (offset < (Elf ## B ## _Off)elf->len) { \
447 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
199 rpath = (char*)(elf->data + offset); \ 448 *r = (char*)(elf->data + offset); \
449 /* cache the length in case we need to nuke this section later on */ \
450 if (fix_elf) \
451 offset = strlen(*r); \
452 /* If quiet, don't output paths in ld.so.conf */ \
453 if (be_quiet) { \
454 size_t len; \
455 char *start, *end; \
456 /* note that we only 'chop' off leading known paths. */ \
457 /* since *r is read-only memory, we can only move the ptr forward. */ \
458 start = *r; \
459 /* scan each path in : delimited list */ \
460 while (start) { \
461 rpath_security_checks(elf, start, get_elfdtype(word)); \
462 end = strchr(start, ':'); \
463 len = (end ? abs(end - start) : strlen(start)); \
464 if (use_ldcache) \
465 for (s = 0; ldpaths[s]; ++s) \
466 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
467 *r = end; \
468 /* corner case ... if RPATH reads "/usr/lib:", we want \
469 * to show ':' rather than '' */ \
470 if (end && end[1] != '\0') \
471 (*r)++; \
472 break; \
473 } \
474 if (!*r || !end) \
475 break; \
476 else \
477 start = start + len + 1; \
478 } \
479 } \
480 if (*r) { \
481 if (fix_elf > 2 || (fix_elf && **r == '\0')) { \
482 /* just nuke it */ \
483 nuke_it##B: \
484 memset(*r, 0x00, offset); \
485 *r = NULL; \
486 ESET(dyn->d_tag, DT_DEBUG); \
487 ESET(dyn->d_un.d_ptr, 0); \
488 } else if (fix_elf) { \
489 /* try to clean "bad" paths */ \
490 size_t len, tmpdir_len; \
491 char *start, *end; \
492 const char *tmpdir; \
493 start = *r; \
494 tmpdir = (getenv("TMPDIR") ? : "."); \
495 tmpdir_len = strlen(tmpdir); \
496 while (1) { \
497 end = strchr(start, ':'); \
498 if (start == end) { \
499 eat_this_path##B: \
500 len = strlen(end); \
501 memmove(start, end+1, len); \
502 start[len-1] = '\0'; \
503 end = start - 1; \
504 } else if (tmpdir && !strncmp(start, tmpdir, tmpdir_len)) { \
505 if (!end) { \
506 if (start == *r) \
507 goto nuke_it##B; \
508 *--start = '\0'; \
509 } else \
510 goto eat_this_path##B; \
511 } \
512 if (!end) \
513 break; \
514 start = end + 1; \
515 } \
516 if (**r == '\0') \
517 goto nuke_it##B; \
518 } \
519 if (*r) \
200 *found_rpath = 1; \ 520 *found_rpath = 1; \
201 } else if (EGET(dyn->d_tag) == DT_RUNPATH) { \ 521 } \
202 if (runpath) warn("ELF has multiple DT_RUNPATH's !?"); \
203 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
204 if (offset >= elf->len) continue; \
205 runpath = (char*)(elf->data + offset); \
206 *found_rpath = 1; \
207 } \ 522 } \
208 ++dyn; \ 523 ++dyn; \
209 } \ 524 } \
210 } } 525 } }
211 SHOW_RPATH(32) 526 SHOW_RPATH(32)
212 SHOW_RPATH(64) 527 SHOW_RPATH(64)
213 } 528 }
529
530 if (be_wewy_wewy_quiet) return;
214 531
215 if (rpath && runpath) { 532 if (rpath && runpath) {
216 if (!strcmp(rpath, runpath)) { 533 if (!strcmp(rpath, runpath)) {
217 xstrcat(ret, runpath, ret_len); 534 xstrcat(ret, runpath, ret_len);
218 } else { 535 } else {
226 } else if (rpath || runpath) 543 } else if (rpath || runpath)
227 xstrcat(ret, (runpath ? runpath : rpath), ret_len); 544 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
228 else if (!be_quiet) 545 else if (!be_quiet)
229 xstrcat(ret, " - ", ret_len); 546 xstrcat(ret, " - ", ret_len);
230} 547}
548
549#define LDSO_CACHE_MAGIC "ld.so-"
550#define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
551#define LDSO_CACHE_VER "1.7.0"
552#define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
553#define FLAG_ANY -1
554#define FLAG_TYPE_MASK 0x00ff
555#define FLAG_LIBC4 0x0000
556#define FLAG_ELF 0x0001
557#define FLAG_ELF_LIBC5 0x0002
558#define FLAG_ELF_LIBC6 0x0003
559#define FLAG_REQUIRED_MASK 0xff00
560#define FLAG_SPARC_LIB64 0x0100
561#define FLAG_IA64_LIB64 0x0200
562#define FLAG_X8664_LIB64 0x0300
563#define FLAG_S390_LIB64 0x0400
564#define FLAG_POWERPC_LIB64 0x0500
565#define FLAG_MIPS64_LIBN32 0x0600
566#define FLAG_MIPS64_LIBN64 0x0700
567
568static char *lookup_cache_lib(elfobj *, char *);
569static char *lookup_cache_lib(elfobj *elf, char *fname)
570{
571 int fd = 0;
572 char *strs;
573 static char buf[__PAX_UTILS_PATH_MAX] = "";
574 const char *cachefile = "/etc/ld.so.cache";
575 struct stat st;
576
577 typedef struct {
578 char magic[LDSO_CACHE_MAGIC_LEN];
579 char version[LDSO_CACHE_VER_LEN];
580 int nlibs;
581 } header_t;
582 header_t *header;
583
584 typedef struct {
585 int flags;
586 int sooffset;
587 int liboffset;
588 } libentry_t;
589 libentry_t *libent;
590
591 if (fname == NULL)
592 return NULL;
593
594 if (ldcache == 0) {
595 if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY)) == -1)
596 return NULL;
597
598 /* cache these values so we only map/unmap the cache file once */
599 ldcache_size = st.st_size;
600 ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
601
602 close(fd);
603
604 if (ldcache == (caddr_t)-1) {
605 ldcache = 0;
606 return NULL;
607 }
608
609 if (memcmp(((header_t *) ldcache)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN))
610 return NULL;
611 if (memcmp (((header_t *) ldcache)->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
612 return NULL;
613 }
614
615 header = (header_t *) ldcache;
616 libent = (libentry_t *) (ldcache + sizeof(header_t));
617 strs = (char *) &libent[header->nlibs];
618
619 for (fd = 0; fd < header->nlibs; fd++) {
620 /* this should be more fine grained, but for now we assume that
621 * diff arches will not be cached together. and we ignore the
622 * the different multilib mips cases. */
623 if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
624 continue;
625 if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
626 continue;
627
628 if (strcmp(fname, strs + libent[fd].sooffset) != 0)
629 continue;
630 strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
631 }
632 return buf;
633}
634
635
231static void scanelf_file_needed(elfobj *elf, char *found_needed, char **ret, size_t *ret_len) 636static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
232{ 637{
233 unsigned long i; 638 unsigned long i;
234 char *needed; 639 char *needed;
235 void *strtbl_void; 640 void *strtbl_void;
641 char *p;
236 642
237 if (!show_needed) return; 643 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL;
238 644
239 strtbl_void = elf_findsecbyname(elf, ".dynstr"); 645 strtbl_void = elf_findsecbyname(elf, ".dynstr");
240 646
241 if (elf->phdr && strtbl_void) { 647 if (elf->phdr && strtbl_void) {
242#define SHOW_NEEDED(B) \ 648#define SHOW_NEEDED(B) \
252 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 658 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
253 dyn = DYN ## B (elf->data + offset); \ 659 dyn = DYN ## B (elf->data + offset); \
254 while (EGET(dyn->d_tag) != DT_NULL) { \ 660 while (EGET(dyn->d_tag) != DT_NULL) { \
255 if (EGET(dyn->d_tag) == DT_NEEDED) { \ 661 if (EGET(dyn->d_tag) == DT_NEEDED) { \
256 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 662 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
257 if (offset >= elf->len) continue; \ 663 if (offset >= (Elf ## B ## _Off)elf->len) { \
664 ++dyn; \
665 continue; \
666 } \
258 needed = (char*)(elf->data + offset); \ 667 needed = (char*)(elf->data + offset); \
668 if (op == 0) { \
669 if (!be_wewy_wewy_quiet) { \
259 if (*found_needed) xchrcat(ret, ',', ret_len); \ 670 if (*found_needed) xchrcat(ret, ',', ret_len); \
671 if (use_ldcache) \
672 if ((p = lookup_cache_lib(elf, needed)) != NULL) \
673 needed = p; \
260 xstrcat(ret, needed, ret_len); \ 674 xstrcat(ret, needed, ret_len); \
675 } \
261 *found_needed = 1; \ 676 *found_needed = 1; \
677 } else { \
678 if (!strncmp(find_lib, needed, strlen( !gmatch ? needed : find_lib))) { \
679 *found_lib = 1; \
680 return (be_wewy_wewy_quiet ? NULL : needed); \
681 } \
682 } \
262 } \ 683 } \
263 ++dyn; \ 684 ++dyn; \
264 } \ 685 } \
265 } } 686 } }
266 SHOW_NEEDED(32) 687 SHOW_NEEDED(32)
267 SHOW_NEEDED(64) 688 SHOW_NEEDED(64)
689 if (op == 0 && !*found_needed && be_verbose)
690 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
268 } 691 }
692
693 return NULL;
269} 694}
270static char *scanelf_file_interp(elfobj *elf, char *found_interp) 695static char *scanelf_file_interp(elfobj *elf, char *found_interp)
271{ 696{
272 void *strtbl_void; 697 void *strtbl_void;
273 698
278 if (strtbl_void) { 703 if (strtbl_void) {
279#define SHOW_INTERP(B) \ 704#define SHOW_INTERP(B) \
280 if (elf->elf_class == ELFCLASS ## B) { \ 705 if (elf->elf_class == ELFCLASS ## B) { \
281 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 706 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
282 *found_interp = 1; \ 707 *found_interp = 1; \
283 return elf->data + EGET(strtbl->sh_offset); \ 708 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
284 } 709 }
285 SHOW_INTERP(32) 710 SHOW_INTERP(32)
286 SHOW_INTERP(64) 711 SHOW_INTERP(64)
287 } 712 }
288 return NULL; 713 return NULL;
289} 714}
290static char *scanelf_file_sym(elfobj *elf, char *found_sym, const char *filename) 715static char *scanelf_file_bind(elfobj *elf, char *found_bind)
291{ 716{
292 unsigned long i; 717 unsigned long i;
718 struct stat s;
719
720 if (!show_bind) return NULL;
721 if (!elf->phdr) return NULL;
722
723#define SHOW_BIND(B) \
724 if (elf->elf_class == ELFCLASS ## B) { \
725 Elf ## B ## _Dyn *dyn; \
726 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
727 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
728 Elf ## B ## _Off offset; \
729 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
730 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
731 offset = EGET(phdr[i].p_offset); \
732 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
733 dyn = DYN ## B (elf->data + offset); \
734 while (EGET(dyn->d_tag) != DT_NULL) { \
735 if (EGET(dyn->d_tag) == DT_BIND_NOW || \
736 (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
737 { \
738 if (be_quiet) return NULL; \
739 *found_bind = 1; \
740 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
741 } \
742 ++dyn; \
743 } \
744 } \
745 }
746 SHOW_BIND(32)
747 SHOW_BIND(64)
748
749 if (be_wewy_wewy_quiet) return NULL;
750
751 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
752 return NULL;
753 } else {
754 *found_bind = 1;
755 return (char *) "LAZY";
756 }
757}
758static char *scanelf_file_soname(elfobj *elf, char *found_soname)
759{
760 unsigned long i;
761 char *soname;
762 void *strtbl_void;
763
764 if (!show_soname) return NULL;
765
766 strtbl_void = elf_findsecbyname(elf, ".dynstr");
767
768 if (elf->phdr && strtbl_void) {
769#define SHOW_SONAME(B) \
770 if (elf->elf_class == ELFCLASS ## B) { \
771 Elf ## B ## _Dyn *dyn; \
772 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
773 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
774 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
775 Elf ## B ## _Off offset; \
776 /* only look for soname in shared objects */ \
777 if (ehdr->e_type != ET_DYN) \
778 return NULL; \
779 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
780 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
781 offset = EGET(phdr[i].p_offset); \
782 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
783 dyn = DYN ## B (elf->data + offset); \
784 while (EGET(dyn->d_tag) != DT_NULL) { \
785 if (EGET(dyn->d_tag) == DT_SONAME) { \
786 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
787 if (offset >= (Elf ## B ## _Off)elf->len) { \
788 ++dyn; \
789 continue; \
790 } \
791 soname = (char*)(elf->data + offset); \
792 *found_soname = 1; \
793 return (be_wewy_wewy_quiet ? NULL : soname); \
794 } \
795 ++dyn; \
796 } \
797 } }
798 SHOW_SONAME(32)
799 SHOW_SONAME(64)
800 }
801
802 return NULL;
803}
804static char *scanelf_file_sym(elfobj *elf, char *found_sym)
805{
806 unsigned long i;
807 char *ret;
293 void *symtab_void, *strtab_void; 808 void *symtab_void, *strtab_void;
294 809
295 if (!find_sym) return NULL; 810 if (!find_sym) return NULL;
811 ret = find_sym;
296 812
297 symtab_void = elf_findsecbyname(elf, ".symtab"); 813 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
298 strtab_void = elf_findsecbyname(elf, ".strtab");
299 814
300 if (symtab_void && strtab_void) { 815 if (symtab_void && strtab_void) {
301#define FIND_SYM(B) \ 816#define FIND_SYM(B) \
302 if (elf->elf_class == ELFCLASS ## B) { \ 817 if (elf->elf_class == ELFCLASS ## B) { \
303 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ 818 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
304 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \ 819 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
305 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ 820 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
306 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \ 821 unsigned long cnt = EGET(symtab->sh_entsize); \
307 char *symname; \ 822 char *symname; \
823 if (cnt) \
824 cnt = EGET(symtab->sh_size) / cnt; \
308 for (i = 0; i < cnt; ++i) { \ 825 for (i = 0; i < cnt; ++i) { \
309 if (sym->st_name) { \ 826 if (sym->st_name) { \
310 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \ 827 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
828 if ((void*)symname > (void*)elf->data_end) { \
829 warnf("%s: corrupt ELF symbols", elf->filename); \
830 continue; \
831 } \
311 if (*find_sym == '*') { \ 832 if (*find_sym == '*') { \
312 printf("%s(%s) %5lX %15s %s\n", \ 833 printf("%s(%s) %5lX %15s %s\n", \
313 ((*found_sym == 0) ? "\n\t" : "\t"), \ 834 ((*found_sym == 0) ? "\n\t" : "\t"), \
314 (char *)basename(filename), \ 835 elf->base_filename, \
315 (long)sym->st_size, \ 836 (unsigned long)sym->st_size, \
316 (char *)get_elfstttype(sym->st_info), \ 837 get_elfstttype(sym->st_info), \
317 symname); \ 838 symname); \
318 *found_sym = 1; \ 839 *found_sym = 1; \
319 } else if ((strcmp(find_sym, symname) == 0) || \ 840 } else { \
841 char *this_sym, *next_sym; \
842 this_sym = find_sym; \
843 do { \
844 next_sym = strchr(this_sym, ','); \
845 if (next_sym == NULL) \
846 next_sym = this_sym + strlen(this_sym); \
847 if ((strncmp(this_sym, symname, (next_sym-this_sym)) == 0 && symname[next_sym-this_sym] == '\0') || \
320 (strcmp(symname, versioned_symname) == 0)) \ 848 (strcmp(symname, versioned_symname) == 0)) { \
849 ret = this_sym; \
321 (*found_sym)++; \ 850 (*found_sym)++; \
851 goto break_out; \
852 } \
853 this_sym = next_sym + 1; \
854 } while (*next_sym != '\0'); \
855 } \
322 } \ 856 } \
323 ++sym; \ 857 ++sym; \
324 } } 858 } }
325 FIND_SYM(32) 859 FIND_SYM(32)
326 FIND_SYM(64) 860 FIND_SYM(64)
327 } 861 }
862
863break_out:
864 if (be_wewy_wewy_quiet) return NULL;
865
328 if (*find_sym != '*' && *found_sym) 866 if (*find_sym != '*' && *found_sym)
329 return find_sym; 867 return ret;
330 if (be_quiet) 868 if (be_quiet)
331 return NULL; 869 return NULL;
332 else 870 else
333 return " - "; 871 return (char *)" - ";
334} 872}
873
874
335/* scan an elf file and show all the fun stuff */ 875/* scan an elf file and show all the fun stuff */
336#define prints(str) fputs(str, stdout) 876#define prints(str) write(fileno(stdout), str, strlen(str))
337static void scanelf_file(const char *filename) 877static int scanelf_elfobj(elfobj *elf)
338{ 878{
339 unsigned long i; 879 unsigned long i;
340 char found_pax, found_stack, found_relro, found_textrel, 880 char found_pax, found_phdr, found_relro, found_load, found_textrel,
341 found_rpath, found_needed, found_interp, found_sym, 881 found_rpath, found_needed, found_interp, found_bind, found_soname,
342 found_file; 882 found_sym, found_lib, found_file, found_textrels;
343 elfobj *elf;
344 struct stat st;
345 static char *out_buffer = NULL; 883 static char *out_buffer = NULL;
346 static size_t out_len; 884 static size_t out_len;
885
886 found_pax = found_phdr = found_relro = found_load = found_textrel = \
887 found_rpath = found_needed = found_interp = found_bind = found_soname = \
888 found_sym = found_lib = found_file = found_textrels = 0;
889
890 if (be_verbose > 2)
891 printf("%s: scanning file {%s,%s}\n", elf->filename,
892 get_elfeitype(EI_CLASS, elf->elf_class),
893 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
894 else if (be_verbose > 1)
895 printf("%s: scanning file\n", elf->filename);
896
897 /* init output buffer */
898 if (!out_buffer) {
899 out_len = sizeof(char) * 80;
900 out_buffer = (char*)xmalloc(out_len);
901 }
902 *out_buffer = '\0';
903
904 /* show the header */
905 if (!be_quiet && show_banner) {
906 for (i = 0; out_format[i]; ++i) {
907 if (!IS_MODIFIER(out_format[i])) continue;
908
909 switch (out_format[++i]) {
910 case '%': break;
911 case '#': break;
912 case 'F':
913 case 'p':
914 case 'f': prints("FILE "); found_file = 1; break;
915 case 'o': prints(" TYPE "); break;
916 case 'x': prints(" PAX "); break;
917 case 'e': prints("STK/REL/PTL "); break;
918 case 't': prints("TEXTREL "); break;
919 case 'r': prints("RPATH "); break;
920 case 'n': prints("NEEDED "); break;
921 case 'i': prints("INTERP "); break;
922 case 'b': prints("BIND "); break;
923 case 'S': prints("SONAME "); break;
924 case 's': prints("SYM "); break;
925 case 'N': prints("LIB "); break;
926 case 'T': prints("TEXTRELS "); break;
927 default: warnf("'%c' has no title ?", out_format[i]);
928 }
929 }
930 if (!found_file) prints("FILE ");
931 prints("\n");
932 found_file = 0;
933 show_banner = 0;
934 }
935
936 /* dump all the good stuff */
937 for (i = 0; out_format[i]; ++i) {
938 const char *out;
939 const char *tmp;
940
941 if (!IS_MODIFIER(out_format[i])) {
942 xchrcat(&out_buffer, out_format[i], &out_len);
943 continue;
944 }
945
946 out = NULL;
947 be_wewy_wewy_quiet = (out_format[i] == '#');
948 switch (out_format[++i]) {
949 case '%':
950 case '#':
951 xchrcat(&out_buffer, out_format[i], &out_len); break;
952 case 'F':
953 found_file = 1;
954 if (be_wewy_wewy_quiet) break;
955 xstrcat(&out_buffer, elf->filename, &out_len);
956 break;
957 case 'p':
958 found_file = 1;
959 if (be_wewy_wewy_quiet) break;
960 tmp = elf->filename;
961 if (search_path) {
962 ssize_t len_search = strlen(search_path);
963 ssize_t len_file = strlen(elf->filename);
964 if (!strncmp(elf->filename, search_path, len_search) && \
965 len_file > len_search)
966 tmp += len_search;
967 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
968 }
969 xstrcat(&out_buffer, tmp, &out_len);
970 break;
971 case 'f':
972 found_file = 1;
973 if (be_wewy_wewy_quiet) break;
974 tmp = strrchr(elf->filename, '/');
975 tmp = (tmp == NULL ? elf->filename : tmp+1);
976 xstrcat(&out_buffer, tmp, &out_len);
977 break;
978 case 'o': out = get_elfetype(elf); break;
979 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
980 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
981 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
982 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
983 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
984 case 'n':
985 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
986 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
987 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
988 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
989 case 's': out = scanelf_file_sym(elf, &found_sym); break;
990 default: warnf("'%c' has no scan code?", out_format[i]);
991 }
992 if (out) {
993 /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */
994 if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL)
995 xstrncat(&out_buffer, out, &out_len, (tmp-out));
996 else
997 xstrcat(&out_buffer, out, &out_len);
998 }
999 }
1000
1001#define FOUND_SOMETHING() \
1002 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
1003 found_rpath || found_needed || found_interp || found_bind || \
1004 found_soname || found_sym || found_lib || found_textrels)
1005
1006 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
1007 xchrcat(&out_buffer, ' ', &out_len);
1008 xstrcat(&out_buffer, elf->filename, &out_len);
1009 }
1010 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
1011 puts(out_buffer);
1012 fflush(stdout);
1013 }
1014
1015 return 0;
1016}
1017
1018/* scan a single elf */
1019static int scanelf_elf(const char *filename, int fd, size_t len)
1020{
1021 int ret = 1;
1022 elfobj *elf;
1023
1024 /* verify this is real ELF */
1025 if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1026 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1027 return ret;
1028 }
1029 switch (match_bits) {
1030 case 32:
1031 if (elf->elf_class != ELFCLASS32)
1032 goto label_done;
1033 break;
1034 case 64:
1035 if (elf->elf_class != ELFCLASS64)
1036 goto label_done;
1037 break;
1038 default: break;
1039 }
1040 if (strlen(match_etypes)) {
1041 char sbuf[126];
1042 strncpy(sbuf, match_etypes, sizeof(sbuf));
1043 if (strchr(match_etypes, ',') != NULL) {
1044 char *p;
1045 while((p = strrchr(sbuf, ',')) != NULL) {
1046 *p = 0;
1047 if (atoi(p+1) == get_etype(elf))
1048 goto label_ret;
1049 }
1050 }
1051 if (atoi(sbuf) != get_etype(elf))
1052 goto label_done;
1053 }
1054
1055label_ret:
1056 ret = scanelf_elfobj(elf);
1057
1058label_done:
1059 unreadelf(elf);
1060 return ret;
1061}
1062
1063/* scan an archive of elfs */
1064static int scanelf_archive(const char *filename, int fd, size_t len)
1065{
1066 archive_handle *ar;
1067 archive_member *m;
1068 char *ar_buffer;
1069 elfobj *elf;
1070
1071 ar = ar_open_fd(filename, fd);
1072 if (ar == NULL)
1073 return 1;
1074
1075 ar_buffer = (char*)mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1076 while ((m=ar_next(ar)) != NULL) {
1077 elf = readelf_buffer(m->name, ar_buffer+lseek(fd,0,SEEK_CUR), m->size);
1078 if (elf) {
1079 scanelf_elfobj(elf);
1080 unreadelf(elf);
1081 }
1082 }
1083 munmap(ar_buffer, len);
1084
1085 return 0;
1086}
1087/* scan a file which may be an elf or an archive or some other magical beast */
1088static void scanelf_file(const char *filename)
1089{
1090 struct stat st;
1091 int fd;
347 1092
348 /* make sure 'filename' exists */ 1093 /* make sure 'filename' exists */
349 if (lstat(filename, &st) == -1) { 1094 if (lstat(filename, &st) == -1) {
350 if (be_verbose > 2) printf("%s: does not exist\n", filename); 1095 if (be_verbose > 2) printf("%s: does not exist\n", filename);
351 return; 1096 return;
352 } 1097 }
1098
353 /* always handle regular files and handle symlinked files if no -y */ 1099 /* always handle regular files and handle symlinked files if no -y */
354 if (!(S_ISREG(st.st_mode) || (S_ISLNK(st.st_mode) && scan_symlink))) { 1100 if (S_ISLNK(st.st_mode)) {
1101 if (!scan_symlink) return;
1102 stat(filename, &st);
1103 }
1104 if (!S_ISREG(st.st_mode)) {
355 if (be_verbose > 2) printf("%s: skipping non-file\n", filename); 1105 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
356 return; 1106 return;
357 } 1107 }
358 1108
359 found_pax = found_stack = found_relro = found_textrel = \ 1109 if ((fd=open(filename, (fix_elf ? O_RDWR : O_RDONLY))) == -1)
360 found_rpath = found_needed = found_interp = found_sym = \
361 found_file = 0;
362
363 /* verify this is real ELF */
364 if ((elf = readelf(filename)) == NULL) {
365 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
366 return; 1110 return;
367 }
368 1111
369 if (be_verbose > 1) 1112 if (scanelf_elf(filename, fd, st.st_size) == 1 && scan_archives)
370 printf("%s: scanning file {%s,%s}\n", filename, 1113 /* if it isn't an ELF, maybe it's an .a archive */
371 get_elfeitype(elf, EI_CLASS, elf->elf_class), 1114 scanelf_archive(filename, fd, st.st_size);
372 get_elfeitype(elf, EI_DATA, elf->data[EI_DATA]));
373 else if (be_verbose)
374 printf("%s: scanning file\n", filename);
375 1115
376 /* init output buffer */ 1116 close(fd);
377 if (!out_buffer) {
378 out_len = sizeof(char) * 80;
379 out_buffer = (char*)xmalloc(out_len);
380 }
381 *out_buffer = '\0';
382
383 /* show the header */
384 if (!be_quiet && show_banner) {
385 for (i=0; out_format[i]; ++i) {
386 if (out_format[i] != '%') continue;
387
388 switch (out_format[++i]) {
389 case '%': break;
390 case 'F': prints("FILE "); break;
391 case 'o': prints(" TYPE "); break;
392 case 'x': prints(" PAX "); break;
393 case 'e': prints("STK/REL "); break;
394 case 't': prints("TEXTREL "); break;
395 case 'r': prints("RPATH "); break;
396 case 'n': prints("NEEDED "); break;
397 case 'i': prints("INTERP "); break;
398 case 's': prints("SYM "); break;
399 }
400 }
401 prints("\n");
402 show_banner = 0;
403 }
404
405 /* dump all the good stuff */
406 for (i=0; out_format[i]; ++i) {
407 const char *out;
408
409 /* make sure we trim leading spaces in quiet mode */
410 if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
411 *out_buffer = '\0';
412
413 if (out_format[i] != '%') {
414 xchrcat(&out_buffer, out_format[i], &out_len);
415 continue;
416 }
417
418 out = NULL;
419 switch (out_format[++i]) {
420 case '%': xchrcat(&out_buffer, '%', &out_len); break;
421 case 'F': found_file = 1; xstrcat(&out_buffer, filename, &out_len); break;
422 case 'o': out = get_elfetype(elf); break;
423 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
424 case 'e': out = scanelf_file_stack(elf, &found_stack, &found_relro); break;
425 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
426 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
427 case 'n': scanelf_file_needed(elf, &found_needed, &out_buffer, &out_len); break;
428 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
429 case 's': out = scanelf_file_sym(elf, &found_sym, filename); break;
430 }
431 if (out) xstrcat(&out_buffer, out, &out_len);
432 }
433
434 if (!found_file) {
435 if (!be_quiet || found_pax || found_stack || found_textrel || \
436 found_rpath || found_needed || found_interp || found_sym)
437 xstrcat(&out_buffer, filename, &out_len);
438 }
439 if (!(be_quiet && xemptybuffer(out_buffer)))
440 puts(out_buffer);
441
442 unreadelf(elf);
443} 1117}
444 1118
445/* scan a directory for ET_EXEC files and print when we find one */ 1119/* scan a directory for ET_EXEC files and print when we find one */
446static void scanelf_dir(const char *path) 1120static void scanelf_dir(const char *path)
447{ 1121{
448 register DIR *dir; 1122 register DIR *dir;
449 register struct dirent *dentry; 1123 register struct dirent *dentry;
450 struct stat st_top, st; 1124 struct stat st_top, st;
451 char buf[_POSIX_PATH_MAX]; 1125 char buf[__PAX_UTILS_PATH_MAX];
452 size_t pathlen = 0, len = 0; 1126 size_t pathlen = 0, len = 0;
453 1127
454 /* make sure path exists */ 1128 /* make sure path exists */
455 if (lstat(path, &st_top) == -1) { 1129 if (lstat(path, &st_top) == -1) {
456 if (be_verbose > 2) printf("%s: does not exist\n", path); 1130 if (be_verbose > 2) printf("%s: does not exist\n", path);
466 /* now scan the dir looking for fun stuff */ 1140 /* now scan the dir looking for fun stuff */
467 if ((dir = opendir(path)) == NULL) { 1141 if ((dir = opendir(path)) == NULL) {
468 warnf("could not opendir %s: %s", path, strerror(errno)); 1142 warnf("could not opendir %s: %s", path, strerror(errno));
469 return; 1143 return;
470 } 1144 }
471 if (be_verbose) printf("%s: scanning dir\n", path); 1145 if (be_verbose > 1) printf("%s: scanning dir\n", path);
472 1146
473 pathlen = strlen(path); 1147 pathlen = strlen(path);
474 while ((dentry = readdir(dir))) { 1148 while ((dentry = readdir(dir))) {
475 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) 1149 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
476 continue; 1150 continue;
477 len = (pathlen + 1 + strlen(dentry->d_name) + 1); 1151 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
478 if (len >= sizeof(buf)) { 1152 if (len >= sizeof(buf)) {
479 warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf)); 1153 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
1154 (unsigned long)len, (unsigned long)sizeof(buf));
480 continue; 1155 continue;
481 } 1156 }
482 sprintf(buf, "%s/%s", path, dentry->d_name); 1157 sprintf(buf, "%s/%s", path, dentry->d_name);
483 if (lstat(buf, &st) != -1) { 1158 if (lstat(buf, &st) != -1) {
484 if (S_ISREG(st.st_mode)) 1159 if (S_ISREG(st.st_mode))
490 } 1165 }
491 } 1166 }
492 closedir(dir); 1167 closedir(dir);
493} 1168}
494 1169
1170static int scanelf_from_file(char *filename)
1171{
1172 FILE *fp = NULL;
1173 char *p;
1174 char path[__PAX_UTILS_PATH_MAX];
1175
1176 if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
1177 fp = stdin;
1178 else if ((fp = fopen(filename, "r")) == NULL)
1179 return 1;
1180
1181 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1182 if ((p = strchr(path, '\n')) != NULL)
1183 *p = 0;
1184 search_path = path;
1185 scanelf_dir(path);
1186 }
1187 if (fp != stdin)
1188 fclose(fp);
1189 return 0;
1190}
1191
1192static void load_ld_so_conf()
1193{
1194 FILE *fp = NULL;
1195 char *p;
1196 char path[__PAX_UTILS_PATH_MAX];
1197 int i = 0;
1198
1199 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
1200 return;
1201
1202 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1203 if (*path != '/')
1204 continue;
1205
1206 if ((p = strrchr(path, '\r')) != NULL)
1207 *p = 0;
1208 if ((p = strchr(path, '\n')) != NULL)
1209 *p = 0;
1210
1211 ldpaths[i++] = xstrdup(path);
1212
1213 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
1214 break;
1215 }
1216 ldpaths[i] = NULL;
1217
1218 fclose(fp);
1219}
1220
495/* scan /etc/ld.so.conf for paths */ 1221/* scan /etc/ld.so.conf for paths */
496static void scanelf_ldpath() 1222static void scanelf_ldpath()
497{ 1223{
498 char scan_l, scan_ul, scan_ull; 1224 char scan_l, scan_ul, scan_ull;
499 char *path, *p; 1225 int i = 0;
500 FILE *fp;
501 1226
502 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL) 1227 if (!ldpaths[0])
503 err("Unable to open ld.so.conf: %s", strerror(errno)); 1228 err("Unable to load any paths from ld.so.conf");
504 1229
505 scan_l = scan_ul = scan_ull = 0; 1230 scan_l = scan_ul = scan_ull = 0;
506 1231
507 path = (char*)xmalloc(_POSIX_PATH_MAX); 1232 while (ldpaths[i]) {
508 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL)
509 if (*path == '/') {
510 if ((p = strrchr(path, '\r')) != NULL)
511 *p = 0;
512 if ((p = strrchr(path, '\n')) != NULL)
513 *p = 0;
514 if (!scan_l && !strcmp(path, "/lib")) scan_l = 1; 1233 if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
515 if (!scan_ul && !strcmp(path, "/usr/lib")) scan_ul = 1; 1234 if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
516 if (!scan_ull && !strcmp(path, "/usr/local/lib")) scan_ull = 1; 1235 if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
517 scanelf_dir(path); 1236 scanelf_dir(ldpaths[i]);
1237 ++i;
518 } 1238 }
519 free(path);
520 fclose(fp);
521 1239
522 if (!scan_l) scanelf_dir("/lib"); 1240 if (!scan_l) scanelf_dir("/lib");
523 if (!scan_ul) scanelf_dir("/usr/lib"); 1241 if (!scan_ul) scanelf_dir("/usr/lib");
524 if (!scan_ull) scanelf_dir("/usr/local/lib"); 1242 if (!scan_ull) scanelf_dir("/usr/local/lib");
525} 1243}
541 1259
542 free(path); 1260 free(path);
543} 1261}
544 1262
545 1263
546
547/* usage / invocation handling functions */ 1264/* usage / invocation handling functions */
548#define PARSE_FLAGS "plRmyxetrnis:aqvF:o:BhV" 1265#define PARSE_FLAGS "plRmyAXxetrnLibSs:gN:TaqvF:f:o:M:E:BhV"
549#define a_argument required_argument 1266#define a_argument required_argument
550static struct option const long_opts[] = { 1267static struct option const long_opts[] = {
551 {"path", no_argument, NULL, 'p'}, 1268 {"path", no_argument, NULL, 'p'},
552 {"ldpath", no_argument, NULL, 'l'}, 1269 {"ldpath", no_argument, NULL, 'l'},
553 {"recursive", no_argument, NULL, 'R'}, 1270 {"recursive", no_argument, NULL, 'R'},
554 {"mount", no_argument, NULL, 'm'}, 1271 {"mount", no_argument, NULL, 'm'},
555 {"symlink", no_argument, NULL, 'y'}, 1272 {"symlink", no_argument, NULL, 'y'},
1273 {"archives", no_argument, NULL, 'A'},
1274 {"ldcache", no_argument, NULL, 'L'},
1275 {"fix", no_argument, NULL, 'X'},
556 {"pax", no_argument, NULL, 'x'}, 1276 {"pax", no_argument, NULL, 'x'},
557 {"header", no_argument, NULL, 'e'}, 1277 {"header", no_argument, NULL, 'e'},
558 {"textrel", no_argument, NULL, 't'}, 1278 {"textrel", no_argument, NULL, 't'},
559 {"rpath", no_argument, NULL, 'r'}, 1279 {"rpath", no_argument, NULL, 'r'},
560 {"needed", no_argument, NULL, 'n'}, 1280 {"needed", no_argument, NULL, 'n'},
561 {"interp", no_argument, NULL, 'i'}, 1281 {"interp", no_argument, NULL, 'i'},
1282 {"bind", no_argument, NULL, 'b'},
1283 {"soname", no_argument, NULL, 'S'},
562 {"symbol", a_argument, NULL, 's'}, 1284 {"symbol", a_argument, NULL, 's'},
1285 {"lib", a_argument, NULL, 'N'},
1286 {"gmatch", no_argument, NULL, 'g'},
1287 {"textrels", no_argument, NULL, 'T'},
1288 {"etype", a_argument, NULL, 'E'},
1289 {"bits", a_argument, NULL, 'M'},
563 {"all", no_argument, NULL, 'a'}, 1290 {"all", no_argument, NULL, 'a'},
564 {"quiet", no_argument, NULL, 'q'}, 1291 {"quiet", no_argument, NULL, 'q'},
565 {"verbose", no_argument, NULL, 'v'}, 1292 {"verbose", no_argument, NULL, 'v'},
566 {"format", a_argument, NULL, 'F'}, 1293 {"format", a_argument, NULL, 'F'},
1294 {"from", a_argument, NULL, 'f'},
567 {"file", a_argument, NULL, 'o'}, 1295 {"file", a_argument, NULL, 'o'},
568 {"nobanner", no_argument, NULL, 'B'}, 1296 {"nobanner", no_argument, NULL, 'B'},
569 {"help", no_argument, NULL, 'h'}, 1297 {"help", no_argument, NULL, 'h'},
570 {"version", no_argument, NULL, 'V'}, 1298 {"version", no_argument, NULL, 'V'},
571 {NULL, no_argument, NULL, 0x0} 1299 {NULL, no_argument, NULL, 0x0}
572}; 1300};
1301
573static char *opts_help[] = { 1302static const char *opts_help[] = {
574 "Scan all directories in PATH environment", 1303 "Scan all directories in PATH environment",
575 "Scan all directories in /etc/ld.so.conf", 1304 "Scan all directories in /etc/ld.so.conf",
576 "Scan directories recursively", 1305 "Scan directories recursively",
577 "Don't recursively cross mount points", 1306 "Don't recursively cross mount points",
578 "Don't scan symlinks\n", 1307 "Don't scan symlinks",
1308 "Scan archives (.a files)",
1309 "Utilize ld.so.cache information (use with -r/-n)",
1310 "Try and 'fix' bad things (use with -r/-e)\n",
579 "Print PaX markings", 1311 "Print PaX markings",
580 "Print GNU_STACK markings", 1312 "Print GNU_STACK/PT_LOAD markings",
581 "Print TEXTREL information", 1313 "Print TEXTREL information",
582 "Print RPATH information", 1314 "Print RPATH information",
583 "Print NEEDED information", 1315 "Print NEEDED information",
584 "Print INTERP information", 1316 "Print INTERP information",
1317 "Print BIND information",
1318 "Print SONAME information",
585 "Find a specified symbol", 1319 "Find a specified symbol",
1320 "Find a specified library",
1321 "Use strncmp to match libraries. (use with -N)",
1322 "Locate cause of TEXTREL",
1323 "Print only ELF files matching numeric constant type",
1324 "Print only ELF files matching numeric bits",
586 "Print all scanned info (-x -e -t -r)\n", 1325 "Print all scanned info (-x -e -t -r -b)\n",
587 "Only output 'bad' things", 1326 "Only output 'bad' things",
588 "Be verbose (can be specified more than once)", 1327 "Be verbose (can be specified more than once)",
589 "Use specified format for output", 1328 "Use specified format for output",
1329 "Read input stream from a filename",
590 "Write output stream to a filename", 1330 "Write output stream to a filename",
591 "Don't display the header", 1331 "Don't display the header",
592 "Print this help and exit", 1332 "Print this help and exit",
593 "Print version and exit", 1333 "Print version and exit",
594 NULL 1334 NULL
596 1336
597/* display usage and exit */ 1337/* display usage and exit */
598static void usage(int status) 1338static void usage(int status)
599{ 1339{
600 unsigned long i; 1340 unsigned long i;
601 printf(" Scan ELF binaries for stuff\n\n" 1341 printf("* Scan ELF binaries for stuff\n\n"
602 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0); 1342 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
603 printf("Options: -[%s]\n", PARSE_FLAGS); 1343 printf("Options: -[%s]\n", PARSE_FLAGS);
604 for (i = 0; long_opts[i].name; ++i) 1344 for (i = 0; long_opts[i].name; ++i)
605 if (long_opts[i].has_arg == no_argument) 1345 if (long_opts[i].has_arg == no_argument)
606 printf(" -%c, --%-13s %s\n", long_opts[i].val, 1346 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
607 long_opts[i].name, opts_help[i]); 1347 long_opts[i].name, opts_help[i]);
608 else 1348 else
609 printf(" -%c, --%-6s <arg> %s\n", long_opts[i].val, 1349 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
610 long_opts[i].name, opts_help[i]); 1350 long_opts[i].name, opts_help[i]);
1351
1352 if (status != EXIT_SUCCESS)
1353 exit(status);
1354
1355 puts("\nThe format modifiers for the -F option are:");
1356 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1357 puts(" t TEXTREL \tr RPATH \tn NEEDED");
1358 puts(" i INTERP \tb BIND \ts symbol");
1359 puts(" N library \to Type \tT TEXTRELs");
1360 puts(" S SONAME");
1361 puts(" p filename (with search path removed)");
1362 puts(" f filename (short name/basename)");
1363 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1364
1365 puts("\nELF Etypes:");
1366 print_etypes(stdout);
1367
611 exit(status); 1368 exit(status);
612} 1369}
613 1370
614/* parse command line arguments and preform needed actions */ 1371/* parse command line arguments and preform needed actions */
615static void parseargs(int argc, char *argv[]) 1372static void parseargs(int argc, char *argv[])
616{ 1373{
617 int flag; 1374 int i;
1375 char *from_file = NULL;
618 1376
619 opterr = 0; 1377 opterr = 0;
620 while ((flag=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { 1378 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
621 switch (flag) { 1379 switch (i) {
622 1380
623 case 'V': 1381 case 'V':
624 printf("%s compiled %s\n%s\n" 1382 printf("pax-utils-%s: %s compiled %s\n%s\n"
625 "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n", 1383 "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
626 __FILE__, __DATE__, rcsid, argv0); 1384 VERSION, __FILE__, __DATE__, rcsid, argv0);
627 exit(EXIT_SUCCESS); 1385 exit(EXIT_SUCCESS);
628 break; 1386 break;
629 case 'h': usage(EXIT_SUCCESS); break; 1387 case 'h': usage(EXIT_SUCCESS); break;
630 1388 case 'f':
1389 if (from_file) warn("You prob don't want to specify -f twice");
1390 from_file = optarg;
1391 break;
1392 case 'E':
1393 strncpy(match_etypes, optarg, sizeof(match_etypes));
1394 break;
1395 case 'M':
1396 match_bits = atoi(optarg);
1397 break;
631 case 'o': { 1398 case 'o': {
632 FILE *fp = NULL; 1399 FILE *fp = NULL;
633 fp = freopen(optarg, "w", stdout); 1400 if ((fp = freopen(optarg, "w", stdout)) == NULL)
634 if (fp == NULL)
635 err("Could not open output stream '%s': %s", optarg, strerror(errno)); 1401 err("Could not open output stream '%s': %s", optarg, strerror(errno));
636 stdout = fp; 1402 SET_STDOUT(fp);
637 break; 1403 break;
638 } 1404 }
639 1405
640 case 's': { 1406 case 's': {
641 size_t len; 1407 if (find_sym) warn("You prob don't want to specify -s twice");
642 find_sym = xstrdup(optarg); 1408 find_sym = optarg;
643 len = strlen(find_sym) + 1;
644 versioned_symname = (char*)xmalloc(sizeof(char) * (len+1)); 1409 versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
645 sprintf(versioned_symname, "%s@", find_sym); 1410 sprintf(versioned_symname, "%s@", find_sym);
646 break; 1411 break;
647 } 1412 }
1413 case 'N': {
1414 if (find_lib) warn("You prob don't want to specify -N twice");
1415 find_lib = optarg;
1416 break;
1417 }
648 1418
649 case 'F': { 1419 case 'F': {
1420 if (out_format) warn("You prob don't want to specify -F twice");
650 out_format = strdup(optarg); 1421 out_format = optarg;
651 break; 1422 break;
652 } 1423 }
653 1424
1425 case 'g': gmatch = 1; break;
1426 case 'L': use_ldcache = 1; break;
654 case 'y': scan_symlink = 0; break; 1427 case 'y': scan_symlink = 0; break;
1428 case 'A': scan_archives = 1; break;
655 case 'B': show_banner = 0; break; 1429 case 'B': show_banner = 0; break;
656 case 'l': scan_ldpath = 1; break; 1430 case 'l': scan_ldpath = 1; break;
657 case 'p': scan_envpath = 1; break; 1431 case 'p': scan_envpath = 1; break;
658 case 'R': dir_recurse = 1; break; 1432 case 'R': dir_recurse = 1; break;
659 case 'm': dir_crossmount = 0; break; 1433 case 'm': dir_crossmount = 0; break;
1434 case 'X': ++fix_elf; break;
660 case 'x': show_pax = 1; break; 1435 case 'x': show_pax = 1; break;
661 case 'e': show_stack = 1; break; 1436 case 'e': show_phdr = 1; break;
662 case 't': show_textrel = 1; break; 1437 case 't': show_textrel = 1; break;
663 case 'r': show_rpath = 1; break; 1438 case 'r': show_rpath = 1; break;
664 case 'n': show_needed = 1; break; 1439 case 'n': show_needed = 1; break;
665 case 'i': show_interp = 1; break; 1440 case 'i': show_interp = 1; break;
1441 case 'b': show_bind = 1; break;
1442 case 'S': show_soname = 1; break;
1443 case 'T': show_textrels = 1; break;
666 case 'q': be_quiet = 1; break; 1444 case 'q': be_quiet = 1; break;
667 case 'v': be_verbose = (be_verbose % 20) + 1; break; 1445 case 'v': be_verbose = (be_verbose % 20) + 1; break;
668 case 'a': show_pax = show_stack = show_textrel = show_rpath = show_needed = show_interp = 1; break; 1446 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
669 1447
670 case ':': 1448 case ':':
671 warn("Option missing parameter\n"); 1449 err("Option '%c' is missing parameter", optopt);
672 usage(EXIT_FAILURE);
673 break;
674 case '?': 1450 case '?':
675 warn("Unknown option\n"); 1451 err("Unknown option '%c' or argument missing", optopt);
676 usage(EXIT_FAILURE);
677 break;
678 default: 1452 default:
679 err("Unhandled option '%c'", flag); 1453 err("Unhandled option '%c'; please report this", i);
680 break;
681 }
682 } 1454 }
683 1455 }
684 if (be_quiet && be_verbose)
685 err("You can be quiet or you can be verbose, not both, stupid");
686 1456
687 /* let the format option override all other options */ 1457 /* let the format option override all other options */
688 if (out_format) { 1458 if (out_format) {
689 show_pax = show_stack = show_textrel = show_rpath = show_needed = show_interp = 0; 1459 show_pax = show_phdr = show_textrel = show_rpath = \
1460 show_needed = show_interp = show_bind = show_soname = \
1461 show_textrels = 0;
690 for (flag=0; out_format[flag]; ++flag) { 1462 for (i = 0; out_format[i]; ++i) {
691 if (out_format[flag] != '%') continue; 1463 if (!IS_MODIFIER(out_format[i])) continue;
692 1464
693 switch (out_format[++flag]) { 1465 switch (out_format[++i]) {
694 case '%': break; 1466 case '%': break;
1467 case '#': break;
695 case 'F': break; 1468 case 'F': break;
1469 case 'p': break;
1470 case 'f': break;
696 case 's': break; 1471 case 's': break;
1472 case 'N': break;
697 case 'o': break; 1473 case 'o': break;
698 case 'x': show_pax = 1; break; 1474 case 'x': show_pax = 1; break;
699 case 'e': show_stack = 1; break; 1475 case 'e': show_phdr = 1; break;
700 case 't': show_textrel = 1; break; 1476 case 't': show_textrel = 1; break;
701 case 'r': show_rpath = 1; break; 1477 case 'r': show_rpath = 1; break;
702 case 'n': show_needed = 1; break; 1478 case 'n': show_needed = 1; break;
703 case 'i': show_interp = 1; break; 1479 case 'i': show_interp = 1; break;
1480 case 'b': show_bind = 1; break;
1481 case 'S': show_soname = 1; break;
1482 case 'T': show_textrels = 1; break;
704 default: 1483 default:
705 err("Invalid format specifier '%c' (byte %i)", 1484 err("Invalid format specifier '%c' (byte %i)",
706 out_format[flag], flag+1); 1485 out_format[i], i+1);
707 } 1486 }
708 } 1487 }
709 1488
710 /* construct our default format */ 1489 /* construct our default format */
711 } else { 1490 } else {
712 size_t fmt_len = 30; 1491 size_t fmt_len = 30;
713 out_format = (char*)xmalloc(sizeof(char) * fmt_len); 1492 out_format = (char*)xmalloc(sizeof(char) * fmt_len);
714 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len); 1493 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
715 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len); 1494 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
716 if (show_stack) xstrcat(&out_format, "%e ", &fmt_len); 1495 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
717 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len); 1496 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
718 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len); 1497 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
719 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len); 1498 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
720 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); 1499 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1500 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1501 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1502 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
721 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len); 1503 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1504 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
722 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len); 1505 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
723 } 1506 }
724 if (be_verbose > 2) printf("Format: %s\n", out_format); 1507 if (be_verbose > 2) printf("Format: %s\n", out_format);
725 1508
726 /* now lets actually do the scanning */ 1509 /* now lets actually do the scanning */
1510 if (scan_ldpath || use_ldcache)
1511 load_ld_so_conf();
727 if (scan_ldpath) scanelf_ldpath(); 1512 if (scan_ldpath) scanelf_ldpath();
728 if (scan_envpath) scanelf_envpath(); 1513 if (scan_envpath) scanelf_envpath();
1514 if (from_file) {
1515 scanelf_from_file(from_file);
1516 from_file = *argv;
1517 }
729 if (optind == argc && !scan_ldpath && !scan_envpath) 1518 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
730 err("Nothing to scan !?"); 1519 err("Nothing to scan !?");
731 while (optind < argc) 1520 while (optind < argc) {
732 scanelf_dir(argv[optind++]); 1521 search_path = argv[optind++];
1522 scanelf_dir(search_path);
1523 }
733 1524
734 /* clean up */ 1525 /* clean up */
735 if (find_sym) { 1526 if (versioned_symname) free(versioned_symname);
736 free(find_sym); 1527 for (i = 0; ldpaths[i]; ++i)
737 free(versioned_symname); 1528 free(ldpaths[i]);
738 } 1529
739 if (out_format) free(out_format); 1530 if (ldcache != 0)
1531 munmap(ldcache, ldcache_size);
740} 1532}
741 1533
742 1534
743 1535
744/* utility funcs */ 1536/* utility funcs */
745static char *xstrdup(char *s) 1537static char *xstrdup(const char *s)
746{ 1538{
747 char *ret = strdup(s); 1539 char *ret = strdup(s);
748 if (!ret) err("Could not strdup(): %s", strerror(errno)); 1540 if (!ret) err("Could not strdup(): %s", strerror(errno));
749 return ret; 1541 return ret;
750} 1542}
752{ 1544{
753 void *ret = malloc(size); 1545 void *ret = malloc(size);
754 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size); 1546 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
755 return ret; 1547 return ret;
756} 1548}
757static void xstrcat(char **dst, const char *src, size_t *curr_len) 1549static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n)
758{ 1550{
759 long new_len; 1551 size_t new_len;
760 1552
761 new_len = strlen(*dst) + strlen(src); 1553 new_len = strlen(*dst) + strlen(src);
762 if (*curr_len <= new_len) { 1554 if (*curr_len <= new_len) {
763 *curr_len = new_len + (*curr_len / 2); 1555 *curr_len = new_len + (*curr_len / 2);
764 *dst = realloc(*dst, *curr_len); 1556 *dst = realloc(*dst, *curr_len);
765 if (!*dst) 1557 if (!*dst)
766 err("could not realloc %li bytes", (unsigned long)*curr_len); 1558 err("could not realloc() %li bytes", (unsigned long)*curr_len);
767 } 1559 }
768 1560
1561 if (n)
1562 strncat(*dst, src, n);
1563 else
769 strcat(*dst, src); 1564 strcat(*dst, src);
770} 1565}
771static inline void xchrcat(char **dst, const char append, size_t *curr_len) 1566static inline void xchrcat(char **dst, const char append, size_t *curr_len)
772{ 1567{
773 static char my_app[2]; 1568 static char my_app[2];
774 my_app[0] = append; 1569 my_app[0] = append;
775 my_app[1] = '\0'; 1570 my_app[1] = '\0';
776 xstrcat(dst, my_app, curr_len); 1571 xstrcat(dst, my_app, curr_len);
777} 1572}
778static int xemptybuffer(const char *buff)
779{
780 long i;
781 for (i=0; buff[i]; ++i)
782 if (buff[i] != ' ')
783 return 0;
784 return 1;
785}
786 1573
787 1574
788 1575
789int main(int argc, char *argv[]) 1576int main(int argc, char *argv[])
790{ 1577{
791 if (argc < 2) 1578 if (argc < 2)
792 usage(EXIT_FAILURE); 1579 usage(EXIT_FAILURE);
793 parseargs(argc, argv); 1580 parseargs(argc, argv);
794 fclose(stdout); 1581 fclose(stdout);
1582#ifdef __BOUNDS_CHECKING_ON
1583 warn("The calls to add/delete heap should be off by 1 due to the out_buffer not being freed in scanelf_file()");
1584#endif
795 return EXIT_SUCCESS; 1585 return EXIT_SUCCESS;
796} 1586}

Legend:
Removed from v.1.44  
changed lines
  Added in v.1.122

  ViewVC Help
Powered by ViewVC 1.1.20