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