/[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.32 Revision 1.226
1/* 1/*
2 * Copyright 2003 Ned Ludd <solar@gentoo.org>
3 * Copyright 1999-2005 Gentoo Foundation 2 * Copyright 2003-2007 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.32 2005/04/07 00:01:40 vapier Exp $ 4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.226 2011/09/27 18:37:22 vapier Exp $
6 * 5 *
7 ******************************************************************** 6 * Copyright 2003-2007 Ned Ludd - <solar@gentoo.org>
8 * This program is free software; you can redistribute it and/or 7 * Copyright 2004-2007 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"
37
38static const char *rcsid = "$Id: scanelf.c,v 1.32 2005/04/07 00:01:40 vapier Exp $"; 10static const char *rcsid = "$Id: scanelf.c,v 1.226 2011/09/27 18:37:22 vapier Exp $";
11const char argv0[] = "scanelf";
39 12
13#include "paxinc.h"
40 14
41/* helper functions for showing errors */ 15#define IS_MODIFIER(c) (c == '%' || c == '#' || c == '+')
42#define argv0 "scanelf" /*((*argv != NULL) ? argv[0] : __FILE__ "\b\b")*/
43#define warn(fmt, args...) \
44 fprintf(stderr, "%s: " fmt "\n", argv0, ## args)
45#define warnf(fmt, args...) warn("%s(): " fmt, __FUNCTION__, ## args)
46#define err(fmt, args...) \
47 do { \
48 warn(fmt, ## args); \
49 exit(EXIT_FAILURE); \
50 } while (0)
51
52
53 16
54/* prototypes */ 17/* prototypes */
55static void scanelf_file(const char *filename); 18static int file_matches_list(const char *filename, char **matchlist);
19static int scanelf_elfobj(elfobj *elf);
20static int scanelf_elf(const char *filename, int fd, size_t len);
21static int scanelf_archive(const char *filename, int fd, size_t len);
22static int scanelf_file(const char *filename, const struct stat *st_cache);
56static void scanelf_dir(const char *path); 23static int scanelf_dir(const char *path);
57static void scanelf_ldpath(); 24static void scanelf_ldpath(void);
58static void scanelf_envpath(); 25static void scanelf_envpath(void);
59static void usage(int status); 26static void usage(int status);
27static char **get_split_env(const char *envvar);
28static void parseenv(void);
60static void parseargs(int argc, char *argv[]); 29static int parseargs(int argc, char *argv[]);
61 30
62/* variables to control behavior */ 31/* variables to control behavior */
32static char match_etypes[126] = "";
33static char *ldpaths[256];
63static char scan_ldpath = 0; 34static char scan_ldpath = 0;
64static char scan_envpath = 0; 35static char scan_envpath = 0;
36static char scan_symlink = 1;
37static char scan_archives = 0;
65static char dir_recurse = 0; 38static char dir_recurse = 0;
66static char dir_crossmount = 1; 39static char dir_crossmount = 1;
67static char show_pax = 0; 40static char show_pax = 0;
41static char show_perms = 0;
68static char show_stack = 0; 42static char show_size = 0;
43static char show_phdr = 0;
69static char show_textrel = 0; 44static char show_textrel = 0;
70static char show_rpath = 0; 45static char show_rpath = 0;
71static char show_needed = 0; 46static char show_needed = 0;
47static char show_interp = 0;
48static char show_bind = 0;
49static char show_soname = 0;
50static char show_textrels = 0;
72static char show_banner = 1; 51static char show_banner = 1;
52static char show_endian = 0;
53static char show_osabi = 0;
54static char show_eabi = 0;
73static char be_quiet = 0; 55static char be_quiet = 0;
74static char be_verbose = 0; 56static char be_verbose = 0;
57static char be_wewy_wewy_quiet = 0;
58static char be_semi_verbose = 0;
75static char *find_sym = NULL; 59static char *find_sym = NULL;
60static char *find_lib = NULL;
61static array_t _find_lib_arr = array_init_decl, *find_lib_arr = &_find_lib_arr;
62static char *find_section = NULL;
63static array_t _find_section_arr = array_init_decl, *find_section_arr = &_find_section_arr;
64static char *out_format = NULL;
65static char *search_path = NULL;
66static char fix_elf = 0;
67static char g_match = 0;
68static char use_ldcache = 0;
76 69
70static char **qa_textrels = NULL;
71static char **qa_execstack = NULL;
72static char **qa_wx_load = NULL;
73static char *root;
77 74
75static int match_bits = 0;
76static unsigned int match_perms = 0;
77static void *ldcache = NULL;
78static size_t ldcache_size = 0;
79static unsigned long setpax = 0UL;
78 80
79/* scan an elf file and show all the fun stuff */ 81static int has_objdump = 0;
80static void scanelf_file(const char *filename)
81{
82 int i;
83 char found_pax, found_stack, found_relro, found_textrel,
84 found_rpath, found_needed, found_sym;
85 elfobj *elf;
86 82
87 found_pax = found_stack = found_relro = found_textrel = \ 83/* find the path to a file by name */
88 found_rpath = found_needed = found_sym = 0; 84static char *which(const char *fname)
85{
86 static char fullpath[__PAX_UTILS_PATH_MAX];
87 char *path, *p;
89 88
90 /* verify this is real ELF */ 89 path = getenv("PATH");
91 if ((elf = readelf(filename)) == NULL) { 90 if (!path)
92 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
93 return; 91 return NULL;
92
93 path = xstrdup(path);
94 while ((p = strrchr(path, ':')) != NULL) {
95 snprintf(fullpath, sizeof(fullpath), "%s/%s", p + 1, fname);
96 *p = 0;
97 if (access(fullpath, R_OK) != -1) {
98 free(path);
99 return fullpath;
94 } 100 }
95
96 if (be_verbose > 1)
97 printf("%s: {%s,%s} scanning file\n", filename,
98 get_elfeitype(elf, EI_CLASS, elf->elf_class),
99 get_elfeitype(elf, EI_DATA, elf->data[EI_DATA]));
100 else if (be_verbose)
101 printf("%s: scanning file\n", filename);
102
103 /* show the header */
104 if (!be_quiet && show_banner) {
105 printf(" TYPE ");
106 if (show_pax) printf(" PAX ");
107 if (show_stack) printf("STK/REL ");
108 if (show_textrel) printf("TEXTREL ");
109 if (show_rpath) printf("RPATH ");
110 if (show_needed) printf("NEEDED ");
111 printf(" FILE\n");
112 show_banner = 0;
113 }
114
115 /* dump all the good stuff */
116 if (!be_quiet)
117 printf("%-7s ", get_elfetype(elf));
118
119 if (show_pax) {
120 char *paxflags = pax_short_hf_flags(PAX_FLAGS(elf));
121 if (!be_quiet || (be_quiet && strncmp(paxflags, "PeMRxS", 6))) {
122 found_pax = 1;
123 printf("%s ", pax_short_hf_flags(PAX_FLAGS(elf)));
124 } 101 }
125 } 102 free(path);
103 return NULL;
104}
126 105
127 /* stack fun */ 106/* 1 on failure. 0 otherwise */
128 if (show_stack) { 107static int rematch(const char *regex, const char *match, int cflags)
129#define SHOW_STACK(B) \ 108{
109 regex_t preg;
110 int ret;
111
112 if ((match == NULL) || (regex == NULL))
113 return EXIT_FAILURE;
114
115 if ((ret = regcomp(&preg, regex, cflags))) {
116 char err[256];
117
118 if (regerror(ret, &preg, err, sizeof(err)))
119 fprintf(stderr, "regcomp failed: %s", err);
120 else
121 fprintf(stderr, "regcomp failed");
122
123 return EXIT_FAILURE;
124 }
125 ret = regexec(&preg, match, 0, NULL, 0);
126 regfree(&preg);
127
128 return ret;
129}
130
131/* sub-funcs for scanelf_file() */
132static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
133{
134 /* find the best SHT_DYNSYM and SHT_STRTAB sections */
135
136 /* debug sections */
137 void *symtab = elf_findsecbyname(elf, ".symtab");
138 void *strtab = elf_findsecbyname(elf, ".strtab");
139 /* runtime sections */
140 void *dynsym = elf_findsecbyname(elf, ".dynsym");
141 void *dynstr = elf_findsecbyname(elf, ".dynstr");
142
143#define GET_SYMTABS(B) \
130 if (elf->elf_class == ELFCLASS ## B) { \ 144 if (elf->elf_class == ELFCLASS ## B) { \
145 if (symtab && dynsym) { \
146 Elf ## B ## _Shdr *esymtab = symtab; \
147 Elf ## B ## _Shdr *edynsym = dynsym; \
148 *sym = (EGET(esymtab->sh_size) > EGET(edynsym->sh_size)) ? symtab : dynsym; \
149 } else \
150 *sym = symtab ? symtab : dynsym; \
151 if (strtab && dynstr) { \
152 Elf ## B ## _Shdr *estrtab = strtab; \
153 Elf ## B ## _Shdr *edynstr = dynstr; \
154 *tab = (EGET(estrtab->sh_size) > EGET(edynstr->sh_size)) ? strtab : dynstr; \
155 } else \
156 *tab = strtab ? strtab : dynstr; \
157 }
158 GET_SYMTABS(32)
159 GET_SYMTABS(64)
160}
161
162static char *scanelf_file_pax(elfobj *elf, char *found_pax)
163{
164 static char ret[7];
165 unsigned long i, shown;
166
167 if (!show_pax) return NULL;
168
169 shown = 0;
170 memset(&ret, 0, sizeof(ret));
171
172 if (elf->phdr) {
173#define SHOW_PAX(B) \
174 if (elf->elf_class == ELFCLASS ## B) { \
131 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 175 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
176 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
177 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
178 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
179 continue; \
180 if (fix_elf && setpax) { \
181 /* set the paxctl flags */ \
182 ESET(phdr[i].p_flags, setpax); \
183 } \
184 if (be_quiet && (EGET(phdr[i].p_flags) == (PF_NOEMUTRAMP | PF_NORANDEXEC))) \
185 continue; \
186 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
187 *found_pax = 1; \
188 ++shown; \
189 break; \
190 } \
191 }
192 SHOW_PAX(32)
193 SHOW_PAX(64)
194 }
195
196 if (fix_elf && setpax) {
197 /* set the chpax settings */
198 if (elf->elf_class == ELFCLASS32) {
199 if (EHDR32(elf->ehdr)->e_type == ET_DYN || EHDR32(elf->ehdr)->e_type == ET_EXEC)
200 ESET(EHDR32(elf->ehdr)->e_ident[EI_PAX], pax_pf2hf_flags(setpax));
201 } else {
202 if (EHDR64(elf->ehdr)->e_type == ET_DYN || EHDR64(elf->ehdr)->e_type == ET_EXEC)
203 ESET(EHDR64(elf->ehdr)->e_ident[EI_PAX], pax_pf2hf_flags(setpax));
204 }
205 }
206
207 /* fall back to EI_PAX if no PT_PAX was found */
208 if (!*ret) {
209 static char *paxflags;
210 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
211 if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
212 *found_pax = 1;
213 return (be_wewy_wewy_quiet ? NULL : paxflags);
214 }
215 strncpy(ret, paxflags, sizeof(ret));
216 }
217
218 if (be_wewy_wewy_quiet || (be_quiet && !shown))
219 return NULL;
220 else
221 return ret;
222}
223
224static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
225{
226 static char ret[12];
227 char *found;
228 unsigned long i, shown, multi_stack, multi_relro, multi_load;
229 int max_pt_load;
230
231 if (!show_phdr) return NULL;
232
233 memcpy(ret, "--- --- ---\0", 12);
234
235 shown = 0;
236 multi_stack = multi_relro = multi_load = 0;
237 max_pt_load = elf_max_pt_load(elf);
238
239#define NOTE_GNU_STACK ".note.GNU-stack"
240#define SHOW_PHDR(B) \
241 if (elf->elf_class == ELFCLASS ## B) { \
242 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
243 Elf ## B ## _Off offset; \
244 uint32_t flags, check_flags; \
245 if (elf->phdr != NULL) { \
132 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 246 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
133 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 247 for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \
134 if (EGET(phdr[i].p_type) != PT_GNU_STACK && \ 248 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
249 if (multi_stack++) \
250 warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
251 if (file_matches_list(elf->filename, qa_execstack)) \
252 continue; \
253 found = found_phdr; \
254 offset = 0; \
255 check_flags = PF_X; \
135 EGET(phdr[i].p_type) != PT_GNU_RELRO) continue; \ 256 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
136 if (be_quiet && !(EGET(phdr[i].p_flags) & PF_X)) \ 257 if (multi_relro++) \
258 warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
259 found = found_relro; \
260 offset = 4; \
261 check_flags = PF_X; \
262 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
263 if (EGET(ehdr->e_type) == ET_DYN || EGET(ehdr->e_type) == ET_EXEC) \
264 if (multi_load++ > max_pt_load) \
265 warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
266 if (file_matches_list(elf->filename, qa_wx_load)) \
267 continue; \
268 found = found_load; \
269 offset = 8; \
270 check_flags = PF_W|PF_X; \
271 } else \
137 continue; \ 272 continue; \
138 if (EGET(phdr[i].p_type) == PT_GNU_STACK) \ 273 flags = EGET(phdr[i].p_flags); \
274 if (be_quiet && ((flags & check_flags) != check_flags)) \
275 continue; \
276 if ((EGET(phdr[i].p_type) != PT_LOAD) && (fix_elf && ((flags & PF_X) != flags))) { \
277 ESET(phdr[i].p_flags, flags & (PF_X ^ (size_t)-1)); \
278 ret[3] = ret[7] = '!'; \
279 flags = EGET(phdr[i].p_flags); \
280 } \
281 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \
139 found_stack = 1; \ 282 *found = 1; \
140 if (EGET(phdr[i].p_type) == PT_GNU_RELRO) \ 283 ++shown; \
141 found_relro = 1; \
142 printf("%s ", gnu_short_stack_flags(EGET(phdr[i].p_flags))); \
143 } \ 284 } \
285 } else if (elf->shdr != NULL) { \
286 /* no program headers which means this is prob an object file */ \
287 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
288 Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \
289 char *str; \
290 if ((void*)strtbl > elf->data_end) \
291 goto skip_this_shdr##B; \
292 check_flags = SHF_WRITE|SHF_EXECINSTR; \
293 for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \
294 if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \
295 offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \
296 str = elf->data + offset; \
297 if (str > elf->data + offset + sizeof(NOTE_GNU_STACK)) continue; \
298 if (!strcmp(str, NOTE_GNU_STACK)) { \
299 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \
300 flags = EGET(shdr[i].sh_flags); \
301 if (be_quiet && ((flags & check_flags) != check_flags)) \
302 continue; \
303 ++*found_phdr; \
304 shown = 1; \
305 if (flags & SHF_WRITE) ret[0] = 'W'; \
306 if (flags & SHF_ALLOC) ret[1] = 'A'; \
307 if (flags & SHF_EXECINSTR) ret[2] = 'X'; \
308 if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \
309 break; \
310 } \
311 } \
312 skip_this_shdr##B: \
313 if (!multi_stack) { \
314 if (file_matches_list(elf->filename, qa_execstack)) \
315 return NULL; \
316 *found_phdr = 1; \
317 shown = 1; \
318 memcpy(ret, "!WX", 3); \
319 } \
320 } \
144 } 321 }
145 SHOW_STACK(32) 322 SHOW_PHDR(32)
146 SHOW_STACK(64) 323 SHOW_PHDR(64)
147 if (!be_quiet && !found_stack) printf("--- ");
148 if (!be_quiet && !found_relro) printf("--- ");
149 }
150 324
151 /* textrel fun */ 325 if (be_wewy_wewy_quiet || (be_quiet && !shown))
152 if (show_textrel) { 326 return NULL;
327 else
328 return ret;
329}
330
331/*
332 * See if this ELF contains a DT_TEXTREL tag in any of its
333 * PT_DYNAMIC sections.
334 */
335static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
336{
337 static const char *ret = "TEXTREL";
338 unsigned long i;
339
340 if (!show_textrel && !show_textrels) return NULL;
341
342 if (file_matches_list(elf->filename, qa_textrels)) return NULL;
343
344 if (elf->phdr) {
153#define SHOW_TEXTREL(B) \ 345#define SHOW_TEXTREL(B) \
154 if (elf->elf_class == ELFCLASS ## B) { \ 346 if (elf->elf_class == ELFCLASS ## B) { \
155 Elf ## B ## _Dyn *dyn; \ 347 Elf ## B ## _Dyn *dyn; \
156 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 348 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
157 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 349 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
350 Elf ## B ## _Off offset; \
158 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 351 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
159 if (phdr[i].p_type != PT_DYNAMIC) continue; \ 352 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
353 offset = EGET(phdr[i].p_offset); \
354 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
160 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \ 355 dyn = DYN ## B (elf->vdata + offset); \
161 while (EGET(dyn->d_tag) != DT_NULL) { \ 356 while (EGET(dyn->d_tag) != DT_NULL) { \
162 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \ 357 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
163 found_textrel = 1; \ 358 *found_textrel = 1; \
164 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \ 359 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
165 printf("TEXTREL "); \ 360 return (be_wewy_wewy_quiet ? NULL : ret); \
361 } \
362 ++dyn; \
363 } \
364 } }
365 SHOW_TEXTREL(32)
366 SHOW_TEXTREL(64)
367 }
368
369 if (be_quiet || be_wewy_wewy_quiet)
370 return NULL;
371 else
372 return " - ";
373}
374
375/*
376 * Scan the .text section to see if there are any relocations in it.
377 * Should rewrite this to check PT_LOAD sections that are marked
378 * Executable rather than the section named '.text'.
379 */
380static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
381{
382 unsigned long s, r, rmax;
383 void *symtab_void, *strtab_void, *text_void;
384
385 if (!show_textrels) return NULL;
386
387 /* don't search for TEXTREL's if the ELF doesn't have any */
388 if (!*found_textrel) scanelf_file_textrel(elf, found_textrel);
389 if (!*found_textrel) return NULL;
390
391 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
392 text_void = elf_findsecbyname(elf, ".text");
393
394 if (symtab_void && strtab_void && text_void && elf->shdr) {
395#define SHOW_TEXTRELS(B) \
396 if (elf->elf_class == ELFCLASS ## B) { \
397 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
398 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
399 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
400 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
401 Elf ## B ## _Shdr *text = SHDR ## B (text_void); \
402 Elf ## B ## _Addr vaddr = EGET(text->sh_addr); \
403 uint ## B ## _t memsz = EGET(text->sh_size); \
404 Elf ## B ## _Rel *rel; \
405 Elf ## B ## _Rela *rela; \
406 /* search the section headers for relocations */ \
407 for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
408 uint32_t sh_type = EGET(shdr[s].sh_type); \
409 if (sh_type == SHT_REL) { \
410 rel = REL ## B (elf->vdata + EGET(shdr[s].sh_offset)); \
411 rela = NULL; \
412 rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
413 } else if (sh_type == SHT_RELA) { \
414 rel = NULL; \
415 rela = RELA ## B (elf->vdata + EGET(shdr[s].sh_offset)); \
416 rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
417 } else \
418 continue; \
419 /* now see if any of the relocs are in the .text */ \
420 for (r = 0; r < rmax; ++r) { \
421 unsigned long sym_max; \
422 Elf ## B ## _Addr offset_tmp; \
423 Elf ## B ## _Sym *func; \
424 Elf ## B ## _Sym *sym; \
425 Elf ## B ## _Addr r_offset; \
426 uint ## B ## _t r_info; \
427 if (sh_type == SHT_REL) { \
428 r_offset = EGET(rel[r].r_offset); \
429 r_info = EGET(rel[r].r_info); \
430 } else { \
431 r_offset = EGET(rela[r].r_offset); \
432 r_info = EGET(rela[r].r_info); \
433 } \
434 /* make sure this relocation is inside of the .text */ \
435 if (r_offset < vaddr || r_offset >= vaddr + memsz) { \
436 if (be_verbose <= 2) continue; \
437 } else \
438 *found_textrels = 1; \
439 /* locate this relocation symbol name */ \
440 sym = SYM ## B (elf->vdata + EGET(symtab->sh_offset)); \
441 if ((void*)sym > elf->data_end) { \
442 warn("%s: corrupt ELF symbol", elf->filename); \
443 continue; \
444 } \
445 sym_max = ELF ## B ## _R_SYM(r_info); \
446 if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
447 sym += sym_max; \
448 else \
449 sym = NULL; \
450 sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
451 /* show the raw details about this reloc */ \
452 printf(" %s: ", elf->base_filename); \
453 if (sym && sym->st_name) \
454 printf("%s", elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
455 else \
456 printf("(memory/data?)"); \
457 printf(" [0x%lX]", (unsigned long)r_offset); \
458 /* now try to find the closest symbol that this rel is probably in */ \
459 sym = SYM ## B (elf->vdata + EGET(symtab->sh_offset)); \
460 func = NULL; \
461 offset_tmp = 0; \
462 while (sym_max--) { \
463 if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
464 func = sym; \
465 offset_tmp = EGET(sym->st_value); \
166 } \ 466 } \
167 ++dyn; \ 467 ++sym; \
168 } \ 468 } \
469 printf(" in "); \
470 if (func && func->st_name) { \
471 const char *func_name = elf->data + EGET(strtab->sh_offset) + EGET(func->st_name); \
472 if (r_offset > EGET(func->st_size)) \
473 printf("(optimized out: previous %s)", func_name); \
474 else \
475 printf("%s", func_name); \
476 } else \
477 printf("(optimized out)"); \
478 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
479 if (be_verbose && has_objdump) { \
480 Elf ## B ## _Addr end_addr = offset_tmp + EGET(func->st_size); \
481 char *sysbuf; \
482 size_t syslen; \
483 int sysret; \
484 const char sysfmt[] = "objdump -r -R -d -w -l --start-address=0x%lX --stop-address=0x%lX %s | grep --color -i -C 3 '.*[[:space:]]%lX:[[:space:]]*R_.*'\n"; \
485 syslen = sizeof(sysfmt) + strlen(elf->filename) + 3 * sizeof(unsigned long) + 1; \
486 sysbuf = xmalloc(syslen); \
487 if (end_addr < r_offset) \
488 /* not uncommon when things are optimized out */ \
489 end_addr = r_offset + 0x100; \
490 snprintf(sysbuf, syslen, sysfmt, \
491 (unsigned long)offset_tmp, \
492 (unsigned long)end_addr, \
493 elf->filename, \
494 (unsigned long)r_offset); \
495 fflush(stdout); \
496 sysret = system(sysbuf); \
497 fflush(stdout); \
498 free(sysbuf); \
499 } \
500 } \
169 } } 501 } }
170 SHOW_TEXTREL(32) 502 SHOW_TEXTRELS(32)
171 SHOW_TEXTREL(64) 503 SHOW_TEXTRELS(64)
172 if (!be_quiet && !found_textrel) printf("------- ");
173 } 504 }
505 if (!*found_textrels)
506 warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
174 507
175 /* rpath fun */ 508 return NULL;
176 /* TODO: if be_quiet, only output RPATH's which aren't in /etc/ld.so.conf */ 509}
177 if (show_rpath) { 510
511static void rpath_security_checks(elfobj *, char *, const char *);
512static void rpath_security_checks(elfobj *elf, char *item, const char *dt_type)
513{
514 struct stat st;
515 switch (*item) {
516 case '/': break;
517 case '.':
518 warnf("Security problem with relative %s '%s' in %s", dt_type, item, elf->filename);
519 break;
520 case ':':
521 case '\0':
522 warnf("Security problem NULL %s in %s", dt_type, elf->filename);
523 break;
524 case '$':
525 if (fstat(elf->fd, &st) != -1)
526 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
527 warnf("Security problem with %s='%s' in %s with mode set of %o",
528 dt_type, item, elf->filename, (unsigned int) st.st_mode & 07777);
529 break;
530 default:
531 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename);
532 break;
533 }
534}
535static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
536{
537 unsigned long i, s;
178 char *rpath, *runpath; 538 char *rpath, *runpath, **r;
539 void *strtbl_void;
540
541 if (!show_rpath) return;
542
179 void *strtbl_void = elf_findsecbyname(elf, ".dynstr"); 543 strtbl_void = elf_findsecbyname(elf, ".dynstr");
180 rpath = runpath = NULL; 544 rpath = runpath = NULL;
181 545
182 if (strtbl_void) { 546 if (elf->phdr && strtbl_void) {
183#define SHOW_RPATH(B) \ 547#define SHOW_RPATH(B) \
184 if (elf->elf_class == ELFCLASS ## B) { \ 548 if (elf->elf_class == ELFCLASS ## B) { \
185 Elf ## B ## _Dyn *dyn; \ 549 Elf ## B ## _Dyn *dyn; \
186 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 550 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
187 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 551 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
188 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 552 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
553 Elf ## B ## _Off offset; \
554 Elf ## B ## _Xword word; \
555 /* Scan all the program headers */ \
189 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 556 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
557 /* Just scan dynamic headers */ \
190 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 558 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
559 offset = EGET(phdr[i].p_offset); \
560 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
561 /* Just scan dynamic RPATH/RUNPATH headers */ \
191 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \ 562 dyn = DYN ## B (elf->vdata + offset); \
192 while (EGET(dyn->d_tag) != DT_NULL) { \ 563 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
193 if (EGET(dyn->d_tag) == DT_RPATH) { \ 564 if (word == DT_RPATH) { \
565 r = &rpath; \
566 } else if (word == DT_RUNPATH) { \
567 r = &runpath; \
568 } else { \
569 ++dyn; \
570 continue; \
571 } \
572 /* Verify the memory is somewhat sane */ \
194 rpath = elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 573 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
574 if (offset < (Elf ## B ## _Off)elf->len) { \
575 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
576 *r = elf->data + offset; \
577 /* cache the length in case we need to nuke this section later on */ \
578 if (fix_elf) \
579 offset = strlen(*r); \
580 /* If quiet, don't output paths in ld.so.conf */ \
581 if (be_quiet) { \
582 size_t len; \
583 char *start, *end; \
584 /* note that we only 'chop' off leading known paths. */ \
585 /* since *r is read-only memory, we can only move the ptr forward. */ \
586 start = *r; \
587 /* scan each path in : delimited list */ \
588 while (start) { \
589 rpath_security_checks(elf, start, get_elfdtype(word)); \
590 end = strchr(start, ':'); \
591 len = (end ? abs(end - start) : strlen(start)); \
592 if (use_ldcache) \
593 for (s = 0; ldpaths[s]; ++s) \
594 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
595 *r = end; \
596 /* corner case ... if RPATH reads "/usr/lib:", we want \
597 * to show ':' rather than '' */ \
598 if (end && end[1] != '\0') \
599 (*r)++; \
600 break; \
601 } \
602 if (!*r || !end) \
603 break; \
604 else \
605 start = start + len + 1; \
606 } \
607 } \
608 if (*r) { \
609 if (fix_elf > 2 || (fix_elf && **r == '\0')) { \
610 /* just nuke it */ \
611 nuke_it##B: \
612 memset(*r, 0x00, offset); \
613 *r = NULL; \
614 ESET(dyn->d_tag, DT_DEBUG); \
615 ESET(dyn->d_un.d_ptr, 0); \
616 } else if (fix_elf) { \
617 /* try to clean "bad" paths */ \
618 size_t len, tmpdir_len; \
619 char *start, *end; \
620 const char *tmpdir; \
621 start = *r; \
622 tmpdir = (getenv("TMPDIR") ? : "."); \
623 tmpdir_len = strlen(tmpdir); \
624 while (1) { \
625 end = strchr(start, ':'); \
626 if (start == end) { \
627 eat_this_path##B: \
628 len = strlen(end); \
629 memmove(start, end+1, len); \
630 start[len-1] = '\0'; \
631 end = start - 1; \
632 } else if (tmpdir && !strncmp(start, tmpdir, tmpdir_len)) { \
633 if (!end) { \
634 if (start == *r) \
635 goto nuke_it##B; \
636 *--start = '\0'; \
637 } else \
638 goto eat_this_path##B; \
639 } \
640 if (!end) \
641 break; \
642 start = end + 1; \
643 } \
644 if (**r == '\0') \
645 goto nuke_it##B; \
646 } \
647 if (*r) \
195 found_rpath = 1; \ 648 *found_rpath = 1; \
196 } else if (EGET(dyn->d_tag) == DT_RUNPATH) { \ 649 } \
197 runpath = elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
198 found_rpath = 1; \
199 } \ 650 } \
200 ++dyn; \ 651 ++dyn; \
201 } \ 652 } \
202 } } 653 } }
203 SHOW_RPATH(32) 654 SHOW_RPATH(32)
204 SHOW_RPATH(64) 655 SHOW_RPATH(64)
205 } 656 }
657
658 if (be_wewy_wewy_quiet) return;
659
206 if (rpath && runpath) { 660 if (rpath && runpath) {
207 if (!strcmp(rpath, runpath)) 661 if (!strcmp(rpath, runpath)) {
208 printf("%-5s ", runpath); 662 xstrcat(ret, runpath, ret_len);
209 else { 663 } else {
210 fprintf(stderr, "%s's RPATH [%s] != RUNPATH [%s]\n", filename, rpath, runpath); 664 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
211 printf("{%s,%s} ", rpath, runpath); 665 xchrcat(ret, '{', ret_len);
666 xstrcat(ret, rpath, ret_len);
667 xchrcat(ret, ',', ret_len);
668 xstrcat(ret, runpath, ret_len);
669 xchrcat(ret, '}', ret_len);
212 } 670 }
213 } else if (rpath || runpath) 671 } else if (rpath || runpath)
214 printf("%-5s ", (runpath ? runpath : rpath)); 672 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
215 else if (!be_quiet && !found_rpath) 673 else if (!be_quiet)
216 printf(" - "); 674 xstrcat(ret, " - ", ret_len);
675}
676
677#define LDSO_CACHE_MAGIC "ld.so-"
678#define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
679#define LDSO_CACHE_VER "1.7.0"
680#define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
681#define FLAG_ANY -1
682#define FLAG_TYPE_MASK 0x00ff
683#define FLAG_LIBC4 0x0000
684#define FLAG_ELF 0x0001
685#define FLAG_ELF_LIBC5 0x0002
686#define FLAG_ELF_LIBC6 0x0003
687#define FLAG_REQUIRED_MASK 0xff00
688#define FLAG_SPARC_LIB64 0x0100
689#define FLAG_IA64_LIB64 0x0200
690#define FLAG_X8664_LIB64 0x0300
691#define FLAG_S390_LIB64 0x0400
692#define FLAG_POWERPC_LIB64 0x0500
693#define FLAG_MIPS64_LIBN32 0x0600
694#define FLAG_MIPS64_LIBN64 0x0700
695
696static char *lookup_cache_lib(elfobj *, char *);
697
698#if defined(__GLIBC__) || defined(__UCLIBC__)
699
700static char *lookup_cache_lib(elfobj *elf, char *fname)
701{
702 int fd;
703 char *strs;
704 static char buf[__PAX_UTILS_PATH_MAX] = "";
705 const char cachefile[] = "/etc/ld.so.cache";
706 struct stat st;
707
708 typedef struct {
709 char magic[LDSO_CACHE_MAGIC_LEN];
710 char version[LDSO_CACHE_VER_LEN];
711 int nlibs;
712 } header_t;
713 header_t *header;
714
715 typedef struct {
716 int flags;
717 int sooffset;
718 int liboffset;
719 } libentry_t;
720 libentry_t *libent;
721
722 if (fname == NULL)
723 return NULL;
724
725 if (ldcache == NULL) {
726 if (stat(cachefile, &st))
727 return NULL;
728
729 fd = open(cachefile, O_RDONLY);
730 if (fd == -1)
731 return NULL;
732
733 /* cache these values so we only map/unmap the cache file once */
734 ldcache_size = st.st_size;
735 header = ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
736 close(fd);
737
738 if (ldcache == MAP_FAILED) {
739 ldcache = NULL;
740 return NULL;
217 } 741 }
218 742
219 /* print out all the NEEDED entries */ 743 if (memcmp(header->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN) ||
220 if (show_needed) { 744 memcmp(header->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
745 {
746 munmap(ldcache, ldcache_size);
747 ldcache = NULL;
748 return NULL;
749 }
750 } else
751 header = ldcache;
752
753 libent = ldcache + sizeof(header_t);
754 strs = (char *) &libent[header->nlibs];
755
756 for (fd = 0; fd < header->nlibs; ++fd) {
757 /* This should be more fine grained, but for now we assume that
758 * diff arches will not be cached together, and we ignore the
759 * the different multilib mips cases.
760 */
761 if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
762 continue;
763 if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
764 continue;
765
766 if (strcmp(fname, strs + libent[fd].sooffset) != 0)
767 continue;
768
769 /* Return first hit because that is how the ldso rolls */
770 strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
771 break;
772 }
773
774 return buf;
775}
776
777#elif defined(__NetBSD__)
778static char *lookup_cache_lib(elfobj *elf, char *fname)
779{
780 static char buf[__PAX_UTILS_PATH_MAX] = "";
781 static struct stat st;
782
783 char **ldpath;
784 for (ldpath = ldpaths; *ldpath != NULL; ldpath++) {
785 if ((unsigned) snprintf(buf, sizeof(buf), "%s/%s", *ldpath, fname) >= sizeof(buf))
786 continue; /* if the pathname is too long, or something went wrong, ignore */
787
788 if (stat(buf, &st) != 0)
789 continue; /* if the lib doesn't exist in *ldpath, look further */
790
791 /* NetBSD doesn't actually do sanity checks, it just loads the file
792 * and if that doesn't work, continues looking in other directories.
793 * This cannot easily be safely emulated, unfortunately. For now,
794 * just assume that if it exists, it's a valid library. */
795
796 return buf;
797 }
798
799 /* not found in any path */
800 return NULL;
801}
802#else
803#ifdef __ELF__
804#warning Cache support not implemented for your target
805#endif
806static char *lookup_cache_lib(elfobj *elf, char *fname)
807{
808 return NULL;
809}
810#endif
811
812static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
813{
814 unsigned long i;
221 char *needed; 815 char *needed;
816 void *strtbl_void;
817 char *p;
818
819 /*
820 * -n -> op==0 -> print all
821 * -N -> op==1 -> print requested
822 */
823 if ((op == 0 && !show_needed) || (op == 1 && !find_lib))
824 return NULL;
825
222 void *strtbl_void = elf_findsecbyname(elf, ".dynstr"); 826 strtbl_void = elf_findsecbyname(elf, ".dynstr");
223 827
224 if (strtbl_void) { 828 if (elf->phdr && strtbl_void) {
225#define SHOW_NEEDED(B) \ 829#define SHOW_NEEDED(B) \
226 if (elf->elf_class == ELFCLASS ## B) { \ 830 if (elf->elf_class == ELFCLASS ## B) { \
227 Elf ## B ## _Dyn *dyn; \ 831 Elf ## B ## _Dyn *dyn; \
228 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 832 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
229 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 833 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
230 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 834 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
835 Elf ## B ## _Off offset; \
836 size_t matched = 0; \
837 /* Walk all the program headers to find the PT_DYNAMIC */ \
231 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 838 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
232 if (be_verbose && EGET(phdr[i].p_type) == PT_INTERP) { \ 839 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) \
233 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \ 840 continue; \
234 printf("%s\n", elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr)); \ 841 offset = EGET(phdr[i].p_offset); \
235 exit(0); \ 842 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) \
236 } \ 843 continue; \
237 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 844 /* Walk all the dynamic tags to find NEEDED entries */ \
238 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \ 845 dyn = DYN ## B (elf->vdata + offset); \
239 while (EGET(dyn->d_tag) != DT_NULL) { \ 846 while (EGET(dyn->d_tag) != DT_NULL) { \
240 if (EGET(dyn->d_tag) == DT_NEEDED) { \ 847 if (EGET(dyn->d_tag) == DT_NEEDED) { \
241 needed = elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 848 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
242 if (found_needed) printf(","); \ 849 if (offset >= (Elf ## B ## _Off)elf->len) { \
243 printf("%s", needed); \ 850 ++dyn; \
851 continue; \
852 } \
853 needed = elf->data + offset; \
854 if (op == 0) { \
855 /* -n -> print all entries */ \
856 if (!be_wewy_wewy_quiet) { \
857 if (*found_needed) xchrcat(ret, ',', ret_len); \
858 if (use_ldcache) \
859 if ((p = lookup_cache_lib(elf, needed)) != NULL) \
860 needed = p; \
861 xstrcat(ret, needed, ret_len); \
862 } \
244 found_needed = 1; \ 863 *found_needed = 1; \
864 } else { \
865 /* -N -> print matching entries */ \
866 size_t n; \
867 const char *find_lib_name; \
868 \
869 array_for_each(find_lib_arr, n, find_lib_name) \
870 if (!strcmp(find_lib_name, needed)) \
871 ++matched; \
872 \
873 if (matched == array_cnt(find_lib_arr)) { \
874 *found_lib = 1; \
875 return (be_wewy_wewy_quiet ? NULL : find_lib); \
876 } \
877 } \
245 } \ 878 } \
246 ++dyn; \ 879 ++dyn; \
247 } \ 880 } \
248 } } 881 } }
249 SHOW_NEEDED(32) 882 SHOW_NEEDED(32)
250 SHOW_NEEDED(64) 883 SHOW_NEEDED(64)
884 if (op == 0 && !*found_needed && be_verbose)
885 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
886 }
887
888 return NULL;
889}
890static char *scanelf_file_interp(elfobj *elf, char *found_interp)
891{
892 void *strtbl_void;
893
894 if (!show_interp) return NULL;
895
896 strtbl_void = elf_findsecbyname(elf, ".interp");
897
898 if (strtbl_void) {
899#define SHOW_INTERP(B) \
900 if (elf->elf_class == ELFCLASS ## B) { \
901 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
902 *found_interp = 1; \
903 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
904 }
905 SHOW_INTERP(32)
906 SHOW_INTERP(64)
907 }
908 return NULL;
909}
910static char *scanelf_file_bind(elfobj *elf, char *found_bind)
911{
912 unsigned long i;
913 struct stat s;
914 char dynamic = 0;
915
916 if (!show_bind) return NULL;
917 if (!elf->phdr) return NULL;
918
919#define SHOW_BIND(B) \
920 if (elf->elf_class == ELFCLASS ## B) { \
921 Elf ## B ## _Dyn *dyn; \
922 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
923 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
924 Elf ## B ## _Off offset; \
925 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
926 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
927 dynamic = 1; \
928 offset = EGET(phdr[i].p_offset); \
929 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
930 dyn = DYN ## B (elf->vdata + offset); \
931 while (EGET(dyn->d_tag) != DT_NULL) { \
932 if (EGET(dyn->d_tag) == DT_BIND_NOW || \
933 (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
934 { \
935 if (be_quiet) return NULL; \
936 *found_bind = 1; \
937 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
938 } \
939 ++dyn; \
940 } \
941 } \
942 }
943 SHOW_BIND(32)
944 SHOW_BIND(64)
945
946 if (be_wewy_wewy_quiet) return NULL;
947
948 /* don't output anything if quiet mode and the ELF is static or not setuid */
949 if (be_quiet && (!dynamic || (!fstat(elf->fd, &s) && !(s.st_mode & (S_ISUID|S_ISGID))))) {
950 return NULL;
951 } else {
952 *found_bind = 1;
953 return (char *)(dynamic ? "LAZY" : "STATIC");
954 }
955}
956static char *scanelf_file_soname(elfobj *elf, char *found_soname)
957{
958 unsigned long i;
959 char *soname;
960 void *strtbl_void;
961
962 if (!show_soname) return NULL;
963
964 strtbl_void = elf_findsecbyname(elf, ".dynstr");
965
966 if (elf->phdr && strtbl_void) {
967#define SHOW_SONAME(B) \
968 if (elf->elf_class == ELFCLASS ## B) { \
969 Elf ## B ## _Dyn *dyn; \
970 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
971 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
972 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
973 Elf ## B ## _Off offset; \
974 /* only look for soname in shared objects */ \
975 if (EGET(ehdr->e_type) != ET_DYN) \
976 return NULL; \
977 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
978 if (EGET(phdr[i].p_type) != PT_DYNAMIC || EGET(phdr[i].p_filesz) == 0) continue; \
979 offset = EGET(phdr[i].p_offset); \
980 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
981 dyn = DYN ## B (elf->vdata + offset); \
982 while (EGET(dyn->d_tag) != DT_NULL) { \
983 if (EGET(dyn->d_tag) == DT_SONAME) { \
984 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
985 if (offset >= (Elf ## B ## _Off)elf->len) { \
986 ++dyn; \
987 continue; \
988 } \
989 soname = elf->data + offset; \
990 *found_soname = 1; \
991 return (be_wewy_wewy_quiet ? NULL : soname); \
992 } \
993 ++dyn; \
994 } \
995 } }
996 SHOW_SONAME(32)
997 SHOW_SONAME(64)
998 }
999
1000 return NULL;
1001}
1002
1003/*
1004 * We support the symbol form:
1005 * [%[modifiers]%][[+-]<symbol name>][,[.....]]
1006 * If the symbol name is empty, then all symbols are matched.
1007 * If the symbol name is a glob ("*"), then all symbols are dumped (debug).
1008 * Do not rely on this output format at all.
1009 * Otherwise the symbol name is used to search (either regex or string compare).
1010 * If the first char of the symbol name is a plus ("+"), then only match
1011 * defined symbols. If it's a minus ("-"), only match undefined symbols.
1012 * Putting modifiers in between the percent signs allows for more in depth
1013 * filters. There are groups of modifiers. If you don't specify a member
1014 * of a group, then all types in that group are matched. The current
1015 * groups and their types are:
1016 * STT group: STT_NOTYPE:n STT_OBJECT:o STT_FUNC:f SST_FILE:F
1017 * STB group: STB_LOCAL:l STB_GLOBAL:g STB_WEAK:w
1018 * SHN group: SHN_UNDEF:u SHN_ABS:a SHN_COMMON:c {defined}:d
1019 * The "defined" value in the SHN group does not correspond to a SHN_xxx define.
1020 * You can search for multiple symbols at once by seperating with a comma (",").
1021 *
1022 * Some examples:
1023 * ELFs with a weak function "foo":
1024 * scanelf -s %wf%foo <ELFs>
1025 * ELFs that define the symbol "main":
1026 * scanelf -s +main <ELFs>
1027 * scanelf -s %d%main <ELFs>
1028 * ELFs that refer to the undefined symbol "brk":
1029 * scanelf -s -brk <ELFs>
1030 * scanelf -s %u%brk <ELFs>
1031 * All global defined objects in an ELF:
1032 * scanelf -s %ogd% <ELF>
1033 */
1034static void
1035scanelf_match_symname(elfobj *elf, char *found_sym, char **ret, size_t *ret_len, const char *symname,
1036 unsigned int stt, unsigned int stb, unsigned int shn, unsigned long size)
1037{
1038 char *this_sym, *next_sym, saved = saved;
1039
1040 /* allow the user to specify a comma delimited list of symbols to search for */
1041 next_sym = NULL;
1042 do {
1043 bool inc_notype, inc_object, inc_func, inc_file,
1044 inc_local, inc_global, inc_weak,
1045 inc_def, inc_undef, inc_abs, inc_common;
1046
1047 if (next_sym) {
1048 next_sym[-1] = saved;
1049 this_sym = next_sym;
1050 } else
1051 this_sym = find_sym;
1052 if ((next_sym = strchr(this_sym, ','))) {
1053 /* make parsing easier by killing the comma temporarily */
1054 saved = *next_sym;
1055 *next_sym = '\0';
1056 next_sym += 1;
1057 }
1058
1059 /* symbol selection! */
1060 inc_notype = inc_object = inc_func = inc_file = \
1061 inc_local = inc_global = inc_weak = \
1062 inc_def = inc_undef = inc_abs = inc_common = \
1063 (*this_sym != '%');
1064
1065 /* parse the contents of %...% */
1066 if (!inc_notype) {
1067 while (*(this_sym++)) {
1068 if (*this_sym == '%') {
1069 ++this_sym;
1070 break;
1071 }
1072 switch (*this_sym) {
1073 case 'n': inc_notype = true; break;
1074 case 'o': inc_object = true; break;
1075 case 'f': inc_func = true; break;
1076 case 'F': inc_file = true; break;
1077 case 'l': inc_local = true; break;
1078 case 'g': inc_global = true; break;
1079 case 'w': inc_weak = true; break;
1080 case 'd': inc_def = true; break;
1081 case 'u': inc_undef = true; break;
1082 case 'a': inc_abs = true; break;
1083 case 'c': inc_common = true; break;
1084 default: err("invalid symbol selector '%c'", *this_sym);
1085 }
251 } 1086 }
252 if (!be_quiet && !found_needed) 1087
253 printf(" - "); 1088 /* If no types are matched, not match all */
254 else if (found_needed) 1089 if (!inc_notype && !inc_object && !inc_func && !inc_file)
255 printf(" "); 1090 inc_notype = inc_object = inc_func = inc_file = true;
1091 if (!inc_local && !inc_global && !inc_weak)
1092 inc_local = inc_global = inc_weak = true;
1093 if (!inc_def && !inc_undef && !inc_abs && !inc_common)
1094 inc_def = inc_undef = inc_abs = inc_common = true;
1095
1096 /* backwards compat for defined/undefined short hand */
1097 } else if (*this_sym == '+') {
1098 inc_undef = false;
1099 ++this_sym;
1100 } else if (*this_sym == '-') {
1101 inc_def = inc_abs = inc_common = false;
1102 ++this_sym;
256 } 1103 }
257 1104
258 /* search the symbol table for a specified symbol */ 1105 /* filter symbols */
259 if (find_sym) { 1106 if ((!inc_notype && stt == STT_NOTYPE) || \
1107 (!inc_object && stt == STT_OBJECT) || \
1108 (!inc_func && stt == STT_FUNC ) || \
1109 (!inc_file && stt == STT_FILE ) || \
1110 (!inc_local && stb == STB_LOCAL ) || \
1111 (!inc_global && stb == STB_GLOBAL) || \
1112 (!inc_weak && stb == STB_WEAK ) || \
1113 (!inc_def && shn && shn < SHN_LORESERVE) || \
1114 (!inc_undef && shn == SHN_UNDEF ) || \
1115 (!inc_abs && shn == SHN_ABS ) || \
1116 (!inc_common && shn == SHN_COMMON))
1117 continue;
1118
1119 if (*this_sym == '*') {
1120 /* a "*" symbol gets you debug output */
1121 printf("%s(%s) %5lX %15s %15s %15s %s\n",
1122 ((*found_sym == 0) ? "\n\t" : "\t"),
1123 elf->base_filename,
1124 size,
1125 get_elfstttype(stt),
1126 get_elfstbtype(stb),
1127 get_elfshntype(shn),
1128 symname);
1129 goto matched;
1130
1131 } else {
1132 if (g_match) {
1133 /* regex match the symbol */
1134 if (rematch(this_sym, symname, REG_EXTENDED) != 0)
1135 continue;
1136
1137 } else if (*this_sym) {
1138 /* give empty symbols a "pass", else do a normal compare */
1139 const size_t len = strlen(this_sym);
1140 if (!(strncmp(this_sym, symname, len) == 0 &&
1141 /* Accept unversioned symbol names */
1142 (symname[len] == '\0' || symname[len] == '@')))
1143 continue;
1144 }
1145
1146 if (be_semi_verbose) {
1147 char buf[1024];
1148 snprintf(buf, sizeof(buf), "%lX %s %s",
1149 size,
1150 get_elfstttype(stt),
1151 this_sym);
1152 *ret = xstrdup(buf);
1153 } else {
1154 if (*ret) xchrcat(ret, ',', ret_len);
1155 xstrcat(ret, symname, ret_len);
1156 }
1157
1158 goto matched;
1159 }
1160 } while (next_sym);
1161
1162 return;
1163
1164 matched:
1165 *found_sym = 1;
1166 if (next_sym)
1167 next_sym[-1] = saved;
1168}
1169
1170static const char *scanelf_file_sym(elfobj *elf, char *found_sym)
1171{
1172 unsigned long i;
1173 char *ret;
260 void *symtab_void, *strtab_void; 1174 void *symtab_void, *strtab_void;
261 char *versioned_symname;
262 size_t len;
263 1175
264 len = strlen(find_sym) + 1; 1176 if (!find_sym) return NULL;
265 versioned_symname = (char *)malloc(sizeof(char) * (len+1)); 1177 ret = NULL;
266 if (!versioned_symname) {
267 warnf("Could not malloc() mem for sym scan");
268 return;
269 }
270 sprintf(versioned_symname, "%s@", find_sym);
271 1178
272 symtab_void = elf_findsecbyname(elf, ".symtab"); 1179 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
273 strtab_void = elf_findsecbyname(elf, ".strtab");
274 1180
275 if (symtab_void && strtab_void) { 1181 if (symtab_void && strtab_void) {
276#define FIND_SYM(B) \ 1182#define FIND_SYM(B) \
277 if (elf->elf_class == ELFCLASS ## B) { \ 1183 if (elf->elf_class == ELFCLASS ## B) { \
278 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ 1184 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
279 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \ 1185 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
280 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ 1186 Elf ## B ## _Sym *sym = SYM ## B (elf->vdata + EGET(symtab->sh_offset)); \
281 int cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \ 1187 unsigned long cnt = EGET(symtab->sh_entsize); \
282 char *symname; \ 1188 char *symname; \
1189 size_t ret_len = 0; \
1190 if (cnt) \
1191 cnt = EGET(symtab->sh_size) / cnt; \
283 for (i = 0; i < cnt; ++i) { \ 1192 for (i = 0; i < cnt; ++i) { \
1193 if ((void*)sym > elf->data_end) { \
1194 warnf("%s: corrupt ELF symbols - aborting", elf->filename); \
1195 goto break_out; \
1196 } \
284 if (sym->st_name) { \ 1197 if (sym->st_name) { \
1198 /* make sure the symbol name is in acceptable memory range */ \
285 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \ 1199 symname = elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name); \
286 if (*find_sym == '*') { \ 1200 if ((void*)symname > elf->data_end) { \
287 printf("%s(%s) %5lX %15s %s\n", \ 1201 warnf("%s: corrupt ELF symbols", elf->filename); \
288 ((found_sym == 0) ? "\n\t" : "\t"), \ 1202 ++sym; \
289 (char *)basename(filename), \ 1203 continue; \
290 (long)sym->st_size, \ 1204 } \
291 (char *)get_elfstttype(sym->st_info), \ 1205 scanelf_match_symname(elf, found_sym, \
292 symname); \ 1206 &ret, &ret_len, symname, \
293 found_sym = 1; \ 1207 ELF##B##_ST_TYPE(EGET(sym->st_info)), \
294 } else if ((strcmp(find_sym, symname) == 0) || \ 1208 ELF##B##_ST_BIND(EGET(sym->st_info)), \
295 (strncmp(symname, versioned_symname, len) == 0)) \ 1209 EGET(sym->st_shndx), \
296 found_sym++; \ 1210 /* st_size can be 64bit, but no one is really that big, so screw em */ \
1211 EGET(sym->st_size)); \
297 } \ 1212 } \
298 ++sym; \ 1213 ++sym; \
299 } } 1214 } }
300 FIND_SYM(32) 1215 FIND_SYM(32)
301 FIND_SYM(64) 1216 FIND_SYM(64)
1217 }
1218
1219break_out:
1220 if (be_wewy_wewy_quiet) return NULL;
1221
1222 if (*find_sym != '*' && *found_sym)
1223 return ret;
1224 if (be_quiet)
1225 return NULL;
1226 else
1227 return " - ";
1228}
1229
1230static const char *scanelf_file_sections(elfobj *elf, char *found_section)
1231{
1232 if (!find_section)
1233 return NULL;
1234
1235#define FIND_SECTION(B) \
1236 if (elf->elf_class == ELFCLASS ## B) { \
1237 size_t matched, n; \
1238 int invert; \
1239 const char *section_name; \
1240 Elf ## B ## _Shdr *section; \
1241 \
1242 matched = 0; \
1243 array_for_each(find_section_arr, n, section_name) { \
1244 invert = (*section_name == '!' ? 1 : 0); \
1245 section = SHDR ## B (elf_findsecbyname(elf, section_name + invert)); \
1246 if ((section == NULL && invert) || (section != NULL && !invert)) \
1247 ++matched; \
1248 } \
1249 \
1250 if (matched == array_cnt(find_section_arr)) \
1251 *found_section = 1; \
1252 }
1253 FIND_SECTION(32)
1254 FIND_SECTION(64)
1255
1256 if (be_wewy_wewy_quiet)
1257 return NULL;
1258
1259 if (*found_section)
1260 return find_section;
1261
1262 if (be_quiet)
1263 return NULL;
1264 else
1265 return " - ";
1266}
1267
1268/* scan an elf file and show all the fun stuff */
1269#define prints(str) ({ ssize_t ret = write(fileno(stdout), str, strlen(str)); ret; })
1270static int scanelf_elfobj(elfobj *elf)
1271{
1272 unsigned long i;
1273 char found_pax, found_phdr, found_relro, found_load, found_textrel,
1274 found_rpath, found_needed, found_interp, found_bind, found_soname,
1275 found_sym, found_lib, found_file, found_textrels, found_section;
1276 static char *out_buffer = NULL;
1277 static size_t out_len;
1278
1279 found_pax = found_phdr = found_relro = found_load = found_textrel = \
1280 found_rpath = found_needed = found_interp = found_bind = found_soname = \
1281 found_sym = found_lib = found_file = found_textrels = found_section = 0;
1282
1283 if (be_verbose > 2)
1284 printf("%s: scanning file {%s,%s}\n", elf->filename,
1285 get_elfeitype(EI_CLASS, elf->elf_class),
1286 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
1287 else if (be_verbose > 1)
1288 printf("%s: scanning file\n", elf->filename);
1289
1290 /* init output buffer */
1291 if (!out_buffer) {
1292 out_len = sizeof(char) * 80;
1293 out_buffer = xmalloc(out_len);
1294 }
1295 *out_buffer = '\0';
1296
1297 /* show the header */
1298 if (!be_quiet && show_banner) {
1299 for (i = 0; out_format[i]; ++i) {
1300 if (!IS_MODIFIER(out_format[i])) continue;
1301
1302 switch (out_format[++i]) {
1303 case '+': break;
1304 case '%': break;
1305 case '#': break;
1306 case 'F':
1307 case 'p':
1308 case 'f': prints("FILE "); found_file = 1; break;
1309 case 'o': prints(" TYPE "); break;
1310 case 'x': prints(" PAX "); break;
1311 case 'e': prints("STK/REL/PTL "); break;
1312 case 't': prints("TEXTREL "); break;
1313 case 'r': prints("RPATH "); break;
1314 case 'M': prints("CLASS "); break;
1315 case 'n': prints("NEEDED "); break;
1316 case 'i': prints("INTERP "); break;
1317 case 'b': prints("BIND "); break;
1318 case 'Z': prints("SIZE "); break;
1319 case 'S': prints("SONAME "); break;
1320 case 's': prints("SYM "); break;
1321 case 'N': prints("LIB "); break;
1322 case 'T': prints("TEXTRELS "); break;
1323 case 'k': prints("SECTION "); break;
1324 case 'a': prints("ARCH "); break;
1325 case 'I': prints("OSABI "); break;
1326 case 'Y': prints("EABI "); break;
1327 case 'O': prints("PERM "); break;
1328 case 'D': prints("ENDIAN "); break;
1329 default: warnf("'%c' has no title ?", out_format[i]);
302 } 1330 }
303 free(versioned_symname); 1331 }
304 if (*find_sym != '*') { 1332 if (!found_file) prints("FILE ");
305 if (found_sym) 1333 prints("\n");
306 printf(" %s ", find_sym); 1334 found_file = 0;
307 else if (!be_quiet) 1335 show_banner = 0;
308 printf(" - "); 1336 }
1337
1338 /* dump all the good stuff */
1339 for (i = 0; out_format[i]; ++i) {
1340 const char *out;
1341 const char *tmp;
1342 static char ubuf[sizeof(unsigned long)*2];
1343 if (!IS_MODIFIER(out_format[i])) {
1344 xchrcat(&out_buffer, out_format[i], &out_len);
1345 continue;
1346 }
1347
1348 out = NULL;
1349 be_wewy_wewy_quiet = (out_format[i] == '#');
1350 be_semi_verbose = (out_format[i] == '+');
1351 switch (out_format[++i]) {
1352 case '+':
1353 case '%':
1354 case '#':
1355 xchrcat(&out_buffer, out_format[i], &out_len); break;
1356 case 'F':
1357 found_file = 1;
1358 if (be_wewy_wewy_quiet) break;
1359 xstrcat(&out_buffer, elf->filename, &out_len);
1360 break;
1361 case 'p':
1362 found_file = 1;
1363 if (be_wewy_wewy_quiet) break;
1364 tmp = elf->filename;
1365 if (search_path) {
1366 ssize_t len_search = strlen(search_path);
1367 ssize_t len_file = strlen(elf->filename);
1368 if (!strncmp(elf->filename, search_path, len_search) && \
1369 len_file > len_search)
1370 tmp += len_search;
1371 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
309 } 1372 }
1373 xstrcat(&out_buffer, tmp, &out_len);
1374 break;
1375 case 'f':
1376 found_file = 1;
1377 if (be_wewy_wewy_quiet) break;
1378 tmp = strrchr(elf->filename, '/');
1379 tmp = (tmp == NULL ? elf->filename : tmp+1);
1380 xstrcat(&out_buffer, tmp, &out_len);
1381 break;
1382 case 'o': out = get_elfetype(elf); break;
1383 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
1384 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
1385 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
1386 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
1387 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
1388 case 'M': out = get_elfeitype(EI_CLASS, elf->data[EI_CLASS]); break;
1389 case 'D': out = get_endian(elf); break;
1390 case 'O': out = strfileperms(elf->filename); break;
1391 case 'n':
1392 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
1393 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
1394 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
1395 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
1396 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1397 case 'k': out = scanelf_file_sections(elf, &found_section); break;
1398 case 'a': out = get_elfemtype(elf); break;
1399 case 'I': out = get_elfosabi(elf); break;
1400 case 'Y': out = get_elf_eabi(elf); break;
1401 case 'Z': snprintf(ubuf, sizeof(ubuf), "%lu", (unsigned long)elf->len); out = ubuf; break;;
1402 default: warnf("'%c' has no scan code?", out_format[i]);
310 } 1403 }
1404 if (out)
1405 xstrcat(&out_buffer, out, &out_len);
1406 }
311 1407
312 if (!be_quiet || found_pax || found_stack || found_textrel || \ 1408#define FOUND_SOMETHING() \
313 found_rpath || found_needed || found_sym) 1409 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
314 printf("%s\n", filename); 1410 found_rpath || found_needed || found_interp || found_bind || \
1411 found_soname || found_sym || found_lib || found_textrels || found_section )
315 1412
1413 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
1414 xchrcat(&out_buffer, ' ', &out_len);
1415 xstrcat(&out_buffer, elf->filename, &out_len);
1416 }
1417 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
1418 puts(out_buffer);
1419 fflush(stdout);
1420 }
1421
1422 return 0;
1423}
1424
1425/* scan a single elf */
1426static int scanelf_elf(const char *filename, int fd, size_t len)
1427{
1428 int ret = 1;
1429 elfobj *elf;
1430
1431 /* verify this is real ELF */
1432 if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1433 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1434 return ret;
1435 }
1436 switch (match_bits) {
1437 case 32:
1438 if (elf->elf_class != ELFCLASS32)
1439 goto label_done;
1440 break;
1441 case 64:
1442 if (elf->elf_class != ELFCLASS64)
1443 goto label_done;
1444 break;
1445 default: break;
1446 }
1447 if (strlen(match_etypes)) {
1448 char sbuf[126];
1449 strncpy(sbuf, match_etypes, sizeof(sbuf));
1450 if (strchr(match_etypes, ',') != NULL) {
1451 char *p;
1452 while ((p = strrchr(sbuf, ',')) != NULL) {
1453 *p = 0;
1454 if (etype_lookup(p+1) == get_etype(elf))
1455 goto label_ret;
1456 }
1457 }
1458 if (etype_lookup(sbuf) != get_etype(elf))
1459 goto label_done;
1460 }
1461
1462label_ret:
1463 ret = scanelf_elfobj(elf);
1464
1465label_done:
316 unreadelf(elf); 1466 unreadelf(elf);
1467 return ret;
1468}
1469
1470/* scan an archive of elfs */
1471static int scanelf_archive(const char *filename, int fd, size_t len)
1472{
1473 archive_handle *ar;
1474 archive_member *m;
1475 char *ar_buffer;
1476 elfobj *elf;
1477
1478 ar = ar_open_fd(filename, fd);
1479 if (ar == NULL)
1480 return 1;
1481
1482 ar_buffer = mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1483 while ((m = ar_next(ar)) != NULL) {
1484 off_t cur_pos = lseek(fd, 0, SEEK_CUR);
1485 if (cur_pos == -1)
1486 errp("lseek() failed");
1487 elf = readelf_buffer(m->name, ar_buffer + cur_pos, m->size);
1488 if (elf) {
1489 scanelf_elfobj(elf);
1490 unreadelf(elf);
1491 }
1492 }
1493 munmap(ar_buffer, len);
1494
1495 return 0;
1496}
1497/* scan a file which may be an elf or an archive or some other magical beast */
1498static int scanelf_file(const char *filename, const struct stat *st_cache)
1499{
1500 const struct stat *st = st_cache;
1501 struct stat symlink_st;
1502 int fd;
1503
1504 /* always handle regular files and handle symlinked files if no -y */
1505 if (S_ISLNK(st->st_mode)) {
1506 if (!scan_symlink) return 1;
1507 stat(filename, &symlink_st);
1508 st = &symlink_st;
1509 }
1510
1511 if (!S_ISREG(st->st_mode)) {
1512 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1513 return 1;
1514 }
1515
1516 if (match_perms) {
1517 if ((st->st_mode | match_perms) != st->st_mode)
1518 return 1;
1519 }
1520 if ((fd=open(filename, (fix_elf ? O_RDWR : O_RDONLY))) == -1)
1521 return 1;
1522
1523 if (scanelf_elf(filename, fd, st->st_size) == 1 && scan_archives)
1524 /* if it isn't an ELF, maybe it's an .a archive */
1525 scanelf_archive(filename, fd, st->st_size);
1526
1527 close(fd);
1528 return 0;
1529}
1530
1531static const char *maybe_add_root(const char *fname, char *buf)
1532{
1533 if (root && strncmp(fname, root, strlen(root))) {
1534 strcpy(buf, root);
1535 strncat(buf, fname, __PAX_UTILS_PATH_MAX - strlen(root) - 1);
1536 fname = buf;
1537 }
1538 return fname;
317} 1539}
318 1540
319/* scan a directory for ET_EXEC files and print when we find one */ 1541/* scan a directory for ET_EXEC files and print when we find one */
320static void scanelf_dir(const char *path) 1542static int scanelf_dir(const char *path)
321{ 1543{
322 register DIR *dir; 1544 register DIR *dir;
323 register struct dirent *dentry; 1545 register struct dirent *dentry;
324 struct stat st_top, st; 1546 struct stat st_top, st;
325 char buf[_POSIX_PATH_MAX]; 1547 char buf[__PAX_UTILS_PATH_MAX];
1548 char _path[__PAX_UTILS_PATH_MAX];
326 size_t pathlen = 0, len = 0; 1549 size_t pathlen = 0, len = 0;
1550 int ret = 0;
1551
1552 path = maybe_add_root(path, _path);
327 1553
328 /* make sure path exists */ 1554 /* make sure path exists */
329 if (lstat(path, &st_top) == -1) 1555 if (lstat(path, &st_top) == -1) {
1556 if (be_verbose > 2) printf("%s: does not exist\n", path);
330 return; 1557 return 1;
1558 }
331 1559
332 /* ok, if it isn't a directory, assume we can open it */ 1560 /* ok, if it isn't a directory, assume we can open it */
333 if (!S_ISDIR(st_top.st_mode)) { 1561 if (!S_ISDIR(st_top.st_mode)) {
334 scanelf_file(path); 1562 return scanelf_file(path, &st_top);
335 return;
336 } 1563 }
337 1564
338 /* now scan the dir looking for fun stuff */ 1565 /* now scan the dir looking for fun stuff */
339 if ((dir = opendir(path)) == NULL) { 1566 if ((dir = opendir(path)) == NULL) {
340 warnf("could not opendir %s: %s", path, strerror(errno)); 1567 warnf("could not opendir %s: %s", path, strerror(errno));
341 return; 1568 return 1;
342 } 1569 }
343 if (be_verbose) printf("%s: scanning dir\n", path); 1570 if (be_verbose > 1) printf("%s: scanning dir\n", path);
344 1571
345 pathlen = strlen(path); 1572 pathlen = strlen(path);
346 while ((dentry = readdir(dir))) { 1573 while ((dentry = readdir(dir))) {
347 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) 1574 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
348 continue; 1575 continue;
349 len = (pathlen + 1 + strlen(dentry->d_name) + 1); 1576 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
350 if (len >= sizeof(buf)) { 1577 if (len >= sizeof(buf)) {
351 warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf)); 1578 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
1579 (unsigned long)len, (unsigned long)sizeof(buf));
352 continue; 1580 continue;
353 } 1581 }
354 sprintf(buf, "%s/%s", path, dentry->d_name); 1582 snprintf(buf, sizeof(buf), "%s%s%s", path, (path[pathlen-1] == '/') ? "" : "/", dentry->d_name);
355 if (lstat(buf, &st) != -1) { 1583 if (lstat(buf, &st) != -1) {
356 if (S_ISREG(st.st_mode)) 1584 if (S_ISREG(st.st_mode))
357 scanelf_file(buf); 1585 ret = scanelf_file(buf, &st);
358 else if (dir_recurse && S_ISDIR(st.st_mode)) { 1586 else if (dir_recurse && S_ISDIR(st.st_mode)) {
359 if (dir_crossmount || (st_top.st_dev == st.st_dev)) 1587 if (dir_crossmount || (st_top.st_dev == st.st_dev))
360 scanelf_dir(buf); 1588 ret = scanelf_dir(buf);
361 } 1589 }
362 } 1590 }
363 } 1591 }
364 closedir(dir); 1592 closedir(dir);
1593 return ret;
365} 1594}
1595
1596static int scanelf_from_file(const char *filename)
1597{
1598 FILE *fp = NULL;
1599 char *p;
1600 char path[__PAX_UTILS_PATH_MAX];
1601 int ret = 0;
1602
1603 if (strcmp(filename, "-") == 0)
1604 fp = stdin;
1605 else if ((fp = fopen(filename, "r")) == NULL)
1606 return 1;
1607
1608 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1609 if ((p = strchr(path, '\n')) != NULL)
1610 *p = 0;
1611 search_path = path;
1612 ret = scanelf_dir(path);
1613 }
1614 if (fp != stdin)
1615 fclose(fp);
1616 return ret;
1617}
1618
1619#if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
1620
1621static int load_ld_cache_config(int i, const char *fname)
1622{
1623 FILE *fp = NULL;
1624 char *p;
1625 char path[__PAX_UTILS_PATH_MAX];
1626 char _fname[__PAX_UTILS_PATH_MAX];
1627
1628 fname = maybe_add_root(fname, _fname);
1629
1630 if (i + 1 == ARRAY_SIZE(ldpaths))
1631 return i;
1632
1633 if ((fp = fopen(fname, "r")) == NULL)
1634 return i;
1635
1636 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1637 if ((p = strrchr(path, '\r')) != NULL)
1638 *p = 0;
1639 if ((p = strchr(path, '\n')) != NULL)
1640 *p = 0;
1641
1642 /* recursive includes of the same file will make this segfault. */
1643 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1644 glob_t gl;
1645 size_t x;
1646 char gpath[__PAX_UTILS_PATH_MAX];
1647
1648 memset(gpath, 0, sizeof(gpath));
1649 if (root)
1650 strcpy(gpath, root);
1651
1652 if (path[8] != '/')
1653 snprintf(gpath+strlen(gpath), sizeof(gpath)-strlen(gpath), "/etc/%s", &path[8]);
1654 else
1655 strncpy(gpath+strlen(gpath), &path[8], sizeof(gpath)-strlen(gpath));
1656
1657 if (glob(gpath, 0, NULL, &gl) == 0) {
1658 for (x = 0; x < gl.gl_pathc; ++x) {
1659 /* try to avoid direct loops */
1660 if (strcmp(gl.gl_pathv[x], fname) == 0)
1661 continue;
1662 i = load_ld_cache_config(i, gl.gl_pathv[x]);
1663 if (i + 1 >= ARRAY_SIZE(ldpaths)) {
1664 globfree(&gl);
1665 return i;
1666 }
1667 }
1668 globfree(&gl);
1669 continue;
1670 }
1671 }
1672
1673 if (*path != '/')
1674 continue;
1675
1676 ldpaths[i++] = xstrdup(path);
1677
1678 if (i + 1 == ARRAY_SIZE(ldpaths))
1679 break;
1680 }
1681 ldpaths[i] = NULL;
1682
1683 fclose(fp);
1684 return i;
1685}
1686
1687#elif defined(__FreeBSD__) || defined(__DragonFly__)
1688
1689static int load_ld_cache_config(int i, const char *fname)
1690{
1691 FILE *fp = NULL;
1692 char *b = NULL, *p;
1693 struct elfhints_hdr hdr;
1694 char _fname[__PAX_UTILS_PATH_MAX];
1695
1696 fname = maybe_add_root(fname, _fname);
1697
1698 if (i + 1 == ARRAY_SIZE(ldpaths))
1699 return i;
1700
1701 if ((fp = fopen(fname, "r")) == NULL)
1702 return i;
1703
1704 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
1705 hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
1706 fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
1707 {
1708 fclose(fp);
1709 return i;
1710 }
1711
1712 b = xmalloc(hdr.dirlistlen + 1);
1713 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1714 fclose(fp);
1715 free(b);
1716 return i;
1717 }
1718
1719 while ((p = strsep(&b, ":"))) {
1720 if (*p == '\0') continue;
1721 ldpaths[i++] = xstrdup(p);
1722
1723 if (i + 1 == ARRAY_SIZE(ldpaths))
1724 break;
1725 }
1726 ldpaths[i] = NULL;
1727
1728 free(b);
1729 fclose(fp);
1730 return i;
1731}
1732
1733#else
1734#ifdef __ELF__
1735#warning Cache config support not implemented for your target
1736#endif
1737static int load_ld_cache_config(int i, const char *fname)
1738{
1739 memset(ldpaths, 0x00, sizeof(ldpaths));
1740 return 0;
1741}
1742#endif
366 1743
367/* scan /etc/ld.so.conf for paths */ 1744/* scan /etc/ld.so.conf for paths */
368static void scanelf_ldpath() 1745static void scanelf_ldpath(void)
369{ 1746{
370 char scan_l, scan_ul, scan_ull; 1747 char scan_l, scan_ul, scan_ull;
371 char *path, *p; 1748 int i = 0;
372 FILE *fp;
373 1749
374 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL) 1750 if (!ldpaths[0])
375 err("Unable to open ld.so.conf: %s", strerror(errno)); 1751 err("Unable to load any paths from ld.so.conf");
376 1752
377 scan_l = scan_ul = scan_ull = 0; 1753 scan_l = scan_ul = scan_ull = 0;
378 1754
379 if ((path = malloc(_POSIX_PATH_MAX)) == NULL) { 1755 while (ldpaths[i]) {
380 warn("Can not malloc() memory for ldpath scanning");
381 return;
382 }
383 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL)
384 if (*path == '/') {
385 if ((p = strrchr(path, '\r')) != NULL)
386 *p = 0;
387 if ((p = strrchr(path, '\n')) != NULL)
388 *p = 0;
389 if (!scan_l && !strcmp(path, "/lib")) scan_l = 1; 1756 if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
390 if (!scan_ul && !strcmp(path, "/usr/lib")) scan_ul = 1; 1757 if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
391 if (!scan_ull && !strcmp(path, "/usr/local/lib")) scan_ull = 1; 1758 if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
392 scanelf_dir(path); 1759 scanelf_dir(ldpaths[i]);
1760 ++i;
393 } 1761 }
394 free(path);
395 fclose(fp);
396 1762
397 if (!scan_l) scanelf_dir("/lib"); 1763 if (!scan_l) scanelf_dir("/lib");
398 if (!scan_ul) scanelf_dir("/usr/lib"); 1764 if (!scan_ul) scanelf_dir("/usr/lib");
399 if (!scan_ull) scanelf_dir("/usr/local/lib"); 1765 if (!scan_ull) scanelf_dir("/usr/local/lib");
400} 1766}
401 1767
402/* scan env PATH for paths */ 1768/* scan env PATH for paths */
403static void scanelf_envpath() 1769static void scanelf_envpath(void)
404{ 1770{
405 char *path, *p; 1771 char *path, *p;
406 1772
407 path = getenv("PATH"); 1773 path = getenv("PATH");
408 if (!path) 1774 if (!path)
409 err("PATH is not set in your env !"); 1775 err("PATH is not set in your env !");
410 1776 path = xstrdup(path);
411 if ((path = strdup(path)) == NULL)
412 err("strdup failed: %s", strerror(errno));
413 1777
414 while ((p = strrchr(path, ':')) != NULL) { 1778 while ((p = strrchr(path, ':')) != NULL) {
415 scanelf_dir(p + 1); 1779 scanelf_dir(p + 1);
416 *p = 0; 1780 *p = 0;
417 } 1781 }
418 1782
419 free(path); 1783 free(path);
420} 1784}
421 1785
422 1786/* usage / invocation handling functions */ /* Free Flags: c d j u w G H J K P Q U W */
423 1787#define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:DIYO:ZCBhV"
424/* usage / invocation handling functions */
425#define PARSE_FLAGS "plRmxetrns:aqvo:BhV"
426#define a_argument required_argument 1788#define a_argument required_argument
427static struct option const long_opts[] = { 1789static struct option const long_opts[] = {
428 {"path", no_argument, NULL, 'p'}, 1790 {"path", no_argument, NULL, 'p'},
429 {"ldpath", no_argument, NULL, 'l'}, 1791 {"ldpath", no_argument, NULL, 'l'},
1792 {"root", a_argument, NULL, 128},
430 {"recursive", no_argument, NULL, 'R'}, 1793 {"recursive", no_argument, NULL, 'R'},
431 {"mount", no_argument, NULL, 'm'}, 1794 {"mount", no_argument, NULL, 'm'},
1795 {"symlink", no_argument, NULL, 'y'},
1796 {"archives", no_argument, NULL, 'A'},
1797 {"ldcache", no_argument, NULL, 'L'},
1798 {"fix", no_argument, NULL, 'X'},
1799 {"setpax", a_argument, NULL, 'z'},
432 {"pax", no_argument, NULL, 'x'}, 1800 {"pax", no_argument, NULL, 'x'},
433 {"header", no_argument, NULL, 'e'}, 1801 {"header", no_argument, NULL, 'e'},
434 {"textrel", no_argument, NULL, 't'}, 1802 {"textrel", no_argument, NULL, 't'},
435 {"rpath", no_argument, NULL, 'r'}, 1803 {"rpath", no_argument, NULL, 'r'},
436 {"needed", no_argument, NULL, 'n'}, 1804 {"needed", no_argument, NULL, 'n'},
1805 {"interp", no_argument, NULL, 'i'},
1806 {"bind", no_argument, NULL, 'b'},
1807 {"soname", no_argument, NULL, 'S'},
437 {"symbol", a_argument, NULL, 's'}, 1808 {"symbol", a_argument, NULL, 's'},
1809 {"section", a_argument, NULL, 'k'},
1810 {"lib", a_argument, NULL, 'N'},
1811 {"gmatch", no_argument, NULL, 'g'},
1812 {"textrels", no_argument, NULL, 'T'},
1813 {"etype", a_argument, NULL, 'E'},
1814 {"bits", a_argument, NULL, 'M'},
1815 {"endian", no_argument, NULL, 'D'},
1816 {"osabi", no_argument, NULL, 'I'},
1817 {"eabi", no_argument, NULL, 'Y'},
1818 {"perms", a_argument, NULL, 'O'},
1819 {"size", no_argument, NULL, 'Z'},
438 {"all", no_argument, NULL, 'a'}, 1820 {"all", no_argument, NULL, 'a'},
439 {"quiet", no_argument, NULL, 'q'}, 1821 {"quiet", no_argument, NULL, 'q'},
440 {"verbose", no_argument, NULL, 'v'}, 1822 {"verbose", no_argument, NULL, 'v'},
1823 {"format", a_argument, NULL, 'F'},
1824 {"from", a_argument, NULL, 'f'},
441 {"file", a_argument, NULL, 'o'}, 1825 {"file", a_argument, NULL, 'o'},
1826 {"nocolor", no_argument, NULL, 'C'},
442 {"nobanner", no_argument, NULL, 'B'}, 1827 {"nobanner", no_argument, NULL, 'B'},
443 {"help", no_argument, NULL, 'h'}, 1828 {"help", no_argument, NULL, 'h'},
444 {"version", no_argument, NULL, 'V'}, 1829 {"version", no_argument, NULL, 'V'},
445 {NULL, no_argument, NULL, 0x0} 1830 {NULL, no_argument, NULL, 0x0}
446}; 1831};
1832
447static char *opts_help[] = { 1833static const char * const opts_help[] = {
448 "Scan all directories in PATH environment", 1834 "Scan all directories in PATH environment",
449 "Scan all directories in /etc/ld.so.conf", 1835 "Scan all directories in /etc/ld.so.conf",
1836 "Root directory (use with -l or -p)",
450 "Scan directories recursively", 1837 "Scan directories recursively",
451 "Don't recursively cross mount points\n", 1838 "Don't recursively cross mount points",
1839 "Don't scan symlinks",
1840 "Scan archives (.a files)",
1841 "Utilize ld.so.cache information (use with -r/-n)",
1842 "Try and 'fix' bad things (use with -r/-e)",
1843 "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
452 "Print PaX markings", 1844 "Print PaX markings",
453 "Print GNU_STACK markings", 1845 "Print GNU_STACK/PT_LOAD markings",
454 "Print TEXTREL information", 1846 "Print TEXTREL information",
455 "Print RPATH information", 1847 "Print RPATH information",
456 "Print NEEDED information", 1848 "Print NEEDED information",
1849 "Print INTERP information",
1850 "Print BIND information",
1851 "Print SONAME information",
457 "Find a specified symbol", 1852 "Find a specified symbol",
458 "Print all scanned info (-x -e -t -r)\n", 1853 "Find a specified section",
1854 "Find a specified library",
1855 "Use regex matching rather than string compare (use with -s)",
1856 "Locate cause of TEXTREL",
1857 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
1858 "Print only ELF files matching numeric bits",
1859 "Print Endianness",
1860 "Print OSABI",
1861 "Print EABI (EM_ARM Only)",
1862 "Print only ELF files matching octal permissions",
1863 "Print ELF file size",
1864 "Print all useful/simple info\n",
459 "Only output 'bad' things", 1865 "Only output 'bad' things",
460 "Be verbose (can be specified more than once)", 1866 "Be verbose (can be specified more than once)",
1867 "Use specified format for output",
1868 "Read input stream from a filename",
461 "Write output stream to a filename", 1869 "Write output stream to a filename",
1870 "Don't emit color in output",
462 "Don't display the header", 1871 "Don't display the header",
463 "Print this help and exit", 1872 "Print this help and exit",
464 "Print version and exit", 1873 "Print version and exit",
465 NULL 1874 NULL
466}; 1875};
467 1876
468/* display usage and exit */ 1877/* display usage and exit */
469static void usage(int status) 1878static void usage(int status)
470{ 1879{
471 int i; 1880 unsigned long i;
472 printf(" Scan ELF binaries for stuff\n" 1881 printf("* Scan ELF binaries for stuff\n\n"
473 "Usage: %s [options] <dir1> [dir2 dirN ...]\n\n", argv0); 1882 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
474 printf("Options:\n"); 1883 printf("Options: -[%s]\n", PARSE_FLAGS);
475 for (i = 0; long_opts[i].name; ++i) 1884 for (i = 0; long_opts[i].name; ++i)
476 if (long_opts[i].has_arg == no_argument) 1885 if (long_opts[i].has_arg == no_argument)
477 printf(" -%c, --%-13s %s\n", long_opts[i].val, 1886 printf(" -%c, --%-14s* %s\n", long_opts[i].val,
1887 long_opts[i].name, opts_help[i]);
1888 else if (long_opts[i].val > '~')
1889 printf(" --%-7s <arg> * %s\n",
478 long_opts[i].name, opts_help[i]); 1890 long_opts[i].name, opts_help[i]);
479 else 1891 else
480 printf(" -%c, --%-6s <arg> %s\n", long_opts[i].val, 1892 printf(" -%c, --%-7s <arg> * %s\n", long_opts[i].val,
481 long_opts[i].name, opts_help[i]); 1893 long_opts[i].name, opts_help[i]);
1894
1895 puts("\nFor more information, see the scanelf(1) manpage");
482 exit(status); 1896 exit(status);
483} 1897}
484 1898
485/* parse command line arguments and preform needed actions */ 1899/* parse command line arguments and preform needed actions */
1900#define do_pax_state(option, flag) \
1901 if (islower(option)) { \
1902 flags &= ~PF_##flag; \
1903 flags |= PF_NO##flag; \
1904 } else { \
1905 flags &= ~PF_NO##flag; \
1906 flags |= PF_##flag; \
1907 }
486static void parseargs(int argc, char *argv[]) 1908static int parseargs(int argc, char *argv[])
487{ 1909{
488 int flag; 1910 int i;
1911 const char *from_file = NULL;
1912 int ret = 0;
489 1913
490 opterr = 0; 1914 opterr = 0;
491 while ((flag=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { 1915 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
492 switch (flag) { 1916 switch (i) {
493 1917
494 case 'V': /* version info */ 1918 case 'V':
495 printf("%s compiled %s\n%s\n" 1919 printf("pax-utils-%s: %s compiled %s\n%s\n"
496 "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n", 1920 "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
497 __FILE__, __DATE__, rcsid, argv0); 1921 VERSION, __FILE__, __DATE__, rcsid, argv0);
498 exit(EXIT_SUCCESS); 1922 exit(EXIT_SUCCESS);
499 break; 1923 break;
500 case 'h': usage(EXIT_SUCCESS); break; 1924 case 'h': usage(EXIT_SUCCESS); break;
501 1925 case 'f':
1926 if (from_file) warn("You prob don't want to specify -f twice");
1927 from_file = optarg;
1928 break;
1929 case 'E':
1930 strncpy(match_etypes, optarg, sizeof(match_etypes));
1931 break;
1932 case 'M':
1933 match_bits = atoi(optarg);
1934 if (match_bits == 0) {
1935 if (strcmp(optarg, "ELFCLASS32") == 0)
1936 match_bits = 32;
1937 if (strcmp(optarg, "ELFCLASS64") == 0)
1938 match_bits = 64;
1939 }
1940 break;
1941 case 'O':
1942 if (sscanf(optarg, "%o", &match_perms) == -1)
1943 match_bits = 0;
1944 break;
502 case 'o': { 1945 case 'o': {
503 FILE *fp = NULL;
504 fp = freopen(optarg, "w", stdout); 1946 if (freopen(optarg, "w", stdout) == NULL)
505 if (fp == NULL)
506 err("Could not open output stream '%s': %s", optarg, strerror(errno)); 1947 err("Could not open output stream '%s': %s", optarg, strerror(errno));
507 stdout = fp;
508 break; 1948 break;
509 } 1949 }
1950 case 'k':
1951 xarraypush(find_section_arr, optarg, strlen(optarg));
1952 break;
1953 case 's': {
1954 if (find_sym) warn("You prob don't want to specify -s twice");
1955 find_sym = optarg;
1956 break;
1957 }
1958 case 'N':
1959 xarraypush(find_lib_arr, optarg, strlen(optarg));
1960 break;
1961 case 'F': {
1962 if (out_format) warn("You prob don't want to specify -F twice");
1963 out_format = optarg;
1964 break;
1965 }
1966 case 'z': {
1967 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
1968 size_t x;
510 1969
511 case 's': find_sym = strdup(optarg); break; 1970 for (x = 0; x < strlen(optarg); x++) {
512 1971 switch (optarg[x]) {
1972 case 'p':
1973 case 'P':
1974 do_pax_state(optarg[x], PAGEEXEC);
1975 break;
1976 case 's':
1977 case 'S':
1978 do_pax_state(optarg[x], SEGMEXEC);
1979 break;
1980 case 'm':
1981 case 'M':
1982 do_pax_state(optarg[x], MPROTECT);
1983 break;
1984 case 'e':
1985 case 'E':
1986 do_pax_state(optarg[x], EMUTRAMP);
1987 break;
1988 case 'r':
1989 case 'R':
1990 do_pax_state(optarg[x], RANDMMAP);
1991 break;
1992 case 'x':
1993 case 'X':
1994 do_pax_state(optarg[x], RANDEXEC);
1995 break;
1996 default:
1997 break;
1998 }
1999 }
2000 if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
2001 ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
2002 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
2003 ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
2004 ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
2005 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
2006 setpax = flags;
2007 break;
2008 }
2009 case 'Z': show_size = 1; break;
2010 case 'g': g_match = 1; break;
2011 case 'L': use_ldcache = 1; break;
2012 case 'y': scan_symlink = 0; break;
2013 case 'A': scan_archives = 1; break;
2014 case 'C': color_init(true); break;
513 case 'B': show_banner = 0; break; 2015 case 'B': show_banner = 0; break;
514 case 'l': scan_ldpath = 1; break; 2016 case 'l': scan_ldpath = 1; break;
515 case 'p': scan_envpath = 1; break; 2017 case 'p': scan_envpath = 1; break;
516 case 'R': dir_recurse = 1; break; 2018 case 'R': dir_recurse = 1; break;
517 case 'm': dir_crossmount = 0; break; 2019 case 'm': dir_crossmount = 0; break;
2020 case 'X': ++fix_elf; break;
518 case 'x': show_pax = 1; break; 2021 case 'x': show_pax = 1; break;
519 case 'e': show_stack = 1; break; 2022 case 'e': show_phdr = 1; break;
520 case 't': show_textrel = 1; break; 2023 case 't': show_textrel = 1; break;
521 case 'r': show_rpath = 1; break; 2024 case 'r': show_rpath = 1; break;
522 case 'n': show_needed = 1; break; 2025 case 'n': show_needed = 1; break;
2026 case 'i': show_interp = 1; break;
2027 case 'b': show_bind = 1; break;
2028 case 'S': show_soname = 1; break;
2029 case 'T': show_textrels = 1; break;
523 case 'q': be_quiet = 1; break; 2030 case 'q': be_quiet = 1; break;
524 case 'v': be_verbose = (be_verbose % 20) + 1; break; 2031 case 'v': be_verbose = (be_verbose % 20) + 1; break;
525 case 'a': show_pax = show_stack = show_textrel = show_needed = show_rpath = 1; break; 2032 case 'a': show_perms = show_pax = show_phdr = show_textrel = show_rpath = show_bind = show_endian = 1; break;
526 2033 case 'D': show_endian = 1; break;
2034 case 'I': show_osabi = 1; break;
2035 case 'Y': show_eabi = 1; break;
2036 case 128:
2037 root = optarg;
2038 break;
527 case ':': 2039 case ':':
528 warn("Option missing parameter"); 2040 err("Option '%c' is missing parameter", optopt);
529 usage(EXIT_FAILURE);
530 break;
531 case '?': 2041 case '?':
532 warn("Unknown option"); 2042 err("Unknown option '%c' or argument missing", optopt);
533 usage(EXIT_FAILURE);
534 break;
535 default: 2043 default:
536 err("Unhandled option '%c'", flag); 2044 err("Unhandled option '%c'; please report this", i);
537 break; 2045 }
2046 }
2047 if (show_textrels && be_verbose) {
2048 if (which("objdump") != NULL)
2049 has_objdump = 1;
2050 }
2051 /* flatten arrays for display */
2052 if (array_cnt(find_lib_arr))
2053 find_lib = array_flatten_str(find_lib_arr);
2054 if (array_cnt(find_section_arr))
2055 find_section = array_flatten_str(find_section_arr);
2056 /* let the format option override all other options */
2057 if (out_format) {
2058 show_pax = show_phdr = show_textrel = show_rpath = \
2059 show_needed = show_interp = show_bind = show_soname = \
2060 show_textrels = show_perms = show_endian = show_size = \
2061 show_osabi = show_eabi = 0;
2062 for (i = 0; out_format[i]; ++i) {
2063 if (!IS_MODIFIER(out_format[i])) continue;
2064
2065 switch (out_format[++i]) {
2066 case '+': break;
2067 case '%': break;
2068 case '#': break;
2069 case 'F': break;
2070 case 'p': break;
2071 case 'f': break;
2072 case 'k': break;
2073 case 's': break;
2074 case 'N': break;
2075 case 'o': break;
2076 case 'a': break;
2077 case 'M': break;
2078 case 'Z': show_size = 1; break;
2079 case 'D': show_endian = 1; break;
2080 case 'I': show_osabi = 1; break;
2081 case 'Y': show_eabi = 1; break;
2082 case 'O': show_perms = 1; break;
2083 case 'x': show_pax = 1; break;
2084 case 'e': show_phdr = 1; break;
2085 case 't': show_textrel = 1; break;
2086 case 'r': show_rpath = 1; break;
2087 case 'n': show_needed = 1; break;
2088 case 'i': show_interp = 1; break;
2089 case 'b': show_bind = 1; break;
2090 case 'S': show_soname = 1; break;
2091 case 'T': show_textrels = 1; break;
2092 default:
2093 err("Invalid format specifier '%c' (byte %i)",
2094 out_format[i], i+1);
538 } 2095 }
539 } 2096 }
540 2097
541 if (be_quiet && be_verbose) 2098 /* construct our default format */
542 err("You can be quiet or you can be verbose, not both, stupid"); 2099 } else {
2100 size_t fmt_len = 30;
2101 out_format = xmalloc(sizeof(char) * fmt_len);
2102 *out_format = '\0';
2103 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
2104 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
2105 if (show_perms) xstrcat(&out_format, "%O ", &fmt_len);
2106 if (show_size) xstrcat(&out_format, "%Z ", &fmt_len);
2107 if (show_endian) xstrcat(&out_format, "%D ", &fmt_len);
2108 if (show_osabi) xstrcat(&out_format, "%I ", &fmt_len);
2109 if (show_eabi) xstrcat(&out_format, "%Y ", &fmt_len);
2110 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
2111 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
2112 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
2113 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
2114 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
2115 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
2116 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
2117 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
2118 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
2119 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
2120 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
2121 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
2122 }
2123 if (be_verbose > 2) printf("Format: %s\n", out_format);
543 2124
2125 /* now lets actually do the scanning */
2126 if (scan_ldpath || use_ldcache)
2127 load_ld_cache_config(0, __PAX_UTILS_DEFAULT_LD_CACHE_CONFIG);
544 if (scan_ldpath) scanelf_ldpath(); 2128 if (scan_ldpath) scanelf_ldpath();
545 if (scan_envpath) scanelf_envpath(); 2129 if (scan_envpath) scanelf_envpath();
2130 if (!from_file && optind == argc && ttyname(0) == NULL && !scan_ldpath && !scan_envpath)
2131 from_file = "-";
2132 if (from_file) {
2133 scanelf_from_file(from_file);
2134 from_file = *argv;
2135 }
546 if (optind == argc && !scan_ldpath && !scan_envpath) 2136 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
547 err("Nothing to scan !?"); 2137 err("Nothing to scan !?");
548 while (optind < argc) 2138 while (optind < argc) {
549 scanelf_dir(argv[optind++]); 2139 search_path = argv[optind++];
2140 ret = scanelf_dir(search_path);
2141 }
550 2142
551 if (find_sym) free(find_sym); 2143 /* clean up */
552} 2144 for (i = 0; ldpaths[i]; ++i)
2145 free(ldpaths[i]);
2146 free(find_lib);
2147 free(find_section);
553 2148
2149 if (ldcache != 0)
2150 munmap(ldcache, ldcache_size);
2151 return ret;
2152}
554 2153
2154static char **get_split_env(const char *envvar)
2155{
2156 const char *delims = " \t\n";
2157 char **envvals = NULL;
2158 char *env, *s;
2159 int nentry;
2160
2161 if ((env = getenv(envvar)) == NULL)
2162 return NULL;
2163
2164 env = xstrdup(env);
2165 if (env == NULL)
2166 return NULL;
2167
2168 s = strtok(env, delims);
2169 if (s == NULL) {
2170 free(env);
2171 return NULL;
2172 }
2173
2174 nentry = 0;
2175 while (s != NULL) {
2176 ++nentry;
2177 envvals = xrealloc(envvals, sizeof(*envvals) * (nentry+1));
2178 envvals[nentry-1] = s;
2179 s = strtok(NULL, delims);
2180 }
2181 envvals[nentry] = NULL;
2182
2183 /* don't want to free(env) as it contains the memory that backs
2184 * the envvals array of strings */
2185 return envvals;
2186}
2187
2188static void parseenv(void)
2189{
2190 color_init(false);
2191 qa_textrels = get_split_env("QA_TEXTRELS");
2192 qa_execstack = get_split_env("QA_EXECSTACK");
2193 qa_wx_load = get_split_env("QA_WX_LOAD");
2194}
2195
2196#ifdef __PAX_UTILS_CLEANUP
2197static void cleanup(void)
2198{
2199 free(out_format);
2200 free(qa_textrels);
2201 free(qa_execstack);
2202 free(qa_wx_load);
2203}
2204#endif
555 2205
556int main(int argc, char *argv[]) 2206int main(int argc, char *argv[])
557{ 2207{
2208 int ret;
558 if (argc < 2) 2209 if (argc < 2)
559 usage(EXIT_FAILURE); 2210 usage(EXIT_FAILURE);
2211 parseenv();
560 parseargs(argc, argv); 2212 ret = parseargs(argc, argv);
561 fclose(stdout); 2213 fclose(stdout);
562 return EXIT_SUCCESS; 2214#ifdef __PAX_UTILS_CLEANUP
2215 cleanup();
2216 warn("The calls to add/delete heap should be off:\n"
2217 "\t- 1 due to the out_buffer not being freed in scanelf_file()\n"
2218 "\t- 1 per QA_TEXTRELS/QA_EXECSTACK/QA_WX_LOAD");
2219#endif
2220 return ret;
563} 2221}
2222
2223/* Match filename against entries in matchlist, return TRUE
2224 * if the file is listed */
2225static int file_matches_list(const char *filename, char **matchlist)
2226{
2227 char **file;
2228 char *match;
2229 char buf[__PAX_UTILS_PATH_MAX];
2230
2231 if (matchlist == NULL)
2232 return 0;
2233
2234 for (file = matchlist; *file != NULL; file++) {
2235 if (search_path) {
2236 snprintf(buf, sizeof(buf), "%s%s", search_path, *file);
2237 match = buf;
2238 } else {
2239 match = *file;
2240 }
2241 if (fnmatch(match, filename, 0) == 0)
2242 return 1;
2243 }
2244 return 0;
2245}

Legend:
Removed from v.1.32  
changed lines
  Added in v.1.226

  ViewVC Help
Powered by ViewVC 1.1.20