1 |
/* |
2 |
* Copyright 2008 Gentoo Foundation |
3 |
* Distributed under the terms of the GNU General Public License v2 |
4 |
* $Header: /var/cvsroot/gentoo-projects/pax-utils/scanmacho.c,v 1.20 2010/12/08 01:16:01 vapier Exp $ |
5 |
* |
6 |
* based on scanelf by: |
7 |
* Copyright 2003-2007 Ned Ludd - <solar@gentoo.org> |
8 |
* Copyright 2004-2007 Mike Frysinger - <vapier@gentoo.org> |
9 |
* for Darwin specific fun: |
10 |
* 2008-2010 Fabian Groffen - <grobian@gentoo.org> |
11 |
*/ |
12 |
|
13 |
static const char *rcsid = "$Id: scanmacho.c,v 1.20 2010/12/08 01:16:01 vapier Exp $"; |
14 |
const char argv0[] = "scanmacho"; |
15 |
|
16 |
#include "paxinc.h" |
17 |
|
18 |
#define IS_MODIFIER(c) (c == '%' || c == '#' || c == '+') |
19 |
|
20 |
/* prototypes */ |
21 |
static int scanmacho_fatobj(fatobj *fobj); |
22 |
static int scanmacho_file(const char *filename, const struct stat *st_cache); |
23 |
static int scanmacho_from_file(const char *filename); |
24 |
static int scanmacho_dir(const char *path); |
25 |
static void scanmacho_envpath(void); |
26 |
static void usage(int status); |
27 |
static int parseargs(int argc, char *argv[]); |
28 |
|
29 |
/* variables to control behavior */ |
30 |
static char match_etypes[126] = ""; |
31 |
static char scan_envpath = 0; |
32 |
static char scan_symlink = 1; |
33 |
static char scan_archives = 0; |
34 |
static char dir_recurse = 0; |
35 |
static char dir_crossmount = 1; |
36 |
static char show_perms = 0; |
37 |
static char show_size = 0; |
38 |
static char show_needed = 0; |
39 |
static char show_interp = 0; |
40 |
static char show_bind = 0; |
41 |
static char show_soname = 0; |
42 |
static char show_banner = 1; |
43 |
static char show_endian = 0; |
44 |
static char be_quiet = 0; |
45 |
static char be_verbose = 0; |
46 |
static char be_wewy_wewy_quiet = 0; |
47 |
static char be_semi_verbose = 0; |
48 |
static char *find_lib = NULL; |
49 |
static char *out_format = NULL; |
50 |
static char *search_path = NULL; |
51 |
static char g_match = 0; |
52 |
|
53 |
static int match_bits = 0; |
54 |
static unsigned int match_perms = 0; |
55 |
|
56 |
static const char *macho_file_needed_lib( |
57 |
fatobj *fobj, |
58 |
char *found_needed, |
59 |
char *found_lib, |
60 |
int op, |
61 |
char **ret, |
62 |
size_t *ret_len |
63 |
) { |
64 |
loadcmd *lcmd; |
65 |
uint32_t lc_load_dylib; |
66 |
|
67 |
if ((op == 0 && !show_needed) || (op == 1 && !find_lib)) |
68 |
return NULL; |
69 |
|
70 |
lcmd = firstloadcmd(fobj); |
71 |
lc_load_dylib = MGET(fobj->swapped, LC_LOAD_DYLIB); |
72 |
|
73 |
do { |
74 |
if (lcmd->lcmd->cmd == lc_load_dylib) { |
75 |
struct dylib_command *dlcmd = lcmd->data; |
76 |
char *needed; |
77 |
needed = lcmd->data + MGET(fobj->swapped, dlcmd->dylib.name.offset); |
78 |
if (op == 0) { |
79 |
if (!be_wewy_wewy_quiet) { |
80 |
if (*found_needed) |
81 |
xchrcat(ret, ',', ret_len); |
82 |
xstrcat(ret, needed, ret_len); |
83 |
} |
84 |
*found_needed = 1; |
85 |
} else { |
86 |
if (!strncmp(find_lib, needed, |
87 |
strlen(!g_match ? needed : find_lib))) |
88 |
{ |
89 |
*found_lib = 1; |
90 |
free(lcmd); |
91 |
return (be_wewy_wewy_quiet ? NULL : needed); |
92 |
} |
93 |
} |
94 |
} |
95 |
} while (nextloadcmd(lcmd)); |
96 |
|
97 |
if (op == 0 && !*found_needed && be_verbose) |
98 |
warn("Mach-O lacks LC_LOAD_DYLIB commands: %s", fobj->filename); |
99 |
|
100 |
return NULL; |
101 |
} |
102 |
|
103 |
static char *macho_file_interp(fatobj *fobj, char *found_interp) |
104 |
{ |
105 |
loadcmd *lcmd; |
106 |
uint32_t lc_load_dylinker; |
107 |
|
108 |
if (!show_interp) |
109 |
return NULL; |
110 |
|
111 |
lcmd = firstloadcmd(fobj); |
112 |
lc_load_dylinker = MGET(fobj->swapped, LC_LOAD_DYLINKER); |
113 |
|
114 |
do { |
115 |
if (lcmd->lcmd->cmd == lc_load_dylinker) { |
116 |
struct dylinker_command *dlcmd = lcmd->data; |
117 |
char *dylinker; |
118 |
dylinker = lcmd->data + MGET(fobj->swapped, dlcmd->name.offset); |
119 |
*found_interp = 1; |
120 |
free(lcmd); |
121 |
return (be_wewy_wewy_quiet ? NULL : dylinker); |
122 |
} |
123 |
} while (nextloadcmd(lcmd)); |
124 |
|
125 |
return NULL; |
126 |
} |
127 |
|
128 |
static char *macho_file_soname(fatobj *fobj, char *found_soname) |
129 |
{ |
130 |
loadcmd *lcmd; |
131 |
uint32_t lc_id_dylib; |
132 |
|
133 |
if (!show_soname) |
134 |
return NULL; |
135 |
|
136 |
lcmd = firstloadcmd(fobj); |
137 |
lc_id_dylib = MGET(fobj->swapped, LC_ID_DYLIB); |
138 |
|
139 |
do { |
140 |
if (lcmd->lcmd->cmd == lc_id_dylib) { |
141 |
struct dylib_command *dlcmd = lcmd->data; |
142 |
char *soname; |
143 |
soname = lcmd->data + MGET(fobj->swapped, dlcmd->dylib.name.offset); |
144 |
*found_soname = 1; |
145 |
free(lcmd); |
146 |
return (be_wewy_wewy_quiet ? NULL : soname); |
147 |
} |
148 |
} while (nextloadcmd(lcmd)); |
149 |
|
150 |
return NULL; |
151 |
} |
152 |
|
153 |
/* scan a macho file and show all the fun stuff */ |
154 |
#define prints(str) ({ ssize_t ret = write(fileno(stdout), str, strlen(str)); ret; }) |
155 |
static int scanmacho_fatobj(fatobj *fobj) |
156 |
{ |
157 |
unsigned long i; |
158 |
char found_needed, found_interp, found_soname, found_lib, found_file; |
159 |
static char *out_buffer = NULL; |
160 |
static size_t out_len; |
161 |
|
162 |
found_needed = found_interp = found_soname = \ |
163 |
found_lib = found_file = 0; |
164 |
|
165 |
if (be_verbose > 2) |
166 |
printf("%s: scanning file {%s,%s}\n", fobj->filename, |
167 |
get_machocputype(fobj), |
168 |
get_machosubcputype(fobj)); |
169 |
else if (be_verbose > 1) |
170 |
printf("%s: scanning file\n", fobj->filename); |
171 |
|
172 |
/* init output buffer */ |
173 |
if (!out_buffer) { |
174 |
out_len = sizeof(char) * 80; |
175 |
out_buffer = xmalloc(out_len); |
176 |
} |
177 |
*out_buffer = '\0'; |
178 |
|
179 |
/* show the header */ |
180 |
if (!be_quiet && show_banner) { |
181 |
for (i = 0; out_format[i]; ++i) { |
182 |
if (!IS_MODIFIER(out_format[i])) continue; |
183 |
|
184 |
switch (out_format[++i]) { |
185 |
case '+': break; |
186 |
case '%': break; |
187 |
case '#': break; |
188 |
case 'F': |
189 |
case 'p': |
190 |
case 'f': prints("FILE "); found_file = 1; break; |
191 |
case 'o': prints(" TYPE "); break; |
192 |
case 'M': prints("CPU "); break; |
193 |
case 'n': prints("NEEDED "); break; |
194 |
case 'i': prints("DYLINKER "); break; |
195 |
case 'b': prints("FLAGS "); break; |
196 |
case 'Z': prints("SIZE "); break; |
197 |
case 'S': prints("INSTALLNAME "); break; |
198 |
case 'N': prints("LIB "); break; |
199 |
case 'a': prints("ARCH "); break; |
200 |
case 'O': prints("PERM "); break; |
201 |
case 'D': prints("ENDIAN "); break; |
202 |
default: warnf("'%c' has no title ?", out_format[i]); |
203 |
} |
204 |
} |
205 |
if (!found_file) prints("FILE "); |
206 |
prints("\n"); |
207 |
found_file = 0; |
208 |
show_banner = 0; |
209 |
} |
210 |
|
211 |
/* dump all the good stuff */ |
212 |
for (i = 0; out_format[i]; ++i) { |
213 |
const char *out; |
214 |
const char *tmp; |
215 |
static char ubuf[sizeof(unsigned long)*2]; |
216 |
if (!IS_MODIFIER(out_format[i])) { |
217 |
xchrcat(&out_buffer, out_format[i], &out_len); |
218 |
continue; |
219 |
} |
220 |
|
221 |
out = NULL; |
222 |
be_wewy_wewy_quiet = (out_format[i] == '#'); |
223 |
be_semi_verbose = (out_format[i] == '+'); |
224 |
switch (out_format[++i]) { |
225 |
case '+': |
226 |
case '%': |
227 |
case '#': |
228 |
xchrcat(&out_buffer, out_format[i], &out_len); break; |
229 |
case 'F': |
230 |
found_file = 1; |
231 |
if (be_wewy_wewy_quiet) break; |
232 |
xstrcat(&out_buffer, fobj->filename, &out_len); |
233 |
break; |
234 |
case 'p': |
235 |
found_file = 1; |
236 |
if (be_wewy_wewy_quiet) break; |
237 |
tmp = fobj->filename; |
238 |
if (search_path) { |
239 |
ssize_t len_search = strlen(search_path); |
240 |
ssize_t len_file = strlen(fobj->filename); |
241 |
if (!strncmp(fobj->filename, search_path, len_search) && \ |
242 |
len_file > len_search) |
243 |
tmp += len_search; |
244 |
if (*tmp == '/' && search_path[len_search-1] == '/') tmp++; |
245 |
} |
246 |
xstrcat(&out_buffer, tmp, &out_len); |
247 |
break; |
248 |
case 'f': |
249 |
found_file = 1; |
250 |
if (be_wewy_wewy_quiet) break; |
251 |
xstrcat(&out_buffer, fobj->base_filename, &out_len); |
252 |
break; |
253 |
case 'o': out = get_machomhtype(fobj); break; |
254 |
case 'M': out = get_machocputype(fobj); break; |
255 |
case 'D': out = get_machoendian(fobj); break; |
256 |
case 'O': out = strfileperms(fobj->filename); break; |
257 |
case 'n': |
258 |
case 'N': out = macho_file_needed_lib(fobj, &found_needed, &found_lib, (out_format[i]=='N'), &out_buffer, &out_len); break; |
259 |
case 'i': out = macho_file_interp(fobj, &found_interp); break; |
260 |
case 'b': get_machomhflags(fobj, &out_buffer, &out_len); break; |
261 |
case 'S': out = macho_file_soname(fobj, &found_soname); break; |
262 |
case 'a': out = get_machomtype(fobj); break; |
263 |
case 'Z': snprintf(ubuf, sizeof(ubuf), "%llu", (unsigned long long int)fobj->len); out = ubuf; break;; |
264 |
default: warnf("'%c' has no scan code?", out_format[i]); |
265 |
} |
266 |
if (out) { |
267 |
/* hack for comma delimited output like `scanelf -s sym1,sym2,sym3` */ |
268 |
if (out_format[i] == 's' && (tmp=strchr(out,',')) != NULL) |
269 |
xstrncat(&out_buffer, out, &out_len, (tmp-out)); |
270 |
else |
271 |
xstrcat(&out_buffer, out, &out_len); |
272 |
} |
273 |
} |
274 |
|
275 |
#define FOUND_SOMETHING() \ |
276 |
( found_needed || found_interp || found_soname || found_lib ) |
277 |
|
278 |
if (!found_file && (!be_quiet || (be_quiet && FOUND_SOMETHING()))) { |
279 |
xchrcat(&out_buffer, ' ', &out_len); |
280 |
xstrcat(&out_buffer, fobj->filename, &out_len); |
281 |
} |
282 |
if (!be_quiet || (be_quiet && FOUND_SOMETHING())) { |
283 |
puts(out_buffer); |
284 |
fflush(stdout); |
285 |
} |
286 |
|
287 |
return 0; |
288 |
} |
289 |
|
290 |
/* scan a single Mach-O */ |
291 |
static int scanmacho_fat(const char *filename, int fd, size_t len) |
292 |
{ |
293 |
int ret = 1; |
294 |
fatobj *fobj; |
295 |
fatobj *walk; |
296 |
|
297 |
/* verify this is real Mach-O */ |
298 |
if ((fobj = readmacho_fd(filename, fd, len)) == NULL) { |
299 |
if (be_verbose > 2) printf("%s: not a Mach-O object\n", filename); |
300 |
return ret; |
301 |
} |
302 |
switch (match_bits) { |
303 |
case 32: |
304 |
case 64: |
305 |
walk = fobj; |
306 |
do { |
307 |
if ((walk->ismach64 && match_bits == 64) || |
308 |
(!walk->ismach64 && match_bits == 32)) |
309 |
{ |
310 |
ret = scanmacho_fatobj(walk); |
311 |
} |
312 |
} while (walk->next != NULL && (walk = walk->next)); |
313 |
goto label_done; |
314 |
default: |
315 |
break; |
316 |
} |
317 |
if (strlen(match_etypes)) { |
318 |
char sbuf[128]; |
319 |
char ftype[32]; |
320 |
|
321 |
snprintf(sbuf, 128, ",%s,", match_etypes); |
322 |
|
323 |
walk = fobj; |
324 |
do { |
325 |
snprintf(ftype, 32, ",%s,", get_machomhtype(walk)); |
326 |
if (strstr(sbuf, ftype) != NULL) { |
327 |
ret = scanmacho_fatobj(walk); |
328 |
} |
329 |
} while (walk->next != NULL && (walk = walk->next)); |
330 |
goto label_done; |
331 |
} |
332 |
|
333 |
walk = fobj; |
334 |
do { |
335 |
ret = scanmacho_fatobj(walk); |
336 |
} while (walk->next != NULL && (walk = walk->next)); |
337 |
|
338 |
label_done: |
339 |
unreadmacho(fobj); |
340 |
return ret; |
341 |
} |
342 |
|
343 |
/* scan an archive of Mach-Os */ |
344 |
static int scanmacho_archive(const char *filename, int fd, size_t len) |
345 |
{ |
346 |
archive_handle *ar; |
347 |
archive_member *m; |
348 |
char *ar_buffer; |
349 |
fatobj *fobj; |
350 |
fatobj *walk; |
351 |
|
352 |
ar = ar_open_fd(filename, fd); |
353 |
if (ar == NULL) |
354 |
return 1; |
355 |
|
356 |
ar_buffer = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0); |
357 |
while ((m = ar_next(ar)) != NULL) { |
358 |
fobj = readmacho_buffer(m->name, ar_buffer + lseek(fd, 0, SEEK_CUR), m->size); |
359 |
if (fobj) { |
360 |
walk = fobj; |
361 |
do { |
362 |
scanmacho_fatobj(walk); |
363 |
} while (walk->next != NULL && (walk = walk->next)); |
364 |
fobj->data = NULL; |
365 |
unreadmacho(fobj); |
366 |
} |
367 |
} |
368 |
munmap(ar_buffer, len); |
369 |
|
370 |
return 0; |
371 |
} |
372 |
/* scan a file which may be an Mach-O or an archive or some other |
373 |
* magical beast */ |
374 |
static int scanmacho_file(const char *filename, const struct stat *st_cache) |
375 |
{ |
376 |
const struct stat *st = st_cache; |
377 |
struct stat symlink_st; |
378 |
int fd; |
379 |
|
380 |
/* always handle regular files and handle symlinked files if no -y */ |
381 |
if (S_ISLNK(st->st_mode)) { |
382 |
if (!scan_symlink) |
383 |
return 1; |
384 |
stat(filename, &symlink_st); |
385 |
st = &symlink_st; |
386 |
} |
387 |
|
388 |
if (!S_ISREG(st->st_mode)) { |
389 |
if (be_verbose > 2) printf("%s: skipping non-file\n", filename); |
390 |
return 1; |
391 |
} |
392 |
|
393 |
if (match_perms) { |
394 |
if ((st->st_mode | match_perms) != st->st_mode) |
395 |
return 1; |
396 |
} |
397 |
if ((fd = open(filename, O_RDONLY)) == -1) |
398 |
return 1; |
399 |
|
400 |
if (scanmacho_fat(filename, fd, st->st_size) == 1 && scan_archives) |
401 |
/* if it isn't an Mach-O, maybe it's an .a archive */ |
402 |
scanmacho_archive(filename, fd, st->st_size); |
403 |
|
404 |
close(fd); |
405 |
return 0; |
406 |
} |
407 |
|
408 |
/* scan a directory for Mach-O files and print when we find one */ |
409 |
static int scanmacho_dir(const char *path) |
410 |
{ |
411 |
register DIR *dir; |
412 |
register struct dirent *dentry; |
413 |
struct stat st_top, st; |
414 |
char buf[__PAX_UTILS_PATH_MAX]; |
415 |
size_t pathlen = 0, len = 0; |
416 |
int ret = 0; |
417 |
|
418 |
/* make sure path exists */ |
419 |
if (lstat(path, &st_top) == -1) { |
420 |
if (be_verbose > 2) printf("%s: does not exist\n", path); |
421 |
return 1; |
422 |
} |
423 |
|
424 |
/* ok, if it isn't a directory, assume we can open it */ |
425 |
if (!S_ISDIR(st_top.st_mode)) |
426 |
return scanmacho_file(path, &st_top); |
427 |
|
428 |
/* now scan the dir looking for fun stuff */ |
429 |
if ((dir = opendir(path)) == NULL) { |
430 |
warnf("could not opendir %s: %s", path, strerror(errno)); |
431 |
return 1; |
432 |
} |
433 |
if (be_verbose > 1) printf("%s: scanning dir\n", path); |
434 |
|
435 |
pathlen = strlen(path); |
436 |
while ((dentry = readdir(dir))) { |
437 |
if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) |
438 |
continue; |
439 |
len = (pathlen + 1 + strlen(dentry->d_name) + 1); |
440 |
if (len >= sizeof(buf)) { |
441 |
warnf("Skipping '%s': len > sizeof(buf); %lu > %lu\n", path, |
442 |
(unsigned long)len, (unsigned long)sizeof(buf)); |
443 |
continue; |
444 |
} |
445 |
snprintf(buf, sizeof(buf), "%s%s%s", path, (path[pathlen-1] == '/') ? "" : "/", dentry->d_name); |
446 |
if (lstat(buf, &st) != -1) { |
447 |
if (S_ISREG(st.st_mode)) |
448 |
ret = scanmacho_file(buf, &st); |
449 |
else if (dir_recurse && S_ISDIR(st.st_mode)) { |
450 |
if (dir_crossmount || (st_top.st_dev == st.st_dev)) |
451 |
ret = scanmacho_dir(buf); |
452 |
} |
453 |
} |
454 |
} |
455 |
closedir(dir); |
456 |
return ret; |
457 |
} |
458 |
|
459 |
static int scanmacho_from_file(const char *filename) |
460 |
{ |
461 |
FILE *fp = NULL; |
462 |
char *p; |
463 |
char path[__PAX_UTILS_PATH_MAX]; |
464 |
int ret = 0; |
465 |
|
466 |
if (strcmp(filename, "-") == 0) |
467 |
fp = stdin; |
468 |
else if ((fp = fopen(filename, "r")) == NULL) |
469 |
return 1; |
470 |
|
471 |
while ((fgets(path, __PAX_UTILS_PATH_MAX, fp)) != NULL) { |
472 |
if ((p = strchr(path, '\n')) != NULL) |
473 |
*p = 0; |
474 |
search_path = path; |
475 |
ret = scanmacho_dir(path); |
476 |
} |
477 |
if (fp != stdin) |
478 |
fclose(fp); |
479 |
return ret; |
480 |
} |
481 |
|
482 |
/* scan env PATH for paths */ |
483 |
static void scanmacho_envpath(void) |
484 |
{ |
485 |
char *path, *p; |
486 |
|
487 |
path = getenv("PATH"); |
488 |
if (!path) |
489 |
err("PATH is not set in your env !"); |
490 |
path = xstrdup(path); |
491 |
|
492 |
while ((p = strrchr(path, ':')) != NULL) { |
493 |
scanmacho_dir(p + 1); |
494 |
*p = 0; |
495 |
} |
496 |
|
497 |
free(path); |
498 |
} |
499 |
|
500 |
/* usage / invocation handling functions */ /* Free Flags: c d e j k l r s t u w x z G H I J K L P Q T U W X Y */ |
501 |
#define PARSE_FLAGS "pRmyAnibSN:gE:M:DO:ZaqvF:f:o:CBhV" |
502 |
#define a_argument required_argument |
503 |
static struct option const long_opts[] = { |
504 |
{"path", no_argument, NULL, 'p'}, |
505 |
{"recursive", no_argument, NULL, 'R'}, |
506 |
{"mount", no_argument, NULL, 'm'}, |
507 |
{"symlink", no_argument, NULL, 'y'}, |
508 |
{"archives", no_argument, NULL, 'A'}, |
509 |
{"needed", no_argument, NULL, 'n'}, |
510 |
{"interp", no_argument, NULL, 'i'}, |
511 |
{"bind", no_argument, NULL, 'b'}, |
512 |
{"soname", no_argument, NULL, 'S'}, |
513 |
{"lib", a_argument, NULL, 'N'}, |
514 |
{"gmatch", no_argument, NULL, 'g'}, |
515 |
{"etype", a_argument, NULL, 'E'}, |
516 |
{"bits", a_argument, NULL, 'M'}, |
517 |
{"endian", no_argument, NULL, 'D'}, |
518 |
{"perms", a_argument, NULL, 'O'}, |
519 |
{"size", no_argument, NULL, 'Z'}, |
520 |
{"all", no_argument, NULL, 'a'}, |
521 |
{"quiet", no_argument, NULL, 'q'}, |
522 |
{"verbose", no_argument, NULL, 'v'}, |
523 |
{"format", a_argument, NULL, 'F'}, |
524 |
{"from", a_argument, NULL, 'f'}, |
525 |
{"file", a_argument, NULL, 'o'}, |
526 |
{"nocolor", no_argument, NULL, 'C'}, |
527 |
{"nobanner", no_argument, NULL, 'B'}, |
528 |
{"help", no_argument, NULL, 'h'}, |
529 |
{"version", no_argument, NULL, 'V'}, |
530 |
{NULL, no_argument, NULL, 0x0} |
531 |
}; |
532 |
|
533 |
static const char * const opts_help[] = { |
534 |
"Scan all directories in PATH environment", |
535 |
"Scan directories recursively", |
536 |
"Don't recursively cross mount points", |
537 |
"Don't scan symlinks", |
538 |
"Scan archives (.a files)\n", |
539 |
"Print LC_LOAD_DYLIB information (ELF: NEEDED)", |
540 |
"Print LC_LOAD_DYLINKER information (ELF: INTERP)", |
541 |
"Print flags from mach_header (ELF: BIND)", |
542 |
"Print LC_ID_DYLIB information (ELF: SONAME)", |
543 |
"Find a specified library", |
544 |
"Use strncmp to match libraries. (use with -N)", |
545 |
"Print only Mach-O files matching mach_header\n" |
546 |
" MH_OBJECT,MH_EXECUTE ... (ELF: etype)", |
547 |
"Print only Mach-O files matching numeric bits", |
548 |
"Print Endianness", |
549 |
"Print only Mach-O files matching octal permissions", |
550 |
"Print Mach-O file size", |
551 |
"Print all useful/simple info\n", |
552 |
"Only output 'bad' things", |
553 |
"Be verbose (can be specified more than once)", |
554 |
"Use specified format for output", |
555 |
"Read input stream from a filename", |
556 |
"Write output stream to a filename", |
557 |
"Don't emit color in output", |
558 |
"Don't display the header", |
559 |
"Print this help and exit", |
560 |
"Print version and exit", |
561 |
NULL |
562 |
}; |
563 |
|
564 |
/* display usage and exit */ |
565 |
static void usage(int status) |
566 |
{ |
567 |
unsigned long i; |
568 |
printf("* Scan Mach-O binaries for stuff\n\n" |
569 |
"Usage: %s [options] <dir1/file1> [dir2 dirN file2 fileN ...]\n\n", argv0); |
570 |
printf("Options: -[%s]\n", PARSE_FLAGS); |
571 |
for (i = 0; long_opts[i].name; ++i) |
572 |
if (long_opts[i].has_arg == no_argument) |
573 |
printf(" -%c, --%-14s* %s\n", long_opts[i].val, |
574 |
long_opts[i].name, opts_help[i]); |
575 |
else |
576 |
printf(" -%c, --%-7s <arg> * %s\n", long_opts[i].val, |
577 |
long_opts[i].name, opts_help[i]); |
578 |
|
579 |
puts("\nFor more information, see the scanmacho(1) manpage"); |
580 |
exit(status); |
581 |
} |
582 |
|
583 |
/* parse command line arguments and preform needed actions */ |
584 |
static int parseargs(int argc, char *argv[]) |
585 |
{ |
586 |
int i; |
587 |
const char *from_file = NULL; |
588 |
int ret = 0; |
589 |
|
590 |
opterr = 0; |
591 |
while ((i = getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) { |
592 |
switch (i) { |
593 |
|
594 |
case 'V': |
595 |
printf("pax-utils-%s: %s compiled %s\n%s\n" |
596 |
"%s written for Gentoo by <solar, vapier and grobian @ gentoo.org>\n", |
597 |
VERSION, __FILE__, __DATE__, rcsid, argv0); |
598 |
exit(EXIT_SUCCESS); |
599 |
break; |
600 |
case 'h': usage(EXIT_SUCCESS); break; |
601 |
case 'f': |
602 |
if (from_file) warn("You prob don't want to specify -f twice"); |
603 |
from_file = optarg; |
604 |
break; |
605 |
case 'E': |
606 |
strncpy(match_etypes, optarg, sizeof(match_etypes)); |
607 |
break; |
608 |
case 'M': |
609 |
match_bits = atoi(optarg); |
610 |
break; |
611 |
case 'O': |
612 |
if (sscanf(optarg, "%o", &match_perms) == -1) |
613 |
match_bits = 0; |
614 |
break; |
615 |
case 'o': { |
616 |
if (freopen(optarg, "w", stdout) == NULL) |
617 |
err("Could not open output stream '%s': %s", optarg, strerror(errno)); |
618 |
break; |
619 |
} |
620 |
case 'N': { |
621 |
if (find_lib) warn("You prob don't want to specify -N twice"); |
622 |
find_lib = optarg; |
623 |
break; |
624 |
} |
625 |
case 'F': { |
626 |
if (out_format) warn("You prob don't want to specify -F twice"); |
627 |
out_format = optarg; |
628 |
break; |
629 |
} |
630 |
case 'Z': show_size = 1; break; |
631 |
case 'g': g_match = 1; break; |
632 |
case 'y': scan_symlink = 0; break; |
633 |
case 'A': scan_archives = 1; break; |
634 |
case 'C': color_init(true); break; |
635 |
case 'B': show_banner = 0; break; |
636 |
case 'p': scan_envpath = 1; break; |
637 |
case 'R': dir_recurse = 1; break; |
638 |
case 'm': dir_crossmount = 0; break; |
639 |
case 'n': show_needed = 1; break; |
640 |
case 'i': show_interp = 1; break; |
641 |
case 'b': show_bind = 1; break; |
642 |
case 'S': show_soname = 1; break; |
643 |
case 'q': be_quiet = 1; break; |
644 |
case 'v': be_verbose = (be_verbose % 20) + 1; break; |
645 |
case 'a': show_perms = show_endian = show_bind = 1; break; |
646 |
case 'D': show_endian = 1; break; |
647 |
case ':': |
648 |
err("Option '%c' is missing parameter", optopt); |
649 |
case '?': |
650 |
err("Unknown option '%c' or argument missing", optopt); |
651 |
default: |
652 |
err("Unhandled option '%c'; please report this", i); |
653 |
} |
654 |
} |
655 |
/* let the format option override all other options */ |
656 |
if (out_format) { |
657 |
show_needed = show_interp = show_bind = show_soname = \ |
658 |
show_perms = show_endian = show_size = 0; |
659 |
for (i = 0; out_format[i]; ++i) { |
660 |
if (!IS_MODIFIER(out_format[i])) continue; |
661 |
|
662 |
switch (out_format[++i]) { |
663 |
case '+': break; |
664 |
case '%': break; |
665 |
case '#': break; |
666 |
case 'F': break; |
667 |
case 'p': break; |
668 |
case 'f': break; |
669 |
case 'k': break; |
670 |
case 'N': break; |
671 |
case 'o': break; |
672 |
case 'a': break; |
673 |
case 'M': break; |
674 |
case 'Z': show_size = 1; break; |
675 |
case 'D': show_endian = 1; break; |
676 |
case 'O': show_perms = 1; break; |
677 |
case 'n': show_needed = 1; break; |
678 |
case 'i': show_interp = 1; break; |
679 |
case 'b': show_bind = 1; break; |
680 |
case 'S': show_soname = 1; break; |
681 |
default: |
682 |
err("Invalid format specifier '%c' (byte %i)", |
683 |
out_format[i], i+1); |
684 |
} |
685 |
} |
686 |
|
687 |
/* construct our default format */ |
688 |
} else { |
689 |
size_t fmt_len = 30; |
690 |
out_format = xmalloc(sizeof(char) * fmt_len); |
691 |
*out_format = '\0'; |
692 |
if (!be_quiet) xstrcat(&out_format, "%a ", &fmt_len); |
693 |
if (!be_quiet) xstrcat(&out_format, "%o ", &fmt_len); |
694 |
if (show_perms) xstrcat(&out_format, "%O ", &fmt_len); |
695 |
if (show_size) xstrcat(&out_format, "%Z ", &fmt_len); |
696 |
if (show_endian) xstrcat(&out_format, "%D ", &fmt_len); |
697 |
if (show_needed) xstrcat(&out_format, "%n ", &fmt_len); |
698 |
if (show_interp) xstrcat(&out_format, "%i ", &fmt_len); |
699 |
if (show_bind) xstrcat(&out_format, "%b ", &fmt_len); |
700 |
if (show_soname) xstrcat(&out_format, "%S ", &fmt_len); |
701 |
if (find_lib) xstrcat(&out_format, "%N ", &fmt_len); |
702 |
if (!be_quiet) xstrcat(&out_format, "%F ", &fmt_len); |
703 |
} |
704 |
if (be_verbose > 2) printf("Format: %s\n", out_format); |
705 |
|
706 |
/* now lets actually do the scanning */ |
707 |
if (scan_envpath) |
708 |
scanmacho_envpath(); |
709 |
if (!from_file && optind == argc && ttyname(0) == NULL && !scan_envpath) |
710 |
from_file = "-"; |
711 |
if (from_file) { |
712 |
scanmacho_from_file(from_file); |
713 |
from_file = *argv; |
714 |
} |
715 |
if (optind == argc && !scan_envpath && !from_file) |
716 |
err("Nothing to scan !?"); |
717 |
while (optind < argc) { |
718 |
search_path = argv[optind++]; |
719 |
ret = scanmacho_dir(search_path); |
720 |
} |
721 |
|
722 |
return ret; |
723 |
} |
724 |
|
725 |
int main(int argc, char *argv[]) |
726 |
{ |
727 |
int ret; |
728 |
if (argc < 2) |
729 |
usage(EXIT_FAILURE); |
730 |
color_init(false); |
731 |
ret = parseargs(argc, argv); |
732 |
fclose(stdout); |
733 |
return ret; |
734 |
} |