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

Legend:
Removed from v.1.44  
changed lines
  Added in v.1.227

  ViewVC Help
Powered by ViewVC 1.1.20