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

Legend:
Removed from v.1.88  
changed lines
  Added in v.1.163

  ViewVC Help
Powered by ViewVC 1.1.20