blob: d7ded13f495a773c1f84619831bfe4f49963b1ab [file] [log] [blame]
Thomas Tuttle60a7ddf2010-07-30 17:51:59 -07001#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
12static enum { HIDE_DUPS, PRUNE_DUPS, SHOW_DUPS } dup_mode;
13static char *root_name;
14
15static 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
26static 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
40static 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
51struct seen {
52 char *name;
53 struct seen *next;
54};
55
56struct tree_state {
57 int level;
58 struct seen *seen;
59};
60
61static 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
73static 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
82char *indent_str = " ";
83
84static 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
92struct search_dir {
93 char *path;
94 struct search_dir *next;
95} *dirs = NULL;
96
97static 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
106struct file_state {
107 struct tree_state *t;
108 Elf *e;
109 Elf_Data *strtab_data;
110};
111
112static 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
125struct dyn_state {
126 struct file_state *f;
127 Elf_Data *dyn_data;
128 int count;
129};
130
131static 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
146static int dump_file(struct tree_state *t, char *name, char *path);
147
148static 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
172static 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
203static 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
256static 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
265static 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
290static 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
306int 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}