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

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

  ViewVC Help
Powered by ViewVC 1.1.20