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

Legend:
Removed from v.1.127  
changed lines
  Added in v.1.276

  ViewVC Help
Powered by ViewVC 1.1.20