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