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

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

  ViewVC Help
Powered by ViewVC 1.1.20