/[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.35 Revision 1.90
1/* 1/*
2 * Copyright 2003 Ned Ludd <solar@gentoo.org>
3 * Copyright 1999-2005 Gentoo Foundation 2 * Copyright 2003-2005 Gentoo Foundation
4 * Distributed under the terms of the GNU General Public License v2 3 * Distributed under the terms of the GNU General Public License v2
5 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.35 2005/04/14 00:17:30 solar Exp $ 4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/Attic/scanelf.c,v 1.90 2005/12/04 18:12:44 vapier Exp $
6 * 5 *
7 ******************************************************************** 6 * Copyright 2003-2005 Ned Ludd - <solar@gentoo.org>
8 * This program is free software; you can redistribute it and/or 7 * Copyright 2004-2005 Mike Frysinger - <vapier@gentoo.org>
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21 * MA 02111-1307, USA.
22 */ 8 */
23 9
24#include <stdio.h> 10#include <stdio.h>
25#include <stdlib.h> 11#include <stdlib.h>
26#include <sys/types.h> 12#include <sys/types.h>
13#include <libgen.h>
14#include <limits.h>
27#define __USE_GNU 15#define __USE_GNU
28#include <string.h> 16#include <string.h>
29#include <errno.h> 17#include <errno.h>
30#include <unistd.h> 18#include <unistd.h>
31#include <sys/stat.h> 19#include <sys/stat.h>
32#include <dirent.h> 20#include <dirent.h>
33#include <getopt.h> 21#include <getopt.h>
34#include <assert.h> 22#include <assert.h>
35
36#include "paxelf.h" 23#include "paxinc.h"
37 24
38static const char *rcsid = "$Id: scanelf.c,v 1.35 2005/04/14 00:17:30 solar Exp $"; 25static const char *rcsid = "$Id: scanelf.c,v 1.90 2005/12/04 18:12:44 vapier Exp $";
26#define argv0 "scanelf"
39 27
40 28#define IS_MODIFIER(c) (c == '%' || c == '#')
41/* helper functions for showing errors */
42#define argv0 "scanelf" /*((*argv != NULL) ? argv[0] : __FILE__ "\b\b")*/
43#define warn(fmt, args...) \
44 fprintf(stderr, "%s: " fmt "\n", argv0, ## args)
45#define warnf(fmt, args...) warn("%s(): " fmt, __FUNCTION__, ## args)
46#define err(fmt, args...) \
47 do { \
48 warn(fmt, ## args); \
49 exit(EXIT_FAILURE); \
50 } while (0)
51 29
52 30
53 31
54/* prototypes */ 32/* prototypes */
55static void scanelf_file(const char *filename); 33static void scanelf_file(const char *filename);
56static void scanelf_dir(const char *path); 34static void scanelf_dir(const char *path);
57static void scanelf_ldpath(); 35static void scanelf_ldpath();
58static void scanelf_envpath(); 36static void scanelf_envpath();
59static void usage(int status); 37static void usage(int status);
60static void parseargs(int argc, char *argv[]); 38static void parseargs(int argc, char *argv[]);
39static char *xstrdup(const char *s);
40static void *xmalloc(size_t size);
41static void xstrcat(char **dst, const char *src, size_t *curr_len);
42static inline void xchrcat(char **dst, const char append, size_t *curr_len);
61 43
62/* variables to control behavior */ 44/* variables to control behavior */
45static char *ldpaths[256];
63static char scan_ldpath = 0; 46static char scan_ldpath = 0;
64static char scan_envpath = 0; 47static char scan_envpath = 0;
48static char scan_symlink = 1;
65static char dir_recurse = 0; 49static char dir_recurse = 0;
66static char dir_crossmount = 1; 50static char dir_crossmount = 1;
67static char show_pax = 0; 51static char show_pax = 0;
68static char show_stack = 0; 52static char show_phdr = 0;
69static char show_textrel = 0; 53static char show_textrel = 0;
70static char show_rpath = 0; 54static char show_rpath = 0;
71static char show_needed = 0; 55static char show_needed = 0;
56static char show_interp = 0;
57static char show_bind = 0;
58static char show_soname = 0;
59static char show_textrels = 0;
72static char show_banner = 1; 60static char show_banner = 1;
73static char be_quiet = 0; 61static char be_quiet = 0;
74static char be_verbose = 0; 62static char be_verbose = 0;
63static char be_wewy_wewy_quiet = 0;
64static char *find_sym = NULL, *versioned_symname = NULL;
75static char *find_sym = NULL; 65static char *find_lib = NULL;
66static char *out_format = NULL;
67static char *search_path = NULL;
68static char gmatch = 0;
76 69
77 70
78 71/* sub-funcs for scanelf_file() */
79/* scan an elf file and show all the fun stuff */ 72static void scanelf_file_get_symtabs(elfobj *elf, void **sym, void **tab)
80static void scanelf_file(const char *filename)
81{ 73{
82 int i; 74 /* find the best SHT_DYNSYM and SHT_STRTAB sections */
83 char found_pax, found_stack, found_relro, found_textrel, 75#define GET_SYMTABS(B) \
84 found_rpath, found_needed, found_sym; 76 if (elf->elf_class == ELFCLASS ## B) { \
85 elfobj *elf; 77 Elf ## B ## _Shdr *symtab, *strtab, *dynsym, *dynstr; \
78 /* debug sections */ \
79 symtab = SHDR ## B (elf_findsecbyname(elf, ".symtab")); \
80 strtab = SHDR ## B (elf_findsecbyname(elf, ".strtab")); \
81 /* runtime sections */ \
82 dynsym = SHDR ## B (elf_findsecbyname(elf, ".dynsym")); \
83 dynstr = SHDR ## B (elf_findsecbyname(elf, ".dynstr")); \
84 if (symtab && dynsym) { \
85 *sym = (void*)((EGET(symtab->sh_size) > EGET(dynsym->sh_size)) ? symtab : dynsym); \
86 } else { \
87 *sym = (void*)(symtab ? symtab : dynsym); \
88 } \
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 }
95 GET_SYMTABS(32)
96 GET_SYMTABS(64)
97}
98static char *scanelf_file_pax(elfobj *elf, char *found_pax)
99{
100 static char ret[7];
101 unsigned long i, shown;
102
103 if (!show_pax) return NULL;
104
105 shown = 0;
106 memset(&ret, 0, sizeof(ret));
107
108 if (elf->phdr) {
109#define SHOW_PAX(B) \
110 if (elf->elf_class == ELFCLASS ## B) { \
111 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
112 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
113 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
114 if (EGET(phdr[i].p_type) != PT_PAX_FLAGS) \
115 continue; \
116 if (be_quiet && (EGET(phdr[i].p_flags) == 10240)) \
117 continue; \
118 memcpy(ret, pax_short_pf_flags(EGET(phdr[i].p_flags)), 6); \
119 *found_pax = 1; \
120 ++shown; \
121 break; \
122 } \
123 }
124 SHOW_PAX(32)
125 SHOW_PAX(64)
126 }
127
128 /* fall back to EI_PAX if no PT_PAX was found */
129 if (!*ret) {
130 static char *paxflags;
131 paxflags = pax_short_hf_flags(EI_PAX_FLAGS(elf));
132 if (!be_quiet || (be_quiet && EI_PAX_FLAGS(elf))) {
133 *found_pax = 1;
134 return (be_wewy_wewy_quiet ? NULL : paxflags);
135 }
136 strncpy(ret, paxflags, sizeof(ret));
137 }
138
139 if (be_wewy_wewy_quiet || (be_quiet && !shown))
140 return NULL;
141 else
142 return ret;
143}
144
145static char *scanelf_file_phdr(elfobj *elf, char *found_phdr, char *found_relro, char *found_load)
146{
147 static char ret[12];
148 char *found;
149 unsigned long i, off, shown, check_flags;
150 unsigned char multi_stack, multi_relro, multi_load;
151
152 if (!show_phdr) return NULL;
153
154 memcpy(ret, "--- --- ---\0", 12);
155
156 shown = 0;
157 multi_stack = multi_relro = multi_load = 0;
158
159 if (elf->phdr) {
160#define SHOW_PHDR(B) \
161 if (elf->elf_class == ELFCLASS ## B) { \
162 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
163 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
164 uint32_t flags; \
165 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
166 if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
167 if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
168 found = found_phdr; \
169 off = 0; \
170 check_flags = PF_X; \
171 } else if (EGET(phdr[i].p_type) == PT_GNU_RELRO) { \
172 if (multi_relro++) warnf("%s: multiple PT_GNU_RELRO's !?", elf->filename); \
173 found = found_relro; \
174 off = 4; \
175 check_flags = PF_X; \
176 } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
177 if (multi_load++ > 2) warnf("%s: more than 2 PT_LOAD's !?", elf->filename); \
178 found = found_load; \
179 off = 8; \
180 check_flags = PF_W|PF_X; \
181 } else \
182 continue; \
183 flags = EGET(phdr[i].p_flags); \
184 if (be_quiet && ((flags & check_flags) != check_flags)) \
185 continue; \
186 memcpy(ret+off, gnu_short_stack_flags(flags), 3); \
187 *found = 1; \
188 ++shown; \
189 } \
190 }
191 SHOW_PHDR(32)
192 SHOW_PHDR(64)
193 }
194
195 if (be_wewy_wewy_quiet || (be_quiet && !shown))
196 return NULL;
197 else
198 return ret;
199}
200static const char *scanelf_file_textrel(elfobj *elf, char *found_textrel)
201{
202 static const char *ret = "TEXTREL";
203 unsigned long i;
204
205 if (!show_textrel && !show_textrels) return NULL;
206
207 if (elf->phdr) {
208#define SHOW_TEXTREL(B) \
209 if (elf->elf_class == ELFCLASS ## B) { \
210 Elf ## B ## _Dyn *dyn; \
211 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
212 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
213 Elf ## B ## _Off offset; \
214 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
215 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
216 offset = EGET(phdr[i].p_offset); \
217 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
218 dyn = DYN ## B (elf->data + offset); \
219 while (EGET(dyn->d_tag) != DT_NULL) { \
220 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
221 *found_textrel = 1; \
222 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
223 return (be_wewy_wewy_quiet ? NULL : ret); \
224 } \
225 ++dyn; \
226 } \
227 } }
228 SHOW_TEXTREL(32)
229 SHOW_TEXTREL(64)
230 }
231
232 if (be_quiet || be_wewy_wewy_quiet)
233 return NULL;
234 else
235 return " - ";
236}
237static char *scanelf_file_textrels(elfobj *elf, char *found_textrels, char *found_textrel)
238{
239 unsigned long s, r, rmax;
240 void *symtab_void, *strtab_void, *text_void;
241
242 if (!show_textrels) return NULL;
243
244 /* don't search for TEXTREL's if the ELF doesn't have any */
245 if (!*found_textrel) scanelf_file_textrel(elf, found_textrel);
246 if (!*found_textrel) return NULL;
247
248 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
249 text_void = elf_findsecbyname(elf, ".text");
250
251 if (symtab_void && strtab_void && text_void && elf->shdr) {
252#define SHOW_TEXTRELS(B) \
253 if (elf->elf_class == ELFCLASS ## B) { \
254 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
255 Elf ## B ## _Shdr *shdr = SHDR ## B (elf->shdr); \
256 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
257 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
258 Elf ## B ## _Shdr *text = SHDR ## B (text_void); \
259 Elf ## B ## _Addr vaddr = EGET(text->sh_addr); \
260 uint ## B ## _t memsz = EGET(text->sh_size); \
261 Elf ## B ## _Rel *rel; \
262 Elf ## B ## _Rela *rela; \
263 /* search the section headers for relocations */ \
264 for (s = 0; s < EGET(ehdr->e_shnum); ++s) { \
265 uint32_t sh_type = EGET(shdr[s].sh_type); \
266 if (sh_type == SHT_REL) { \
267 rel = REL ## B (elf->data + EGET(shdr[s].sh_offset)); \
268 rela = NULL; \
269 rmax = EGET(shdr[s].sh_size) / sizeof(*rel); \
270 } else if (sh_type == SHT_RELA) { \
271 rel = NULL; \
272 rela = RELA ## B (elf->data + EGET(shdr[s].sh_offset)); \
273 rmax = EGET(shdr[s].sh_size) / sizeof(*rela); \
274 } else \
275 continue; \
276 /* now see if any of the relocs are in the .text */ \
277 for (r = 0; r < rmax; ++r) { \
278 unsigned long sym_max; \
279 Elf ## B ## _Addr offset_tmp; \
280 Elf ## B ## _Sym *func; \
281 Elf ## B ## _Sym *sym; \
282 Elf ## B ## _Addr r_offset; \
283 uint ## B ## _t r_info; \
284 if (sh_type == SHT_REL) { \
285 r_offset = EGET(rel[r].r_offset); \
286 r_info = EGET(rel[r].r_info); \
287 } else { \
288 r_offset = EGET(rela[r].r_offset); \
289 r_info = EGET(rela[r].r_info); \
290 } \
291 /* make sure this relocation is inside of the .text */ \
292 if (r_offset < vaddr || r_offset >= vaddr + memsz) { \
293 if (be_verbose <= 2) continue; \
294 } else \
295 *found_textrels = 1; \
296 /* locate this relocation symbol name */ \
297 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
298 sym_max = ELF ## B ## _R_SYM(r_info); \
299 if (sym_max * EGET(symtab->sh_entsize) < symtab->sh_size) \
300 sym += sym_max; \
301 else \
302 sym = NULL; \
303 sym_max = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
304 /* show the raw details about this reloc */ \
305 printf(" %s: ", elf->base_filename); \
306 if (sym && sym->st_name) \
307 printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name))); \
308 else \
309 printf("(memory/fake?)"); \
310 printf(" [0x%lX]", (unsigned long)r_offset); \
311 /* now try to find the closest symbol that this rel is probably in */ \
312 sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
313 func = NULL; \
314 offset_tmp = 0; \
315 while (sym_max--) { \
316 if (EGET(sym->st_value) < r_offset && EGET(sym->st_value) > offset_tmp) { \
317 func = sym; \
318 offset_tmp = EGET(sym->st_value); \
319 } \
320 ++sym; \
321 } \
322 printf(" in "); \
323 if (func && func->st_name) \
324 printf("%s", (char*)(elf->data + EGET(strtab->sh_offset) + EGET(func->st_name))); \
325 else \
326 printf("(NULL: fake?)"); \
327 printf(" [0x%lX]\n", (unsigned long)offset_tmp); \
328 } \
329 } }
330 SHOW_TEXTRELS(32)
331 SHOW_TEXTRELS(64)
332 }
333 if (!*found_textrels)
334 warnf("ELF %s has TEXTREL markings but doesnt appear to have any real TEXTREL's !?", elf->filename);
335
336 return NULL;
337}
338
339static void rpath_security_checks(elfobj *, char *);
340static void rpath_security_checks(elfobj *elf, char *item) {
86 struct stat st; 341 struct stat st;
87 342 switch (*item) {
88 /* make sure path exists */ 343 case '/': break;
89 if (lstat(filename, &st) == -1) 344 case '.':
90 return; 345 warnf("Security problem with relative RPATH '%s' in %s", item, elf->filename);
91 if (!S_ISREG(st.st_mode)) 346 break;
92 return; 347 case '\0':
93 found_pax = found_stack = found_relro = found_textrel = \ 348 warnf("Security problem NULL RPATH in %s", elf->filename);
94 found_rpath = found_needed = found_sym = 0; 349 break;
95 350 case '$':
96 /* verify this is real ELF */ 351 if (fstat(elf->fd, &st) != -1)
97 if ((elf = readelf(filename)) == NULL) { 352 if ((st.st_mode & S_ISUID) || (st.st_mode & S_ISGID))
98 if (be_verbose > 2) printf("%s: not an ELF\n", filename); 353 warnf("Security problem with RPATH='%s' in %s with mode set of %o",
99 return; 354 item, elf->filename, st.st_mode & 07777);
100 } 355 break;
101 356 default:
102 if (be_verbose > 1) 357 warnf("Maybe? sec problem with RPATH='%s' in %s", item, elf->filename);
103 printf("%s: {%s,%s} scanning file\n", filename, 358 break;
104 get_elfeitype(elf, EI_CLASS, elf->elf_class),
105 get_elfeitype(elf, EI_DATA, elf->data[EI_DATA]));
106 else if (be_verbose)
107 printf("%s: scanning file\n", filename);
108
109 /* show the header */
110 if (!be_quiet && show_banner) {
111 printf(" TYPE ");
112 if (show_pax) printf(" PAX ");
113 if (show_stack) printf("STK/REL ");
114 if (show_textrel) printf("TEXTREL ");
115 if (show_rpath) printf("RPATH ");
116 if (show_needed) printf("NEEDED ");
117 printf(" FILE\n");
118 show_banner = 0;
119 }
120
121 /* dump all the good stuff */
122 if (!be_quiet)
123 printf("%-7s ", get_elfetype(elf));
124
125 if (show_pax) {
126 char *paxflags = pax_short_hf_flags(PAX_FLAGS(elf));
127 if (!be_quiet || (be_quiet && strncmp(paxflags, "PeMRxS", 6))) {
128 found_pax = 1;
129 printf("%s ", pax_short_hf_flags(PAX_FLAGS(elf)));
130 } 359 }
131 } 360}
132 361static void scanelf_file_rpath(elfobj *elf, char *found_rpath, char **ret, size_t *ret_len)
133 /* stack fun */ 362{
134 if (show_stack) { 363 unsigned long i, s;
135#define SHOW_STACK(B) \
136 if (elf->elf_class == ELFCLASS ## B) { \
137 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
138 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
139 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
140 if (EGET(phdr[i].p_type) != PT_GNU_STACK && \
141 EGET(phdr[i].p_type) != PT_GNU_RELRO) continue; \
142 if (be_quiet && !(EGET(phdr[i].p_flags) & PF_X)) \
143 continue; \
144 if (EGET(phdr[i].p_type) == PT_GNU_STACK) \
145 found_stack = 1; \
146 if (EGET(phdr[i].p_type) == PT_GNU_RELRO) \
147 found_relro = 1; \
148 printf("%s ", gnu_short_stack_flags(EGET(phdr[i].p_flags))); \
149 } \
150 }
151 SHOW_STACK(32)
152 SHOW_STACK(64)
153 if (!be_quiet && !found_stack) printf("--- ");
154 if (!be_quiet && !found_relro) printf("--- ");
155 }
156
157 /* textrel fun */
158 if (show_textrel) {
159#define SHOW_TEXTREL(B) \
160 if (elf->elf_class == ELFCLASS ## B) { \
161 Elf ## B ## _Dyn *dyn; \
162 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
163 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
164 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
165 if (phdr[i].p_type != PT_DYNAMIC) continue; \
166 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \
167 while (EGET(dyn->d_tag) != DT_NULL) { \
168 if (EGET(dyn->d_tag) == DT_TEXTREL) { /*dyn->d_tag != DT_FLAGS)*/ \
169 found_textrel = 1; \
170 /*if (dyn->d_un.d_val & DF_TEXTREL)*/ \
171 fputs("TEXTREL ", stdout); \
172 } \
173 ++dyn; \
174 } \
175 } }
176 SHOW_TEXTREL(32)
177 SHOW_TEXTREL(64)
178 if (!be_quiet && !found_textrel) fputs("------- ", stdout);
179 }
180
181 /* rpath fun */
182 /* TODO: if be_quiet, only output RPATH's which aren't in /etc/ld.so.conf */
183 if (show_rpath) {
184 char *rpath, *runpath; 364 char *rpath, *runpath, **r;
365 void *strtbl_void;
366
367 if (!show_rpath) return;
368
185 void *strtbl_void = elf_findsecbyname(elf, ".dynstr"); 369 strtbl_void = elf_findsecbyname(elf, ".dynstr");
186 rpath = runpath = NULL; 370 rpath = runpath = NULL;
187 371
188 if (strtbl_void) { 372 if (elf->phdr && strtbl_void) {
189#define SHOW_RPATH(B) \ 373#define SHOW_RPATH(B) \
190 if (elf->elf_class == ELFCLASS ## B) { \ 374 if (elf->elf_class == ELFCLASS ## B) { \
191 Elf ## B ## _Dyn *dyn; \ 375 Elf ## B ## _Dyn *dyn; \
192 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 376 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
193 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 377 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
194 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 378 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
379 Elf ## B ## _Off offset; \
380 Elf ## B ## _Xword word; \
381 /* Scan all the program headers */ \
195 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 382 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
383 /* Just scan dynamic headers */ \
196 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 384 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
385 offset = EGET(phdr[i].p_offset); \
386 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
387 /* Just scan dynamic RPATH/RUNPATH headers */ \
197 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \ 388 dyn = DYN ## B (elf->data + offset); \
198 while (EGET(dyn->d_tag) != DT_NULL) { \ 389 while ((word=EGET(dyn->d_tag)) != DT_NULL) { \
199 if (EGET(dyn->d_tag) == DT_RPATH) { \ 390 if (word == DT_RPATH) { \
391 r = &rpath; \
392 } else if (word == DT_RUNPATH) { \
393 r = &runpath; \
394 } else { \
395 ++dyn; \
396 continue; \
397 } \
398 /* Verify the memory is somewhat sane */ \
200 rpath = elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 399 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
400 if (offset < (Elf ## B ## _Off)elf->len) { \
401 if (*r) warn("ELF has multiple %s's !?", get_elfdtype(word)); \
402 *r = (char*)(elf->data + offset); \
403 /* If quiet, don't output paths in ld.so.conf */ \
404 if (be_quiet) { \
405 size_t len; \
406 char *start, *end; \
407 /* note that we only 'chop' off leading known paths. */ \
408 /* since *r is read-only memory, we can only move the ptr forward. */ \
409 start = *r; \
410 /* scan each path in : delimited list */ \
411 while (start) { \
412 rpath_security_checks(elf, start); \
413 end = strchr(start, ':'); \
414 len = (end ? abs(end - start) : strlen(start)); \
415 for (s = 0; ldpaths[s]; ++s) { \
416 if (!strncmp(ldpaths[s], start, len) && !ldpaths[s][len]) { \
417 *r = (end ? end + 1 : NULL); \
418 break; \
419 } \
420 } \
421 if (!*r || !ldpaths[s] || !end) \
422 start = NULL; \
423 else \
424 start = start + len + 1; \
425 } \
426 } \
201 found_rpath = 1; \ 427 if (*r) *found_rpath = 1; \
202 } else if (EGET(dyn->d_tag) == DT_RUNPATH) { \
203 runpath = elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
204 found_rpath = 1; \
205 } \ 428 } \
206 ++dyn; \ 429 ++dyn; \
207 } \ 430 } \
208 } } 431 } }
209 SHOW_RPATH(32) 432 SHOW_RPATH(32)
210 SHOW_RPATH(64) 433 SHOW_RPATH(64)
211 } 434 }
435
436 if (be_wewy_wewy_quiet) return;
437
212 if (rpath && runpath) { 438 if (rpath && runpath) {
213 if (!strcmp(rpath, runpath)) 439 if (!strcmp(rpath, runpath)) {
214 printf("%-5s ", runpath); 440 xstrcat(ret, runpath, ret_len);
215 else { 441 } else {
216 fprintf(stderr, "%s's RPATH [%s] != RUNPATH [%s]\n", filename, rpath, runpath); 442 fprintf(stderr, "RPATH [%s] != RUNPATH [%s]\n", rpath, runpath);
217 printf("{%s,%s} ", rpath, runpath); 443 xchrcat(ret, '{', ret_len);
444 xstrcat(ret, rpath, ret_len);
445 xchrcat(ret, ',', ret_len);
446 xstrcat(ret, runpath, ret_len);
447 xchrcat(ret, '}', ret_len);
218 } 448 }
219 } else if (rpath || runpath) 449 } else if (rpath || runpath)
220 printf("%-5s ", (runpath ? runpath : rpath)); 450 xstrcat(ret, (runpath ? runpath : rpath), ret_len);
221 else if (!be_quiet && !found_rpath) 451 else if (!be_quiet)
222 printf(" - "); 452 xstrcat(ret, " - ", ret_len);
223 } 453}
224 454static const char *scanelf_file_needed_lib(elfobj *elf, char *found_needed, char *found_lib, int op, char **ret, size_t *ret_len)
225 /* print out all the NEEDED entries */ 455{
226 if (show_needed) { 456 unsigned long i;
227 char *needed; 457 char *needed;
458 void *strtbl_void;
459
460 if ((op==0 && !show_needed) || (op==1 && !find_lib)) return NULL;
461
228 void *strtbl_void = elf_findsecbyname(elf, ".dynstr"); 462 strtbl_void = elf_findsecbyname(elf, ".dynstr");
229 463
230 if (strtbl_void) { 464 if (elf->phdr && strtbl_void) {
231#define SHOW_NEEDED(B) \ 465#define SHOW_NEEDED(B) \
232 if (elf->elf_class == ELFCLASS ## B) { \ 466 if (elf->elf_class == ELFCLASS ## B) { \
233 Elf ## B ## _Dyn *dyn; \ 467 Elf ## B ## _Dyn *dyn; \
234 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \ 468 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
235 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \ 469 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
236 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \ 470 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
471 Elf ## B ## _Off offset; \
237 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \ 472 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
238 if (be_verbose && EGET(phdr[i].p_type) == PT_INTERP) { \
239 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \
240 printf("%s\n", elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr)); \
241 exit(0); \
242 } \
243 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \ 473 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
474 offset = EGET(phdr[i].p_offset); \
475 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
244 dyn = DYN ## B (elf->data + EGET(phdr[i].p_offset)); \ 476 dyn = DYN ## B (elf->data + offset); \
245 while (EGET(dyn->d_tag) != DT_NULL) { \ 477 while (EGET(dyn->d_tag) != DT_NULL) { \
246 if (EGET(dyn->d_tag) == DT_NEEDED) { \ 478 if (EGET(dyn->d_tag) == DT_NEEDED) { \
247 needed = elf->data + EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \ 479 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
248 if (found_needed) printf(","); \ 480 if (offset >= (Elf ## B ## _Off)elf->len) { \
249 printf("%s", needed); \ 481 ++dyn; \
482 continue; \
483 } \
484 needed = (char*)(elf->data + offset); \
485 if (op == 0) { \
486 if (!be_wewy_wewy_quiet) { \
487 if (*found_needed) xchrcat(ret, ',', ret_len); \
488 xstrcat(ret, needed, ret_len); \
489 } \
250 found_needed = 1; \ 490 *found_needed = 1; \
491 } else { \
492 if (!strncmp(find_lib, needed, strlen( !gmatch ? needed : find_lib))) { \
493 *found_lib = 1; \
494 return (be_wewy_wewy_quiet ? NULL : needed); \
495 } \
496 } \
251 } \ 497 } \
252 ++dyn; \ 498 ++dyn; \
253 } \ 499 } \
254 } } 500 } }
255 SHOW_NEEDED(32) 501 SHOW_NEEDED(32)
256 SHOW_NEEDED(64) 502 SHOW_NEEDED(64)
503 if (op == 0 && !*found_needed && be_verbose)
504 warn("ELF lacks DT_NEEDED sections: %s", elf->filename);
257 } 505 }
258 if (!be_quiet && !found_needed) 506
259 printf(" - "); 507 return NULL;
260 else if (found_needed) 508}
261 printf(" "); 509static char *scanelf_file_interp(elfobj *elf, char *found_interp)
510{
511 void *strtbl_void;
512
513 if (!show_interp) return NULL;
514
515 strtbl_void = elf_findsecbyname(elf, ".interp");
516
517 if (strtbl_void) {
518#define SHOW_INTERP(B) \
519 if (elf->elf_class == ELFCLASS ## B) { \
520 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
521 *found_interp = 1; \
522 return (be_wewy_wewy_quiet ? NULL : elf->data + EGET(strtbl->sh_offset)); \
262 } 523 }
524 SHOW_INTERP(32)
525 SHOW_INTERP(64)
526 }
527 return NULL;
528}
529static char *scanelf_file_bind(elfobj *elf, char *found_bind)
530{
531 unsigned long i;
532 struct stat s;
263 533
264 /* search the symbol table for a specified symbol */ 534 if (!show_bind) return NULL;
265 if (find_sym) { 535 if (!elf->phdr) return NULL;
536
537#define SHOW_BIND(B) \
538 if (elf->elf_class == ELFCLASS ## B) { \
539 Elf ## B ## _Dyn *dyn; \
540 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
541 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
542 Elf ## B ## _Off offset; \
543 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
544 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
545 offset = EGET(phdr[i].p_offset); \
546 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
547 dyn = DYN ## B (elf->data + offset); \
548 while (EGET(dyn->d_tag) != DT_NULL) { \
549 if (EGET(dyn->d_tag) == DT_BIND_NOW || \
550 (EGET(dyn->d_tag) == DT_FLAGS && EGET(dyn->d_un.d_val) & DF_BIND_NOW)) \
551 { \
552 if (be_quiet) return NULL; \
553 *found_bind = 1; \
554 return (char *)(be_wewy_wewy_quiet ? NULL : "NOW"); \
555 } \
556 ++dyn; \
557 } \
558 } \
559 }
560 SHOW_BIND(32)
561 SHOW_BIND(64)
562
563 if (be_wewy_wewy_quiet) return NULL;
564
565 if (be_quiet && !fstat(elf->fd, &s) && !(s.st_mode & S_ISUID || s.st_mode & S_ISGID)) {
566 return NULL;
567 } else {
568 *found_bind = 1;
569 return (char *) "LAZY";
570 }
571}
572static char *scanelf_file_soname(elfobj *elf, char *found_soname)
573{
574 unsigned long i;
575 char *soname;
576 void *strtbl_void;
577
578 if (!show_soname) return NULL;
579
580 strtbl_void = elf_findsecbyname(elf, ".dynstr");
581
582 if (elf->phdr && strtbl_void) {
583#define SHOW_SONAME(B) \
584 if (elf->elf_class == ELFCLASS ## B) { \
585 Elf ## B ## _Dyn *dyn; \
586 Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
587 Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
588 Elf ## B ## _Shdr *strtbl = SHDR ## B (strtbl_void); \
589 Elf ## B ## _Off offset; \
590 /* only look for soname in shared objects */ \
591 if (ehdr->e_type != ET_DYN) \
592 return NULL; \
593 for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
594 if (EGET(phdr[i].p_type) != PT_DYNAMIC) continue; \
595 offset = EGET(phdr[i].p_offset); \
596 if (offset >= elf->len - sizeof(Elf ## B ## _Dyn)) continue; \
597 dyn = DYN ## B (elf->data + offset); \
598 while (EGET(dyn->d_tag) != DT_NULL) { \
599 if (EGET(dyn->d_tag) == DT_SONAME) { \
600 offset = EGET(strtbl->sh_offset) + EGET(dyn->d_un.d_ptr); \
601 if (offset >= (Elf ## B ## _Off)elf->len) { \
602 ++dyn; \
603 continue; \
604 } \
605 soname = (char*)(elf->data + offset); \
606 *found_soname = 1; \
607 return (be_wewy_wewy_quiet ? NULL : soname); \
608 } \
609 ++dyn; \
610 } \
611 } }
612 SHOW_SONAME(32)
613 SHOW_SONAME(64)
614 }
615
616 return NULL;
617}
618static char *scanelf_file_sym(elfobj *elf, char *found_sym)
619{
620 unsigned long i;
266 void *symtab_void, *strtab_void; 621 void *symtab_void, *strtab_void;
267 char *versioned_symname;
268 size_t len;
269 622
270 len = strlen(find_sym) + 1; 623 if (!find_sym) return NULL;
271 versioned_symname = (char *)malloc(sizeof(char) * (len+1));
272 if (!versioned_symname) {
273 warnf("Could not malloc() mem for sym scan");
274 return;
275 }
276 sprintf(versioned_symname, "%s@", find_sym);
277 624
278 symtab_void = elf_findsecbyname(elf, ".symtab"); 625 scanelf_file_get_symtabs(elf, &symtab_void, &strtab_void);
279 strtab_void = elf_findsecbyname(elf, ".strtab");
280 626
281 if (symtab_void && strtab_void) { 627 if (symtab_void && strtab_void) {
282#define FIND_SYM(B) \ 628#define FIND_SYM(B) \
283 if (elf->elf_class == ELFCLASS ## B) { \ 629 if (elf->elf_class == ELFCLASS ## B) { \
284 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \ 630 Elf ## B ## _Shdr *symtab = SHDR ## B (symtab_void); \
285 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \ 631 Elf ## B ## _Shdr *strtab = SHDR ## B (strtab_void); \
286 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \ 632 Elf ## B ## _Sym *sym = SYM ## B (elf->data + EGET(symtab->sh_offset)); \
287 int cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \ 633 unsigned long cnt = EGET(symtab->sh_size) / EGET(symtab->sh_entsize); \
288 char *symname; \ 634 char *symname; \
289 for (i = 0; i < cnt; ++i) { \ 635 for (i = 0; i < cnt; ++i) { \
290 if (sym->st_name) { \ 636 if (sym->st_name) { \
291 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \ 637 symname = (char *)(elf->data + EGET(strtab->sh_offset) + EGET(sym->st_name)); \
292 if (*find_sym == '*') { \ 638 if (*find_sym == '*') { \
293 printf("%s(%s) %5lX %15s %s\n", \ 639 printf("%s(%s) %5lX %15s %s\n", \
294 ((found_sym == 0) ? "\n\t" : "\t"), \ 640 ((*found_sym == 0) ? "\n\t" : "\t"), \
295 (char *)basename(filename), \ 641 elf->base_filename, \
296 (long)sym->st_size, \ 642 (long)sym->st_size, \
297 (char *)get_elfstttype(sym->st_info), \ 643 (char *)get_elfstttype(sym->st_info), \
298 symname); \ 644 symname); \
299 found_sym = 1; \ 645 *found_sym = 1; \
300 } else if ((strcmp(find_sym, symname) == 0) || \ 646 } else if ((strcmp(find_sym, symname) == 0) || \
301 (strncmp(symname, versioned_symname, len) == 0)) \ 647 (strcmp(symname, versioned_symname) == 0)) \
302 found_sym++; \ 648 (*found_sym)++; \
303 } \ 649 } \
304 ++sym; \ 650 ++sym; \
305 } } 651 } }
306 FIND_SYM(32) 652 FIND_SYM(32)
307 FIND_SYM(64) 653 FIND_SYM(64)
654 }
655
656 if (be_wewy_wewy_quiet) return NULL;
657
658 if (*find_sym != '*' && *found_sym)
659 return find_sym;
660 if (be_quiet)
661 return NULL;
662 else
663 return (char *)" - ";
664}
665/* scan an elf file and show all the fun stuff */
666#define prints(str) write(fileno(stdout), str, strlen(str))
667static void scanelf_file(const char *filename)
668{
669 unsigned long i;
670 char found_pax, found_phdr, found_relro, found_load, found_textrel,
671 found_rpath, found_needed, found_interp, found_bind, found_soname,
672 found_sym, found_lib, found_file, found_textrels;
673 elfobj *elf;
674 struct stat st;
675 static char *out_buffer = NULL;
676 static size_t out_len;
677
678 /* make sure 'filename' exists */
679 if (lstat(filename, &st) == -1) {
680 if (be_verbose > 2) printf("%s: does not exist\n", filename);
681 return;
682 }
683 /* always handle regular files and handle symlinked files if no -y */
684 if (S_ISLNK(st.st_mode)) {
685 if (!scan_symlink) return;
686 stat(filename, &st);
687 }
688 if (!S_ISREG(st.st_mode)) {
689 if (be_verbose > 2) printf("%s: skipping non-file\n", filename);
690 return;
691 }
692
693 found_pax = found_phdr = found_relro = found_load = found_textrel = \
694 found_rpath = found_needed = found_interp = found_bind = found_soname = \
695 found_sym = found_lib = found_file = found_textrels = 0;
696
697 /* verify this is real ELF */
698 if ((elf = readelf(filename)) == NULL) {
699 if (be_verbose > 2) printf("%s: not an ELF\n", filename);
700 return;
701 }
702
703 if (be_verbose > 1)
704 printf("%s: scanning file {%s,%s}\n", filename,
705 get_elfeitype(EI_CLASS, elf->elf_class),
706 get_elfeitype(EI_DATA, elf->data[EI_DATA]));
707 else if (be_verbose)
708 printf("%s: scanning file\n", filename);
709
710 /* init output buffer */
711 if (!out_buffer) {
712 out_len = sizeof(char) * 80;
713 out_buffer = (char*)xmalloc(out_len);
714 }
715 *out_buffer = '\0';
716
717 /* show the header */
718 if (!be_quiet && show_banner) {
719 for (i = 0; out_format[i]; ++i) {
720 if (!IS_MODIFIER(out_format[i])) continue;
721
722 switch (out_format[++i]) {
723 case '%': break;
724 case '#': break;
725 case 'F':
726 case 'p':
727 case 'f': prints("FILE "); found_file = 1; break;
728 case 'o': prints(" TYPE "); break;
729 case 'x': prints(" PAX "); break;
730 case 'e': prints("STK/REL/PTL "); break;
731 case 't': prints("TEXTREL "); break;
732 case 'r': prints("RPATH "); break;
733 case 'n': prints("NEEDED "); break;
734 case 'i': prints("INTERP "); break;
735 case 'b': prints("BIND "); break;
736 case 'S': prints("SONAME "); break;
737 case 's': prints("SYM "); break;
738 case 'N': prints("LIB "); break;
739 case 'T': prints("TEXTRELS "); break;
740 default: warnf("'%c' has no title ?", out_format[i]);
308 } 741 }
309 free(versioned_symname); 742 }
310 if (*find_sym != '*') { 743 if (!found_file) prints("FILE ");
311 if (found_sym) 744 prints("\n");
312 printf(" %s ", find_sym); 745 found_file = 0;
313 else if (!be_quiet) 746 show_banner = 0;
314 fputs(" - ", stdout); 747 }
748
749 /* dump all the good stuff */
750 for (i = 0; out_format[i]; ++i) {
751 const char *out;
752 const char *tmp;
753
754 /* make sure we trim leading spaces in quiet mode */
755 if (be_quiet && *out_buffer == ' ' && !out_buffer[1])
756 *out_buffer = '\0';
757
758 if (!IS_MODIFIER(out_format[i])) {
759 xchrcat(&out_buffer, out_format[i], &out_len);
760 continue;
761 }
762
763 out = NULL;
764 be_wewy_wewy_quiet = (out_format[i] == '#');
765 switch (out_format[++i]) {
766 case '%':
767 case '#':
768 xchrcat(&out_buffer, out_format[i], &out_len); break;
769 case 'F':
770 found_file = 1;
771 if (be_wewy_wewy_quiet) break;
772 xstrcat(&out_buffer, filename, &out_len);
773 break;
774 case 'p':
775 found_file = 1;
776 if (be_wewy_wewy_quiet) break;
777 tmp = filename;
778 if (search_path) {
779 ssize_t len_search = strlen(search_path);
780 ssize_t len_file = strlen(filename);
781 if (!strncmp(filename, search_path, len_search) && \
782 len_file > len_search)
783 tmp += len_search;
784 if (*tmp == '/' && search_path[len_search-1] == '/') tmp++;
315 } 785 }
786 xstrcat(&out_buffer, tmp, &out_len);
787 break;
788 case 'f':
789 found_file = 1;
790 if (be_wewy_wewy_quiet) break;
791 tmp = strrchr(filename, '/');
792 tmp = (tmp == NULL ? filename : tmp+1);
793 xstrcat(&out_buffer, tmp, &out_len);
794 break;
795 case 'o': out = get_elfetype(elf); break;
796 case 'x': out = scanelf_file_pax(elf, &found_pax); break;
797 case 'e': out = scanelf_file_phdr(elf, &found_phdr, &found_relro, &found_load); break;
798 case 't': out = scanelf_file_textrel(elf, &found_textrel); break;
799 case 'T': out = scanelf_file_textrels(elf, &found_textrels, &found_textrel); break;
800 case 'r': scanelf_file_rpath(elf, &found_rpath, &out_buffer, &out_len); break;
801 case 'n':
802 case 'N': out = scanelf_file_needed_lib(elf, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break;
803 case 'i': out = scanelf_file_interp(elf, &found_interp); break;
804 case 'b': out = scanelf_file_bind(elf, &found_bind); break;
805 case 'S': out = scanelf_file_soname(elf, &found_soname); break;
806 case 's': out = scanelf_file_sym(elf, &found_sym); break;
807 default: warnf("'%c' has no scan code?", out_format[i]);
316 } 808 }
809 if (out) xstrcat(&out_buffer, out, &out_len);
810 }
317 811
318 if (!be_quiet || found_pax || found_stack || found_textrel || \ 812#define FOUND_SOMETHING() \
319 found_rpath || found_needed || found_sym) 813 (found_pax || found_phdr || found_relro || found_load || found_textrel || \
320 puts(filename); 814 found_rpath || found_needed || found_interp || found_bind || \
815 found_soname || found_sym || found_lib || found_textrels)
816
817 if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) {
818 xchrcat(&out_buffer, ' ', &out_len);
819 xstrcat(&out_buffer, filename, &out_len);
820 }
821 if (!be_quiet || (be_quiet && FOUND_SOMETHING())) {
822 puts(out_buffer);
823 fflush(stdout);
824 }
321 825
322 unreadelf(elf); 826 unreadelf(elf);
323} 827}
324 828
325/* scan a directory for ET_EXEC files and print when we find one */ 829/* scan a directory for ET_EXEC files and print when we find one */
330 struct stat st_top, st; 834 struct stat st_top, st;
331 char buf[_POSIX_PATH_MAX]; 835 char buf[_POSIX_PATH_MAX];
332 size_t pathlen = 0, len = 0; 836 size_t pathlen = 0, len = 0;
333 837
334 /* make sure path exists */ 838 /* make sure path exists */
335 if (lstat(path, &st_top) == -1) 839 if (lstat(path, &st_top) == -1) {
840 if (be_verbose > 2) printf("%s: does not exist\n", path);
336 return; 841 return;
842 }
337 843
338 /* ok, if it isn't a directory, assume we can open it */ 844 /* ok, if it isn't a directory, assume we can open it */
339 if (!S_ISDIR(st_top.st_mode)) { 845 if (!S_ISDIR(st_top.st_mode)) {
340 scanelf_file(path); 846 scanelf_file(path);
341 return; 847 return;
352 while ((dentry = readdir(dir))) { 858 while ((dentry = readdir(dir))) {
353 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) 859 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
354 continue; 860 continue;
355 len = (pathlen + 1 + strlen(dentry->d_name) + 1); 861 len = (pathlen + 1 + strlen(dentry->d_name) + 1);
356 if (len >= sizeof(buf)) { 862 if (len >= sizeof(buf)) {
357 warnf("Skipping '%s': len > sizeof(buf); %d > %d\n", path, (int)len, (int)sizeof(buf)); 863 warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path,
864 (unsigned long)len, (unsigned long)sizeof(buf));
358 continue; 865 continue;
359 } 866 }
360 sprintf(buf, "%s/%s", path, dentry->d_name); 867 sprintf(buf, "%s/%s", path, dentry->d_name);
361 if (lstat(buf, &st) != -1) { 868 if (lstat(buf, &st) != -1) {
362 if (S_ISREG(st.st_mode)) 869 if (S_ISREG(st.st_mode))
368 } 875 }
369 } 876 }
370 closedir(dir); 877 closedir(dir);
371} 878}
372 879
880static int scanelf_from_file(char *filename)
881{
882 FILE *fp = NULL;
883 char *p;
884 char path[_POSIX_PATH_MAX];
885
886 if (((strcmp(filename, "-")) == 0) && (ttyname(0) == NULL))
887 fp = stdin;
888 else if ((fp = fopen(filename, "r")) == NULL)
889 return 1;
890
891 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
892 if ((p = strchr(path, '\n')) != NULL)
893 *p = 0;
894 search_path = path;
895 scanelf_dir(path);
896 }
897 if (fp != stdin)
898 fclose(fp);
899 return 0;
900}
901
902static void load_ld_so_conf()
903{
904 FILE *fp = NULL;
905 char *p;
906 char path[_POSIX_PATH_MAX];
907 int i = 0;
908
909 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL)
910 return;
911
912 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL) {
913 if (*path != '/')
914 continue;
915
916 if ((p = strrchr(path, '\r')) != NULL)
917 *p = 0;
918 if ((p = strchr(path, '\n')) != NULL)
919 *p = 0;
920
921 ldpaths[i++] = xstrdup(path);
922
923 if (i + 1 == sizeof(ldpaths) / sizeof(*ldpaths))
924 break;
925 }
926 ldpaths[i] = NULL;
927
928 fclose(fp);
929}
930
373/* scan /etc/ld.so.conf for paths */ 931/* scan /etc/ld.so.conf for paths */
374static void scanelf_ldpath() 932static void scanelf_ldpath()
375{ 933{
376 char scan_l, scan_ul, scan_ull; 934 char scan_l, scan_ul, scan_ull;
377 char *path, *p; 935 int i = 0;
378 FILE *fp;
379 936
380 if ((fp = fopen("/etc/ld.so.conf", "r")) == NULL) 937 if (!ldpaths[0])
381 err("Unable to open ld.so.conf: %s", strerror(errno)); 938 err("Unable to load any paths from ld.so.conf");
382 939
383 scan_l = scan_ul = scan_ull = 0; 940 scan_l = scan_ul = scan_ull = 0;
384 941
385 if ((path = malloc(_POSIX_PATH_MAX)) == NULL) { 942 while (ldpaths[i]) {
386 warn("Can not malloc() memory for ldpath scanning");
387 return;
388 }
389 while ((fgets(path, _POSIX_PATH_MAX, fp)) != NULL)
390 if (*path == '/') {
391 if ((p = strrchr(path, '\r')) != NULL)
392 *p = 0;
393 if ((p = strrchr(path, '\n')) != NULL)
394 *p = 0;
395 if (!scan_l && !strcmp(path, "/lib")) scan_l = 1; 943 if (!scan_l && !strcmp(ldpaths[i], "/lib")) scan_l = 1;
396 if (!scan_ul && !strcmp(path, "/usr/lib")) scan_ul = 1; 944 if (!scan_ul && !strcmp(ldpaths[i], "/usr/lib")) scan_ul = 1;
397 if (!scan_ull && !strcmp(path, "/usr/local/lib")) scan_ull = 1; 945 if (!scan_ull && !strcmp(ldpaths[i], "/usr/local/lib")) scan_ull = 1;
398 scanelf_dir(path); 946 scanelf_dir(ldpaths[i]);
947 ++i;
399 } 948 }
400 free(path);
401 fclose(fp);
402 949
403 if (!scan_l) scanelf_dir("/lib"); 950 if (!scan_l) scanelf_dir("/lib");
404 if (!scan_ul) scanelf_dir("/usr/lib"); 951 if (!scan_ul) scanelf_dir("/usr/lib");
405 if (!scan_ull) scanelf_dir("/usr/local/lib"); 952 if (!scan_ull) scanelf_dir("/usr/local/lib");
406} 953}
411 char *path, *p; 958 char *path, *p;
412 959
413 path = getenv("PATH"); 960 path = getenv("PATH");
414 if (!path) 961 if (!path)
415 err("PATH is not set in your env !"); 962 err("PATH is not set in your env !");
416 963 path = xstrdup(path);
417 if ((path = strdup(path)) == NULL)
418 err("strdup failed: %s", strerror(errno));
419 964
420 while ((p = strrchr(path, ':')) != NULL) { 965 while ((p = strrchr(path, ':')) != NULL) {
421 scanelf_dir(p + 1); 966 scanelf_dir(p + 1);
422 *p = 0; 967 *p = 0;
423 } 968 }
426} 971}
427 972
428 973
429 974
430/* usage / invocation handling functions */ 975/* usage / invocation handling functions */
431#define PARSE_FLAGS "plRmxetrns:aqvo:BhV" 976#define PARSE_FLAGS "plRmyxetrnibSs:gN:TaqvF:f:o:BhV"
432#define a_argument required_argument 977#define a_argument required_argument
433static struct option const long_opts[] = { 978static struct option const long_opts[] = {
434 {"path", no_argument, NULL, 'p'}, 979 {"path", no_argument, NULL, 'p'},
435 {"ldpath", no_argument, NULL, 'l'}, 980 {"ldpath", no_argument, NULL, 'l'},
436 {"recursive", no_argument, NULL, 'R'}, 981 {"recursive", no_argument, NULL, 'R'},
437 {"mount", no_argument, NULL, 'm'}, 982 {"mount", no_argument, NULL, 'm'},
983 {"symlink", no_argument, NULL, 'y'},
438 {"pax", no_argument, NULL, 'x'}, 984 {"pax", no_argument, NULL, 'x'},
439 {"header", no_argument, NULL, 'e'}, 985 {"header", no_argument, NULL, 'e'},
440 {"textrel", no_argument, NULL, 't'}, 986 {"textrel", no_argument, NULL, 't'},
441 {"rpath", no_argument, NULL, 'r'}, 987 {"rpath", no_argument, NULL, 'r'},
442 {"needed", no_argument, NULL, 'n'}, 988 {"needed", no_argument, NULL, 'n'},
989 {"interp", no_argument, NULL, 'i'},
990 {"bind", no_argument, NULL, 'b'},
991 {"soname", no_argument, NULL, 'S'},
443 {"symbol", a_argument, NULL, 's'}, 992 {"symbol", a_argument, NULL, 's'},
993 {"lib", a_argument, NULL, 'N'},
994 {"gmatch", no_argument, NULL, 'g'},
995 {"textrels", no_argument, NULL, 'T'},
444 {"all", no_argument, NULL, 'a'}, 996 {"all", no_argument, NULL, 'a'},
445 {"quiet", no_argument, NULL, 'q'}, 997 {"quiet", no_argument, NULL, 'q'},
446 {"verbose", no_argument, NULL, 'v'}, 998 {"verbose", no_argument, NULL, 'v'},
999 {"format", a_argument, NULL, 'F'},
1000 {"from", a_argument, NULL, 'f'},
447 {"file", a_argument, NULL, 'o'}, 1001 {"file", a_argument, NULL, 'o'},
448 {"nobanner", no_argument, NULL, 'B'}, 1002 {"nobanner", no_argument, NULL, 'B'},
449 {"help", no_argument, NULL, 'h'}, 1003 {"help", no_argument, NULL, 'h'},
450 {"version", no_argument, NULL, 'V'}, 1004 {"version", no_argument, NULL, 'V'},
451 {NULL, no_argument, NULL, 0x0} 1005 {NULL, no_argument, NULL, 0x0}
452}; 1006};
1007
453static char *opts_help[] = { 1008static const char *opts_help[] = {
454 "Scan all directories in PATH environment", 1009 "Scan all directories in PATH environment",
455 "Scan all directories in /etc/ld.so.conf", 1010 "Scan all directories in /etc/ld.so.conf",
456 "Scan directories recursively", 1011 "Scan directories recursively",
457 "Don't recursively cross mount points\n", 1012 "Don't recursively cross mount points",
1013 "Don't scan symlinks\n",
458 "Print PaX markings", 1014 "Print PaX markings",
459 "Print GNU_STACK markings", 1015 "Print GNU_STACK/PT_LOAD markings",
460 "Print TEXTREL information", 1016 "Print TEXTREL information",
461 "Print RPATH information", 1017 "Print RPATH information",
462 "Print NEEDED information", 1018 "Print NEEDED information",
1019 "Print INTERP information",
1020 "Print BIND information",
1021 "Print SONAME information",
463 "Find a specified symbol", 1022 "Find a specified symbol",
1023 "Find a specified library",
1024 "Use strncmp to match libraries. (use with -N)",
1025 "Locate cause of TEXTREL",
464 "Print all scanned info (-x -e -t -r)\n", 1026 "Print all scanned info (-x -e -t -r -b)\n",
465 "Only output 'bad' things", 1027 "Only output 'bad' things",
466 "Be verbose (can be specified more than once)", 1028 "Be verbose (can be specified more than once)",
1029 "Use specified format for output",
1030 "Read input stream from a filename",
467 "Write output stream to a filename", 1031 "Write output stream to a filename",
468 "Don't display the header", 1032 "Don't display the header",
469 "Print this help and exit", 1033 "Print this help and exit",
470 "Print version and exit", 1034 "Print version and exit",
471 NULL 1035 NULL
472}; 1036};
473 1037
474/* display usage and exit */ 1038/* display usage and exit */
475static void usage(int status) 1039static void usage(int status)
476{ 1040{
477 int i; 1041 unsigned long i;
478 printf(" Scan ELF binaries for stuff\n" 1042 printf("* Scan ELF binaries for stuff\n\n"
479 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0); 1043 "Usage: %s [options] <dir1/file1> [dir2 dirN fileN ...]\n\n", argv0);
480 printf("Options: -[%s]\n", PARSE_FLAGS); 1044 printf("Options: -[%s]\n", PARSE_FLAGS);
481 for (i = 0; long_opts[i].name; ++i) 1045 for (i = 0; long_opts[i].name; ++i)
482 if (long_opts[i].has_arg == no_argument) 1046 if (long_opts[i].has_arg == no_argument)
483 printf(" -%c, --%-13s %s\n", long_opts[i].val, 1047 printf(" -%c, --%-13s* %s\n", long_opts[i].val,
484 long_opts[i].name, opts_help[i]); 1048 long_opts[i].name, opts_help[i]);
485 else 1049 else
486 printf(" -%c, --%-6s <arg> %s\n", long_opts[i].val, 1050 printf(" -%c, --%-6s <arg> * %s\n", long_opts[i].val,
487 long_opts[i].name, opts_help[i]); 1051 long_opts[i].name, opts_help[i]);
1052
1053 if (status != EXIT_SUCCESS)
1054 exit(status);
1055
1056 puts("\nThe format modifiers for the -F option are:");
1057 puts(" F Filename \tx PaX Flags \te STACK/RELRO");
1058 puts(" t TEXTREL \tr RPATH \tn NEEDED");
1059 puts(" i INTERP \tb BIND \ts symbol");
1060 puts(" N library \to Type \tT TEXTRELs");
1061 puts(" S SONAME");
1062 puts(" p filename (with search path removed)");
1063 puts(" f filename (short name/basename)");
1064 puts("Prefix each modifier with '%' (verbose) or '#' (silent)");
1065
488 exit(status); 1066 exit(status);
489} 1067}
490 1068
491/* parse command line arguments and preform needed actions */ 1069/* parse command line arguments and preform needed actions */
492static void parseargs(int argc, char *argv[]) 1070static void parseargs(int argc, char *argv[])
493{ 1071{
494 int flag; 1072 int i;
1073 char *from_file = NULL;
495 1074
496 opterr = 0; 1075 opterr = 0;
497 while ((flag=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { 1076 while ((i=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
498 switch (flag) { 1077 switch (i) {
499 1078
500 case 'V': /* version info */ 1079 case 'V':
501 printf("%s compiled %s\n%s\n" 1080 printf("pax-utils-%s: %s compiled %s\n%s\n"
502 "%s written for Gentoo Linux by <solar and vapier @ gentoo.org>\n", 1081 "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
503 __FILE__, __DATE__, rcsid, argv0); 1082 VERSION, __FILE__, __DATE__, rcsid, argv0);
504 exit(EXIT_SUCCESS); 1083 exit(EXIT_SUCCESS);
505 break; 1084 break;
506 case 'h': usage(EXIT_SUCCESS); break; 1085 case 'h': usage(EXIT_SUCCESS); break;
507 1086 case 'f':
1087 if (from_file) err("Don't specify -f twice");
1088 from_file = xstrdup(optarg);
1089 break;
508 case 'o': { 1090 case 'o': {
509 FILE *fp = NULL; 1091 FILE *fp = NULL;
510 fp = freopen(optarg, "w", stdout); 1092 if ((fp = freopen(optarg, "w", stdout)) == NULL)
511 if (fp == NULL)
512 err("Could not open output stream '%s': %s", optarg, strerror(errno)); 1093 err("Could not open output stream '%s': %s", optarg, strerror(errno));
513 stdout = fp; 1094 SET_STDOUT(fp);
514 break; 1095 break;
515 } 1096 }
516 1097
517 case 's': find_sym = strdup(optarg); break; 1098 case 's': {
1099 size_t len;
1100 if (find_sym) err("Don't specify -s twice");
1101 find_sym = xstrdup(optarg);
1102 len = strlen(find_sym) + 1;
1103 versioned_symname = (char*)xmalloc(sizeof(char) * (len+1));
1104 sprintf(versioned_symname, "%s@", find_sym);
1105 break;
1106 }
1107 case 'N': {
1108 if (find_lib) err("Don't specify -N twice");
1109 find_lib = xstrdup(optarg);
1110 break;
1111 }
518 1112
1113 case 'F': {
1114 if (out_format) err("Don't specify -F twice");
1115 out_format = xstrdup(optarg);
1116 break;
1117 }
1118
1119 case 'g': gmatch = 1;
1120 case 'y': scan_symlink = 0; break;
519 case 'B': show_banner = 0; break; 1121 case 'B': show_banner = 0; break;
520 case 'l': scan_ldpath = 1; break; 1122 case 'l': scan_ldpath = 1; break;
521 case 'p': scan_envpath = 1; break; 1123 case 'p': scan_envpath = 1; break;
522 case 'R': dir_recurse = 1; break; 1124 case 'R': dir_recurse = 1; break;
523 case 'm': dir_crossmount = 0; break; 1125 case 'm': dir_crossmount = 0; break;
524 case 'x': show_pax = 1; break; 1126 case 'x': show_pax = 1; break;
525 case 'e': show_stack = 1; break; 1127 case 'e': show_phdr = 1; break;
526 case 't': show_textrel = 1; break; 1128 case 't': show_textrel = 1; break;
527 case 'r': show_rpath = 1; break; 1129 case 'r': show_rpath = 1; break;
528 case 'n': show_needed = 1; break; 1130 case 'n': show_needed = 1; break;
1131 case 'i': show_interp = 1; break;
1132 case 'b': show_bind = 1; break;
1133 case 'S': show_soname = 1; break;
1134 case 'T': show_textrels = 1; break;
529 case 'q': be_quiet = 1; break; 1135 case 'q': be_quiet = 1; break;
530 case 'v': be_verbose = (be_verbose % 20) + 1; break; 1136 case 'v': be_verbose = (be_verbose % 20) + 1; break;
531 case 'a': show_pax = show_stack = show_textrel = show_needed = show_rpath = 1; break; 1137 case 'a': show_pax = show_phdr = show_textrel = show_rpath = show_bind = 1; break;
532 1138
533 case ':': 1139 case ':':
534 warn("Option missing parameter"); 1140 err("Option missing parameter\n");
535 usage(EXIT_FAILURE);
536 break;
537 case '?': 1141 case '?':
538 warn("Unknown option"); 1142 err("Unknown option\n");
539 usage(EXIT_FAILURE);
540 break;
541 default: 1143 default:
542 err("Unhandled option '%c'", flag); 1144 err("Unhandled option '%c'", i);
543 break; 1145 }
1146 }
1147
1148 /* let the format option override all other options */
1149 if (out_format) {
1150 show_pax = show_phdr = show_textrel = show_rpath = \
1151 show_needed = show_interp = show_bind = show_soname = \
1152 show_textrels = 0;
1153 for (i = 0; out_format[i]; ++i) {
1154 if (!IS_MODIFIER(out_format[i])) continue;
1155
1156 switch (out_format[++i]) {
1157 case '%': break;
1158 case '#': break;
1159 case 'F': break;
1160 case 'p': break;
1161 case 'f': break;
1162 case 's': break;
1163 case 'N': break;
1164 case 'o': break;
1165 case 'x': show_pax = 1; break;
1166 case 'e': show_phdr = 1; break;
1167 case 't': show_textrel = 1; break;
1168 case 'r': show_rpath = 1; break;
1169 case 'n': show_needed = 1; break;
1170 case 'i': show_interp = 1; break;
1171 case 'b': show_bind = 1; break;
1172 case 'S': show_soname = 1; break;
1173 case 'T': show_textrels = 1; break;
1174 default:
1175 err("Invalid format specifier '%c' (byte %i)",
1176 out_format[i], i+1);
544 } 1177 }
545 } 1178 }
546 1179
547 if (be_quiet && be_verbose) 1180 /* construct our default format */
548 err("You can be quiet or you can be verbose, not both, stupid"); 1181 } else {
1182 size_t fmt_len = 30;
1183 out_format = (char*)xmalloc(sizeof(char) * fmt_len);
1184 if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len);
1185 if (show_pax) xstrcat(&out_format, "%x ", &fmt_len);
1186 if (show_phdr) xstrcat(&out_format, "%e ", &fmt_len);
1187 if (show_textrel) xstrcat(&out_format, "%t ", &fmt_len);
1188 if (show_rpath) xstrcat(&out_format, "%r ", &fmt_len);
1189 if (show_needed) xstrcat(&out_format, "%n ", &fmt_len);
1190 if (show_interp) xstrcat(&out_format, "%i ", &fmt_len);
1191 if (show_bind) xstrcat(&out_format, "%b ", &fmt_len);
1192 if (show_soname) xstrcat(&out_format, "%S ", &fmt_len);
1193 if (show_textrels) xstrcat(&out_format, "%T ", &fmt_len);
1194 if (find_sym) xstrcat(&out_format, "%s ", &fmt_len);
1195 if (find_lib) xstrcat(&out_format, "%N ", &fmt_len);
1196 if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len);
1197 }
1198 if (be_verbose > 2) printf("Format: %s\n", out_format);
549 1199
1200 /* now lets actually do the scanning */
1201 if (scan_ldpath || (show_rpath && be_quiet))
1202 load_ld_so_conf();
550 if (scan_ldpath) scanelf_ldpath(); 1203 if (scan_ldpath) scanelf_ldpath();
551 if (scan_envpath) scanelf_envpath(); 1204 if (scan_envpath) scanelf_envpath();
1205 if (from_file) {
1206 scanelf_from_file(from_file);
1207 free(from_file);
1208 from_file = *argv;
1209 }
552 if (optind == argc && !scan_ldpath && !scan_envpath) 1210 if (optind == argc && !scan_ldpath && !scan_envpath && !from_file)
553 err("Nothing to scan !?"); 1211 err("Nothing to scan !?");
554 while (optind < argc) 1212 while (optind < argc) {
555 scanelf_dir(argv[optind++]); 1213 search_path = argv[optind++];
1214 scanelf_dir(search_path);
1215 }
556 1216
1217 /* clean up */
1218 if (find_sym) {
1219 free(find_sym);
1220 free(versioned_symname);
1221 }
557 if (find_sym) free(find_sym); 1222 if (find_lib) free(find_lib);
1223 if (out_format) free(out_format);
1224 for (i = 0; ldpaths[i]; ++i)
1225 free(ldpaths[i]);
1226}
1227
1228
1229
1230/* utility funcs */
1231static char *xstrdup(const char *s)
1232{
1233 char *ret = strdup(s);
1234 if (!ret) err("Could not strdup(): %s", strerror(errno));
1235 return ret;
1236}
1237
1238static void *xmalloc(size_t size)
1239{
1240 void *ret = malloc(size);
1241 if (!ret) err("Could not malloc() %li bytes", (unsigned long)size);
1242 return ret;
1243}
1244
1245static void xstrcat(char **dst, const char *src, size_t *curr_len)
1246{
1247 size_t new_len;
1248
1249 new_len = strlen(*dst) + strlen(src);
1250 if (*curr_len <= new_len) {
1251 *curr_len = new_len + (*curr_len / 2);
1252 *dst = realloc(*dst, *curr_len);
1253 if (!*dst)
1254 err("could not realloc %li bytes", (unsigned long)*curr_len);
1255 }
1256
1257 strcat(*dst, src);
1258}
1259
1260static inline void xchrcat(char **dst, const char append, size_t *curr_len)
1261{
1262 static char my_app[2];
1263 my_app[0] = append;
1264 my_app[1] = '\0';
1265 xstrcat(dst, my_app, curr_len);
558} 1266}
559 1267
560 1268
561 1269
562int main(int argc, char *argv[]) 1270int main(int argc, char *argv[])
563{ 1271{
564 if (argc < 2) 1272 if (argc < 2)
565 usage(EXIT_FAILURE); 1273 usage(EXIT_FAILURE);
566 parseargs(argc, argv); 1274 parseargs(argc, argv);
567 fclose(stdout); 1275 fclose(stdout);
1276#ifdef __BOUNDS_CHECKING_ON
1277 warn("The calls to add/delete heap should be off by 1 due to the out_buffer not being freed in scanelf_file()");
1278#endif
568 return EXIT_SUCCESS; 1279 return EXIT_SUCCESS;
569} 1280}

Legend:
Removed from v.1.35  
changed lines
  Added in v.1.90

  ViewVC Help
Powered by ViewVC 1.1.20