• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <errno.h>
2 #include <fcntl.h>
3 #include <gelf.h>
4 #include <stdarg.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 
10 #define PATH_MAX 256
11 
12 static enum { HIDE_DUPS, PRUNE_DUPS, SHOW_DUPS } dup_mode;
13 static char *root_name;
14 
app_err(const char * fmt,...)15 static void app_err(const char *fmt, ...)
16 {
17 	va_list ap;
18 
19 	va_start(ap, fmt);
20 	vfprintf(stderr, fmt, ap);
21 	va_end(ap);
22 
23 	fprintf(stderr, "\n");
24 }
25 
unix_err(const char * fmt,...)26 static void unix_err(const char *fmt, ...)
27 {
28 	va_list ap;
29 	int errsv;
30 
31 	errsv = errno;
32 
33 	va_start(ap, fmt);
34 	vfprintf(stderr, fmt, ap);
35 	va_end(ap);
36 
37 	fprintf(stderr, ": %s\n", strerror(errsv));
38 }
39 
elf_err(const char * fmt,...)40 static void elf_err(const char *fmt, ...)
41 {
42 	va_list ap;
43 
44 	va_start(ap, fmt);
45 	vfprintf(stderr, fmt, ap);
46 	va_end(ap);
47 
48 	fprintf(stderr, ": %s\n", elf_errmsg(-1));
49 }
50 
51 struct seen {
52 	char *name;
53 	struct seen *next;
54 };
55 
56 struct tree_state {
57 	int level;
58 	struct seen *seen;
59 };
60 
seen(struct tree_state * t,char * name)61 static int seen(struct tree_state *t, char *name)
62 {
63 	struct seen *s;
64 
65 	for (s = t->seen; s; s = s->next) {
66 		if (!strcmp(s->name, name))
67 			return 1;
68 	}
69 
70 	return 0;
71 }
72 
see(struct tree_state * t,char * name)73 static void see(struct tree_state *t, char *name)
74 {
75 	struct seen *s = malloc(sizeof(*s));
76 	s->name = malloc(strlen(name) + 1);
77 	strcpy(s->name, name);
78 	s->next = t->seen;
79 	t->seen = s;
80 }
81 
82 char *indent_str = "  ";
83 
indent(struct tree_state * t)84 static void indent(struct tree_state *t)
85 {
86 	int i;
87 
88 	for (i = 0; i < t->level; i++)
89 		printf("%s", indent_str);
90 }
91 
92 struct search_dir {
93 	char *path;
94 	struct search_dir *next;
95 } *dirs = NULL;
96 
add_search_dir(char * path)97 static void add_search_dir(char *path)
98 {
99 	struct search_dir *dir = malloc(sizeof(*dir));
100 	dir->path = malloc(strlen(path) + 1);
101 	strcpy(dir->path, path);
102 	dir->next = dirs;
103 	dirs = dir;
104 }
105 
106 struct file_state {
107 	struct tree_state *t;
108 	Elf *e;
109 	Elf_Data *strtab_data;
110 };
111 
find_scn(struct file_state * f,GElf_Word sht,Elf_Scn * scn,GElf_Shdr * shdr_out)112 static Elf_Scn *find_scn(struct file_state *f, GElf_Word sht, Elf_Scn *scn, GElf_Shdr *shdr_out)
113 {
114 	while ((scn = elf_nextscn(f->e, scn))) {
115 		if (!gelf_getshdr(scn, shdr_out))
116 			continue;
117 
118 		if (shdr_out->sh_type == sht)
119 			break;
120 	}
121 
122 	return scn;
123 }
124 
125 struct dyn_state {
126 	struct file_state *f;
127 	Elf_Data *dyn_data;
128 	int count;
129 };
130 
find_dyn(struct dyn_state * d,GElf_Sxword tag,GElf_Dyn * dyn_out)131 static int find_dyn(struct dyn_state *d, GElf_Sxword tag, GElf_Dyn *dyn_out)
132 {
133 	int i;
134 
135 	for (i = 0; i < d->count; i++) {
136 		if (!gelf_getdyn(d->dyn_data, i, dyn_out))
137 			continue;
138 
139 		if (dyn_out->d_tag == tag)
140 			return 0;
141 	}
142 
143 	return -1;
144 }
145 
146 static int dump_file(struct tree_state *t, char *name, char *path);
147 
dump_needed(struct tree_state * t,char * name)148 static int dump_needed(struct tree_state *t, char *name)
149 {
150 	struct search_dir *dir;
151 	char path[PATH_MAX];
152 	int fd;
153 
154 	t->level++;
155 
156 	for (dir = dirs; dir; dir = dir->next) {
157 		snprintf(path, PATH_MAX, "%s/%s", dir->path, name);
158 		fd = open(path, O_RDONLY);
159 		if (fd >= 0) {
160 			close(fd);
161 			dump_file(t, name, path);
162 			t->level--;
163 			return 0;
164 		}
165 	}
166 
167 	app_err("Couldn't resolve dependency \"%s\".", name);
168 	t->level--;
169 	return -1;
170 }
171 
dump_dynamic(struct file_state * f,Elf_Scn * scn,GElf_Shdr * shdr)172 static int dump_dynamic(struct file_state *f, Elf_Scn *scn, GElf_Shdr *shdr)
173 {
174 	struct dyn_state d;
175 	GElf_Dyn needed_dyn;
176 	char *needed_name;
177 	int i;
178 
179 	d.f = f;
180 	d.dyn_data = elf_getdata(scn, NULL);
181 	if (!d.dyn_data) {
182 		elf_err("elf_getdata failed");
183 		return -1;
184 	}
185 	d.count = shdr->sh_size / shdr->sh_entsize;
186 
187 	for (i = 0; i < d.count; i++) {
188 		if (!gelf_getdyn(d.dyn_data, i, &needed_dyn))
189 			continue;
190 
191 		if (needed_dyn.d_tag != DT_NEEDED)
192 			continue;
193 
194 		needed_name = (char *)f->strtab_data->d_buf
195 			      + needed_dyn.d_un.d_val;
196 
197 		dump_needed(f->t, needed_name);
198 	}
199 
200 	return 0;
201 }
202 
dump_file(struct tree_state * t,char * name,char * file)203 static int dump_file(struct tree_state *t, char *name, char *file)
204 {
205 	struct file_state f;
206 	int fd;
207 	Elf_Scn *scn;
208 	GElf_Shdr shdr;
209 
210 	if ((dup_mode == HIDE_DUPS) && seen(t, name))
211 		return 0;
212 
213 	indent(t); printf("%s", name);
214 
215 	if ((dup_mode == PRUNE_DUPS) && seen(t, name)) {
216 		printf("...\n");
217 		return 0;
218 	} else {
219 		printf(":\n");
220 	}
221 
222 	see(t, name);
223 
224 	f.t = t;
225 
226 	fd = open(file, O_RDONLY);
227 	if (fd < 0) {
228 		unix_err("open(%s) failed", file);
229 		return -1;
230 	}
231 
232 	f.e = elf_begin(fd, ELF_C_READ, NULL);
233 	if (!f.e) {
234 		elf_err("elf_begin failed on %s", file);
235 		return -1;
236 	}
237 
238 	scn = find_scn(&f, SHT_STRTAB, NULL, &shdr);
239 	f.strtab_data = elf_getdata(scn, NULL);
240 	if (!f.strtab_data) {
241 		app_err("%s has no strtab section", file);
242 		return -1;
243 	}
244 
245 	scn = NULL;
246 	while ((scn = find_scn(&f, SHT_DYNAMIC, scn, &shdr))) {
247 		dump_dynamic(&f, scn, &shdr);
248 	}
249 
250 	elf_end(f.e);
251 	close(fd);
252 
253 	return 0;
254 }
255 
usage(void)256 static void usage(void)
257 {
258 	fprintf(stderr, "Usage: elftree [ -s | -h ] elf-file\n"
259 	                "  -S  Duplicate entire subtree when a duplicate is found\n"
260 			"  -P  Show duplicates, but only include subtree once\n"
261 			"  -H  Show each library at most once, even if duplicated\n"
262 			"  -h  Show this help screen\n");
263 }
264 
parse_args(int argc,char * argv[])265 static int parse_args(int argc, char *argv[])
266 {
267 	int i;
268 
269 	for (i = 1; i < argc - 1; i++) {
270 		if (!strcmp(argv[i], "-S")) {
271 			dup_mode = SHOW_DUPS;
272 		} else if (!strcmp(argv[i], "-P")) {
273 			dup_mode = PRUNE_DUPS;
274 		} else if (!strcmp(argv[i], "-H")) {
275 			dup_mode = HIDE_DUPS;
276 		} else if (!strcmp(argv[i], "-h")) {
277 			usage();
278 			exit(0);
279 		} else {
280 			app_err("Unexpected argument \"%s\"!\n", argv[i]);
281 			return -1;
282 		}
283 	}
284 
285 	root_name = argv[argc - 1];
286 
287 	return 0;
288 }
289 
add_search_dirs(void)290 static void add_search_dirs(void)
291 {
292 	char *relpath;
293 	char path[PATH_MAX];
294 
295 	relpath = getenv("ANDROID_PRODUCT_OUT");
296 	if (!relpath) {
297 		app_err("Warning: ANDROID_PRODUCT_OUT not set; "
298 		        "using current directory.\n");
299 		relpath = ".";
300 	}
301 
302 	snprintf(path, PATH_MAX, "%s/%s", relpath, "system/lib");
303 	add_search_dir(path);
304 }
305 
main(int argc,char * argv[])306 int main(int argc, char *argv[])
307 {
308 	struct tree_state t;
309 
310 	if (argc < 2 || parse_args(argc, argv)) {
311 		usage();
312 		exit(EXIT_FAILURE);
313 	}
314 
315 	if (elf_version(EV_CURRENT) == EV_NONE) {
316 		elf_err("version mismatch");
317 		exit(EXIT_FAILURE);
318 	}
319 
320 	t.level = 0;
321 	t.seen  = NULL;
322 
323 	add_search_dirs();
324 
325 	dump_file(&t, root_name, root_name);
326 
327 	return 0;
328 }
329