/[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.96 Revision 1.164
1/* 1/*
2 * Copyright 2003-2005 Gentoo Foundation 2 * Copyright 2003-2006 Gentoo Foundation
3 * Distributed under the terms of the GNU General Public License v2 3 * Distributed under the terms of the GNU General Public License v2
4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.96 2005/12/28 22:26:47 solar Exp $ 4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.164 2006/12/03 00:17:18 solar Exp $
5 * 5 *
6 * Copyright 2003-2005 Ned Ludd - <solar@gentoo.org> 6 * Copyright 2003-2006 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2004-2005 Mike Frysinger - <vapier@gentoo.org> 7 * Copyright 2004-2006 Mike Frysinger - <vapier@gentoo.org>
8 */ 8 */
9 9
10#include <stdio.h>
11#include <stdlib.h>
12#include <sys/types.h>
13#include <libgen.h>
14#include <limits.h>
15#include <string.h>
16#include <errno.h>
17#include <unistd.h>
18#include <sys/stat.h>
19#include <sys/mman.h>
20#include <fcntl.h>
21#include <dirent.h>
22#include <getopt.h>
23#include <assert.h>
24#include "paxinc.h" 10#include "paxinc.h"
25 11
26static const char *rcsid = "$Id: scanelf.c,v 1.96 2005/12/28 22:26:47 solar Exp $"; 12static const char *rcsid = "$Id: scanelf.c,v 1.164 2006/12/03 00:17:18 solar Exp $";
27#define argv0 "scanelf" 13#define argv0 "scanelf"
28 14
29#define IS_MODIFIER(c) (c == '%' || c == '#') 15#define IS_MODIFIER(c) (c == '%' || c == '#' || c == '+')
30 16
17#define do_state(option, flag) \
18 if (islower(option)) { \
19 flags &= ~PF_##flag; \
20 flags |= PF_NO##flag; \
21 } else { \
22 flags &= ~PF_NO##flag; \
23 flags |= PF_##flag; \
24 }
31 25
32 26
33/* prototypes */ 27/* prototypes */
34static void scanelf_file(const char *filename); 28static int file_matches_list(const char *filename, char **matchlist);
29static int scanelf_elfobj(elfobj *elf);
30static int scanelf_elf(const char *filename, int fd, size_t len);
31static int scanelf_archive(const char *filename, int fd, size_t len);
32static int scanelf_file(const char *filename, const struct stat *st_cache);
35static void scanelf_dir(const char *path); 33static int scanelf_dir(const char *path);
36static void scanelf_ldpath(); 34static void scanelf_ldpath(void);
37static void scanelf_envpath(); 35static void scanelf_envpath(void);
38static void usage(int status); 36static void usage(int status);
37static char **get_split_env(const char *envvar);
38static void parseenv(void);
39static void parseargs(int argc, char *argv[]); 39static int parseargs(int argc, char *argv[]);
40static char *xstrdup(const char *s); 40static char *xstrdup(const char *s);
41static void *xmalloc(size_t size); 41static void *xmalloc(size_t size);
42static void *xrealloc(void *ptr, size_t size);
42static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n); 43static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n);
43#define xstrcat(dst,src,curr_len) xstrncat(dst,src,curr_len,0) 44#define xstrcat(dst,src,curr_len) xstrncat(dst,src,curr_len,0)
44static inline void xchrcat(char **dst, const char append, size_t *curr_len); 45static inline void xchrcat(char **dst, const char append, size_t *curr_len);
45 46
46/* variables to control behavior */ 47/* variables to control behavior */
48static char match_etypes[126] = "";
47static char *ldpaths[256]; 49static char *ldpaths[256];
48static char scan_ldpath = 0; 50static char scan_ldpath = 0;
49static char scan_envpath = 0; 51static char scan_envpath = 0;
50static char scan_symlink = 1; 52static char scan_symlink = 1;
53static char scan_archives = 0;
51static char dir_recurse = 0; 54static char dir_recurse = 0;
52static char dir_crossmount = 1; 55static char dir_crossmount = 1;
53static char show_pax = 0; 56static char show_pax = 0;
54static char show_phdr = 0; 57static char show_phdr = 0;
55static char show_textrel = 0; 58static char show_textrel = 0;
61static char show_textrels = 0; 64static char show_textrels = 0;
62static char show_banner = 1; 65static char show_banner = 1;
63static char be_quiet = 0; 66static char be_quiet = 0;
64static char be_verbose = 0; 67static char be_verbose = 0;
65static char be_wewy_wewy_quiet = 0; 68static char be_wewy_wewy_quiet = 0;
69static char be_semi_verbose = 0;
66static char *find_sym = NULL, *versioned_symname = NULL; 70static char *find_sym = NULL, *versioned_symname = NULL;
67static char *find_lib = NULL; 71static char *find_lib = NULL;
72static char *find_section = NULL;
68static char *out_format = NULL; 73static char *out_format = NULL;
69static char *search_path = NULL; 74static char *search_path = NULL;
75static char fix_elf = 0;
70static char gmatch = 0; 76static char gmatch = 0;
71static char printcache = 0; 77static char use_ldcache = 0;
72 78
79static char **qa_textrels = NULL;
80static char **qa_execstack = NULL;
81static char **qa_wx_load = NULL;
73 82
83int match_bits = 0;
74caddr_t ldcache = 0; 84caddr_t ldcache = 0;
75size_t ldcache_size = 0; 85size_t ldcache_size = 0;
86unsigned long setpax = 0UL;
87
88
76 89
77/* sub-funcs for scanelf_file() */ 90/* sub-funcs for scanelf_file() */
78static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab) 91static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
79{ 92{
80 /* find the best SHT_DYNSYM and SHT_STRTAB sections */ 93 /* find the best SHT_DYNSYM and SHT_STRTAB sections */
99 } \ 112 } \
100 } 113 }
101 GET_SYMTABS(32) 114 GET_SYMTABS(32)
102 GET_SYMTABS(64) 115 GET_SYMTABS(64)
103} 116}
117
104static char *scanelf_file_pax(elfobj *elf, char *found_pax) 118static char *scanelf_file_pax(elfobj *elf, char *found_pax)
105{ 119{
106 static char ret[7]; 120 static char ret[7];
107 unsigned long i, shown; 121 unsigned long i, shown;
108 122
117 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 131 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
118 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 132 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
119 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 133 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
120 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \ 134 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
121 continue; \ 135 continue; \
122 if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \ 136 if (fix_elf && setpax) { \
137 /* set the paxctl flags */ \
138 ESET(phdr[i].p_flags, setpax); \
139 } \
140 if (be_quiet && (EGET(phdr[i].p_flags) == (PF_NOEMUTRAMP | PF_NORANDEXEC))) \
123 continue; \ 141 continue; \
124 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \ 142 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
125 *found_pax = 1; \ 143 *found_pax = 1; \
126 ++shown; \ 144 ++shown; \
127 break; \ 145 break; \
128 } \ 146 } \
129 } 147 }
130 SHOW_PAX(32) 148 SHOW_PAX(32)
131 SHOW_PAX(64) 149 SHOW_PAX(64)
150 }
151
152
153 if (fix_elf && setpax) {
154 /* set the chpax settings */
155 if (elf->elf_class == ELFCLASS32) {
156 if (EHDR32(elf->ehdr)->e_type == ET_DYN || EHDR32(elf->ehdr)->e_type == ET_EXEC)
157 ESET(EHDR32(elf->ehdr)->e_ident[EI_PAX], pax_pf2hf_flags(setpax));
158 } else {
159 if (EHDR64(elf->ehdr)->e_type == ET_DYN || EHDR64(elf->ehdr)->e_type == ET_EXEC)
160 ESET(EHDR64(elf->ehdr)->e_ident[EI_PAX], pax_pf2hf_flags(setpax));
161 }
132 } 162 }
133 163
134 /* fall back to EI_PAX if no PT_PAX was found */ 164 /* fall back to EI_PAX if no PT_PAX was found */
135 if (!*ret) { 165 if (!*ret) {
136 static char *paxflags; 166 static char *paxflags;
150 180
151static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load) 181static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
152{ 182{
153 static char ret[12]; 183 static char ret[12];
154 char *found; 184 char *found;
155 unsigned long i, shown;
156 unsigned char multi_stack, multi_relro, multi_load; 185 unsigned long i, shown, multi_stack, multi_relro, multi_load;
186 int max_pt_load;
157 187
158 if (!show_phdr) return NULL; 188 if (!show_phdr) return NULL;
159 189
160 memcpy(ret, "--- --- ---\0", 12); 190 memcpy(ret, "--- --- ---\0", 12);
161 191
162 shown = 0; 192 shown = 0;
163 multi_stack = multi_relro = multi_load = 0; 193 multi_stack = multi_relro = multi_load = 0;
194 max_pt_load = elf_max_pt_load(elf);
164 195
196#define NOTE_GNU_STACK ".note.GNU-stack"
165#define SHOW_PHDR(B) \ 197#define SHOW_PHDR(B) \
166 if (elf->elf_class == ELFCLASS ## B) { \ 198 if (elf->elf_class == ELFCLASS ## B) { \
167 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 199 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
168 Elf ## B ## _Off offset; \ 200 Elf ## B ## _Off offset; \
169 uint32_t flags, check_flags; \ 201 uint32_t flags, check_flags; \
170 if (elf->phdr != NULL) { \ 202 if (elf->phdr != NULL) { \
171 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 203 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
172 for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \ 204 for (i = 0; i < EGET(ehdr->e_phnum); ++i) { \
173 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \ 205 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
206 if (multi_stack++) \
174 if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \ 207 warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
208 if (file_matches_list(elf->filename, qa_execstack)) \
209 continue; \
175 found = found_phdr; \ 210 found = found_phdr; \
176 offset = 0; \ 211 offset = 0; \
177 check_flags = PF_X; \ 212 check_flags = PF_X; \
178 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \ 213 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
214 if (multi_relro++) \
179 if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \ 215 warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
180 found = found_relro; \ 216 found = found_relro; \
181 offset = 4; \ 217 offset = 4; \
182 check_flags = PF_X; \ 218 check_flags = PF_X; \
183 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \ 219 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
184 if (multi_load++ > 2) warnf("%s: more than 2 PT_LOAD's !?", elf->filename); \ 220 if (ehdr->e_type == ET_DYN || ehdr->e_type == ET_EXEC) \
221 if (multi_load++ > max_pt_load) \
222 warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
223 if (file_matches_list(elf->filename, qa_wx_load)) \
224 continue; \
185 found = found_load; \ 225 found = found_load; \
186 offset = 8; \ 226 offset = 8; \
187 check_flags = PF_W|PF_X; \ 227 check_flags = PF_W|PF_X; \
188 } else \ 228 } else \
189 continue; \ 229 continue; \
190 flags = EGET(phdr[i].p_flags); \ 230 flags = EGET(phdr[i].p_flags); \
191 if (be_quiet && ((flags & check_flags) != check_flags)) \ 231 if (be_quiet && ((flags & check_flags) != check_flags)) \
192 continue; \ 232 continue; \
233 if ((EGET(phdr[i].p_type) != PT_LOAD) && (fix_elf && ((flags & PF_X) != flags))) { \
234 ESET(phdr[i].p_flags, flags & (PF_X ^ (size_t)-1)); \
235 ret[3] = ret[7] = '!'; \
236 flags = EGET(phdr[i].p_flags); \
237 } \
193 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \ 238 memcpy(ret+offset, gnu_short_stack_flags(flags), 3); \
194 *found = 1; \ 239 *found = 1; \
195 ++shown; \ 240 ++shown; \
196 } \ 241 } \
197 } else if (elf->shdr != NULL) { \ 242 } else if (elf->shdr != NULL) { \
198 /* no program headers which means this is prob an object file */ \ 243 /* no program headers which means this is prob an object file */ \
199 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \ 244 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
200 Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \ 245 Elf ## B ## _Shdr *strtbl = shdr + EGET(ehdr->e_shstrndx); \
246 char *str; \
247 if ((void*)strtbl > (void*)elf->data_end) \
248 goto skip_this_shdr##B; \
201 check_flags = SHF_WRITE|SHF_EXECINSTR; \ 249 check_flags = SHF_WRITE|SHF_EXECINSTR; \
202 for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \ 250 for (i = 0; i < EGET(ehdr->e_shnum); ++i) { \
203 if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \ 251 if (EGET(shdr[i].sh_type) != SHT_PROGBITS) continue; \
204 offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \ 252 offset = EGET(strtbl->sh_offset) + EGET(shdr[i].sh_name); \
205 if (!strcmp((char*)(elf->data + offset), ".note.GNU-stack")) { \ 253 str = elf->data + offset; \
254 if (str > elf->data + offset + sizeof(NOTE_GNU_STACK)) continue; \
255 if (!strcmp(str, NOTE_GNU_STACK)) { \
206 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \ 256 if (multi_stack++) warnf("%s: multiple .note.GNU-stack's !?", elf->filename); \
207 flags = EGET(shdr[i].sh_flags); \ 257 flags = EGET(shdr[i].sh_flags); \
208 if (be_quiet && ((flags & check_flags) != check_flags)) \ 258 if (be_quiet && ((flags & check_flags) != check_flags)) \
209 continue; \ 259 continue; \
210 ++*found_phdr; \ 260 ++*found_phdr; \
214 if (flags & SHF_EXECINSTR) ret[2] = 'X'; \ 264 if (flags & SHF_EXECINSTR) ret[2] = 'X'; \
215 if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \ 265 if (flags & 0xFFFFFFF8) warn("Invalid section flags for GNU-stack"); \
216 break; \ 266 break; \
217 } \ 267 } \
218 } \ 268 } \
269 skip_this_shdr##B: \
219 if (!multi_stack) { \ 270 if (!multi_stack) { \
271 if (file_matches_list(elf->filename, qa_execstack)) \
272 return NULL; \
220 *found_phdr = 1; \ 273 *found_phdr = 1; \
221 shown = 1; \ 274 shown = 1; \
222 memcpy(ret, "!WX", 3); \ 275 memcpy(ret, "!WX", 3); \
223 } \ 276 } \
224 } \ 277 } \
229 if (be_wewy_wewy_quiet || (be_quiet && !shown)) 282 if (be_wewy_wewy_quiet || (be_quiet && !shown))
230 return NULL; 283 return NULL;
231 else 284 else
232 return ret; 285 return ret;
233} 286}
287
234static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel) 288static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
235{ 289{
236 static const char *ret = "TEXTREL"; 290 static const char *ret = "TEXTREL";
237 unsigned long i; 291 unsigned long i;
238 292
239 if (!show_textrel && !show_textrels) return NULL; 293 if (!show_textrel && !show_textrels) return NULL;
294
295 if (file_matches_list(elf->filename, qa_textrels)) return NULL;
240 296
241 if (elf->phdr) { 297 if (elf->phdr) {
242#define SHOW_TEXTREL(B) \ 298#define SHOW_TEXTREL(B) \
243 if (elf->elf_class == ELFCLASS ## B) { \ 299 if (elf->elf_class == ELFCLASS ## B) { \
244 Elf ## B ## _Dyn *dyn; \ 300 Elf ## B ## _Dyn *dyn; \
327 if (be_verbose <= 2) continue; \ 383 if (be_verbose <= 2) continue; \
328 } else \ 384 } else \
329 *found_textrels = 1; \ 385 *found_textrels = 1; \
330 /* locate this relocation symbol name */ \ 386 /* locate this relocation symbol name */ \
331 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ 387 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
388 if ((void*)sym > (void*)elf->data_end) { \
389 warn("%s: corrupt ELF symbol", elf->filename); \
390 continue; \
391 } \
332 sym_max = ELF ## B ## _R_SYM(r_info); \ 392 sym_max = ELF ## B ## _R_SYM(r_info); \
333 if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \ 393 if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
334 sym += sym_max; \ 394 sym += sym_max; \
335 else \ 395 else \
336 sym = NULL; \ 396 sym = NULL; \
368 warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename); 428 warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
369 429
370 return NULL; 430 return NULL;
371} 431}
372 432
373static void rpath_security_checks(elfobj *, char *); 433static void rpath_security_checks(elfobj *, char *, const char *);
374static void rpath_security_checks(elfobj *elf, char *item) { 434static void rpath_security_checks(elfobj *elf, char *item, const char *dt_type)
435{
375 struct stat st; 436 struct stat st;
376 switch (*item) { 437 switch (*item) {
377 case '/': break; 438 case '/': break;
378 case '.': 439 case '.':
379 warnf("Security problem with relative RPATH '%s' in %s", item, elf->filename); 440 warnf("Security problem with relative %s '%s' in %s", dt_type, item, elf->filename);
380 break; 441 break;
442 case ':':
381 case '\0': 443 case '\0':
382 warnf("Security problem NULL RPATH in %s", elf->filename); 444 warnf("Security problem NULL %s in %s", dt_type, elf->filename);
383 break; 445 break;
384 case '$': 446 case '$':
385 if (fstat(elf->fd, &st) != -1) 447 if (fstat(elf->fd, &st) != -1)
386 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID)) 448 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
387 warnf("Security problem with RPATH='%s' in %s with mode set of %o", 449 warnf("Security problem with %s='%s' in %s with mode set of %o",
388 item, elf->filename, st.st_mode & 07777); 450 dt_type, item, elf->filename, st.st_mode & 07777);
389 break; 451 break;
390 default: 452 default:
391 warnf("Maybe? sec problem with RPATH='%s' in %s", item, elf->filename); 453 warnf("Maybe? sec problem with %s='%s' in %s", dt_type, item, elf->filename);
392 break; 454 break;
393 } 455 }
394} 456}
395static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len) 457static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
396{ 458{
432 /* Verify the memory is somewhat sane */ \ 494 /* Verify the memory is somewhat sane */ \
433 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 495 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
434 if (offset < (Elf ## B ## _Off)elf->len) { \ 496 if (offset < (Elf ## B ## _Off)elf->len) { \
435 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \ 497 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
436 *r = (char*)(elf->data + offset); \ 498 *r = (char*)(elf->data + offset); \
499 /* cache the length in case we need to nuke this section later on */ \
500 if (fix_elf) \
501 offset = strlen(*r); \
437 /* If quiet, don't output paths in ld.so.conf */ \ 502 /* If quiet, don't output paths in ld.so.conf */ \
438 if (be_quiet) { \ 503 if (be_quiet) { \
439 size_t len; \ 504 size_t len; \
440 char *start, *end; \ 505 char *start, *end; \
441 /* note that we only 'chop' off leading known paths. */ \ 506 /* note that we only 'chop' off leading known paths. */ \
442 /* since *r is read-only memory, we can only move the ptr forward. */ \ 507 /* since *r is read-only memory, we can only move the ptr forward. */ \
443 start = *r; \ 508 start = *r; \
444 /* scan each path in : delimited list */ \ 509 /* scan each path in : delimited list */ \
445 while (start) { \ 510 while (start) { \
446 rpath_security_checks(elf, start); \ 511 rpath_security_checks(elf, start, get_elfdtype(word)); \
447 end = strchr(start, ':'); \ 512 end = strchr(start, ':'); \
448 len = (end ? abs(end - start) : strlen(start)); \ 513 len = (end ? abs(end - start) : strlen(start)); \
514 if (use_ldcache) \
449 for (s = 0; ldpaths[s]; ++s) { \ 515 for (s = 0; ldpaths[s]; ++s) \
450 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \ 516 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
451 *r = (end ? end + 1 : NULL); \ 517 *r = end; \
518 /* corner case ... if RPATH reads "/usr/lib:", we want \
519 * to show ':' rather than '' */ \
520 if (end && end[1] != '\0') \
521 (*r)++; \
452 break; \ 522 break; \
453 } \ 523 } \
454 } \
455 if (!*r || !ldpaths[s] || !end) \ 524 if (!*r || !end) \
456 start = NULL; \ 525 break; \
457 else \ 526 else \
458 start = start + len + 1; \ 527 start = start + len + 1; \
459 } \ 528 } \
460 } \ 529 } \
530 if (*r) { \
531 if (fix_elf > 2 || (fix_elf && **r == '\0')) { \
532 /* just nuke it */ \
533 nuke_it##B: \
534 memset(*r, 0x00, offset); \
535 *r = NULL; \
536 ESET(dyn->d_tag, DT_DEBUG); \
537 ESET(dyn->d_un.d_ptr, 0); \
538 } else if (fix_elf) { \
539 /* try to clean "bad" paths */ \
540 size_t len, tmpdir_len; \
541 char *start, *end; \
542 const char *tmpdir; \
543 start = *r; \
544 tmpdir = (getenv("TMPDIR") ? : "."); \
545 tmpdir_len = strlen(tmpdir); \
546 while (1) { \
547 end = strchr(start, ':'); \
548 if (start == end) { \
549 eat_this_path##B: \
550 len = strlen(end); \
551 memmove(start, end+1, len); \
552 start[len-1] = '\0'; \
553 end = start - 1; \
554 } else if (tmpdir && !strncmp(start, tmpdir, tmpdir_len)) { \
555 if (!end) { \
556 if (start == *r) \
557 goto nuke_it##B; \
558 *--start = '\0'; \
559 } else \
560 goto eat_this_path##B; \
561 } \
562 if (!end) \
563 break; \
564 start = end + 1; \
565 } \
566 if (**r == '\0') \
567 goto nuke_it##B; \
568 } \
569 if (*r) \
461 if (*r) *found_rpath = 1; \ 570 *found_rpath = 1; \
571 } \
462 } \ 572 } \
463 ++dyn; \ 573 ++dyn; \
464 } \ 574 } \
465 } } 575 } }
466 SHOW_RPATH(32) 576 SHOW_RPATH(32)
488 598
489#define LDSO_CACHE_MAGIC "ld.so-" 599#define LDSO_CACHE_MAGIC "ld.so-"
490#define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1) 600#define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1)
491#define LDSO_CACHE_VER "1.7.0" 601#define LDSO_CACHE_VER "1.7.0"
492#define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1) 602#define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1)
603#define FLAG_ANY -1
604#define FLAG_TYPE_MASK 0x00ff
605#define FLAG_LIBC4 0x0000
606#define FLAG_ELF 0x0001
607#define FLAG_ELF_LIBC5 0x0002
608#define FLAG_ELF_LIBC6 0x0003
609#define FLAG_REQUIRED_MASK 0xff00
610#define FLAG_SPARC_LIB64 0x0100
611#define FLAG_IA64_LIB64 0x0200
612#define FLAG_X8664_LIB64 0x0300
613#define FLAG_S390_LIB64 0x0400
614#define FLAG_POWERPC_LIB64 0x0500
615#define FLAG_MIPS64_LIBN32 0x0600
616#define FLAG_MIPS64_LIBN64 0x0700
493 617
494static char *lookup_cache_lib(char *); 618static char *lookup_cache_lib(elfobj *, char *);
619
620#if defined(__GLIBC__) || defined(__UCLIBC__)
621
495static char *lookup_cache_lib(char *fname) 622static char *lookup_cache_lib(elfobj *elf, char *fname)
496{ 623{
497 int fd = 0; 624 int fd = 0;
498 char *strs; 625 char *strs;
499 static char buf[_POSIX_PATH_MAX] = ""; 626 static char buf[__PAX_UTILS_PATH_MAX] = "";
500 const char *cachefile = "/etc/ld.so.cache"; 627 const char *cachefile = "/etc/ld.so.cache";
501 struct stat st; 628 struct stat st;
502 629
503 typedef struct { 630 typedef struct {
504 char magic[LDSO_CACHE_MAGIC_LEN]; 631 char magic[LDSO_CACHE_MAGIC_LEN];
505 char version[LDSO_CACHE_VER_LEN]; 632 char version[LDSO_CACHE_VER_LEN];
506 int nlibs; 633 int nlibs;
507 } header_t; 634 } header_t;
635 header_t *header;
508 636
509 typedef struct { 637 typedef struct {
510 int flags; 638 int flags;
511 int sooffset; 639 int sooffset;
512 int liboffset; 640 int liboffset;
513 } libentry_t; 641 } libentry_t;
514
515 header_t *header;
516 libentry_t *libent; 642 libentry_t *libent;
517 643
518 if (fname == NULL) 644 if (fname == NULL)
519 return NULL; 645 return NULL;
520 646
521 if (ldcache == 0) { 647 if (ldcache == 0) {
522 if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY)) < 0) 648 if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY)) == -1)
523 return NULL; 649 return NULL;
524 /* save the cache size for latter unmapping */ 650
651 /* cache these values so we only map/unmap the cache file once */
525 ldcache_size = st.st_size; 652 ldcache_size = st.st_size;
526
527 if ((ldcache = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == (caddr_t) -1) 653 ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0);
654
655 close(fd);
656
657 if (ldcache == (caddr_t)-1) {
658 ldcache = 0;
528 return NULL; 659 return NULL;
529 660 }
530 close(fd);
531 661
532 if (memcmp(((header_t *) ldcache)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN)) 662 if (memcmp(((header_t *) ldcache)->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN))
533 return NULL; 663 return NULL;
534
535 if (memcmp (((header_t *) ldcache)->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN)) 664 if (memcmp (((header_t *) ldcache)->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN))
536 return NULL; 665 return NULL;
537 } 666 }
538 667
539 header = (header_t *) ldcache; 668 header = (header_t *) ldcache;
540 libent = (libentry_t *) (ldcache + sizeof(header_t)); 669 libent = (libentry_t *) (ldcache + sizeof(header_t));
541 strs = (char *) &libent[header->nlibs]; 670 strs = (char *) &libent[header->nlibs];
542 671
543 for (fd = 0; fd < header->nlibs; fd++) { 672 for (fd = 0; fd < header->nlibs; fd++) {
673 /* this should be more fine grained, but for now we assume that
674 * diff arches will not be cached together. and we ignore the
675 * the different multilib mips cases. */
676 if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK))
677 continue;
678 if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK))
679 continue;
680
544 if (strcmp(fname, strs + libent[fd].sooffset) != 0) 681 if (strcmp(fname, strs + libent[fd].sooffset) != 0)
545 continue; 682 continue;
546 strncpy(buf, strs + libent[fd].liboffset, sizeof(buf)); 683 strncpy(buf, strs + libent[fd].liboffset, sizeof(buf));
547 } 684 }
548 return buf; 685 return buf;
549} 686}
687#elif defined(__NetBSD__)
688static char *lookup_cache_lib(elfobj *elf, char *fname)
689{
690 static char buf[__PAX_UTILS_PATH_MAX] = "";
691 static struct stat st;
550 692
693 char **ldpath;
694 for (ldpath = ldpaths; *ldpath != NULL; ldpath++) {
695 if ((unsigned) snprintf(buf, sizeof(buf), "%s/%s", *ldpath, fname) >= sizeof(buf))
696 continue; /* if the pathname is too long, or something went wrong, ignore */
697
698 if (stat(buf, &st) != 0)
699 continue; /* if the lib doesn't exist in *ldpath, look further */
700
701 /* NetBSD doesn't actually do sanity checks, it just loads the file
702 * and if that doesn't work, continues looking in other directories.
703 * This cannot easily be safely emulated, unfortunately. For now,
704 * just assume that if it exists, it's a valid library. */
705
706 return buf;
707 }
708
709 /* not found in any path */
710 return NULL;
711}
712#else
713#warning Cache support not implemented for your target
714static char *lookup_cache_lib(elfobj *elf, char *fname)
715{
716 return NULL;
717}
718#endif
551 719
552static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len) 720static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
553{ 721{
554 unsigned long i; 722 unsigned long i;
555 char *needed; 723 char *needed;
582 } \ 750 } \
583 needed = (char*)(elf->data + offset); \ 751 needed = (char*)(elf->data + offset); \
584 if (op == 0) { \ 752 if (op == 0) { \
585 if (!be_wewy_wewy_quiet) { \ 753 if (!be_wewy_wewy_quiet) { \
586 if (*found_needed) xchrcat(ret, ',', ret_len); \ 754 if (*found_needed) xchrcat(ret, ',', ret_len); \
587 if (printcache) \ 755 if (use_ldcache) \
588 if ((p = lookup_cache_lib(needed)) != NULL) \ 756 if ((p = lookup_cache_lib(elf, needed)) != NULL) \
589 needed = p; \ 757 needed = p; \
590 xstrcat(ret, needed, ret_len); \ 758 xstrcat(ret, needed, ret_len); \
591 } \ 759 } \
592 *found_needed = 1; \ 760 *found_needed = 1; \
593 } else { \ 761 } else { \
630} 798}
631static char *scanelf_file_bind(elfobj *elf, char *found_bind) 799static char *scanelf_file_bind(elfobj *elf, char *found_bind)
632{ 800{
633 unsigned long i; 801 unsigned long i;
634 struct stat s; 802 struct stat s;
803 char dynamic = 0;
635 804
636 if (!show_bind) return NULL; 805 if (!show_bind) return NULL;
637 if (!elf->phdr) return NULL; 806 if (!elf->phdr) return NULL;
638 807
639#define SHOW_BIND(B) \ 808#define SHOW_BIND(B) \
642 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 811 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
643 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 812 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
644 Elf ## B ## _Off offset; \ 813 Elf ## B ## _Off offset; \
645 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 814 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
646 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 815 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
816 dynamic = 1; \
647 offset = EGET(phdr[i].p_offset); \ 817 offset = EGET(phdr[i].p_offset); \
648 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \ 818 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
649 dyn = DYN ## B (elf->data + offset); \ 819 dyn = DYN ## B (elf->data + offset); \
650 while (EGET(dyn->d_tag) != DT_NULL) { \ 820 while (EGET(dyn->d_tag) != DT_NULL) { \
651 if (EGET(dyn->d_tag) == DT_BIND_NOW || \ 821 if (EGET(dyn->d_tag) == DT_BIND_NOW || \
662 SHOW_BIND(32) 832 SHOW_BIND(32)
663 SHOW_BIND(64) 833 SHOW_BIND(64)
664 834
665 if (be_wewy_wewy_quiet) return NULL; 835 if (be_wewy_wewy_quiet) return NULL;
666 836
837 /* don't output anything if quiet mode and the ELF is static or not setuid */
667 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) { 838 if (be_quiet && (!dynamic || (!fstat(elf->fd, &s) && !(s.st_mode & (S_ISUID|S_ISGID))))) {
668 return NULL; 839 return NULL;
669 } else { 840 } else {
670 *found_bind = 1; 841 *found_bind = 1;
671 return (char *) "LAZY"; 842 return (char *) (dynamic ? "LAZY" : "STATIC");
672 } 843 }
673} 844}
674static char *scanelf_file_soname(elfobj *elf, char *found_soname) 845static char *scanelf_file_soname(elfobj *elf, char *found_soname)
675{ 846{
676 unsigned long i; 847 unsigned long i;
732#define FIND_SYM(B) \ 903#define FIND_SYM(B) \
733 if (elf->elf_class == ELFCLASS ## B) { \ 904 if (elf->elf_class == ELFCLASS ## B) { \
734 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ 905 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
735 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \ 906 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
736 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ 907 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
737 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \ 908 unsigned long cnt = EGET(symtab->sh_entsize); \
738 char *symname; \ 909 char *symname; \
910 if (cnt) \
911 cnt = EGET(symtab->sh_size) / cnt; \
739 for (i = 0; i < cnt; ++i) { \ 912 for (i = 0; i < cnt; ++i) { \
740 if (sym->st_name) { \ 913 if (sym->st_name) { \
914 /* make sure the symbol name is in acceptable memory range */ \
741 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \ 915 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
916 if ((void*)symname > (void*)elf->data_end) { \
917 warnf("%s: corrupt ELF symbols", elf->filename); \
918 ++sym; \
919 continue; \
920 } \
921 /* debug display ... show all symbols and some extra info */ \
742 if (*find_sym == '*') { \ 922 if (*ret == '*') { \
743 printf("%s(%s) %5lX %15s %s\n", \ 923 printf("%s(%s) %5lX %15s %s\n", \
744 ((*found_sym == 0) ? "\n\t" : "\t"), \ 924 ((*found_sym == 0) ? "\n\t" : "\t"), \
745 elf->base_filename, \ 925 elf->base_filename, \
746 (unsigned long)sym->st_size, \ 926 (unsigned long)sym->st_size, \
747 get_elfstttype(sym->st_info), \ 927 get_elfstttype(sym->st_info), \
748 symname); \ 928 symname); \
749 *found_sym = 1; \ 929 *found_sym = 1; \
750 } else { \ 930 } else { \
931 /* allow the user to specify a comma delimited list of symbols to search for */ \
751 char *this_sym, *next_sym; \ 932 char *this_sym, *this_sym_ver, *next_sym; \
933 this_sym = ret; \
752 this_sym = find_sym; \ 934 this_sym_ver = versioned_symname; \
753 do { \ 935 do { \
754 next_sym = strchr(this_sym, ','); \ 936 next_sym = strchr(this_sym, ','); \
755 if (next_sym == NULL) \ 937 if (next_sym == NULL) \
756 next_sym = this_sym + strlen(this_sym); \ 938 next_sym = this_sym + strlen(this_sym); \
939 /* do we want a defined symbol ? */ \
940 if (*this_sym == '+') { \
941 if (sym->st_shndx == SHN_UNDEF) \
942 goto skip_this_sym##B; \
943 ++this_sym; \
944 ++this_sym_ver; \
945 /* do we want an undefined symbol ? */ \
946 } else if (*this_sym == '-') { \
947 if (sym->st_shndx != SHN_UNDEF) \
948 goto skip_this_sym##B; \
949 ++this_sym; \
950 ++this_sym_ver; \
951 } \
952 /* ok, lets compare the name now */ \
757 if ((strncmp(this_sym, symname, (next_sym-this_sym)) == 0 && symname[next_sym-this_sym] == '\0') || \ 953 if ((strncmp(this_sym, symname, (next_sym-this_sym)) == 0 && symname[next_sym-this_sym] == '\0') || \
758 (strcmp(symname, versioned_symname) == 0)) { \ 954 (strncmp(this_sym_ver, symname, strlen(this_sym_ver)) == 0)) { \
955 if (be_semi_verbose) { \
956 char buf[126]; \
957 snprintf(buf, sizeof(buf), "%lX %s %s", \
958 (unsigned long)sym->st_size, get_elfstttype(sym->st_info), this_sym); \
959 ret = buf; \
960 } else \
759 ret = this_sym; \ 961 ret = this_sym; \
760 (*found_sym)++; \ 962 (*found_sym)++; \
761 goto break_out; \ 963 goto break_out; \
762 } \ 964 } \
763 this_sym = next_sym + 1; \ 965 skip_this_sym##B: this_sym = next_sym + 1; \
764 } while (*next_sym != '\0'); \ 966 } while (*next_sym != '\0'); \
765 } \ 967 } \
766 } \ 968 } \
767 ++sym; \ 969 ++sym; \
768 } } 970 } }
778 if (be_quiet) 980 if (be_quiet)
779 return NULL; 981 return NULL;
780 else 982 else
781 return (char *)" - "; 983 return (char *)" - ";
782} 984}
985
986
987static char *scanelf_file_sections(elfobj *elf, char *found_section)
988{
989 if (!find_section)
990 return NULL;
991
992#define FIND_SECTION(B) \
993 if (elf->elf_class == ELFCLASS ## B) { \
994 int invert; \
995 Elf ## B ## _Shdr *section; \
996 invert = (*find_section == '!' ? 1 : 0); \
997 section = SHDR ## B (elf_findsecbyname(elf, find_section+invert)); \
998 if ((section == NULL && invert) || (section != NULL && !invert)) \
999 *found_section = 1; \
1000 }
1001 FIND_SECTION(32)
1002 FIND_SECTION(64)
1003
1004 if (be_wewy_wewy_quiet)
1005 return NULL;
1006
1007 if (*found_section)
1008 return find_section;
1009
1010 if (be_quiet)
1011 return NULL;
1012 else
1013 return (char *)" - ";
1014}
1015
783/* scan an elf file and show all the fun stuff */ 1016/* scan an elf file and show all the fun stuff */
784#define prints(str) write(fileno(stdout), str, strlen(str)) 1017#define prints(str) write(fileno(stdout), str, strlen(str))
785static void scanelf_file(const char *filename) 1018static int scanelf_elfobj(elfobj *elf)
786{ 1019{
787 unsigned long i; 1020 unsigned long i;
788 char found_pax, found_phdr, found_relro, found_load, found_textrel, 1021 char found_pax, found_phdr, found_relro, found_load, found_textrel,
789 found_rpath, found_needed, found_interp, found_bind, found_soname, 1022 found_rpath, found_needed, found_interp, found_bind, found_soname,
790 found_sym, found_lib, found_file, found_textrels; 1023 found_sym, found_lib, found_file, found_textrels, found_section;
791 elfobj *elf;
792 struct stat st;
793 static char *out_buffer = NULL; 1024 static char *out_buffer = NULL;
794 static size_t out_len; 1025 static size_t out_len;
795 1026
796 /* make sure 'filename' exists */
797 if (lstat(filename, &st) == -1) {
798 if (be_verbose > 2) printf("%s: does not exist\n", filename);
799 return;
800 }
801 /* always handle regular files and handle symlinked files if no -y */
802 if (S_ISLNK(st.st_mode)) {
803 if (!scan_symlink) return;
804 stat(filename, &st);
805 }
806 if (!S_ISREG(st.st_mode)) {
807 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
808 return;
809 }
810
811 found_pax = found_phdr = found_relro = found_load = found_textrel = \ 1027 found_pax = found_phdr = found_relro = found_load = found_textrel = \
812 found_rpath = found_needed = found_interp = found_bind = found_soname = \ 1028 found_rpath = found_needed = found_interp = found_bind = found_soname = \
813 found_sym = found_lib = found_file = found_textrels = 0; 1029 found_sym = found_lib = found_file = found_textrels = found_section = 0;
814 1030
815 /* verify this is real ELF */
816 if ((elf = readelf(filename)) == NULL) {
817 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
818 return;
819 }
820
821 if (be_verbose > 1) 1031 if (be_verbose > 2)
822 printf("%s: scanning file {%s,%s}\n", filename, 1032 printf("%s: scanning file {%s,%s}\n", elf->filename,
823 get_elfeitype(EI_CLASS, elf->elf_class), 1033 get_elfeitype(EI_CLASS, elf->elf_class),
824 get_elfeitype(EI_DATA, elf->data[EI_DATA])); 1034 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
825 else if (be_verbose) 1035 else if (be_verbose > 1)
826 printf("%s: scanning file\n", filename); 1036 printf("%s: scanning file\n", elf->filename);
827 1037
828 /* init output buffer */ 1038 /* init output buffer */
829 if (!out_buffer) { 1039 if (!out_buffer) {
830 out_len = sizeof(char) * 80; 1040 out_len = sizeof(char) * 80;
831 out_buffer = (char*)xmalloc(out_len); 1041 out_buffer = (char*)xmalloc(out_len);
836 if (!be_quiet && show_banner) { 1046 if (!be_quiet && show_banner) {
837 for (i = 0; out_format[i]; ++i) { 1047 for (i = 0; out_format[i]; ++i) {
838 if (!IS_MODIFIER(out_format[i])) continue; 1048 if (!IS_MODIFIER(out_format[i])) continue;
839 1049
840 switch (out_format[++i]) { 1050 switch (out_format[++i]) {
1051 case '+': break;
841 case '%': break; 1052 case '%': break;
842 case '#': break; 1053 case '#': break;
843 case 'F': 1054 case 'F':
844 case 'p': 1055 case 'p':
845 case 'f': prints("FILE "); found_file = 1; break; 1056 case 'f': prints("FILE "); found_file = 1; break;
853 case 'b': prints("BIND "); break; 1064 case 'b': prints("BIND "); break;
854 case 'S': prints("SONAME "); break; 1065 case 'S': prints("SONAME "); break;
855 case 's': prints("SYM "); break; 1066 case 's': prints("SYM "); break;
856 case 'N': prints("LIB "); break; 1067 case 'N': prints("LIB "); break;
857 case 'T': prints("TEXTRELS "); break; 1068 case 'T': prints("TEXTRELS "); break;
1069 case 'k': prints("SECTION "); break;
858 default: warnf("'%c' has no title ?", out_format[i]); 1070 default: warnf("'%c' has no title ?", out_format[i]);
859 } 1071 }
860 } 1072 }
861 if (!found_file) prints("FILE "); 1073 if (!found_file) prints("FILE ");
862 prints("\n"); 1074 prints("\n");
867 /* dump all the good stuff */ 1079 /* dump all the good stuff */
868 for (i = 0; out_format[i]; ++i) { 1080 for (i = 0; out_format[i]; ++i) {
869 const char *out; 1081 const char *out;
870 const char *tmp; 1082 const char *tmp;
871 1083
872 /* make sure we trim leading spaces in quiet mode */
873 if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
874 *out_buffer = '\0';
875
876 if (!IS_MODIFIER(out_format[i])) { 1084 if (!IS_MODIFIER(out_format[i])) {
877 xchrcat(&out_buffer, out_format[i], &out_len); 1085 xchrcat(&out_buffer, out_format[i], &out_len);
878 continue; 1086 continue;
879 } 1087 }
880 1088
881 out = NULL; 1089 out = NULL;
882 be_wewy_wewy_quiet = (out_format[i] == '#'); 1090 be_wewy_wewy_quiet = (out_format[i] == '#');
1091 be_semi_verbose = (out_format[i] == '+');
883 switch (out_format[++i]) { 1092 switch (out_format[++i]) {
1093 case '+':
884 case '%': 1094 case '%':
885 case '#': 1095 case '#':
886 xchrcat(&out_buffer, out_format[i], &out_len); break; 1096 xchrcat(&out_buffer, out_format[i], &out_len); break;
887 case 'F': 1097 case 'F':
888 found_file = 1; 1098 found_file = 1;
889 if (be_wewy_wewy_quiet) break; 1099 if (be_wewy_wewy_quiet) break;
890 xstrcat(&out_buffer, filename, &out_len); 1100 xstrcat(&out_buffer, elf->filename, &out_len);
891 break; 1101 break;
892 case 'p': 1102 case 'p':
893 found_file = 1; 1103 found_file = 1;
894 if (be_wewy_wewy_quiet) break; 1104 if (be_wewy_wewy_quiet) break;
895 tmp = filename; 1105 tmp = elf->filename;
896 if (search_path) { 1106 if (search_path) {
897 ssize_t len_search = strlen(search_path); 1107 ssize_t len_search = strlen(search_path);
898 ssize_t len_file = strlen(filename); 1108 ssize_t len_file = strlen(elf->filename);
899 if (!strncmp(filename, search_path, len_search) && \ 1109 if (!strncmp(elf->filename, search_path, len_search) && \
900 len_file > len_search) 1110 len_file > len_search)
901 tmp += len_search; 1111 tmp += len_search;
902 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++; 1112 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
903 } 1113 }
904 xstrcat(&out_buffer, tmp, &out_len); 1114 xstrcat(&out_buffer, tmp, &out_len);
905 break; 1115 break;
906 case 'f': 1116 case 'f':
907 found_file = 1; 1117 found_file = 1;
908 if (be_wewy_wewy_quiet) break; 1118 if (be_wewy_wewy_quiet) break;
909 tmp = strrchr(filename, '/'); 1119 tmp = strrchr(elf->filename, '/');
910 tmp = (tmp == NULL ? filename : tmp+1); 1120 tmp = (tmp == NULL ? elf->filename : tmp+1);
911 xstrcat(&out_buffer, tmp, &out_len); 1121 xstrcat(&out_buffer, tmp, &out_len);
912 break; 1122 break;
913 case 'o': out = get_elfetype(elf); break; 1123 case 'o': out = get_elfetype(elf); break;
914 case 'x': out = scanelf_file_pax(elf, &found_pax); break; 1124 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
915 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break; 1125 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
920 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break; 1130 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
921 case 'i': out = scanelf_file_interp(elf, &found_interp); break; 1131 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
922 case 'b': out = scanelf_file_bind(elf, &found_bind); break; 1132 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
923 case 'S': out = scanelf_file_soname(elf, &found_soname); break; 1133 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
924 case 's': out = scanelf_file_sym(elf, &found_sym); break; 1134 case 's': out = scanelf_file_sym(elf, &found_sym); break;
1135 case 'k': out = scanelf_file_sections(elf, &found_section); break;
925 default: warnf("'%c' has no scan code?", out_format[i]); 1136 default: warnf("'%c' has no scan code?", out_format[i]);
926 } 1137 }
927 if (out) { 1138 if (out) {
928 /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */ 1139 /* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */
929 if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL) 1140 if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL)
934 } 1145 }
935 1146
936#define FOUND_SOMETHING() \ 1147#define FOUND_SOMETHING() \
937 (found_pax || found_phdr || found_relro || found_load || found_textrel || \ 1148 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
938 found_rpath || found_needed || found_interp || found_bind || \ 1149 found_rpath || found_needed || found_interp || found_bind || \
939 found_soname || found_sym || found_lib || found_textrels) 1150 found_soname || found_sym || found_lib || found_textrels || found_section )
940 1151
941 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) { 1152 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
942 xchrcat(&out_buffer, ' ', &out_len); 1153 xchrcat(&out_buffer, ' ', &out_len);
943 xstrcat(&out_buffer, filename, &out_len); 1154 xstrcat(&out_buffer, elf->filename, &out_len);
944 } 1155 }
945 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) { 1156 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
946 puts(out_buffer); 1157 puts(out_buffer);
947 fflush(stdout); 1158 fflush(stdout);
948 } 1159 }
949 1160
1161 return 0;
1162}
1163
1164/* scan a single elf */
1165static int scanelf_elf(const char *filename, int fd, size_t len)
1166{
1167 int ret = 1;
1168 elfobj *elf;
1169
1170 /* verify this is real ELF */
1171 if ((elf = _readelf_fd(filename, fd, len, !fix_elf)) == NULL) {
1172 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
1173 return ret;
1174 }
1175 switch (match_bits) {
1176 case 32:
1177 if (elf->elf_class != ELFCLASS32)
1178 goto label_done;
1179 break;
1180 case 64:
1181 if (elf->elf_class != ELFCLASS64)
1182 goto label_done;
1183 break;
1184 default: break;
1185 }
1186 if (strlen(match_etypes)) {
1187 char sbuf[126];
1188 strncpy(sbuf, match_etypes, sizeof(sbuf));
1189 if (strchr(match_etypes, ',') != NULL) {
1190 char *p;
1191 while((p = strrchr(sbuf, ',')) != NULL) {
1192 *p = 0;
1193 if (etype_lookup(p+1) == get_etype(elf))
1194 goto label_ret;
1195 }
1196 }
1197 if (etype_lookup(sbuf) != get_etype(elf))
1198 goto label_done;
1199 }
1200
1201label_ret:
1202 ret = scanelf_elfobj(elf);
1203
1204label_done:
950 unreadelf(elf); 1205 unreadelf(elf);
1206 return ret;
1207}
1208
1209/* scan an archive of elfs */
1210static int scanelf_archive(const char *filename, int fd, size_t len)
1211{
1212 archive_handle *ar;
1213 archive_member *m;
1214 char *ar_buffer;
1215 elfobj *elf;
1216
1217 ar = ar_open_fd(filename, fd);
1218 if (ar == NULL)
1219 return 1;
1220
1221 ar_buffer = (char*)mmap(0, len, PROT_READ | (fix_elf ? PROT_WRITE : 0), (fix_elf ? MAP_SHARED : MAP_PRIVATE), fd, 0);
1222 while ((m=ar_next(ar)) != NULL) {
1223 elf = readelf_buffer(m->name, ar_buffer+lseek(fd,0,SEEK_CUR), m->size);
1224 if (elf) {
1225 scanelf_elfobj(elf);
1226 unreadelf(elf);
1227 }
1228 }
1229 munmap(ar_buffer, len);
1230
1231 return 0;
1232}
1233/* scan a file which may be an elf or an archive or some other magical beast */
1234static int scanelf_file(const char *filename, const struct stat *st_cache)
1235{
1236 const struct stat *st = st_cache;
1237 struct stat symlink_st;
1238 int fd;
1239
1240 /* always handle regular files and handle symlinked files if no -y */
1241 if (S_ISLNK(st->st_mode)) {
1242 if (!scan_symlink) return 1;
1243 stat(filename, &symlink_st);
1244 st = &symlink_st;
1245 }
1246
1247 if (!S_ISREG(st->st_mode)) {
1248 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
1249 return 1;
1250 }
1251
1252 if ((fd=open(filename, (fix_elf ? O_RDWR : O_RDONLY))) == -1)
1253 return 1;
1254
1255 if (scanelf_elf(filename, fd, st->st_size) == 1 && scan_archives)
1256 /* if it isn't an ELF, maybe it's an .a archive */
1257 scanelf_archive(filename, fd, st->st_size);
1258
1259 close(fd);
1260 return 0;
951} 1261}
952 1262
953/* scan a directory for ET_EXEC files and print when we find one */ 1263/* scan a directory for ET_EXEC files and print when we find one */
954static void scanelf_dir(const char *path) 1264static int scanelf_dir(const char *path)
955{ 1265{
956 register DIR *dir; 1266 register DIR *dir;
957 register struct dirent *dentry; 1267 register struct dirent *dentry;
958 struct stat st_top, st; 1268 struct stat st_top, st;
959 char buf[_POSIX_PATH_MAX]; 1269 char buf[__PAX_UTILS_PATH_MAX];
960 size_t pathlen = 0, len = 0; 1270 size_t pathlen = 0, len = 0;
1271 int ret = 0;
961 1272
962 /* make sure path exists */ 1273 /* make sure path exists */
963 if (lstat(path, &st_top) == -1) { 1274 if (lstat(path, &st_top) == -1) {
964 if (be_verbose > 2) printf("%s: does not exist\n", path); 1275 if (be_verbose > 2) printf("%s: does not exist\n", path);
965 return; 1276 return 1;
966 } 1277 }
967 1278
968 /* ok, if it isn't a directory, assume we can open it */ 1279 /* ok, if it isn't a directory, assume we can open it */
969 if (!S_ISDIR(st_top.st_mode)) { 1280 if (!S_ISDIR(st_top.st_mode)) {
970 scanelf_file(path); 1281 return scanelf_file(path, &st_top);
971 return;
972 } 1282 }
973 1283
974 /* now scan the dir looking for fun stuff */ 1284 /* now scan the dir looking for fun stuff */
975 if ((dir = opendir(path)) == NULL) { 1285 if ((dir = opendir(path)) == NULL) {
976 warnf("could not opendir %s: %s", path, strerror(errno)); 1286 warnf("could not opendir %s: %s", path, strerror(errno));
977 return; 1287 return 1;
978 } 1288 }
979 if (be_verbose) printf("%s: scanning dir\n", path); 1289 if (be_verbose > 1) printf("%s: scanning dir\n", path);
980 1290
981 pathlen = strlen(path); 1291 pathlen = strlen(path);
982 while ((dentry = readdir(dir))) { 1292 while ((dentry = readdir(dir))) {
983 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) 1293 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
984 continue; 1294 continue;
986 if (len >= sizeof(buf)) { 1296 if (len >= sizeof(buf)) {
987 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path, 1297 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
988 (unsigned long)len, (unsigned long)sizeof(buf)); 1298 (unsigned long)len, (unsigned long)sizeof(buf));
989 continue; 1299 continue;
990 } 1300 }
991 sprintf(buf, "%s/%s", path, dentry->d_name); 1301 snprintf(buf, sizeof(buf), "%s%s%s", path, (path[pathlen-1] == '/') ? "" : "/", dentry->d_name);
992 if (lstat(buf, &st) != -1) { 1302 if (lstat(buf, &st) != -1) {
993 if (S_ISREG(st.st_mode)) 1303 if (S_ISREG(st.st_mode))
994 scanelf_file(buf); 1304 ret = scanelf_file(buf, &st);
995 else if (dir_recurse && S_ISDIR(st.st_mode)) { 1305 else if (dir_recurse && S_ISDIR(st.st_mode)) {
996 if (dir_crossmount || (st_top.st_dev == st.st_dev)) 1306 if (dir_crossmount || (st_top.st_dev == st.st_dev))
997 scanelf_dir(buf); 1307 ret = scanelf_dir(buf);
998 } 1308 }
999 } 1309 }
1000 } 1310 }
1001 closedir(dir); 1311 closedir(dir);
1312 return ret;
1002} 1313}
1003 1314
1004static int scanelf_from_file(char *filename) 1315static int scanelf_from_file(const char *filename)
1005{ 1316{
1006 FILE *fp = NULL; 1317 FILE *fp = NULL;
1007 char *p; 1318 char *p;
1008 char path[_POSIX_PATH_MAX]; 1319 char path[__PAX_UTILS_PATH_MAX];
1320 int ret = 0;
1009 1321
1010 if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL)) 1322 if (strcmp(filename, "-") == 0)
1011 fp = stdin; 1323 fp = stdin;
1012 else if ((fp = fopen(filename, "r")) == NULL) 1324 else if ((fp = fopen(filename, "r")) == NULL)
1013 return 1; 1325 return 1;
1014 1326
1015 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) { 1327 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1016 if ((p = strchr(path, '\n')) != NULL) 1328 if ((p = strchr(path, '\n')) != NULL)
1017 *p = 0; 1329 *p = 0;
1018 search_path = path; 1330 search_path = path;
1019 scanelf_dir(path); 1331 ret = scanelf_dir(path);
1020 } 1332 }
1021 if (fp != stdin) 1333 if (fp != stdin)
1022 fclose(fp); 1334 fclose(fp);
1023 return 0; 1335 return ret;
1024} 1336}
1025 1337
1026static void load_ld_so_conf() 1338#if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__)
1339
1340static int load_ld_cache_config(int i, const char *fname)
1027{ 1341{
1028 FILE *fp = NULL; 1342 FILE *fp = NULL;
1029 char *p; 1343 char *p;
1030 char path[_POSIX_PATH_MAX]; 1344 char path[__PAX_UTILS_PATH_MAX];
1031 int i = 0;
1032 1345
1033 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL) 1346 if (i + 1 == ARRAY_SIZE(ldpaths))
1034 return; 1347 return i;
1035 1348
1349 if ((fp = fopen(fname, "r")) == NULL)
1350 return i;
1351
1036 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) { 1352 while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) {
1037 if (*path != '/')
1038 continue;
1039
1040 if ((p = strrchr(path, '\r')) != NULL) 1353 if ((p = strrchr(path, '\r')) != NULL)
1041 *p = 0; 1354 *p = 0;
1042 if ((p = strchr(path, '\n')) != NULL) 1355 if ((p = strchr(path, '\n')) != NULL)
1043 *p = 0; 1356 *p = 0;
1357#ifdef __linux__
1358 // recursive includes of the same file will make this segfault.
1359 if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) {
1360 glob64_t gl;
1361 size_t x;
1362 char gpath[__PAX_UTILS_PATH_MAX];
1363
1364 memset(gpath, 0, sizeof(gpath));
1365
1366 if (path[8] != '/')
1367 snprintf(gpath, sizeof(gpath), "/etc/%s", &path[8]);
1368 else
1369 strncpy(gpath, &path[8], sizeof(gpath));
1370
1371 if ((glob64(gpath, 0, NULL, &gl)) == 0) {
1372 for (x = 0; x < gl.gl_pathc; ++x) {
1373 /* try to avoid direct loops */
1374 if (strcmp(gl.gl_pathv[x], fname) == 0)
1375 continue;
1376 i = load_ld_cache_config(i, gl.gl_pathv[x]);
1377 if (i + 1 >= ARRAY_SIZE(ldpaths)) {
1378 globfree64(&gl);
1379 return i;
1380 }
1381 }
1382 globfree64 (&gl);
1383 continue;
1384 }
1385 }
1386#endif
1387 if (*path != '/')
1388 continue;
1044 1389
1045 ldpaths[i++] = xstrdup(path); 1390 ldpaths[i++] = xstrdup(path);
1046 1391
1047 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths)) 1392 if (i + 1 == ARRAY_SIZE(ldpaths))
1048 break; 1393 break;
1049 } 1394 }
1050 ldpaths[i] = NULL; 1395 ldpaths[i] = NULL;
1051 1396
1052 fclose(fp); 1397 fclose(fp);
1398 return i;
1053} 1399}
1400
1401#elif defined(__FreeBSD__) || (__DragonFly__)
1402
1403static int load_ld_cache_config(int i, const char *fname)
1404{
1405 FILE *fp = NULL;
1406 char *b = NULL, *p;
1407 struct elfhints_hdr hdr;
1408
1409 if (i + 1 == ARRAY_SIZE(ldpaths))
1410 return i;
1411
1412 if ((fp = fopen(fname, "r")) == NULL)
1413 return i;
1414
1415 if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) ||
1416 hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 ||
1417 fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1)
1418 {
1419 fclose(fp);
1420 return i;
1421 }
1422
1423 b = (char*)malloc(hdr.dirlistlen+1);
1424 if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) {
1425 fclose(fp);
1426 free(b);
1427 return i;
1428 }
1429
1430 while ((p = strsep(&b, ":"))) {
1431 if (*p == '\0') continue;
1432 ldpaths[i++] = xstrdup(p);
1433
1434 if (i + 1 == ARRAY_SIZE(ldpaths))
1435 break;
1436 }
1437 ldpaths[i] = NULL;
1438
1439 free(b);
1440 fclose(fp);
1441 return i;
1442}
1443
1444#else
1445
1446#warning Cache config support not implemented for your target
1447static int load_ld_cache_config(int i, const char *fname)
1448{
1449 memset(ldpaths, 0x00, sizeof(ldpaths));
1450}
1451
1452#endif
1054 1453
1055/* scan /etc/ld.so.conf for paths */ 1454/* scan /etc/ld.so.conf for paths */
1056static void scanelf_ldpath() 1455static void scanelf_ldpath()
1057{ 1456{
1058 char scan_l, scan_ul, scan_ull; 1457 char scan_l, scan_ul, scan_ull;
1092 } 1491 }
1093 1492
1094 free(path); 1493 free(path);
1095} 1494}
1096 1495
1097
1098/* usage / invocation handling functions */ 1496/* usage / invocation handling functions */
1099#define PARSE_FLAGS "plRmyxetrnLibSs:gN:TaqvF:f:o:BhV" 1497#define PARSE_FLAGS "plRmyAXz:xetrnLibSs:k:gN:TaqvF:f:o:E:M:BhV"
1100#define a_argument required_argument 1498#define a_argument required_argument
1101static struct option const long_opts[] = { 1499static struct option const long_opts[] = {
1102 {"path", no_argument, NULL, 'p'}, 1500 {"path", no_argument, NULL, 'p'},
1103 {"ldpath", no_argument, NULL, 'l'}, 1501 {"ldpath", no_argument, NULL, 'l'},
1104 {"recursive", no_argument, NULL, 'R'}, 1502 {"recursive", no_argument, NULL, 'R'},
1105 {"mount", no_argument, NULL, 'm'}, 1503 {"mount", no_argument, NULL, 'm'},
1106 {"symlink", no_argument, NULL, 'y'}, 1504 {"symlink", no_argument, NULL, 'y'},
1505 {"archives", no_argument, NULL, 'A'},
1506 {"ldcache", no_argument, NULL, 'L'},
1507 {"fix", no_argument, NULL, 'X'},
1508 {"setpax", a_argument, NULL, 'z'},
1107 {"pax", no_argument, NULL, 'x'}, 1509 {"pax", no_argument, NULL, 'x'},
1108 {"header", no_argument, NULL, 'e'}, 1510 {"header", no_argument, NULL, 'e'},
1109 {"textrel", no_argument, NULL, 't'}, 1511 {"textrel", no_argument, NULL, 't'},
1110 {"rpath", no_argument, NULL, 'r'}, 1512 {"rpath", no_argument, NULL, 'r'},
1111 {"needed", no_argument, NULL, 'n'}, 1513 {"needed", no_argument, NULL, 'n'},
1112 {"ldcache", no_argument, NULL, 'L'},
1113 {"interp", no_argument, NULL, 'i'}, 1514 {"interp", no_argument, NULL, 'i'},
1114 {"bind", no_argument, NULL, 'b'}, 1515 {"bind", no_argument, NULL, 'b'},
1115 {"soname", no_argument, NULL, 'S'}, 1516 {"soname", no_argument, NULL, 'S'},
1116 {"symbol", a_argument, NULL, 's'}, 1517 {"symbol", a_argument, NULL, 's'},
1518 {"section", a_argument, NULL, 'k'},
1117 {"lib", a_argument, NULL, 'N'}, 1519 {"lib", a_argument, NULL, 'N'},
1118 {"gmatch", no_argument, NULL, 'g'}, 1520 {"gmatch", no_argument, NULL, 'g'},
1119 {"textrels", no_argument, NULL, 'T'}, 1521 {"textrels", no_argument, NULL, 'T'},
1522 {"etype", a_argument, NULL, 'E'},
1523 {"bits", a_argument, NULL, 'M'},
1120 {"all", no_argument, NULL, 'a'}, 1524 {"all", no_argument, NULL, 'a'},
1121 {"quiet", no_argument, NULL, 'q'}, 1525 {"quiet", no_argument, NULL, 'q'},
1122 {"verbose", no_argument, NULL, 'v'}, 1526 {"verbose", no_argument, NULL, 'v'},
1123 {"format", a_argument, NULL, 'F'}, 1527 {"format", a_argument, NULL, 'F'},
1124 {"from", a_argument, NULL, 'f'}, 1528 {"from", a_argument, NULL, 'f'},
1132static const char *opts_help[] = { 1536static const char *opts_help[] = {
1133 "Scan all directories in PATH environment", 1537 "Scan all directories in PATH environment",
1134 "Scan all directories in /etc/ld.so.conf", 1538 "Scan all directories in /etc/ld.so.conf",
1135 "Scan directories recursively", 1539 "Scan directories recursively",
1136 "Don't recursively cross mount points", 1540 "Don't recursively cross mount points",
1137 "Don't scan symlinks\n", 1541 "Don't scan symlinks",
1542 "Scan archives (.a files)",
1543 "Utilize ld.so.cache information (use with -r/-n)",
1544 "Try and 'fix' bad things (use with -r/-e)",
1545 "Sets EI_PAX/PT_PAX_FLAGS to <arg> (use with -Xx)\n",
1138 "Print PaX markings", 1546 "Print PaX markings",
1139 "Print GNU_STACK/PT_LOAD markings", 1547 "Print GNU_STACK/PT_LOAD markings",
1140 "Print TEXTREL information", 1548 "Print TEXTREL information",
1141 "Print RPATH information", 1549 "Print RPATH information",
1142 "Print NEEDED information", 1550 "Print NEEDED information",
1143 "Resolve NEEDED information (use with -n)",
1144 "Print INTERP information", 1551 "Print INTERP information",
1145 "Print BIND information", 1552 "Print BIND information",
1146 "Print SONAME information", 1553 "Print SONAME information",
1147 "Find a specified symbol", 1554 "Find a specified symbol",
1555 "Find a specified section",
1148 "Find a specified library", 1556 "Find a specified library",
1149 "Use strncmp to match libraries. (use with -N)", 1557 "Use strncmp to match libraries. (use with -N)",
1150 "Locate cause of TEXTREL", 1558 "Locate cause of TEXTREL",
1559 "Print only ELF files matching etype ET_DYN,ET_EXEC ...",
1560 "Print only ELF files matching numeric bits",
1151 "Print all scanned info (-x -e -t -r -b)\n", 1561 "Print all scanned info (-x -e -t -r -b)\n",
1152 "Only output 'bad' things", 1562 "Only output 'bad' things",
1153 "Be verbose (can be specified more than once)", 1563 "Be verbose (can be specified more than once)",
1154 "Use specified format for output", 1564 "Use specified format for output",
1155 "Read input stream from a filename", 1565 "Read input stream from a filename",
1163/* display usage and exit */ 1573/* display usage and exit */
1164static void usage(int status) 1574static void usage(int status)
1165{ 1575{
1166 unsigned long i; 1576 unsigned long i;
1167 printf("* Scan ELF binaries for stuff\n\n" 1577 printf("* Scan ELF binaries for stuff\n\n"
1168 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0); 1578 "Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0);
1169 printf("Options: -[%s]\n", PARSE_FLAGS); 1579 printf("Options: -[%s]\n", PARSE_FLAGS);
1170 for (i = 0; long_opts[i].name; ++i) 1580 for (i = 0; long_opts[i].name; ++i)
1171 if (long_opts[i].has_arg == no_argument) 1581 if (long_opts[i].has_arg == no_argument)
1172 printf(" -%c, --%-13s* %s\n", long_opts[i].val, 1582 printf(" -%c, --%-14s* %s\n", long_opts[i].val,
1173 long_opts[i].name, opts_help[i]); 1583 long_opts[i].name, opts_help[i]);
1174 else 1584 else
1175 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val, 1585 printf(" -%c, --%-7s <arg> * %s\n", long_opts[i].val,
1176 long_opts[i].name, opts_help[i]); 1586 long_opts[i].name, opts_help[i]);
1177 1587
1178 if (status != EXIT_SUCCESS) 1588 if (status != EXIT_SUCCESS)
1179 exit(status); 1589 exit(status);
1180 1590
1181 puts("\nThe format modifiers for the -F option are:"); 1591 puts("\nThe format modifiers for the -F option are:");
1182 puts(" F Filename \tx PaX Flags \te STACK/RELRO"); 1592 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1183 puts(" t TEXTREL \tr RPATH \tn NEEDED"); 1593 puts(" t TEXTREL \tr RPATH \tn NEEDED");
1184 puts(" i INTERP \tb BIND \ts symbol"); 1594 puts(" i INTERP \tb BIND \ts symbol");
1185 puts(" N library \to Type \tT TEXTRELs"); 1595 puts(" N library \to Type \tT TEXTRELs");
1186 puts(" S SONAME"); 1596 puts(" S SONAME \tk section");
1187 puts(" p filename (with search path removed)"); 1597 puts(" p filename (with search path removed)");
1188 puts(" f filename (short name/basename)"); 1598 puts(" f filename (short name/basename)");
1189 puts("Prefix each modifier with '%' (verbose) or '#' (silent)"); 1599 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1190 1600
1601 puts("\nELF Etypes:");
1602 print_etypes(stdout);
1603
1191 exit(status); 1604 exit(status);
1192} 1605}
1193 1606
1194/* parse command line arguments and preform needed actions */ 1607/* parse command line arguments and preform needed actions */
1195static void parseargs(int argc, char *argv[]) 1608static int parseargs(int argc, char *argv[])
1196{ 1609{
1197 int i; 1610 int i;
1198 char *from_file = NULL; 1611 const char *from_file = NULL;
1612 int ret = 0;
1199 1613
1200 opterr = 0; 1614 opterr = 0;
1201 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { 1615 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
1202 switch (i) { 1616 switch (i) {
1203 1617
1207 VERSION, __FILE__, __DATE__, rcsid, argv0); 1621 VERSION, __FILE__, __DATE__, rcsid, argv0);
1208 exit(EXIT_SUCCESS); 1622 exit(EXIT_SUCCESS);
1209 break; 1623 break;
1210 case 'h': usage(EXIT_SUCCESS); break; 1624 case 'h': usage(EXIT_SUCCESS); break;
1211 case 'f': 1625 case 'f':
1212 if (from_file) err("Don't specify -f twice"); 1626 if (from_file) warn("You prob don't want to specify -f twice");
1213 from_file = xstrdup(optarg); 1627 from_file = optarg;
1628 break;
1629 case 'E':
1630 strncpy(match_etypes, optarg, sizeof(match_etypes));
1631 break;
1632 case 'M':
1633 match_bits = atoi(optarg);
1214 break; 1634 break;
1215 case 'o': { 1635 case 'o': {
1216 FILE *fp = NULL;
1217 if ((fp = freopen(optarg, "w", stdout)) == NULL) 1636 if (freopen(optarg, "w", stdout) == NULL)
1218 err("Could not open output stream '%s': %s", optarg, strerror(errno)); 1637 err("Could not open output stream '%s': %s", optarg, strerror(errno));
1219 SET_STDOUT(fp);
1220 break; 1638 break;
1221 } 1639 }
1222 1640 case 'k':
1641 if (find_section) warn("You prob don't want to specify -k twice");
1642 find_section = optarg;
1643 break;
1223 case 's': { 1644 case 's': {
1224 if (find_sym) warn("You prob don't want to specify -s twice"); 1645 if (find_sym) warn("You prob don't want to specify -s twice");
1225 find_sym = optarg; 1646 find_sym = optarg;
1226 versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1)); 1647 versioned_symname = (char*)xmalloc(sizeof(char) * (strlen(find_sym)+1+1));
1227 sprintf(versioned_symname, "%s@", find_sym); 1648 sprintf(versioned_symname, "%s@", find_sym);
1236 case 'F': { 1657 case 'F': {
1237 if (out_format) warn("You prob don't want to specify -F twice"); 1658 if (out_format) warn("You prob don't want to specify -F twice");
1238 out_format = optarg; 1659 out_format = optarg;
1239 break; 1660 break;
1240 } 1661 }
1662 case 'z': {
1663 unsigned long flags = (PF_NOEMUTRAMP | PF_NORANDEXEC);
1664 size_t x;
1241 1665
1242 case 'g': gmatch = 1; /* break; any reason we dont breal; here ? */ 1666 for (x = 0 ; x < strlen(optarg); x++) {
1667 switch(optarg[x]) {
1668 case 'p':
1669 case 'P':
1670 do_state(optarg[x], PAGEEXEC);
1671 break;
1672 case 's':
1673 case 'S':
1674 do_state(optarg[x], SEGMEXEC);
1675 break;
1676 case 'm':
1677 case 'M':
1678 do_state(optarg[x], MPROTECT);
1679 break;
1680 case 'e':
1681 case 'E':
1682 do_state(optarg[x], EMUTRAMP);
1683 break;
1684 case 'r':
1685 case 'R':
1686 do_state(optarg[x], RANDMMAP);
1687 break;
1688 case 'x':
1689 case 'X':
1690 do_state(optarg[x], RANDEXEC);
1691 break;
1692 default:
1693 break;
1694 }
1695 }
1696 if (!(((flags & PF_PAGEEXEC) && (flags & PF_NOPAGEEXEC)) ||
1697 ((flags & PF_SEGMEXEC) && (flags & PF_NOSEGMEXEC)) ||
1698 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP)) ||
1699 ((flags & PF_RANDEXEC) && (flags & PF_NORANDEXEC)) ||
1700 ((flags & PF_EMUTRAMP) && (flags & PF_NOEMUTRAMP)) ||
1701 ((flags & PF_RANDMMAP) && (flags & PF_NORANDMMAP))))
1702 setpax = flags;
1703 break;
1704 }
1705 case 'g': gmatch = 1; break;
1243 case 'L': printcache = 1; break; 1706 case 'L': use_ldcache = 1; break;
1244 case 'y': scan_symlink = 0; break; 1707 case 'y': scan_symlink = 0; break;
1708 case 'A': scan_archives = 1; break;
1245 case 'B': show_banner = 0; break; 1709 case 'B': show_banner = 0; break;
1246 case 'l': scan_ldpath = 1; break; 1710 case 'l': scan_ldpath = 1; break;
1247 case 'p': scan_envpath = 1; break; 1711 case 'p': scan_envpath = 1; break;
1248 case 'R': dir_recurse = 1; break; 1712 case 'R': dir_recurse = 1; break;
1249 case 'm': dir_crossmount = 0; break; 1713 case 'm': dir_crossmount = 0; break;
1714 case 'X': ++fix_elf; break;
1250 case 'x': show_pax = 1; break; 1715 case 'x': show_pax = 1; break;
1251 case 'e': show_phdr = 1; break; 1716 case 'e': show_phdr = 1; break;
1252 case 't': show_textrel = 1; break; 1717 case 't': show_textrel = 1; break;
1253 case 'r': show_rpath = 1; break; 1718 case 'r': show_rpath = 1; break;
1254 case 'n': show_needed = 1; break; 1719 case 'n': show_needed = 1; break;
1259 case 'q': be_quiet = 1; break; 1724 case 'q': be_quiet = 1; break;
1260 case 'v': be_verbose = (be_verbose % 20) + 1; break; 1725 case 'v': be_verbose = (be_verbose % 20) + 1; break;
1261 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break; 1726 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
1262 1727
1263 case ':': 1728 case ':':
1264 err("Option missing parameter\n"); 1729 err("Option '%c' is missing parameter", optopt);
1265 case '?': 1730 case '?':
1266 err("Unknown option\n"); 1731 err("Unknown option '%c' or argument missing", optopt);
1267 default: 1732 default:
1268 err("Unhandled option '%c'", i); 1733 err("Unhandled option '%c'; please report this", i);
1269 } 1734 }
1270 } 1735 }
1271 1736
1272 /* let the format option override all other options */ 1737 /* let the format option override all other options */
1273 if (out_format) { 1738 if (out_format) {
1276 show_textrels = 0; 1741 show_textrels = 0;
1277 for (i = 0; out_format[i]; ++i) { 1742 for (i = 0; out_format[i]; ++i) {
1278 if (!IS_MODIFIER(out_format[i])) continue; 1743 if (!IS_MODIFIER(out_format[i])) continue;
1279 1744
1280 switch (out_format[++i]) { 1745 switch (out_format[++i]) {
1746 case '+': break;
1281 case '%': break; 1747 case '%': break;
1282 case '#': break; 1748 case '#': break;
1283 case 'F': break; 1749 case 'F': break;
1284 case 'p': break; 1750 case 'p': break;
1285 case 'f': break; 1751 case 'f': break;
1752 case 'k': break;
1286 case 's': break; 1753 case 's': break;
1287 case 'N': break; 1754 case 'N': break;
1288 case 'o': break; 1755 case 'o': break;
1289 case 'x': show_pax = 1; break; 1756 case 'x': show_pax = 1; break;
1290 case 'e': show_phdr = 1; break; 1757 case 'e': show_phdr = 1; break;
1294 case 'i': show_interp = 1; break; 1761 case 'i': show_interp = 1; break;
1295 case 'b': show_bind = 1; break; 1762 case 'b': show_bind = 1; break;
1296 case 'S': show_soname = 1; break; 1763 case 'S': show_soname = 1; break;
1297 case 'T': show_textrels = 1; break; 1764 case 'T': show_textrels = 1; break;
1298 default: 1765 default:
1299 err("Invalid format specifier '%c' (byte %i)", 1766 err("Invalid format specifier '%c' (byte %i)",
1300 out_format[i], i+1); 1767 out_format[i], i+1);
1301 } 1768 }
1302 } 1769 }
1303 1770
1304 /* construct our default format */ 1771 /* construct our default format */
1314 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); 1781 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1315 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len); 1782 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1316 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len); 1783 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1317 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len); 1784 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1318 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len); 1785 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1786 if (find_section) xstrcat(&out_format, "%k ", &fmt_len);
1319 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len); 1787 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1320 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len); 1788 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1321 } 1789 }
1322 if (be_verbose > 2) printf("Format: %s\n", out_format); 1790 if (be_verbose > 2) printf("Format: %s\n", out_format);
1323 1791
1324 /* now lets actually do the scanning */ 1792 /* now lets actually do the scanning */
1325 if (scan_ldpath || (show_rpath && be_quiet)) 1793 if (scan_ldpath || use_ldcache)
1326 load_ld_so_conf(); 1794 load_ld_cache_config(0, __PAX_UTILS_DEFAULT_LD_CACHE_CONFIG);
1327 if (scan_ldpath) scanelf_ldpath(); 1795 if (scan_ldpath) scanelf_ldpath();
1328 if (scan_envpath) scanelf_envpath(); 1796 if (scan_envpath) scanelf_envpath();
1797 if (!from_file && optind == argc && ttyname(0) == NULL && !scan_ldpath && !scan_envpath)
1798 from_file = "-";
1329 if (from_file) { 1799 if (from_file) {
1330 scanelf_from_file(from_file); 1800 scanelf_from_file(from_file);
1331 free(from_file);
1332 from_file = *argv; 1801 from_file = *argv;
1333 } 1802 }
1334 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file) 1803 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
1335 err("Nothing to scan !?"); 1804 err("Nothing to scan !?");
1336 while (optind < argc) { 1805 while (optind < argc) {
1337 search_path = argv[optind++]; 1806 search_path = argv[optind++];
1338 scanelf_dir(search_path); 1807 ret = scanelf_dir(search_path);
1339 } 1808 }
1340 1809
1341 /* clean up */ 1810 /* clean up */
1342 if (versioned_symname) free(versioned_symname); 1811 free(versioned_symname);
1343 for (i = 0; ldpaths[i]; ++i) 1812 for (i = 0; ldpaths[i]; ++i)
1344 free(ldpaths[i]); 1813 free(ldpaths[i]);
1345 1814
1346 if (ldcache != 0) 1815 if (ldcache != 0)
1347 munmap(ldcache, ldcache_size); 1816 munmap(ldcache, ldcache_size);
1817 return ret;
1818}
1819
1820static char **get_split_env(const char *envvar)
1821{
1822 const char *delims = " \t\n";
1823 char **envvals = NULL;
1824 char *env, *s;
1825 int nentry;
1826
1827 if ((env = getenv(envvar)) == NULL)
1828 return NULL;
1829
1830 env = xstrdup(env);
1831 if (env == NULL)
1832 return NULL;
1833
1834 s = strtok(env, delims);
1835 if (s == NULL) {
1836 free(env);
1837 return NULL;
1838 }
1839
1840 nentry = 0;
1841 while (s != NULL) {
1842 ++nentry;
1843 envvals = xrealloc(envvals, sizeof(*envvals) * (nentry+1));
1844 envvals[nentry-1] = s;
1845 s = strtok(NULL, delims);
1846 }
1847 envvals[nentry] = NULL;
1848
1849 /* don't want to free(env) as it contains the memory that backs
1850 * the envvals array of strings */
1851 return envvals;
1852}
1853static void parseenv()
1854{
1855 qa_textrels = get_split_env("QA_TEXTRELS");
1856 qa_execstack = get_split_env("QA_EXECSTACK");
1857 qa_wx_load = get_split_env("QA_WX_LOAD");
1858}
1859
1860#ifdef __PAX_UTILS_CLEANUP
1861static void cleanup()
1862{
1863 free(out_format);
1864 free(qa_textrels);
1865 free(qa_execstack);
1866 free(qa_wx_load);
1867}
1868#endif
1869
1870
1871int main(int argc, char *argv[])
1872{
1873 int ret;
1874 if (argc < 2)
1875 usage(EXIT_FAILURE);
1876 parseenv();
1877 ret = parseargs(argc, argv);
1878 fclose(stdout);
1879#ifdef __PAX_UTILS_CLEANUP
1880 cleanup();
1881 warn("The calls to add/delete heap should be off:\n"
1882 "\t- 1 due to the out_buffer not being freed in scanelf_file()\n"
1883 "\t- 1 per QA_TEXTRELS/QA_EXECSTACK/QA_WX_LOAD");
1884#endif
1885 return ret;
1348} 1886}
1349 1887
1350 1888
1351 1889
1352/* utility funcs */ 1890/* utility funcs */
1358} 1896}
1359static void *xmalloc(size_t size) 1897static void *xmalloc(size_t size)
1360{ 1898{
1361 void *ret = malloc(size); 1899 void *ret = malloc(size);
1362 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size); 1900 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1901 return ret;
1902}
1903static void *xrealloc(void *ptr, size_t size)
1904{
1905 void *ret = realloc(ptr, size);
1906 if (!ret) err("Could not realloc() %li bytes", (unsigned long)size);
1363 return ret; 1907 return ret;
1364} 1908}
1365static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n) 1909static void xstrncat(char **dst, const char *src, size_t *curr_len, size_t n)
1366{ 1910{
1367 size_t new_len; 1911 size_t new_len;
1385 my_app[0] = append; 1929 my_app[0] = append;
1386 my_app[1] = '\0'; 1930 my_app[1] = '\0';
1387 xstrcat(dst, my_app, curr_len); 1931 xstrcat(dst, my_app, curr_len);
1388} 1932}
1389 1933
1390 1934/* Match filename against entries in matchlist, return TRUE
1391 1935 * if the file is listed */
1392int main(int argc, char *argv[]) 1936static int file_matches_list(const char *filename, char **matchlist)
1393{ 1937{
1394 if (argc < 2) 1938 char **file;
1395 usage(EXIT_FAILURE); 1939 char *match;
1396 parseargs(argc, argv); 1940 char buf[__PAX_UTILS_PATH_MAX];
1397 fclose(stdout); 1941
1398#ifdef __BOUNDS_CHECKING_ON 1942 if (matchlist == NULL)
1399 warn("The calls to add/delete heap should be off by 1 due to the out_buffer not being freed in scanelf_file()"); 1943 return 0;
1400#endif 1944
1401 return EXIT_SUCCESS; 1945 for (file = matchlist; *file != NULL; file++) {
1946 if (search_path) {
1947 snprintf(buf, sizeof(buf), "%s%s", search_path, *file);
1948 match = buf;
1949 } else {
1950 match = *file;
1951 }
1952 if (fnmatch(match, filename, 0) == 0)
1953 return 1;
1954 }
1955 return 0;
1402} 1956}

Legend:
Removed from v.1.96  
changed lines
  Added in v.1.164

  ViewVC Help
Powered by ViewVC 1.1.20