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

Legend:
Removed from v.1.40  
changed lines
  Added in v.1.277

  ViewVC Help
Powered by ViewVC 1.1.20