summaryrefslogtreecommitdiffstats
path: root/tools/lsd/lsd.c
blob: 03c235b93a8ad8cf1979ceba9447117722695541 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
#include <stdio.h>
#include <common.h>
#include <debug.h>
#include <libelf.h>
#include <libebl.h>
#include <elf.h>
#include <gelf.h>
#include <string.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <hash.h>
#include <lsd.h>

extern int verbose_flag;

typedef struct source_t source_t;

typedef struct {
    Elf_Scn *scn;
    GElf_Shdr shdr;
    Elf_Data *data;
} section_info_t;

typedef struct next_export_t { 
    source_t *source;
    int next_idx;
} next_export_t;

struct source_t {
    source_t *next;
    int visited;

    char *name;  /* full path name of this executable file */
    /* ELF-related information: */
    Elf *elf;
    int elf_fd;
    GElf_Ehdr elf_hdr;
    size_t shstrndx;
    int shnum; /* number of sections */

    section_info_t symtab;
    section_info_t strtab;
    section_info_t dynamic;
    section_info_t hash;

    section_info_t *relocations;
    int num_relocations; /* number of relocs (<= relocations_size) */
    int relocations_size; /* sice of array -- NOT number of relocs! */

	/* satisfied_execs: array containing pointers to the libraries or 
	   executables that this executable satisfies symbol references for. */
	source_t **satisfied_execs;
    int num_satisfied_execs;
    int satisfied_execs_size;

    /* satisfied: array is parallel to symbol table; for each undefined symbol 
       in that array, we maintain a flag stating whether that symbol has been 
       satisfied, and if so, by which library.  This applies both to executable
       files and libraries.
    */
    source_t **satisfied;

    /* exports: array is parallel to symbol table; for each global symbol 
       in that array, we maintain a flag stating whether that symbol satisfies 
       a dependency in some other file.  num_syms is the length of the exports
       array, as well as the satisfied array. This applied to libraries only.

       next_exports:  this is a bit tricky.  We use this field to maintain a 
       linked list of source_t for each global symbol of a shared library. 
       For a shared library's global symbol at index N has the property that
       exports[N] is the head of a linked list (threaded through next_export)
       of all source_t that this symbol resolves a reference to.  For example, 
       if symbol printf has index 1000 in libc.so, and an executable A and 
       library L use printf, then the source_t entry corresponding to libc.so
       will have exports[1000] be a linked list that contains the nodes for 
       application A and library L.
    */

    next_export_t *exports;
    /* num_exported is the number of symbols in this file actually used by
       somebody else;  it's not the size of the exports array. */
    int num_exported;
    next_export_t *next_export;
    int num_next_export;
    int next_export_size;

    int num_syms; /* number of symbols in symbol table.  This is the length of
                     both exports[] and satisfied[] arrays. */

    /* This is an array that contains one element for each library dependency
       listed in the executable or shared library. */
    source_t **lib_deps; /* list of library dependencies */
    int num_lib_deps; /* actual number of library dependencies */
    int lib_deps_size; /* size of lib_deps array--NOT actual number of deps! */

};

static source_t *sources = NULL;

static char * find_file(const char *libname, 
                        char **lib_lookup_dirs, 
                        int num_lib_lookup_dirs);

static inline source_t* find_source(const char *name,
                                    char **lib_lookup_dirs, 
                                    int num_lib_lookup_dirs) {
    source_t *trav = sources;
	char *full = find_file(name, lib_lookup_dirs, num_lib_lookup_dirs);
    FAILIF(full == NULL, "Cannot construct full path for file [%s]!\n", name);
    while (trav) {
        if (!strcmp(trav->name, full))
            break;
        trav = trav->next;
    }
	free(full);
    return trav;
}

static inline void add_to_sources(source_t *src) {
    src->next = sources;
    sources = src;
}

static source_t* init_source(char *full_path) {
    source_t *source = (source_t *)CALLOC(1, sizeof(source_t));

    ASSERT(full_path);
    source->name = full_path;
    source->elf_fd = -1;

    INFO("Opening %s...\n", full_path);
    source->elf_fd = open(full_path, O_RDONLY);
    FAILIF(source->elf_fd < 0, "open(%s): %s (%d)\n", 
           full_path, 
           strerror(errno), 
           errno);
    INFO("Calling elf_begin(%s)...\n", full_path);
    source->elf = elf_begin(source->elf_fd, ELF_C_READ, NULL);
    FAILIF_LIBELF(source->elf == NULL, elf_begin);

    /* libelf can recognize COFF and A.OUT formats, but we handle only ELF. */
    if (elf_kind(source->elf) != ELF_K_ELF) {
        ERROR("Input file %s is not in ELF format!\n", full_path);
        return NULL;
    }

    /* Make sure this is a shared library or an executable. */
    {
        INFO("Making sure %s is a shared library or an executable...\n", 
             full_path);
        FAILIF_LIBELF(0 == gelf_getehdr(source->elf, &source->elf_hdr), gelf_getehdr);
        FAILIF(source->elf_hdr.e_type != ET_DYN && 
               source->elf_hdr.e_type != ET_EXEC,
               "%s must be a shared library (elf type is %d, expecting %d).\n", 
               full_path,
               source->elf_hdr.e_type, 
               ET_DYN);
    }

    /* Get the index of the section-header-strings-table section. */
    FAILIF_LIBELF(elf_getshstrndx (source->elf, &source->shstrndx) < 0, 
                  elf_getshstrndx);

    FAILIF_LIBELF(elf_getshnum (source->elf, &source->shnum) < 0, elf_getshnum);

    /* Find various sections. */
    size_t scnidx;
    Elf_Scn *scn;
    GElf_Shdr *shdr, shdr_mem;
    INFO("Locating %d sections in %s...\n", source->shnum, full_path);
    for (scnidx = 1; scnidx < source->shnum; scnidx++) {
        scn = elf_getscn(source->elf, scnidx);
        FAILIF_LIBELF(NULL == scn, elf_getscn);
        shdr = gelf_getshdr(scn, &shdr_mem);
        FAILIF_LIBELF(NULL == shdr, gelf_getshdr);
        INFO("\tfound section [%s]...\n", elf_strptr(source->elf, source->shstrndx, shdr->sh_name));
        if (shdr->sh_type == SHT_DYNSYM) {
            source->symtab.scn = scn;
            source->symtab.data = elf_getdata(scn, NULL);
            FAILIF_LIBELF(NULL == source->symtab.data, elf_getdata);
            memcpy(&source->symtab.shdr, shdr, sizeof(GElf_Shdr));

            /* The sh_link field of the section header of the symbol table
               contains the index of the associated strings table. */
            source->strtab.scn = elf_getscn(source->elf, 
                                            source->symtab.shdr.sh_link);
            FAILIF_LIBELF(NULL == source->strtab.scn, elf_getscn);
            FAILIF_LIBELF(NULL == gelf_getshdr(scn, &source->strtab.shdr),
                          gelf_getshdr);
            source->strtab.data = elf_getdata(source->strtab.scn, NULL);
            FAILIF_LIBELF(NULL == source->strtab.data, elf_getdata);
        }
        else if (shdr->sh_type == SHT_DYNAMIC) {
            source->dynamic.scn = scn;
            source->dynamic.data = elf_getdata(scn, NULL);
            FAILIF_LIBELF(NULL == source->symtab.data, elf_getdata);
            memcpy(&source->dynamic.shdr, shdr, sizeof(GElf_Shdr));
        }
        else if (shdr->sh_type == SHT_HASH) {
            source->hash.scn = scn;
            source->hash.data = elf_getdata(scn, NULL);
            FAILIF_LIBELF(NULL == source->hash.data, elf_getdata);
            memcpy(&source->hash.shdr, shdr, sizeof(GElf_Shdr));
        }
        else if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) {
            if (source->num_relocations == source->relocations_size) {
                source->relocations_size += 5;
                source->relocations = 
                    (section_info_t *)REALLOC(source->relocations,
                                              source->relocations_size *
                                              sizeof(section_info_t));
            }
            section_info_t *reloc = 
                source->relocations + source->num_relocations;
            reloc->scn = scn;
            reloc->data = elf_getdata(scn, NULL);
            FAILIF_LIBELF(NULL == reloc->data, elf_getdata);
            memcpy(&reloc->shdr, shdr, sizeof(GElf_Shdr));
            source->num_relocations++;
        }
    }

    if (source->dynamic.scn == NULL) {
        INFO("File [%s] does not have a dynamic section!\n", full_path);
        return 0;
    }

    FAILIF(source->symtab.scn == NULL, 
           "File [%s] does not have a dynamic symbol table!\n",
           full_path);

    FAILIF(source->hash.scn == NULL, 
           "File [%s] does not have a hash table!\n",
           full_path);
    FAILIF(source->hash.shdr.sh_link != elf_ndxscn(source->symtab.scn),
           "Hash points to section %d, not to %d as expected!\n",
           source->hash.shdr.sh_link,
           elf_ndxscn(scn));

    /* Now, find out how many symbols we have and allocate the array of 
       satisfied symbols.

       NOTE: We don't count the number of undefined symbols here; we will 
       iterate over the symbol table later, and count them then, when it is 
       more convenient. 
    */
    size_t symsize = gelf_fsize (source->elf, 
                                 ELF_T_SYM, 
                                 1, source->elf_hdr.e_version);
    ASSERT(symsize);

    source->num_syms = source->symtab.data->d_size / symsize;
    source->satisfied = (source_t **)CALLOC(source->num_syms, 
                                            sizeof(source_t *));
    source->exports = (source_t **)CALLOC(source->num_syms, 
                                          sizeof(next_export_t));

    source->num_exported = 0;
    source->satisfied_execs = NULL;
    source->num_satisfied_execs = 0;
    source->satisfied_execs_size = 0;

    add_to_sources(source);
    return source;
}

static void destroy_source(source_t *source) {
    FREE(source->satisfied_execs);
    FREE(source->satisfied);
    FREE(source->exports);
    FREE(source->next_export);    
    FREE(source->lib_deps); /* list of library dependencies */
    FAILIF_LIBELF(elf_end(source->elf), elf_end);
    FAILIF(close(source->elf_fd) < 0, "Could not close file %s: %s (%d)!\n", 
           source->name, strerror(errno), errno);
    FREE(source->name);
    FREE(source);
}

static void print_needed_libs(source_t *source)
{
	size_t idx;
	for (idx = 0; idx < source->num_lib_deps; idx++) {
		PRINT("%s:%s\n", 
			  source->name, 
			  source->lib_deps[idx]->name);
	}
}

static int is_symbol_imported(source_t *source,
                              GElf_Sym *sym, 
                              size_t symidx)
{
    const char *symname = elf_strptr(source->elf,
                                     elf_ndxscn(source->strtab.scn),
                                     sym->st_name);

    /* A symbol is imported by an executable or a library if it is undefined
       and is either global or weak. There is an additional case for 
       executables that we will check below. */
    if (sym->st_shndx == SHN_UNDEF &&
        (GELF_ST_BIND(sym->st_info) == STB_GLOBAL ||
         GELF_ST_BIND(sym->st_info) == STB_WEAK)) {
        INFO("*** symbol [%s:%s] is imported (UNDEFIEND).\n",
             source->name,
             symname);
        return 1;
    }

#ifdef ARM_SPECIFIC_HACKS
    /* A symbol is imported by an executable if is marked as an undefined 
       symbol--this is standard to all ELF formats.  Alternatively, according 
       to the ARM specifications, a symbol in a BSS section that is also marked
       by an R_ARM_COPY relocation is also imported. */

    if (source->elf_hdr.e_type != ET_EXEC) {
        INFO("is_symbol_imported(): [%s] is a library, "
             "no further checks.\n", source->name);
        return 0;
    }

    /* Is the symbol in the BSS section, and is there a COPY relocation on 
       that symbol? */
    INFO("*** [%s:%s] checking further to see if symbol is imported.\n",
         source->name, symname);
    if (sym->st_shndx < source->shnum) {
        /* Is it the .bss section? */
        Elf_Scn *scn = elf_getscn(source->elf, sym->st_shndx);
        FAILIF_LIBELF(NULL == scn, elf_getscn);
        GElf_Shdr *shdr, shdr_mem;
        shdr = gelf_getshdr(scn, &shdr_mem);
        FAILIF_LIBELF(NULL == shdr, gelf_getshdr);
        if (!strcmp(".bss", elf_strptr(source->elf,
                                       source->shstrndx,
                                       shdr->sh_name)))
        {
            /* Is there an R_ARM_COPY relocation on this symbol?  Iterate 
               over the list of relocation sections and scan each section for
               an entry that matches the symbol. */
            size_t idx;
            for (idx = 0; idx < source->num_relocations; idx++) {
                section_info_t *reloc = source->relocations + idx;
                /* Does the relocation section refer to the symbol table in
                   which this symbol resides, and does it relocate the .bss
                   section? */
                if (reloc->shdr.sh_link == elf_ndxscn(source->symtab.scn) &&
                    reloc->shdr.sh_info == sym->st_shndx)
                {
                    /* Go over the relocations and see if any of them matches
                       our symbol. */
                    size_t nrels = reloc->shdr.sh_size / reloc->shdr.sh_entsize;
                    size_t relidx, newidx;
                    if (reloc->shdr.sh_type == SHT_REL) {
                        for (newidx = relidx = 0; relidx < nrels; ++relidx) {
                            GElf_Rel rel_mem;
                            FAILIF_LIBELF(gelf_getrel (reloc->data, 
                                                       relidx, 
                                                       &rel_mem) == NULL,
                                          gelf_getrel);
                            if (GELF_R_TYPE(rel_mem.r_info) == R_ARM_COPY &&
                                GELF_R_SYM (rel_mem.r_info) == symidx)
                            {
                                INFO("*** symbol [%s:%s] is imported "
                                     "(DEFINED, REL-COPY-RELOCATED).\n",
                                     source->name,
                                     symname);
                                return 1;
                            }
                        } /* for each rel entry... */
                    } else {
                        for (newidx = relidx = 0; relidx < nrels; ++relidx) {
                            GElf_Rela rel_mem;
                            FAILIF_LIBELF(gelf_getrela (reloc->data, 
                                                        relidx, 
                                                        &rel_mem) == NULL,
                                          gelf_getrela);
                            if (GELF_R_TYPE(rel_mem.r_info) == R_ARM_COPY &&
                                GELF_R_SYM (rel_mem.r_info) == symidx)
                            {
                                INFO("*** symbol [%s:%s] is imported "
                                     "(DEFINED, RELA-COPY-RELOCATED).\n",
                                     source->name,
                                     symname);
                                return 1;
                            }
                        } /* for each rela entry... */
                    } /* if rel else rela */
                }
            }
        }
    }
#endif/*ARM_SPECIFIC_HACKS*/

    return 0;
}

static void resolve(source_t *source) {
    /* Iterate the symbol table.  For each undefined symbol, scan the 
       list of dependencies till we find a global symbol in one of them that 
       satisfies the undefined reference.  At this point, we update both the 
       satisfied[] array of the sources entry, as well as the exports array of 
       the dependency where we found the match.
    */

    GElf_Sym *sym, sym_mem;
    size_t symidx;
    for (symidx = 0; symidx < source->num_syms; symidx++) {
        sym = gelf_getsymshndx(source->symtab.data, 
                               NULL,
                               symidx,
                               &sym_mem,
                               NULL);
        FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
        if (is_symbol_imported(source, sym, symidx)) 
		{
            /* This is an undefined symbol.  Go over the list of libraries 
               and look it up. */
            size_t libidx;
			int found = 0;
			source_t *last_found = NULL;
			const char *symname = elf_strptr(source->elf,
											 elf_ndxscn(source->strtab.scn),
											 sym->st_name);
            for (libidx = 0; libidx < source->num_lib_deps; libidx++) {
                source_t *lib = source->lib_deps[libidx];
                int lib_symidx = hash_lookup(lib->elf,
                                             lib->hash.data,
                                             lib->symtab.data,
                                             lib->strtab.data,
                                             symname);
                if (STN_UNDEF != lib_symidx)
                {
					/* We found the symbol--now check to see if it is global 
					   or weak.  If this is the case, then the symbol satisfies
					   the dependency. */
					GElf_Sym *lib_sym, lib_sym_mem;
					lib_sym = gelf_getsymshndx(lib->symtab.data, 
											   NULL,
											   lib_symidx,
											   &lib_sym_mem,
											   NULL);
					FAILIF_LIBELF(NULL == lib_sym, gelf_getsymshndx);

					if(lib_sym->st_shndx != STN_UNDEF &&
					   (GELF_ST_BIND(lib_sym->st_info) == STB_GLOBAL ||
						GELF_ST_BIND(lib_sym->st_info) == STB_WEAK))
					{
						/* We found the symbol! Update the satisfied array at this
						   index location. */
						source->satisfied[symidx] = lib;
						/* Now, link this structure into the linked list 
						   corresponding to the found symbol in the library's 
						   global array. */
                        if (source->num_next_export == source->next_export_size) {
                            source->next_export_size += 30;
                            source->next_export = 
                                (source_t **)REALLOC(source->next_export,
                                                     source->next_export_size *
                                                     sizeof(struct next_export_t));
                        }
                        source->next_export[source->num_next_export] = lib->exports[lib_symidx];
                        lib->exports[lib_symidx].source = source;
                        lib->exports[lib_symidx].next_idx = source->num_next_export;

                        source->num_next_export++;
                        lib->num_exported++;

                        INFO("[%s:%s (index %d)] satisfied by [%s] (index %d)\n",
							 source->name,
							 symname,
							 symidx,
							 lib->name,
							 lib_symidx);
						if (found) {
							if (found == 1) {
								found++;
								ERROR("ERROR: multiple definitions found for [%s:%s]!\n",
									  source->name, symname);
								ERROR("\tthis definition     [%s]\n", lib->name);
							}
							ERROR("\tprevious definition [%s]\n", last_found->name);
						}

						last_found = lib;
						if (!found) found = 1;
					}
                }
            }
			if(found == 0) {
				ERROR("ERROR: could not find match for %s:%s.\n", 
					  source->name, 
					  symname);
			}
        } /* if we found the symbol... */
    } /* for each symbol... */
} /* resolve() */

static void print_used_symbols(source_t *source) {

    int name_len = strlen(source->name);
    static const char ext[] = ".syms";
    char *filter = (char *)MALLOC(name_len + sizeof(ext));
    strcpy(filter, source->name);
    strcpy(filter + name_len, ext);

    FILE *fp = fopen(filter, "w+");
    FAILIF(NULL == fp, 
           "Can't open %s: %s (%d)\n", 
           filter, 
           strerror(errno), errno);
    
    /* Is anybody using the symbols defined in source? */

    if (source->num_exported > 0) {
        INFO("[%s] exports %d symbols to %d libraries and executables.\n",
             source->name,
             source->num_exported,
             source->num_satisfied_execs);
        size_t symidx;
        for (symidx = 0; symidx < source->num_syms; symidx++) {
            if (source->exports[symidx].source != NULL) {
                GElf_Sym *sym, sym_mem;
                sym = gelf_getsymshndx(source->symtab.data, 
                                       NULL,
                                       symidx,
                                       &sym_mem,
                                       NULL);
                FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
                fprintf(fp, "%s\n", elf_strptr(source->elf,
                                               elf_ndxscn(source->strtab.scn),
                                               sym->st_name));
            }
        }
    }
    else if (source->num_satisfied_execs > 0) {

        /*  Is the source listed as a depenency on anyone?  If so, then the source exports no symbols
            to anyone, but someone lists it as a dependency, which is unnecessary, so we print a warning.
         */

        ERROR("WARNING: [%s] is listed as a dependency in: ", source->name);
        int i;
        for (i = 0; i < source->num_satisfied_execs; i++) {
            ERROR(" [%s],", source->satisfied_execs[i]->name);
        }
        ERROR(" but none of its symbols are used!.\n");
    }
#if 0 /* This is not really an error--a library's symbols may not be used anyone as specified in the ELF file,
         but someone may still open a library via dlopen(). 
      */
    else {
        ERROR("WARNING: None of [%s]'s symbols are used by any library or executable!\n", source->name);
    }
#endif

	fclose(fp);
    FREE(filter);
}

static void print_symbol_references(source_t *source) {

    int name_len = strlen(source->name);
    static const char ext[] = ".info";
    char *filter = (char *)MALLOC(name_len + sizeof(ext));
    strcpy(filter, source->name);
    strcpy(filter + name_len, ext);

    FILE *fp = fopen(filter, "w+");
    FAILIF(NULL == fp, 
           "Can't open %s: %s (%d)\n", 
           filter, 
           strerror(errno), errno);

    if (source->num_exported > 0) {
        size_t symidx;
        for (symidx = 0; symidx < source->num_syms; symidx++) {
            if (source->exports[symidx].source != NULL) {
                const char *symname;
                GElf_Sym *sym, sym_mem;
                sym = gelf_getsymshndx(source->symtab.data, 
                                       NULL,
                                       symidx,
                                       &sym_mem,
                                       NULL);
                FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
                symname = elf_strptr(source->elf, 
                                     elf_ndxscn(source->strtab.scn),
                                     sym->st_name);
                fprintf(fp, "%s\n", symname);
                next_export_t *export = &source->exports[symidx];
                while (export->source != NULL) {
                    //fprintf(stderr, "%s:%s\n", symname, export->source->name);
                    fprintf(fp, "\t%s\n", export->source->name);
                    export = &export->source->next_export[export->next_idx];
                }
            }
        }
    }

	fclose(fp);
    FREE(filter);
}

static char * find_file(const char *libname, 
                        char **lib_lookup_dirs, 
                        int num_lib_lookup_dirs) {
    if (libname[0] == '/') {
        /* This is an absolute path name--just return it. */
        INFO("ABSOLUTE PATH: [%s].\n", libname);
        return strdup(libname);
    } else {
        /* First try the working directory. */
        int fd;
        if ((fd = open(libname, O_RDONLY)) > 0) {
            close(fd);
            INFO("FOUND IN CURRENT DIR: [%s].\n", libname);
            return strdup(libname);
        } else {
            /* Iterate over all library paths.  For each path, append the file
               name and see if there is a file at that place. If that fails, 
               bail out. */

            char *name;
            while (num_lib_lookup_dirs--) {
                size_t lib_len = strlen(*lib_lookup_dirs);
                /* one extra character for the slash, and another for the 
                   terminating NULL. */
                name = (char *)MALLOC(lib_len + strlen(libname) + 2);
                strcpy(name, *lib_lookup_dirs);
                name[lib_len] = '/';
                strcpy(name + lib_len + 1, libname);
                if ((fd = open(name, O_RDONLY)) > 0) {
                    close(fd);
                    INFO("FOUND: [%s] in [%s].\n", libname, name);
                    return name;
                }
                INFO("NOT FOUND: [%s] in [%s].\n", libname, name);
                free(name);
            }
        }
    }
    return NULL;
}

static source_t* process_library(const char *libname,
                                 char **lib_lookup_dirs, 
                                 int num_lib_lookup_dirs) {
    source_t *source = find_source(libname, lib_lookup_dirs, num_lib_lookup_dirs);
    if (NULL == source) {
        INFO("Processing [%s].\n", libname);
        char *full = find_file(libname, lib_lookup_dirs, num_lib_lookup_dirs);
        FAILIF(NULL == full, 
               "Could not find [%s] in the current directory or in any of "
               "the search paths!\n", libname);
        source = init_source(full);
        if (source) {
            GElf_Dyn *dyn, dyn_mem;
            size_t dynidx;
            size_t numdyn =
            source->dynamic.shdr.sh_size / 
            source->dynamic.shdr.sh_entsize;

            for (dynidx = 0; dynidx < numdyn; dynidx++) {
                dyn = gelf_getdyn (source->dynamic.data, 
                                   dynidx, 
                                   &dyn_mem);
                FAILIF_LIBELF(NULL == dyn, gelf_getdyn);
                if (dyn->d_tag == DT_NEEDED) {
                    /* Process the needed library recursively. */
                    const char *dep_lib =
                    elf_strptr (source->elf, 
                                source->dynamic.shdr.sh_link, 
                                dyn->d_un.d_val);
                    INFO("[%s] depends on [%s].\n", libname, dep_lib);
                    source_t *dep = process_library(dep_lib, 
                                                    lib_lookup_dirs,
                                                    num_lib_lookup_dirs);

                    /* Tell dep that source depends on it. */
                    if (dep->num_satisfied_execs == dep->satisfied_execs_size) {
                        dep->satisfied_execs_size += 10;
                        dep->satisfied_execs = 
                            REALLOC(dep->satisfied_execs,
                                    dep->satisfied_execs_size *
                                    sizeof(source_t *));
                    }
                    dep->satisfied_execs[dep->num_satisfied_execs++] = source;

                    /* Add the library to the dependency list. */
                    if (source->num_lib_deps == source->lib_deps_size) {
                        source->lib_deps_size += 10;
                        source->lib_deps = REALLOC(source->lib_deps, 
                                                   source->lib_deps_size *
                                                   sizeof(source_t *));
                    }
                    source->lib_deps[source->num_lib_deps++] = dep;
                }
            } /* for each dynamic entry... */
        }
    } else INFO("[%s] has been processed already.\n", libname);

    return source;
}

void lsd(char **execs, int num_execs,
		 int list_needed_libs,
		 int print_info,
         char **lib_lookup_dirs, int num_lib_lookup_dirs) {

    source_t *source; /* for general usage */
    int input_idx;

    for (input_idx = 0; input_idx < num_execs; input_idx++) {
        INFO("executable: [%s]\n", execs[input_idx]);
        /* Here process library is actually processing the top-level executable
           files. */
        process_library(execs[input_idx], lib_lookup_dirs, num_lib_lookup_dirs);
        /* if source is NULL, then the respective executable is static */
        /* Mark the source as an executable */
    } /* for each input executable... */

	if (list_needed_libs) {
		source = sources;
		while (source) {
			print_needed_libs(source);
			source = source->next;
		}
	}

    /* Now, for each entry in the sources array, iterate its symbol table.  For
       each undefined symbol, scan the list of dependencies till we find a 
       global symbol in one of them that satisfies the undefined reference.  
       At this point, we update both the satisfied[] array of the sources entry, 
       as well as the exports array of the dependency where we found the match.
    */

    source = sources;
    while (source) {
        resolve(source);
        source = source->next;
    }

    /* We are done!  Since the end result of our calculations is a set of 
       symbols for each library that other libraries or executables link 
       against, we iterate over the set of libraries one last time, and for
       each symbol that is marked as satisfying some dependence, we emit 
       a line with the symbol's name to a text file derived from the library's
       name by appending the suffix .syms to it. */

    source = sources;
    while (source) {
        /* If it's a library, print the results. */
        if (source->elf_hdr.e_type == ET_DYN) {
			print_used_symbols(source);
			if (print_info) 
				print_symbol_references(source);
		}
        source = source->next;
    }

	/* Free the resources--you can't do it in the loop above because function 
	   print_symbol_references() accesses nodes other than the one being 
	   iterated over.
	 */
	source = sources;
	while (source) {
		source_t *old = source;
		source = source->next;
		/* Destroy the evidence. */
		destroy_source(old);
	}
}