160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle#include <errno.h>
260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle#include <fcntl.h>
360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle#include <gelf.h>
460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle#include <stdarg.h>
560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle#include <stdio.h>
660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle#include <stdlib.h>
760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle#include <string.h>
860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle#include <unistd.h>
960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
1060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle#define PATH_MAX 256
1160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
1260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic enum { HIDE_DUPS, PRUNE_DUPS, SHOW_DUPS } dup_mode;
1360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic char *root_name;
1460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
1560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic void app_err(const char *fmt, ...)
1660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
1760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	va_list ap;
1860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
1960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	va_start(ap, fmt);
2060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	vfprintf(stderr, fmt, ap);
2160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	va_end(ap);
2260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
2360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	fprintf(stderr, "\n");
2460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
2560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
2660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic void unix_err(const char *fmt, ...)
2760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
2860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	va_list ap;
2960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	int errsv;
3060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
3160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	errsv = errno;
3260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
3360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	va_start(ap, fmt);
3460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	vfprintf(stderr, fmt, ap);
3560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	va_end(ap);
3660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
3760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	fprintf(stderr, ": %s\n", strerror(errsv));
3860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
3960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
4060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic void elf_err(const char *fmt, ...)
4160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
4260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	va_list ap;
4360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
4460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	va_start(ap, fmt);
4560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	vfprintf(stderr, fmt, ap);
4660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	va_end(ap);
4760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
4860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	fprintf(stderr, ": %s\n", elf_errmsg(-1));
4960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
5060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
5160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestruct seen {
5260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	char *name;
5360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct seen *next;
5460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle};
5560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
5660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestruct tree_state {
5760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	int level;
5860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct seen *seen;
5960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle};
6060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
6160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic int seen(struct tree_state *t, char *name)
6260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
6360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct seen *s;
6460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
6560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	for (s = t->seen; s; s = s->next) {
6660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		if (!strcmp(s->name, name))
6760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			return 1;
6860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
6960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
7060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	return 0;
7160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
7260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
7360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic void see(struct tree_state *t, char *name)
7460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
7560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct seen *s = malloc(sizeof(*s));
7660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	s->name = malloc(strlen(name) + 1);
7760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	strcpy(s->name, name);
7860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	s->next = t->seen;
7960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	t->seen = s;
8060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
8160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
8260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlechar *indent_str = "  ";
8360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
8460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic void indent(struct tree_state *t)
8560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
8660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	int i;
8760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
8860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	for (i = 0; i < t->level; i++)
8960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		printf("%s", indent_str);
9060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
9160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
9260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestruct search_dir {
9360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	char *path;
9460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct search_dir *next;
9560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle} *dirs = NULL;
9660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
9760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic void add_search_dir(char *path)
9860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
9960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct search_dir *dir = malloc(sizeof(*dir));
10060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	dir->path = malloc(strlen(path) + 1);
10160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	strcpy(dir->path, path);
10260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	dir->next = dirs;
10360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	dirs = dir;
10460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
10560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
10660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestruct file_state {
10760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct tree_state *t;
10860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	Elf *e;
10960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	Elf_Data *strtab_data;
11060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle};
11160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
11260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic Elf_Scn *find_scn(struct file_state *f, GElf_Word sht, Elf_Scn *scn, GElf_Shdr *shdr_out)
11360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
11460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	while ((scn = elf_nextscn(f->e, scn))) {
11560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		if (!gelf_getshdr(scn, shdr_out))
11660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			continue;
11760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
11860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		if (shdr_out->sh_type == sht)
11960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			break;
12060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
12160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
12260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	return scn;
12360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
12460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
12560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestruct dyn_state {
12660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct file_state *f;
12760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	Elf_Data *dyn_data;
12860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	int count;
12960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle};
13060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
13160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic int find_dyn(struct dyn_state *d, GElf_Sxword tag, GElf_Dyn *dyn_out)
13260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
13360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	int i;
13460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
13560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	for (i = 0; i < d->count; i++) {
13660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		if (!gelf_getdyn(d->dyn_data, i, dyn_out))
13760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			continue;
13860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
13960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		if (dyn_out->d_tag == tag)
14060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			return 0;
14160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
14260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
14360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	return -1;
14460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
14560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
14660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic int dump_file(struct tree_state *t, char *name, char *path);
14760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
14860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic int dump_needed(struct tree_state *t, char *name)
14960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
15060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct search_dir *dir;
15160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	char path[PATH_MAX];
15260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	int fd;
15360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
15460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	t->level++;
15560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
15660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	for (dir = dirs; dir; dir = dir->next) {
15760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		snprintf(path, PATH_MAX, "%s/%s", dir->path, name);
15860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		fd = open(path, O_RDONLY);
15960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		if (fd >= 0) {
16060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			close(fd);
16160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			dump_file(t, name, path);
16260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			t->level--;
16360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			return 0;
16460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		}
16560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
16660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
16760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	app_err("Couldn't resolve dependency \"%s\".", name);
16860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	t->level--;
16960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	return -1;
17060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
17160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
17260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic int dump_dynamic(struct file_state *f, Elf_Scn *scn, GElf_Shdr *shdr)
17360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
17460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct dyn_state d;
17560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	GElf_Dyn needed_dyn;
17660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	char *needed_name;
17760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	int i;
17860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
17960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	d.f = f;
18060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	d.dyn_data = elf_getdata(scn, NULL);
18160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	if (!d.dyn_data) {
18260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		elf_err("elf_getdata failed");
18360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		return -1;
18460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
18560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	d.count = shdr->sh_size / shdr->sh_entsize;
18660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
18760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	for (i = 0; i < d.count; i++) {
18860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		if (!gelf_getdyn(d.dyn_data, i, &needed_dyn))
18960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			continue;
19060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
19160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		if (needed_dyn.d_tag != DT_NEEDED)
19260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			continue;
19360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
19460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		needed_name = (char *)f->strtab_data->d_buf
19560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			      + needed_dyn.d_un.d_val;
19660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
19760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		dump_needed(f->t, needed_name);
19860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
19960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
20060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	return 0;
20160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
20260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
20360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic int dump_file(struct tree_state *t, char *name, char *file)
20460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
20560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct file_state f;
20660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	int fd;
20760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	Elf_Scn *scn;
20860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	GElf_Shdr shdr;
20960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
21060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	if ((dup_mode == HIDE_DUPS) && seen(t, name))
21160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		return 0;
21260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
21360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	indent(t); printf("%s", name);
21460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
21560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	if ((dup_mode == PRUNE_DUPS) && seen(t, name)) {
21660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		printf("...\n");
21760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		return 0;
21860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	} else {
21960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		printf(":\n");
22060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
22160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
22260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	see(t, name);
22360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
22460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	f.t = t;
22560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
22660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	fd = open(file, O_RDONLY);
22760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	if (fd < 0) {
22860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		unix_err("open(%s) failed", file);
22960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		return -1;
23060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
23160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
23260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	f.e = elf_begin(fd, ELF_C_READ, NULL);
23360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	if (!f.e) {
23460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		elf_err("elf_begin failed on %s", file);
23560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		return -1;
23660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
23760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
23860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	scn = find_scn(&f, SHT_STRTAB, NULL, &shdr);
23960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	f.strtab_data = elf_getdata(scn, NULL);
24060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	if (!f.strtab_data) {
24160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		app_err("%s has no strtab section", file);
24260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		return -1;
24360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
24460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
24560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	scn = NULL;
24660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	while ((scn = find_scn(&f, SHT_DYNAMIC, scn, &shdr))) {
24760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		dump_dynamic(&f, scn, &shdr);
24860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
24960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
25060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	elf_end(f.e);
25160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	close(fd);
25260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
25360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	return 0;
25460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
25560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
25660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic void usage(void)
25760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
25860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	fprintf(stderr, "Usage: elftree [ -s | -h ] elf-file\n"
25960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	                "  -S  Duplicate entire subtree when a duplicate is found\n"
26060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			"  -P  Show duplicates, but only include subtree once\n"
26160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			"  -H  Show each library at most once, even if duplicated\n"
26260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			"  -h  Show this help screen\n");
26360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
26460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
26560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic int parse_args(int argc, char *argv[])
26660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
26760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	int i;
26860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
26960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	for (i = 1; i < argc - 1; i++) {
27060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		if (!strcmp(argv[i], "-S")) {
27160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			dup_mode = SHOW_DUPS;
27260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		} else if (!strcmp(argv[i], "-P")) {
27360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			dup_mode = PRUNE_DUPS;
27460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		} else if (!strcmp(argv[i], "-H")) {
27560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			dup_mode = HIDE_DUPS;
27660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		} else if (!strcmp(argv[i], "-h")) {
27760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			usage();
27860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			exit(0);
27960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		} else {
28060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			app_err("Unexpected argument \"%s\"!\n", argv[i]);
28160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle			return -1;
28260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		}
28360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
28460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
28560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	root_name = argv[argc - 1];
28660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
28760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	return 0;
28860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
28960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
29060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttlestatic void add_search_dirs(void)
29160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
29260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	char *relpath;
29360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	char path[PATH_MAX];
29460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
29560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	relpath = getenv("ANDROID_PRODUCT_OUT");
29660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	if (!relpath) {
29760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		app_err("Warning: ANDROID_PRODUCT_OUT not set; "
29860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		        "using current directory.\n");
29960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		relpath = ".";
30060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
30160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
30260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	snprintf(path, PATH_MAX, "%s/%s", relpath, "system/lib");
30360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	add_search_dir(path);
30460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
30560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
30660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttleint main(int argc, char *argv[])
30760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle{
30860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	struct tree_state t;
30960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
31060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	if (argc < 2 || parse_args(argc, argv)) {
31160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		usage();
31260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		exit(EXIT_FAILURE);
31360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
31460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
31560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	if (elf_version(EV_CURRENT) == EV_NONE) {
31660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		elf_err("version mismatch");
31760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle		exit(EXIT_FAILURE);
31860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	}
31960a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
32060a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	t.level = 0;
32160a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	t.seen  = NULL;
32260a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
32360a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	add_search_dirs();
32460a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
32560a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	dump_file(&t, root_name, root_name);
32660a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle
32760a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle	return 0;
32860a7ddf27ecc584d89018e16d90287cffae0bcdaThomas Tuttle}
329