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

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

  ViewVC Help
Powered by ViewVC 1.1.20