OSDN Git Service

reconcile tools-docs into main branch
[android-x86/build.git] / tools / lsd / lsd.c
1 #include <stdio.h>
2 #include <common.h>
3 #include <debug.h>
4 #include <libelf.h>
5 #include <libebl.h>
6 #include <elf.h>
7 #include <gelf.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <hash.h>
16 #include <lsd.h>
17
18 extern int verbose_flag;
19
20 typedef struct source_t source_t;
21
22 typedef struct {
23     Elf_Scn *scn;
24     GElf_Shdr shdr;
25     Elf_Data *data;
26 } section_info_t;
27
28 typedef struct next_export_t { 
29     source_t *source;
30     int next_idx;
31 } next_export_t;
32
33 struct source_t {
34     source_t *next;
35     int visited;
36
37     char *name;  /* full path name of this executable file */
38     /* ELF-related information: */
39     Elf *elf;
40     int elf_fd;
41     GElf_Ehdr elf_hdr;
42     size_t shstrndx;
43     int shnum; /* number of sections */
44
45     section_info_t symtab;
46     section_info_t strtab;
47     section_info_t dynamic;
48     section_info_t hash;
49
50     section_info_t *relocations;
51     int num_relocations; /* number of relocs (<= relocations_size) */
52     int relocations_size; /* sice of array -- NOT number of relocs! */
53
54         /* satisfied_execs: array containing pointers to the libraries or 
55            executables that this executable satisfies symbol references for. */
56         source_t **satisfied_execs;
57     int num_satisfied_execs;
58     int satisfied_execs_size;
59
60     /* satisfied: array is parallel to symbol table; for each undefined symbol 
61        in that array, we maintain a flag stating whether that symbol has been 
62        satisfied, and if so, by which library.  This applies both to executable
63        files and libraries.
64     */
65     source_t **satisfied;
66
67     /* exports: array is parallel to symbol table; for each global symbol 
68        in that array, we maintain a flag stating whether that symbol satisfies 
69        a dependency in some other file.  num_syms is the length of the exports
70        array, as well as the satisfied array. This applied to libraries only.
71
72        next_exports:  this is a bit tricky.  We use this field to maintain a 
73        linked list of source_t for each global symbol of a shared library. 
74        For a shared library's global symbol at index N has the property that
75        exports[N] is the head of a linked list (threaded through next_export)
76        of all source_t that this symbol resolves a reference to.  For example, 
77        if symbol printf has index 1000 in libc.so, and an executable A and 
78        library L use printf, then the source_t entry corresponding to libc.so
79        will have exports[1000] be a linked list that contains the nodes for 
80        application A and library L.
81     */
82
83     next_export_t *exports;
84     /* num_exported is the number of symbols in this file actually used by
85        somebody else;  it's not the size of the exports array. */
86     int num_exported;
87     next_export_t *next_export;
88     int num_next_export;
89     int next_export_size;
90
91     int num_syms; /* number of symbols in symbol table.  This is the length of
92                      both exports[] and satisfied[] arrays. */
93
94     /* This is an array that contains one element for each library dependency
95        listed in the executable or shared library. */
96     source_t **lib_deps; /* list of library dependencies */
97     int num_lib_deps; /* actual number of library dependencies */
98     int lib_deps_size; /* size of lib_deps array--NOT actual number of deps! */
99
100 };
101
102 static source_t *sources = NULL;
103
104 static char * find_file(const char *libname, 
105                         char **lib_lookup_dirs, 
106                         int num_lib_lookup_dirs);
107
108 static inline source_t* find_source(const char *name,
109                                     char **lib_lookup_dirs, 
110                                     int num_lib_lookup_dirs) {
111     source_t *trav = sources;
112         char *full = find_file(name, lib_lookup_dirs, num_lib_lookup_dirs);
113     FAILIF(full == NULL, "Cannot construct full path for file [%s]!\n", name);
114     while (trav) {
115         if (!strcmp(trav->name, full))
116             break;
117         trav = trav->next;
118     }
119         free(full);
120     return trav;
121 }
122
123 static inline void add_to_sources(source_t *src) {
124     src->next = sources;
125     sources = src;
126 }
127
128 static source_t* init_source(char *full_path) {
129     source_t *source = (source_t *)CALLOC(1, sizeof(source_t));
130
131     ASSERT(full_path);
132     source->name = full_path;
133     source->elf_fd = -1;
134
135     INFO("Opening %s...\n", full_path);
136     source->elf_fd = open(full_path, O_RDONLY);
137     FAILIF(source->elf_fd < 0, "open(%s): %s (%d)\n", 
138            full_path, 
139            strerror(errno), 
140            errno);
141     INFO("Calling elf_begin(%s)...\n", full_path);
142     source->elf = elf_begin(source->elf_fd, ELF_C_READ, NULL);
143     FAILIF_LIBELF(source->elf == NULL, elf_begin);
144
145     /* libelf can recognize COFF and A.OUT formats, but we handle only ELF. */
146     if (elf_kind(source->elf) != ELF_K_ELF) {
147         ERROR("Input file %s is not in ELF format!\n", full_path);
148         return NULL;
149     }
150
151     /* Make sure this is a shared library or an executable. */
152     {
153         INFO("Making sure %s is a shared library or an executable...\n", 
154              full_path);
155         FAILIF_LIBELF(0 == gelf_getehdr(source->elf, &source->elf_hdr), gelf_getehdr);
156         FAILIF(source->elf_hdr.e_type != ET_DYN && 
157                source->elf_hdr.e_type != ET_EXEC,
158                "%s must be a shared library (elf type is %d, expecting %d).\n", 
159                full_path,
160                source->elf_hdr.e_type, 
161                ET_DYN);
162     }
163
164     /* Get the index of the section-header-strings-table section. */
165     FAILIF_LIBELF(elf_getshstrndx (source->elf, &source->shstrndx) < 0, 
166                   elf_getshstrndx);
167
168     FAILIF_LIBELF(elf_getshnum (source->elf, &source->shnum) < 0, elf_getshnum);
169
170     /* Find various sections. */
171     size_t scnidx;
172     Elf_Scn *scn;
173     GElf_Shdr *shdr, shdr_mem;
174     INFO("Locating %d sections in %s...\n", source->shnum, full_path);
175     for (scnidx = 1; scnidx < source->shnum; scnidx++) {
176         scn = elf_getscn(source->elf, scnidx);
177         FAILIF_LIBELF(NULL == scn, elf_getscn);
178         shdr = gelf_getshdr(scn, &shdr_mem);
179         FAILIF_LIBELF(NULL == shdr, gelf_getshdr);
180         INFO("\tfound section [%s]...\n", elf_strptr(source->elf, source->shstrndx, shdr->sh_name));
181         if (shdr->sh_type == SHT_DYNSYM) {
182             source->symtab.scn = scn;
183             source->symtab.data = elf_getdata(scn, NULL);
184             FAILIF_LIBELF(NULL == source->symtab.data, elf_getdata);
185             memcpy(&source->symtab.shdr, shdr, sizeof(GElf_Shdr));
186
187             /* The sh_link field of the section header of the symbol table
188                contains the index of the associated strings table. */
189             source->strtab.scn = elf_getscn(source->elf, 
190                                             source->symtab.shdr.sh_link);
191             FAILIF_LIBELF(NULL == source->strtab.scn, elf_getscn);
192             FAILIF_LIBELF(NULL == gelf_getshdr(scn, &source->strtab.shdr),
193                           gelf_getshdr);
194             source->strtab.data = elf_getdata(source->strtab.scn, NULL);
195             FAILIF_LIBELF(NULL == source->strtab.data, elf_getdata);
196         }
197         else if (shdr->sh_type == SHT_DYNAMIC) {
198             source->dynamic.scn = scn;
199             source->dynamic.data = elf_getdata(scn, NULL);
200             FAILIF_LIBELF(NULL == source->symtab.data, elf_getdata);
201             memcpy(&source->dynamic.shdr, shdr, sizeof(GElf_Shdr));
202         }
203         else if (shdr->sh_type == SHT_HASH) {
204             source->hash.scn = scn;
205             source->hash.data = elf_getdata(scn, NULL);
206             FAILIF_LIBELF(NULL == source->hash.data, elf_getdata);
207             memcpy(&source->hash.shdr, shdr, sizeof(GElf_Shdr));
208         }
209         else if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) {
210             if (source->num_relocations == source->relocations_size) {
211                 source->relocations_size += 5;
212                 source->relocations = 
213                     (section_info_t *)REALLOC(source->relocations,
214                                               source->relocations_size *
215                                               sizeof(section_info_t));
216             }
217             section_info_t *reloc = 
218                 source->relocations + source->num_relocations;
219             reloc->scn = scn;
220             reloc->data = elf_getdata(scn, NULL);
221             FAILIF_LIBELF(NULL == reloc->data, elf_getdata);
222             memcpy(&reloc->shdr, shdr, sizeof(GElf_Shdr));
223             source->num_relocations++;
224         }
225     }
226
227     if (source->dynamic.scn == NULL) {
228         INFO("File [%s] does not have a dynamic section!\n", full_path);
229         return 0;
230     }
231
232     FAILIF(source->symtab.scn == NULL, 
233            "File [%s] does not have a dynamic symbol table!\n",
234            full_path);
235
236     FAILIF(source->hash.scn == NULL, 
237            "File [%s] does not have a hash table!\n",
238            full_path);
239     FAILIF(source->hash.shdr.sh_link != elf_ndxscn(source->symtab.scn),
240            "Hash points to section %d, not to %d as expected!\n",
241            source->hash.shdr.sh_link,
242            elf_ndxscn(scn));
243
244     /* Now, find out how many symbols we have and allocate the array of 
245        satisfied symbols.
246
247        NOTE: We don't count the number of undefined symbols here; we will 
248        iterate over the symbol table later, and count them then, when it is 
249        more convenient. 
250     */
251     size_t symsize = gelf_fsize (source->elf, 
252                                  ELF_T_SYM, 
253                                  1, source->elf_hdr.e_version);
254     ASSERT(symsize);
255
256     source->num_syms = source->symtab.data->d_size / symsize;
257     source->satisfied = (source_t **)CALLOC(source->num_syms, 
258                                             sizeof(source_t *));
259     source->exports = (source_t **)CALLOC(source->num_syms, 
260                                           sizeof(next_export_t));
261
262     source->num_exported = 0;
263     source->satisfied_execs = NULL;
264     source->num_satisfied_execs = 0;
265     source->satisfied_execs_size = 0;
266
267     add_to_sources(source);
268     return source;
269 }
270
271 static void destroy_source(source_t *source) {
272     FREE(source->satisfied_execs);
273     FREE(source->satisfied);
274     FREE(source->exports);
275     FREE(source->next_export);    
276     FREE(source->lib_deps); /* list of library dependencies */
277     FAILIF_LIBELF(elf_end(source->elf), elf_end);
278     FAILIF(close(source->elf_fd) < 0, "Could not close file %s: %s (%d)!\n", 
279            source->name, strerror(errno), errno);
280     FREE(source->name);
281     FREE(source);
282 }
283
284 static void print_needed_libs(source_t *source)
285 {
286         size_t idx;
287         for (idx = 0; idx < source->num_lib_deps; idx++) {
288                 PRINT("%s:%s\n", 
289                           source->name, 
290                           source->lib_deps[idx]->name);
291         }
292 }
293
294 static int is_symbol_imported(source_t *source,
295                               GElf_Sym *sym, 
296                               size_t symidx)
297 {
298     const char *symname = elf_strptr(source->elf,
299                                      elf_ndxscn(source->strtab.scn),
300                                      sym->st_name);
301
302     /* A symbol is imported by an executable or a library if it is undefined
303        and is either global or weak. There is an additional case for 
304        executables that we will check below. */
305     if (sym->st_shndx == SHN_UNDEF &&
306         (GELF_ST_BIND(sym->st_info) == STB_GLOBAL ||
307          GELF_ST_BIND(sym->st_info) == STB_WEAK)) {
308         INFO("*** symbol [%s:%s] is imported (UNDEFIEND).\n",
309              source->name,
310              symname);
311         return 1;
312     }
313
314 #ifdef ARM_SPECIFIC_HACKS
315     /* A symbol is imported by an executable if is marked as an undefined 
316        symbol--this is standard to all ELF formats.  Alternatively, according 
317        to the ARM specifications, a symbol in a BSS section that is also marked
318        by an R_ARM_COPY relocation is also imported. */
319
320     if (source->elf_hdr.e_type != ET_EXEC) {
321         INFO("is_symbol_imported(): [%s] is a library, "
322              "no further checks.\n", source->name);
323         return 0;
324     }
325
326     /* Is the symbol in the BSS section, and is there a COPY relocation on 
327        that symbol? */
328     INFO("*** [%s:%s] checking further to see if symbol is imported.\n",
329          source->name, symname);
330     if (sym->st_shndx < source->shnum) {
331         /* Is it the .bss section? */
332         Elf_Scn *scn = elf_getscn(source->elf, sym->st_shndx);
333         FAILIF_LIBELF(NULL == scn, elf_getscn);
334         GElf_Shdr *shdr, shdr_mem;
335         shdr = gelf_getshdr(scn, &shdr_mem);
336         FAILIF_LIBELF(NULL == shdr, gelf_getshdr);
337         if (!strcmp(".bss", elf_strptr(source->elf,
338                                        source->shstrndx,
339                                        shdr->sh_name)))
340         {
341             /* Is there an R_ARM_COPY relocation on this symbol?  Iterate 
342                over the list of relocation sections and scan each section for
343                an entry that matches the symbol. */
344             size_t idx;
345             for (idx = 0; idx < source->num_relocations; idx++) {
346                 section_info_t *reloc = source->relocations + idx;
347                 /* Does the relocation section refer to the symbol table in
348                    which this symbol resides, and does it relocate the .bss
349                    section? */
350                 if (reloc->shdr.sh_link == elf_ndxscn(source->symtab.scn) &&
351                     reloc->shdr.sh_info == sym->st_shndx)
352                 {
353                     /* Go over the relocations and see if any of them matches
354                        our symbol. */
355                     size_t nrels = reloc->shdr.sh_size / reloc->shdr.sh_entsize;
356                     size_t relidx, newidx;
357                     if (reloc->shdr.sh_type == SHT_REL) {
358                         for (newidx = relidx = 0; relidx < nrels; ++relidx) {
359                             GElf_Rel rel_mem;
360                             FAILIF_LIBELF(gelf_getrel (reloc->data, 
361                                                        relidx, 
362                                                        &rel_mem) == NULL,
363                                           gelf_getrel);
364                             if (GELF_R_TYPE(rel_mem.r_info) == R_ARM_COPY &&
365                                 GELF_R_SYM (rel_mem.r_info) == symidx)
366                             {
367                                 INFO("*** symbol [%s:%s] is imported "
368                                      "(DEFINED, REL-COPY-RELOCATED).\n",
369                                      source->name,
370                                      symname);
371                                 return 1;
372                             }
373                         } /* for each rel entry... */
374                     } else {
375                         for (newidx = relidx = 0; relidx < nrels; ++relidx) {
376                             GElf_Rela rel_mem;
377                             FAILIF_LIBELF(gelf_getrela (reloc->data, 
378                                                         relidx, 
379                                                         &rel_mem) == NULL,
380                                           gelf_getrela);
381                             if (GELF_R_TYPE(rel_mem.r_info) == R_ARM_COPY &&
382                                 GELF_R_SYM (rel_mem.r_info) == symidx)
383                             {
384                                 INFO("*** symbol [%s:%s] is imported "
385                                      "(DEFINED, RELA-COPY-RELOCATED).\n",
386                                      source->name,
387                                      symname);
388                                 return 1;
389                             }
390                         } /* for each rela entry... */
391                     } /* if rel else rela */
392                 }
393             }
394         }
395     }
396 #endif/*ARM_SPECIFIC_HACKS*/
397
398     return 0;
399 }
400
401 static void resolve(source_t *source) {
402     /* Iterate the symbol table.  For each undefined symbol, scan the 
403        list of dependencies till we find a global symbol in one of them that 
404        satisfies the undefined reference.  At this point, we update both the 
405        satisfied[] array of the sources entry, as well as the exports array of 
406        the dependency where we found the match.
407     */
408
409     GElf_Sym *sym, sym_mem;
410     size_t symidx;
411     for (symidx = 0; symidx < source->num_syms; symidx++) {
412         sym = gelf_getsymshndx(source->symtab.data, 
413                                NULL,
414                                symidx,
415                                &sym_mem,
416                                NULL);
417         FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
418         if (is_symbol_imported(source, sym, symidx)) 
419                 {
420             /* This is an undefined symbol.  Go over the list of libraries 
421                and look it up. */
422             size_t libidx;
423                         int found = 0;
424                         source_t *last_found = NULL;
425                         const char *symname = elf_strptr(source->elf,
426                                                                                          elf_ndxscn(source->strtab.scn),
427                                                                                          sym->st_name);
428             for (libidx = 0; libidx < source->num_lib_deps; libidx++) {
429                 source_t *lib = source->lib_deps[libidx];
430                 int lib_symidx = hash_lookup(lib->elf,
431                                              lib->hash.data,
432                                              lib->symtab.data,
433                                              lib->strtab.data,
434                                              symname);
435                 if (STN_UNDEF != lib_symidx)
436                 {
437                                         /* We found the symbol--now check to see if it is global 
438                                            or weak.  If this is the case, then the symbol satisfies
439                                            the dependency. */
440                                         GElf_Sym *lib_sym, lib_sym_mem;
441                                         lib_sym = gelf_getsymshndx(lib->symtab.data, 
442                                                                                            NULL,
443                                                                                            lib_symidx,
444                                                                                            &lib_sym_mem,
445                                                                                            NULL);
446                                         FAILIF_LIBELF(NULL == lib_sym, gelf_getsymshndx);
447
448                                         if(lib_sym->st_shndx != STN_UNDEF &&
449                                            (GELF_ST_BIND(lib_sym->st_info) == STB_GLOBAL ||
450                                                 GELF_ST_BIND(lib_sym->st_info) == STB_WEAK))
451                                         {
452                                                 /* We found the symbol! Update the satisfied array at this
453                                                    index location. */
454                                                 source->satisfied[symidx] = lib;
455                                                 /* Now, link this structure into the linked list 
456                                                    corresponding to the found symbol in the library's 
457                                                    global array. */
458                         if (source->num_next_export == source->next_export_size) {
459                             source->next_export_size += 30;
460                             source->next_export = 
461                                 (source_t **)REALLOC(source->next_export,
462                                                      source->next_export_size *
463                                                      sizeof(struct next_export_t));
464                         }
465                         source->next_export[source->num_next_export] = lib->exports[lib_symidx];
466                         lib->exports[lib_symidx].source = source;
467                         lib->exports[lib_symidx].next_idx = source->num_next_export;
468
469                         source->num_next_export++;
470                         lib->num_exported++;
471
472                         INFO("[%s:%s (index %d)] satisfied by [%s] (index %d)\n",
473                                                          source->name,
474                                                          symname,
475                                                          symidx,
476                                                          lib->name,
477                                                          lib_symidx);
478                                                 if (found) {
479                                                         if (found == 1) {
480                                                                 found++;
481                                                                 ERROR("ERROR: multiple definitions found for [%s:%s]!\n",
482                                                                           source->name, symname);
483                                                                 ERROR("\tthis definition     [%s]\n", lib->name);
484                                                         }
485                                                         ERROR("\tprevious definition [%s]\n", last_found->name);
486                                                 }
487
488                                                 last_found = lib;
489                                                 if (!found) found = 1;
490                                         }
491                 }
492             }
493                         if(found == 0) {
494                                 ERROR("ERROR: could not find match for %s:%s.\n", 
495                                           source->name, 
496                                           symname);
497                         }
498         } /* if we found the symbol... */
499     } /* for each symbol... */
500 } /* resolve() */
501
502 static void print_used_symbols(source_t *source) {
503
504     int name_len = strlen(source->name);
505     static const char ext[] = ".syms";
506     char *filter = (char *)MALLOC(name_len + sizeof(ext));
507     strcpy(filter, source->name);
508     strcpy(filter + name_len, ext);
509
510     FILE *fp = fopen(filter, "w+");
511     FAILIF(NULL == fp, 
512            "Can't open %s: %s (%d)\n", 
513            filter, 
514            strerror(errno), errno);
515     
516     /* Is anybody using the symbols defined in source? */
517
518     if (source->num_exported > 0) {
519         INFO("[%s] exports %d symbols to %d libraries and executables.\n",
520              source->name,
521              source->num_exported,
522              source->num_satisfied_execs);
523         size_t symidx;
524         for (symidx = 0; symidx < source->num_syms; symidx++) {
525             if (source->exports[symidx].source != NULL) {
526                 GElf_Sym *sym, sym_mem;
527                 sym = gelf_getsymshndx(source->symtab.data, 
528                                        NULL,
529                                        symidx,
530                                        &sym_mem,
531                                        NULL);
532                 FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
533                 fprintf(fp, "%s\n", elf_strptr(source->elf,
534                                                elf_ndxscn(source->strtab.scn),
535                                                sym->st_name));
536             }
537         }
538     }
539     else if (source->num_satisfied_execs > 0) {
540
541         /*  Is the source listed as a depenency on anyone?  If so, then the source exports no symbols
542             to anyone, but someone lists it as a dependency, which is unnecessary, so we print a warning.
543          */
544
545         ERROR("WARNING: [%s] is listed as a dependency in: ", source->name);
546         int i;
547         for (i = 0; i < source->num_satisfied_execs; i++) {
548             ERROR(" [%s],", source->satisfied_execs[i]->name);
549         }
550         ERROR(" but none of its symbols are used!.\n");
551     }
552 #if 0 /* This is not really an error--a library's symbols may not be used anyone as specified in the ELF file,
553          but someone may still open a library via dlopen(). 
554       */
555     else {
556         ERROR("WARNING: None of [%s]'s symbols are used by any library or executable!\n", source->name);
557     }
558 #endif
559
560         fclose(fp);
561     FREE(filter);
562 }
563
564 static void print_symbol_references(source_t *source) {
565
566     int name_len = strlen(source->name);
567     static const char ext[] = ".info";
568     char *filter = (char *)MALLOC(name_len + sizeof(ext));
569     strcpy(filter, source->name);
570     strcpy(filter + name_len, ext);
571
572     FILE *fp = fopen(filter, "w+");
573     FAILIF(NULL == fp, 
574            "Can't open %s: %s (%d)\n", 
575            filter, 
576            strerror(errno), errno);
577
578     if (source->num_exported > 0) {
579         size_t symidx;
580         for (symidx = 0; symidx < source->num_syms; symidx++) {
581             if (source->exports[symidx].source != NULL) {
582                 const char *symname;
583                 GElf_Sym *sym, sym_mem;
584                 sym = gelf_getsymshndx(source->symtab.data, 
585                                        NULL,
586                                        symidx,
587                                        &sym_mem,
588                                        NULL);
589                 FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
590                 symname = elf_strptr(source->elf, 
591                                      elf_ndxscn(source->strtab.scn),
592                                      sym->st_name);
593                 fprintf(fp, "%s\n", symname);
594                 next_export_t *export = &source->exports[symidx];
595                 while (export->source != NULL) {
596                     //fprintf(stderr, "%s:%s\n", symname, export->source->name);
597                     fprintf(fp, "\t%s\n", export->source->name);
598                     export = &export->source->next_export[export->next_idx];
599                 }
600             }
601         }
602     }
603
604         fclose(fp);
605     FREE(filter);
606 }
607
608 static char * find_file(const char *libname, 
609                         char **lib_lookup_dirs, 
610                         int num_lib_lookup_dirs) {
611     if (libname[0] == '/') {
612         /* This is an absolute path name--just return it. */
613         INFO("ABSOLUTE PATH: [%s].\n", libname);
614         return strdup(libname);
615     } else {
616         /* First try the working directory. */
617         int fd;
618         if ((fd = open(libname, O_RDONLY)) > 0) {
619             close(fd);
620             INFO("FOUND IN CURRENT DIR: [%s].\n", libname);
621             return strdup(libname);
622         } else {
623             /* Iterate over all library paths.  For each path, append the file
624                name and see if there is a file at that place. If that fails, 
625                bail out. */
626
627             char *name;
628             while (num_lib_lookup_dirs--) {
629                 size_t lib_len = strlen(*lib_lookup_dirs);
630                 /* one extra character for the slash, and another for the 
631                    terminating NULL. */
632                 name = (char *)MALLOC(lib_len + strlen(libname) + 2);
633                 strcpy(name, *lib_lookup_dirs);
634                 name[lib_len] = '/';
635                 strcpy(name + lib_len + 1, libname);
636                 if ((fd = open(name, O_RDONLY)) > 0) {
637                     close(fd);
638                     INFO("FOUND: [%s] in [%s].\n", libname, name);
639                     return name;
640                 }
641                 INFO("NOT FOUND: [%s] in [%s].\n", libname, name);
642                 free(name);
643             }
644         }
645     }
646     return NULL;
647 }
648
649 static source_t* process_library(const char *libname,
650                                  char **lib_lookup_dirs, 
651                                  int num_lib_lookup_dirs) {
652     source_t *source = find_source(libname, lib_lookup_dirs, num_lib_lookup_dirs);
653     if (NULL == source) {
654         INFO("Processing [%s].\n", libname);
655         char *full = find_file(libname, lib_lookup_dirs, num_lib_lookup_dirs);
656         FAILIF(NULL == full, 
657                "Could not find [%s] in the current directory or in any of "
658                "the search paths!\n", libname);
659         source = init_source(full);
660         if (source) {
661             GElf_Dyn *dyn, dyn_mem;
662             size_t dynidx;
663             size_t numdyn =
664             source->dynamic.shdr.sh_size / 
665             source->dynamic.shdr.sh_entsize;
666
667             for (dynidx = 0; dynidx < numdyn; dynidx++) {
668                 dyn = gelf_getdyn (source->dynamic.data, 
669                                    dynidx, 
670                                    &dyn_mem);
671                 FAILIF_LIBELF(NULL == dyn, gelf_getdyn);
672                 if (dyn->d_tag == DT_NEEDED) {
673                     /* Process the needed library recursively. */
674                     const char *dep_lib =
675                     elf_strptr (source->elf, 
676                                 source->dynamic.shdr.sh_link, 
677                                 dyn->d_un.d_val);
678                     INFO("[%s] depends on [%s].\n", libname, dep_lib);
679                     source_t *dep = process_library(dep_lib, 
680                                                     lib_lookup_dirs,
681                                                     num_lib_lookup_dirs);
682
683                     /* Tell dep that source depends on it. */
684                     if (dep->num_satisfied_execs == dep->satisfied_execs_size) {
685                         dep->satisfied_execs_size += 10;
686                         dep->satisfied_execs = 
687                             REALLOC(dep->satisfied_execs,
688                                     dep->satisfied_execs_size *
689                                     sizeof(source_t *));
690                     }
691                     dep->satisfied_execs[dep->num_satisfied_execs++] = source;
692
693                     /* Add the library to the dependency list. */
694                     if (source->num_lib_deps == source->lib_deps_size) {
695                         source->lib_deps_size += 10;
696                         source->lib_deps = REALLOC(source->lib_deps, 
697                                                    source->lib_deps_size *
698                                                    sizeof(source_t *));
699                     }
700                     source->lib_deps[source->num_lib_deps++] = dep;
701                 }
702             } /* for each dynamic entry... */
703         }
704     } else INFO("[%s] has been processed already.\n", libname);
705
706     return source;
707 }
708
709 void lsd(char **execs, int num_execs,
710                  int list_needed_libs,
711                  int print_info,
712          char **lib_lookup_dirs, int num_lib_lookup_dirs) {
713
714     source_t *source; /* for general usage */
715     int input_idx;
716
717     for (input_idx = 0; input_idx < num_execs; input_idx++) {
718         INFO("executable: [%s]\n", execs[input_idx]);
719         /* Here process library is actually processing the top-level executable
720            files. */
721         process_library(execs[input_idx], lib_lookup_dirs, num_lib_lookup_dirs);
722         /* if source is NULL, then the respective executable is static */
723         /* Mark the source as an executable */
724     } /* for each input executable... */
725
726         if (list_needed_libs) {
727                 source = sources;
728                 while (source) {
729                         print_needed_libs(source);
730                         source = source->next;
731                 }
732         }
733
734     /* Now, for each entry in the sources array, iterate its symbol table.  For
735        each undefined symbol, scan the list of dependencies till we find a 
736        global symbol in one of them that satisfies the undefined reference.  
737        At this point, we update both the satisfied[] array of the sources entry, 
738        as well as the exports array of the dependency where we found the match.
739     */
740
741     source = sources;
742     while (source) {
743         resolve(source);
744         source = source->next;
745     }
746
747     /* We are done!  Since the end result of our calculations is a set of 
748        symbols for each library that other libraries or executables link 
749        against, we iterate over the set of libraries one last time, and for
750        each symbol that is marked as satisfying some dependence, we emit 
751        a line with the symbol's name to a text file derived from the library's
752        name by appending the suffix .syms to it. */
753
754     source = sources;
755     while (source) {
756         /* If it's a library, print the results. */
757         if (source->elf_hdr.e_type == ET_DYN) {
758                         print_used_symbols(source);
759                         if (print_info) 
760                                 print_symbol_references(source);
761                 }
762         source = source->next;
763     }
764
765         /* Free the resources--you can't do it in the loop above because function 
766            print_symbol_references() accesses nodes other than the one being 
767            iterated over.
768          */
769         source = sources;
770         while (source) {
771                 source_t *old = source;
772                 source = source->next;
773                 /* Destroy the evidence. */
774                 destroy_source(old);
775         }
776 }
777