Home | History | Annotate | Download | only in elftree
      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 
     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 
     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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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