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

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

  ViewVC Help
Powered by ViewVC 1.1.20