1 //
2 // Copyright (C) 2013 The Android Open Source Project
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in
12 // the documentation and/or other materials provided with the
13 // distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 // SUCH DAMAGE.
27 //
28
29 // A small portable program used to dump the dynamic dependencies of a
30 // shared library. Requirements:
31 // - Must support both 32-bit and 64-bit ELF binaries.
32 // - Must support both little and big endian binaries.
33 // - Must be compiled as a Unicode program on Windows.
34 // - Follows Chromium coding-style guide.
35 // - Single source file to make it easier to build anywhere.
36
37 //
38 // Work-around Windows Unicode support.
39 //
40
41 // Enable Windows Unicode support by default. Override this by
42 // setting WINDOWS_UNICODE at build time.
43 #if !defined(WINDOWS_UNICODE) && defined(_WIN32)
44 #define WINDOWS_UNICODE 1
45 #endif
46
47 #ifdef _WIN32
48 #undef UNICODE
49 #undef _UNICODE
50 #ifdef WINDOWS_UNICODE
51 #define UNICODE 1
52 #define _UNICODE 1
53 #endif
54 #include <windows.h>
55 #include <tchar.h>
56 #else
57 #include <stdlib.h>
58 #include <stdio.h>
59 #include <string.h>
60 #endif
61
62 #include <string>
63
64 // Define String as a typedef for either std::string or std::wstring
65 // depending on the platform.
66 #if WINDOWS_UNICODE
67 typedef std::wstring String;
68 #else
69 typedef std::string String;
70 #endif
71
72 // Use the following functions instead of their standard library equivalent.
73 #if !WINDOWS_UNICODE
74 #define TCHAR char
75 #define _T(x) x
76 #define _tgetenv getenv
77 #define _tcslen strlen
78 #define _tcschr strchr
79 #define _tcscmp strcmp
80 #define _tcsncmp strncmp
81 #define _tfopen fopen
82 #define _tprintf printf
83 #define _vftprintf vfprintf
84 #define _ftprintf fprintf
85 #define _tstat stat
86 #define _vtprintf vprintf
87 #define _stat stat
88 #endif
89
90 // Use TO_STRING(xxx) to convert a C-string into the equivalent String.
91 #if WINDOWS_UNICODE == 1
__s2ws(const std::string & s)92 static inline std::wstring __s2ws(const std::string& s) {
93 int s_len = static_cast<int>(s.length() + 1);
94 int len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), s_len, 0, 0);
95 std::wstring result(len, L'\0');
96 MultiByteToWideChar(CP_ACP, 0, s.c_str(), s_len, &result[0], len);
97 return result;
98 }
99 #define TO_STRING(x) __s2ws(x)
100 #else
101 #define TO_STRING(x) (x)
102 #endif
103
104 // Use TO_CONST_TCHAR(xxx) to convert a const char* to a const char/wchar*
105 #if WINDOWS_UNICODE == 1
106 #define TO_CONST_TCHAR(x) TO_STRING(x).c_str()
107 #else
108 #define TO_CONST_TCHAR(x) (x)
109 #endif
110
111
112 //
113 // Start the real program now
114 //
115
116 #include <errno.h>
117 #ifdef __linux__
118 #include <glob.h>
119 #endif
120 #include <limits.h>
121 #include <stdarg.h>
122 #include <stddef.h>
123 #include <stdint.h>
124 #include <stdlib.h>
125 #include <stdio.h>
126 #include <string.h>
127 #include <sys/stat.h>
128
129 #include <algorithm>
130 #include <list>
131 #include <map>
132 #include <string>
133 #include <vector>
134
135 namespace {
136
137 // Utility functions.
138
139 enum {
140 MESSAGE_FLAG_PANIC = (1 << 0),
141 MESSAGE_FLAG_ERRNO = (1 << 1),
142 };
143
vmessage(int flags,const TCHAR * fmt,va_list args)144 void vmessage(int flags, const TCHAR* fmt, va_list args) {
145 int old_errno = errno;
146 if (flags & MESSAGE_FLAG_PANIC)
147 fprintf(stderr, "ERROR: ");
148
149 _vftprintf(stderr, fmt, args);
150 if (flags & MESSAGE_FLAG_ERRNO) {
151 fprintf(stderr, ": %s", strerror(old_errno));
152 }
153 fprintf(stderr, "\n");
154
155 if (flags & MESSAGE_FLAG_PANIC)
156 exit(1);
157
158 errno = old_errno;
159 }
160
panic(const TCHAR * fmt,...)161 void panic(const TCHAR* fmt, ...) {
162 va_list args;
163 va_start(args, fmt);
164 vmessage(MESSAGE_FLAG_PANIC, fmt, args);
165 va_end(args);
166 }
167
168 int g_verbose = 0;
169
log_n(int n,const TCHAR * fmt,...)170 void log_n(int n, const TCHAR* fmt, ...) {
171 if (g_verbose >= n) {
172 va_list args;
173 va_start(args, fmt);
174 _vtprintf(fmt, args);
175 va_end(args);
176 }
177 }
178
179 #define LOG_N(level,...) \
180 ({ if (g_verbose >= (level)) log_n((level), __VA_ARGS__); })
181
182 #define LOG(...) LOG_N(1,__VA_ARGS__)
183 #define LOG2(...) LOG_N(2,__VA_ARGS__)
184
185 #ifndef DEBUG
186 #define DEBUG 0
187 #endif
188
189 #if DEBUG
190 #define DLOG(...) _tprintf(__VA_ARGS__)
191 #else
192 #define DLOG(...) ((void)0)
193 #endif
194
195 // Path utilites
196
197 // Return the position of the last directory separator in a path,
198 // or std::string::npos if none is found.
path_last_dirsep(const String & filepath)199 size_t path_last_dirsep(const String& filepath) {
200 #ifdef _WIN32
201 size_t sep_slash = filepath.rfind(_T('/'));
202 size_t sep_backslash = filepath.rfind(_T('\\'));
203 size_t sep;
204 if (sep_slash == std::string::npos)
205 sep = sep_backslash;
206 else if (sep_backslash == std::string::npos)
207 sep = sep_slash;
208 else
209 sep = std::max(sep_slash, sep_backslash);
210 #else
211 size_t sep = filepath.rfind(_T('/'));
212 #endif
213 return sep;
214 }
215
216 // Return the directory name of a given path.
path_dirname(const String & filepath)217 String path_dirname(const String& filepath) {
218 size_t sep = path_last_dirsep(filepath);
219 if (sep == std::string::npos)
220 return String(_T("."));
221 else if (sep == 0)
222 return String(_T("/"));
223 else
224 return filepath.substr(0, sep);
225 }
226
227 // Return the basename of a given path.
path_basename(const String & filepath)228 String path_basename(const String& filepath) {
229 size_t sep = path_last_dirsep(filepath);
230 if (sep == std::string::npos)
231 return filepath;
232 else
233 return filepath.substr(sep + 1);
234 }
235
236
237 // Reading utilities.
238
get_u16_le(const uint8_t * bytes)239 uint16_t get_u16_le(const uint8_t* bytes) {
240 return static_cast<uint16_t>(bytes[0] | (bytes[1] << 8));
241 }
242
get_u32_le(const uint8_t * bytes)243 uint32_t get_u32_le(const uint8_t* bytes) {
244 return static_cast<uint32_t>(
245 bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24));
246 }
247
get_u64_le(const uint8_t * bytes)248 uint64_t get_u64_le(const uint8_t* bytes) {
249 uint64_t lo = static_cast<uint64_t>(get_u32_le(bytes));
250 uint64_t hi = static_cast<uint64_t>(get_u32_le(bytes + 4));
251 return lo | (hi << 32);
252 }
253
get_u16_be(const uint8_t * bytes)254 uint16_t get_u16_be(const uint8_t* bytes) {
255 return static_cast<uint16_t>((bytes[0] << 8) | bytes[1]);
256 }
257
get_u32_be(const uint8_t * bytes)258 uint32_t get_u32_be(const uint8_t* bytes) {
259 return static_cast<uint32_t>(
260 (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]);
261 }
262
get_u64_be(const uint8_t * bytes)263 uint64_t get_u64_be(const uint8_t* bytes) {
264 uint64_t hi = static_cast<uint64_t>(get_u32_be(bytes));
265 uint64_t lo = static_cast<uint64_t>(get_u32_be(bytes + 4));
266 return lo | (hi << 32);
267 }
268
269 // FileReader utility classes
270
271 class Reader {
272 public:
Reader()273 Reader() {}
274
275 virtual const uint8_t* GetBytesAt(off_t pos, size_t size) = 0;
276 virtual uint16_t GetU16At(off_t pos) = 0;
277 virtual uint32_t GetU32At(off_t pos) = 0;
278 virtual uint64_t GetU64At(off_t pos) = 0;
279
~Reader()280 virtual ~Reader() {}
281 };
282
283 class FileReader : public Reader {
284 public:
FileReader(FILE * file)285 FileReader(FILE* file) : file_(file) {}
286
GetBytesAt(off_t pos,size_t size)287 virtual const uint8_t* GetBytesAt(off_t pos, size_t size) {
288 if (size < kMaxBytes &&
289 fseek(file_, pos, SEEK_SET) == 0 &&
290 fread(buffer_, size, 1, file_) == 1) {
291 return buffer_;
292 } else
293 return NULL;
294 }
295
296 private:
297 static const size_t kMaxBytes = 32;
298 FILE* file_;
299 uint8_t buffer_[kMaxBytes];
300 };
301
302 class FileLittleEndianReader : public FileReader {
303 public:
FileLittleEndianReader(FILE * file)304 FileLittleEndianReader(FILE* file) : FileReader(file) {}
305
GetU16At(off_t pos)306 virtual uint16_t GetU16At(off_t pos) {
307 const uint8_t* buf = GetBytesAt(pos, 2);
308 return (buf != NULL) ? get_u16_le(buf) : 0;
309 }
310
GetU32At(off_t pos)311 virtual uint32_t GetU32At(off_t pos) {
312 const uint8_t* buf = GetBytesAt(pos, 4);
313 return (buf != NULL) ? get_u32_le(buf) : 0;
314 }
315
GetU64At(off_t pos)316 virtual uint64_t GetU64At(off_t pos) {
317 const uint8_t* buf = GetBytesAt(pos, 8);
318 return (buf != NULL) ? get_u64_le(buf) : 0ULL;
319 }
320 };
321
322 class FileBigEndianReader : public FileReader {
323 public:
FileBigEndianReader(FILE * file)324 FileBigEndianReader(FILE* file) : FileReader(file) {}
325
GetU16At(off_t pos)326 virtual uint16_t GetU16At(off_t pos) {
327 const uint8_t* buf = GetBytesAt(pos, 2);
328 return (buf != NULL) ? get_u16_be(buf) : 0;
329 }
330
GetU32At(off_t pos)331 virtual uint32_t GetU32At(off_t pos) {
332 const uint8_t* buf = GetBytesAt(pos, 4);
333 return (buf != NULL) ? get_u32_be(buf) : 0;
334 }
335
GetU64At(off_t pos)336 virtual uint64_t GetU64At(off_t pos) {
337 const uint8_t* buf = GetBytesAt(pos, 8);
338 return (buf != NULL) ? get_u64_be(buf) : 0ULL;
339 }
340 };
341
342 // ELF utility functions.
343
344 // The first 16 common bytes.
345 #define EI_NIDENT 16
346
347 #define EI_CLASS 4
348 #define ELFCLASS32 1
349 #define ELFCLASS64 2
350
351 #define EI_DATA 5
352 #define ELFDATA2LSB 1
353 #define ELFDATA2MSB 2
354
elf_ident_is_elf(const uint8_t * ident)355 bool elf_ident_is_elf(const uint8_t* ident) {
356 return ident[0] == 0x7f &&
357 ident[1] == 'E' &&
358 ident[2] == 'L' &&
359 ident[3] == 'F';
360 }
361
elf_ident_is_big_endian(const uint8_t * ident)362 bool elf_ident_is_big_endian(const uint8_t* ident) {
363 return ident[EI_DATA] == ELFDATA2MSB;
364 }
365
elf_ident_is_64bits(const uint8_t * ident)366 bool elf_ident_is_64bits(const uint8_t* ident) {
367 return ident[EI_CLASS] == ELFCLASS64;
368 }
369
370 #define SHT_STRTAB 3
371 #define SHT_DYNAMIC 6
372
373 // 32-bit ELF definitions.
374
375 class Elf32 {
376 public:
377 typedef uint16_t Half;
378 typedef uint32_t Word;
379 typedef uint32_t Off;
380 typedef uint32_t Addr;
381 typedef int32_t Sword;
382
383 struct Header {
384 uint8_t e_ident[EI_NIDENT];
385 Half e_type;
386 Half e_machine;
387 Word e_version;
388 Addr e_entry;
389 Off e_phoff;
390 Off e_shoff;
391 Word e_flags;
392 Half e_shsize;
393 Half e_phentsize;
394 Half e_phnum;
395 Half e_shentsize;
396 Half e_shnum;
397 Half e_shstrndx;
398 };
399
400 struct Shdr {
401 Word sh_name;
402 Word sh_type;
403 Word sh_flags;
404 Addr sh_addr;
405 Off sh_offset;
406 Word sh_size;
407 Word sh_link;
408 Word sh_info;
409 Word sh_addralign;
410 Word sh_entsize;
411 };
412 };
413
414 class Elf64 {
415 public:
416 typedef uint16_t Half;
417 typedef uint64_t Off;
418 typedef uint64_t Addr;
419 typedef int32_t Sword;
420 typedef uint32_t Word;
421 typedef uint64_t Xword;
422 typedef int64_t Sxword;
423
424 struct Header {
425 uint8_t e_ident[EI_NIDENT];
426 Half e_type;
427 Half e_machine;
428 Word e_version;
429 Addr e_entry;
430 Off e_phoff;
431 Off e_shoff;
432 Word e_flags;
433 Half e_shsize;
434 Half e_phentsize;
435 Half e_phnum;
436 Half e_shentsize;
437 Half e_shnum;
438 Half e_shstrndx;
439 };
440
441 struct Shdr {
442 Word sh_name;
443 Word sh_type;
444 Xword sh_flags;
445 Addr sh_addr;
446 Off sh_offset;
447 Xword sh_size;
448 Word sh_link;
449 Word sh_info;
450 Xword sh_addralign;
451 Xword sh_entsize;
452 };
453 };
454
455 template <class ELF>
456 class ElfParser {
457 public:
ElfParser(Reader & reader)458 ElfParser(Reader& reader) : reader_(reader) {}
459
460 typedef typename ELF::Word Word;
461 typedef typename ELF::Sword Sword;
462 typedef typename ELF::Addr Addr;
463 typedef typename ELF::Off Off;
464 typedef typename ELF::Half Half;
465 typedef typename ELF::Header ElfHeader;
466 typedef typename ELF::Shdr Shdr;
467
468 // Read an ELF::Word at a given position.
GetWordAt(off_t pos)469 Word GetWordAt(off_t pos) {
470 return reader_.GetU32At(pos);
471 }
472
473 // Read an ELF::Half at a given position.
GetHalfAt(off_t pos)474 Half GetHalfAt(off_t pos) {
475 return reader_.GetU16At(pos);
476 }
477
478 // Read an ELF::Sword at a given position.
GetSwordAt(off_t pos)479 Sword GetSwordAt(off_t pos) {
480 return static_cast<Sword>(GetWordAt(pos));
481 }
482
483 // Read an ELF::Addr at a given position.
484 Addr GetAddrAt(off_t pos);
485
486 // Read an ELF::Off at a given position.
GetOffAt(off_t pos)487 Off GetOffAt(off_t pos) {
488 return static_cast<Off>(GetAddrAt(pos));
489 }
490
491 // Helper class to iterate over the section table.
492 class SectionIterator {
493 public:
SectionIterator(ElfParser & parser)494 explicit SectionIterator(ElfParser& parser)
495 : parser_(parser) {
496 table_offset_ = parser_.GetOffAt(offsetof(ElfHeader, e_shoff));
497 table_count_ = parser_.GetHalfAt(offsetof(ElfHeader, e_shnum));
498 table_entry_size_ = parser_.GetHalfAt(offsetof(ElfHeader, e_shentsize));
499 if (table_entry_size_ < static_cast<Half>(sizeof(Shdr))) {
500 // malformed binary. Ignore all sections.
501 table_count_ = 0;
502 }
503 }
504
NextOffset()505 Off NextOffset() {
506 if (table_count_ == 0)
507 return 0;
508 Off result = table_offset_;
509 table_offset_ += table_entry_size_;
510 table_count_ -= 1;
511 return result;
512 }
513
Skip(int count)514 void Skip(int count) {
515 while (count > 0 && table_count_ > 0) {
516 table_offset_ += table_entry_size_;
517 table_count_--;
518 count--;
519 }
520 }
521
522 private:
523 ElfParser& parser_;
524 Off table_offset_;
525 Half table_count_;
526 Half table_entry_size_;
527 };
528
529 // Return the offset of the first section of a given type, or 0 if not
530 // found. |*size| will be set to the section size in bytes in case of
531 // success.
GetSectionOffsetByType(int table_type,Off * size)532 Off GetSectionOffsetByType(int table_type, Off* size) {
533 SectionIterator iter(*this);
534 for (;;) {
535 Off table_offset = iter.NextOffset();
536 if (table_offset == 0)
537 break;
538 Word sh_type = GetWordAt(table_offset + offsetof(Shdr, sh_type));
539 if (sh_type == static_cast<Word>(table_type)) {
540 *size = GetOffAt(table_offset + offsetof(Shdr, sh_size));
541 return GetOffAt(table_offset + offsetof(Shdr, sh_offset));
542 }
543 }
544 return 0;
545 }
546
547 // Return the index of the string table for the dynamic section
548 // in this ELF binary. Or 0 if not found.
GetDynamicStringTableIndex()549 int GetDynamicStringTableIndex() {
550 SectionIterator iter(*this);
551 for (;;) {
552 Off table_offset = iter.NextOffset();
553 if (table_offset == 0)
554 break;
555 Word sh_type = GetWordAt(table_offset + offsetof(Shdr, sh_type));
556 if (sh_type == SHT_DYNAMIC)
557 return GetWordAt(table_offset + offsetof(Shdr, sh_link));
558 }
559 return 0;
560 }
561
562 // Return the offset of a section identified by its index, or 0 in case
563 // of error (bad index).
GetSectionOffsetByIndex(int sec_index,Off * size)564 Off GetSectionOffsetByIndex(int sec_index, Off* size) {
565 SectionIterator iter(*this);
566 iter.Skip(sec_index);
567 Off table_offset = iter.NextOffset();
568 if (table_offset != 0) {
569 *size = GetOffAt(table_offset + offsetof(Shdr, sh_size));
570 return GetOffAt(table_offset + offsetof(Shdr, sh_offset));
571 }
572 return 0;
573 }
574
575 // Return a string identified by its index and its string table
576 // Address. Returns an empty string in case of error.
GetStringByIndex(Off str_index,int str_table_index)577 String GetStringByIndex(Off str_index, int str_table_index) {
578 String result;
579
580 if (str_table_index != 0) {
581 Off str_table_size = 0;
582 Off str_table = GetSectionOffsetByIndex(str_table_index,
583 &str_table_size);
584 if (str_table != 0 && str_index < str_table_size) {
585 str_table += str_index;
586 str_table_size -= str_index;
587 while (str_table_size > 0) {
588 const uint8_t* p = reader_.GetBytesAt(str_table, 1);
589 if (p == NULL || *p == '\0')
590 break;
591 result.append(1, static_cast<const char>(*p));
592 str_table += 1;
593 str_table_size -= 1;
594 }
595 }
596 }
597 return result;
598 }
599
600 private:
601 Reader& reader_;
602 };
603
604 template <>
GetAddrAt(off_t pos)605 Elf32::Addr ElfParser<Elf32>::GetAddrAt(off_t pos) {
606 return reader_.GetU32At(pos);
607 }
608
609 template <>
GetAddrAt(off_t pos)610 Elf64::Addr ElfParser<Elf64>::GetAddrAt(off_t pos) {
611 return reader_.GetU64At(pos);
612 }
613
614 // Helper class to iterate over items of a given type in the dynamic
615 // section. A type of 0 (SHT_NULL) means iterate over all items.
616 //
617 // Examples:
618 // // Iterate over all entries in the table, find the SHT_NEEDED ones.
619 // DynamicIterator<Elf32> iter(parser);
620 // while (iter.GetNext()) {
621 // if (iter.GetTag() == SHT_NEEDED) {
622 // Elf32::Off value = iter.GetValue();
623 // ...
624 // }
625 // }
626 template <class ELF>
627 class DynamicIterator {
628 public:
DynamicIterator(ElfParser<ELF> & parser)629 explicit DynamicIterator(ElfParser<ELF>& parser)
630 : parser_(parser),
631 dyn_size_(0),
632 dyn_offset_(0),
633 started_(false) {
634 dyn_offset_ = parser_.GetSectionOffsetByType(SHT_DYNAMIC, &dyn_size_);
635 started_ = (dyn_size_ < kEntrySize);
636 }
637
GetNext()638 bool GetNext() {
639 if (!started_)
640 started_ = true;
641 else {
642 if (dyn_size_ < kEntrySize)
643 return false;
644 dyn_offset_ += kEntrySize;
645 dyn_size_ -= kEntrySize;
646 }
647 return true;
648 }
649
GetTag()650 typename ELF::Off GetTag() {
651 return parser_.GetOffAt(dyn_offset_);
652 }
653
GetValue()654 typename ELF::Off GetValue() {
655 return parser_.GetOffAt(dyn_offset_ + kTagSize);
656 }
657
658 private:
659 typedef typename ELF::Off Off;
660 static const Off kTagSize = static_cast<Off>(sizeof(Off));
661 static const Off kValueSize = kTagSize;
662 static const Off kEntrySize = kTagSize + kValueSize;
663
664 ElfParser<ELF>& parser_;
665 Off dyn_size_;
666 Off dyn_offset_;
667 bool started_;
668 };
669
670 #define DT_NEEDED 1
671 #define DT_SONAME 14
672
673 template <class ELF>
GetLibNameT(Reader & reader)674 String GetLibNameT(Reader& reader) {
675 ElfParser<ELF> parser(reader);
676 int str_table_index = parser.GetDynamicStringTableIndex();
677 DynamicIterator<ELF> iter(parser);
678 while (iter.GetNext()) {
679 if (iter.GetTag() == DT_SONAME) {
680 typename ELF::Off str_index = iter.GetValue();
681 return parser.GetStringByIndex(str_index, str_table_index);
682 }
683 }
684 return String();
685 }
686
687 template <class ELF>
GetNeededLibsT(Reader & reader,std::vector<String> * result)688 int GetNeededLibsT(Reader& reader, std::vector<String>* result) {
689 ElfParser<ELF> parser(reader);
690 int str_table_index = parser.GetDynamicStringTableIndex();
691 DynamicIterator<ELF> iter(parser);
692 int count = 0;
693 while (iter.GetNext()) {
694 if (iter.GetTag() == DT_NEEDED) {
695 typename ELF::Off str_index = iter.GetValue();
696 String lib_name = parser.GetStringByIndex(str_index, str_table_index);
697 if (!lib_name.empty()) {
698 result->push_back(lib_name);
699 count++;
700 }
701 }
702 }
703 return count;
704 }
705
706 class ElfFile {
707 public:
ElfFile()708 ElfFile()
709 : file_(NULL), big_endian_(false), is_64bits_(false), reader_(NULL) {}
710
~ElfFile()711 virtual ~ElfFile() {
712 delete reader_;
713 Close();
714 }
715
Open(const TCHAR * path,String * error)716 bool Open(const TCHAR* path, String* error) {
717 Close();
718 file_ = _tfopen(path, _T("rb"));
719 if (file_ == NULL) {
720 error->assign(TO_STRING(strerror(errno)));
721 return false;
722 }
723 uint8_t ident[EI_NIDENT];
724 if (fread(ident, sizeof(ident), 1, file_) != 1) {
725 error->assign(TO_STRING(strerror(errno)));
726 Close();
727 return false;
728 }
729 if (!elf_ident_is_elf(ident)) {
730 *error = _T("Not an ELF binary file");
731 Close();
732 return false;
733 }
734 big_endian_ = elf_ident_is_big_endian(ident);
735 is_64bits_ = elf_ident_is_64bits(ident);
736
737 if (big_endian_) {
738 reader_ = new FileBigEndianReader(file_);
739 } else {
740 reader_ = new FileLittleEndianReader(file_);
741 }
742 return true;
743 }
744
IsOk()745 bool IsOk() { return file_ != NULL; }
746
IsBigEndian()747 bool IsBigEndian() { return big_endian_; }
748
GetReader()749 const Reader& GetReader() { return *reader_; };
750
751 // Returns the embedded library name, extracted from the dynamic table.
GetLibName()752 String GetLibName() {
753 if (is_64bits_)
754 return GetLibNameT<Elf64>(*reader_);
755 else
756 return GetLibNameT<Elf32>(*reader_);
757 }
758
759 // Gets the list of needed libraries and appends them to |result|.
760 // Returns the number of library names appended.
GetNeededLibs(std::vector<String> * result)761 int GetNeededLibs(std::vector<String>* result) {
762 if (is_64bits_)
763 return GetNeededLibsT<Elf64>(*reader_, result);
764 else
765 return GetNeededLibsT<Elf32>(*reader_, result);
766 }
767
768 protected:
Close()769 void Close() {
770 if (file_ != NULL) {
771 fclose(file_);
772 file_ = NULL;
773 }
774 }
775
776 FILE* file_;
777 bool big_endian_;
778 bool is_64bits_;
779 Reader* reader_;
780 };
781
782 #ifdef __linux__
IsLdSoConfSeparator(char ch)783 static bool IsLdSoConfSeparator(char ch) {
784 // The ldconfig manpage indicates that /etc/ld.so.conf contains a list
785 // of colon, space, tab newline or comma separated directories.
786 return (ch == ' ' || ch == '\t' || ch == '\r' ||
787 ch == '\n' || ch == ',' || ch == ':');
788 }
789
790 // Parse the content of /etc/ld.so.conf, it contains according to the
791 // documentation a 'comma, space, newline, tab separated list of
792 // directories'. In practice, it can also include comments, and an
793 // include directive and glob patterns, as in:
794 // 'include /etc/ld.so.conf.d/*.conf'
AddHostLdSoConfPaths(const char * ld_so_conf_path,std::vector<String> * lib_search_path)795 void AddHostLdSoConfPaths(const char* ld_so_conf_path,
796 std::vector<String>* lib_search_path) {
797 FILE* file = fopen(ld_so_conf_path, "rb");
798 if (!file)
799 return;
800
801 char line[1024];
802 while (fgets(line, sizeof(line), file) != NULL) {
803 const char* begin = line;
804 const char* end = line + strlen(line);
805 while (end > begin && end[-1] == '\n')
806 end--;
807
808 bool prev_is_include = false;
809 while (begin < end) {
810 // Skip over separators
811 while (begin < end && IsLdSoConfSeparator(*begin))
812 begin++;
813
814 if (begin == end || begin[0] == '#') {
815 // Skip empty lines and comments.
816 break;
817 }
818 // Find end of current item
819 const char* next_pos = begin;
820 while (next_pos < end &&
821 !IsLdSoConfSeparator(*next_pos) &&
822 *next_pos != '#')
823 next_pos++;
824
825 size_t len = static_cast<size_t>(next_pos - begin);
826 if (prev_is_include) {
827 // If previous token was an 'include', treat this as a glob
828 // pattern and try to process all matching files.
829 prev_is_include = false;
830 if (len == 0) {
831 // Ignore stand-alone 'include' in a single line.
832 break;
833 }
834 String pattern(begin, len);
835 DLOG("%s: processing include '%s'\n",
836 __FUNCTION__,
837 pattern.c_str());
838
839 glob_t the_glob;
840 memset(&the_glob, 0, sizeof(the_glob));
841 int ret = ::glob(pattern.c_str(), 0, NULL, &the_glob);
842 if (ret == 0) {
843 // Iterate / include all matching files.
844 String filepath;
845 for (size_t n = 0; n < the_glob.gl_pathc; ++n) {
846 filepath.assign(the_glob.gl_pathv[n]);
847 DLOG("%s: Including %s\n", __FUNCTION__, filepath.c_str());
848 AddHostLdSoConfPaths(filepath.c_str(), lib_search_path);
849 }
850 }
851 globfree(&the_glob);
852 } else {
853 // The previous token was not an 'include'. But is the current one?
854 static const char kInclude[] = "include";
855 const size_t kIncludeLen = sizeof(kInclude) - 1;
856 if (len == kIncludeLen && !memcmp(begin, kInclude, len)) {
857 prev_is_include = true;
858 } else if (len > 0) {
859 // No, it must be a directory name.
860 String dirpath(begin, len);
861 struct stat st;
862 if (::stat(dirpath.c_str(), &st) != 0) {
863 LOG("Could not stat(): %s: %s\n", dirpath.c_str(), strerror(errno));
864 } else if (!S_ISDIR(st.st_mode)) {
865 LOG("Not a directory: %s\n", dirpath.c_str());
866 } else {
867 DLOG("%s: Adding %s\n", __FUNCTION__, dirpath.c_str());
868 lib_search_path->push_back(dirpath);
869 }
870 }
871 }
872 // switch to next item in line.
873 begin = next_pos;
874 }
875 }
876 fclose(file);
877 }
878 #endif // __linux__
879
880 // Add host shared library search path to |lib_search_path|
AddHostLibraryPaths(std::vector<String> * lib_search_path)881 void AddHostLibraryPaths(std::vector<String>* lib_search_path) {
882 // Only add libraries form LD_LIBRARY_PATH on ELF-based systems.
883 #if defined(__ELF__)
884 // If LD_LIBRARY_PATH is defined, process it
885 const TCHAR* env = _tgetenv(_T("LD_LIBRARY_PATH"));
886 if (env != NULL) {
887 const TCHAR* pos = env;
888 while (*pos) {
889 size_t path_len;
890 const TCHAR* next_pos = _tcschr(pos, ':');
891 if (next_pos == NULL) {
892 path_len = _tcslen(pos);
893 next_pos = pos + path_len;
894 } else {
895 path_len = next_pos - pos;
896 next_pos += 1;
897 }
898
899 if (path_len == 0) {
900 // Per POSIX convention, an empty path item means "current path".
901 // Not that this is generally a very bad idea, security-wise.
902 lib_search_path->push_back(_T("."));
903 } else {
904 lib_search_path->push_back(String(pos, path_len));
905 }
906
907 pos = next_pos;
908 }
909 }
910 #ifdef __linux__
911 AddHostLdSoConfPaths("/etc/ld.so.conf", lib_search_path);
912 #endif
913 // TODO(digit): What about BSD systems?
914 #endif
915 }
916
917 // Returns true if |libname| is the name of an Android system library.
IsAndroidSystemLib(const String & libname)918 bool IsAndroidSystemLib(const String& libname) {
919 static const TCHAR* const kAndroidSystemLibs[] = {
920 _T("libc.so"),
921 _T("libdl.so"),
922 _T("liblog.so"),
923 _T("libm.so"),
924 _T("libstdc++.so"),
925 _T("libz.so"),
926 _T("libandroid.so"),
927 _T("libjnigraphics.so"),
928 _T("libEGL.so"),
929 _T("libGLESv1_CM.so"),
930 _T("libGLESv2.so"),
931 _T("libOpenSLES.so"),
932 _T("libOpenMAXAL.so"),
933 NULL
934 };
935 for (size_t n = 0; kAndroidSystemLibs[n] != NULL; ++n) {
936 if (!libname.compare(kAndroidSystemLibs[n]))
937 return true;
938 }
939 return false;
940 }
941
942 // Returns true if |libname| is the name of an NDK-compatible shared
943 // library. This means its must begin with "lib" and end with "so"
944 // (without any version numbers).
IsAndroidNdkCompatibleLib(const String & libname)945 bool IsAndroidNdkCompatibleLib(const String& libname) {
946 return libname.size() > 6 &&
947 !libname.compare(0, 3, _T("lib")) &&
948 !libname.compare(libname.size() - 3, 3, _T(".so"));
949 }
950
951 // Try to find a library named |libname| in |search_paths|
952 // Returns true on success, and sets |result| to the full library path,
953 // false otherwise.
FindLibraryPath(const String & libname,const std::vector<String> & search_paths,String * result)954 bool FindLibraryPath(const String& libname,
955 const std::vector<String>& search_paths,
956 String* result) {
957 // Check in the search paths.
958 LOG2(_T(" looking for library: %s\n"), libname.c_str());
959 for (size_t n = 0; n < search_paths.size(); ++n) {
960 String file_path = search_paths[n];
961 if (file_path.empty())
962 continue;
963 if (file_path[file_path.size() - 1] != _T('/') &&
964 file_path[file_path.size() - 1] != _T('\\')) {
965 file_path.append(_T("/"));
966 }
967 file_path.append(libname);
968
969 LOG2(_T(" in %s: "), file_path.c_str());
970 struct _stat st;
971 if (_tstat(file_path.c_str(), &st) < 0) {
972 LOG2(_T("%s\n"), TO_CONST_TCHAR(strerror(errno)));
973 continue;
974 }
975 if (!S_ISREG(st.st_mode)) {
976 LOG2(_T("Not a regular file!\n"));
977 continue;
978 }
979 // Found the library file.
980 LOG2(_T("OK\n"));
981 result->assign(file_path);
982 return true;
983 }
984
985 return false;
986 }
987
988 // Recursive support
989
990 struct LibNode {
991 // An enumeration listing possible node types, which are:
992 enum Type {
993 NODE_NONE, // No type yet.
994 NODE_PATH, // Valid ELF library, |value| is file path.
995 NODE_ERROR, // Invalid library name, |value| is error string.
996 NODE_SYSTEM, // Android system library, |value| is library name.
997 };
998
999 Type type;
1000 String value;
1001 std::vector<String> needed_libs;
1002
LibNode__anon9f1770860111::LibNode1003 LibNode() : type(NODE_NONE), value(), needed_libs() {}
1004
LibNode__anon9f1770860111::LibNode1005 explicit LibNode(const String& path)
1006 : type(NODE_PATH), value(path), needed_libs() {}
1007
Set__anon9f1770860111::LibNode1008 void Set(Type type_p, const String& value_p) {
1009 type = type_p;
1010 value = value_p;
1011 }
1012 };
1013
1014 typedef std::map<String, LibNode> DependencyGraph;
1015 typedef std::list<String> WorkQueue;
1016
1017 // Used internally by BuildDependencyGraph().
UpdateDependencies(const String & libname,const String & libpath,DependencyGraph & deps,WorkQueue & queue)1018 void UpdateDependencies(
1019 const String& libname,
1020 const String& libpath,
1021 DependencyGraph& deps,
1022 WorkQueue& queue) {
1023 DLOG(_T("UPDATE libname=%s path=%s\n"), libname.c_str(), libpath.c_str());
1024 // Sanity check: find if the library is already in the graph.
1025 if (!libname.empty() && deps.find(libname) != deps.end()) {
1026 // Should not happen.
1027 panic(_T("INTERNAL: Library already in graph: %s"), libname.c_str());
1028 }
1029
1030 LibNode node;
1031 ElfFile libfile;
1032 String error;
1033 String soname = libname;
1034 if (!libfile.Open(libpath.c_str(), &error)) {
1035 node.Set(LibNode::NODE_ERROR, error);
1036 } else {
1037 String soname = libfile.GetLibName();
1038 if (soname.empty())
1039 soname = libname;
1040 else if (soname != libname) {
1041 _ftprintf(stderr,
1042 _T("WARNING: Library has invalid soname ('%s'): %s\n"),
1043 soname.c_str(),
1044 libpath.c_str());
1045 }
1046 // Discovered a new library, get its dependent libraries.
1047 node.Set(LibNode::NODE_PATH, libpath);
1048 libfile.GetNeededLibs(&node.needed_libs);
1049
1050 LOG(_T("%s depends on:"), soname.c_str());
1051
1052 // Add them to the work queue.
1053 for (size_t n = 0; n < node.needed_libs.size(); ++n) {
1054 LOG(_T(" %s"), node.needed_libs[n].c_str());
1055 queue.push_back(node.needed_libs[n]);
1056 }
1057 LOG(_T("\n"));
1058 }
1059 deps[soname] = node;
1060 }
1061
1062 // Build the full dependency graph.
1063 // |root_libpath| is the path of the root library.
1064 // |lib_search_path| is the list of library search paths.
1065 // Returns a new dependency graph object.
BuildDependencyGraph(const String & root_libpath,const std::vector<String> & lib_search_path)1066 DependencyGraph BuildDependencyGraph(
1067 const String& root_libpath,
1068 const std::vector<String>& lib_search_path) {
1069 DependencyGraph deps;
1070 std::list<String> queue;
1071
1072 // As a first step, build the full dependency graph, starting with the
1073 // root library. This records errors in the graph too.
1074 UpdateDependencies(path_basename(root_libpath),
1075 root_libpath,
1076 deps, queue);
1077
1078 while (!queue.empty()) {
1079 // Pop first item from queue.
1080 String libname = queue.front();
1081 queue.pop_front();
1082
1083 // Is the library already in the graph?
1084 DependencyGraph::iterator iter = deps.find(libname);
1085 if (iter != deps.end()) {
1086 // Library already found, skip it.
1087 continue;
1088 }
1089
1090 // Find the library in the current search path.
1091 String libpath;
1092 if (FindLibraryPath(libname, lib_search_path, &libpath)) {
1093 UpdateDependencies(libname, libpath, deps, queue);
1094 continue;
1095 }
1096
1097 if (IsAndroidSystemLib(libname)) {
1098 LOG(_T("Android system library: %s\n"), libname.c_str());
1099 LibNode node;
1100 node.Set(LibNode::NODE_SYSTEM, libname);
1101 deps[libname] = node;
1102 continue;
1103 }
1104
1105 _ftprintf(stderr,
1106 _T("WARNING: Could not find library: %s\n"),
1107 libname.c_str());
1108 LibNode node;
1109 node.Set(LibNode::NODE_ERROR, _T("Could not find library"));
1110 deps[libname] = node;
1111 }
1112
1113 return deps;
1114 }
1115
1116 // Print the dependency graph in a human-readable format to stdout.
DumpDependencyGraph(const DependencyGraph & deps)1117 void DumpDependencyGraph(const DependencyGraph& deps) {
1118 _tprintf(_T("Dependency graph:\n"));
1119 DependencyGraph::const_iterator iter = deps.begin();
1120 for ( ; iter != deps.end(); ++iter ) {
1121 const String& libname = iter->first;
1122 const LibNode& node = iter->second;
1123 String node_type;
1124 switch (node.type) {
1125 case LibNode::NODE_NONE: // should not happen.
1126 node_type = _T("NONE??");
1127 break;
1128 case LibNode::NODE_PATH:
1129 node_type = _T("PATH");
1130 break;
1131 case LibNode::NODE_ERROR:
1132 node_type = _T("ERROR");
1133 break;
1134 case LibNode::NODE_SYSTEM:
1135 node_type = _T("SYSTEM");
1136 }
1137 _tprintf(
1138 _T("[%s] %s %s\n"),
1139 libname.c_str(),
1140 node_type.c_str(),
1141 node.value.c_str());
1142
1143 if (node.type == LibNode::NODE_PATH) {
1144 for (size_t n = 0; n < node.needed_libs.size(); ++n) {
1145 _tprintf(_T(" %s\n"), node.needed_libs[n].c_str());
1146 }
1147 }
1148 }
1149 }
1150
1151 // Return the sorted list of libraries from a dependency graph.
1152 // They are topologically ordered, i.e. a library appears always
1153 // before any other library it depends on.
GetTopologicalSortedLibraries(DependencyGraph & deps,std::vector<String> * result)1154 void GetTopologicalSortedLibraries(
1155 DependencyGraph& deps,
1156 std::vector<String>* result) {
1157 result->clear();
1158 // First: Compute the number of visitors per library in the graph.
1159 typedef std::map<String, int> VisitorMap;
1160 VisitorMap visitors;
1161 for (DependencyGraph::const_iterator iter = deps.begin();
1162 iter != deps.end();
1163 ++iter) {
1164 if (visitors.find(iter->first) == visitors.end()) {
1165 visitors[iter->first] = 0;
1166 }
1167
1168 const std::vector<String>& needed_libs = iter->second.needed_libs;
1169 for (size_t n = 0; n < needed_libs.size(); ++n) {
1170 const String& libname = needed_libs[n];
1171 VisitorMap::iterator lib_iter = visitors.find(libname);
1172 if (lib_iter != visitors.end())
1173 lib_iter->second += 1;
1174 else
1175 visitors[libname] = 1;
1176 }
1177 }
1178
1179 #if DEBUG
1180 {
1181 VisitorMap::const_iterator iter_end = visitors.end();
1182 VisitorMap::const_iterator iter = visitors.begin();
1183 for ( ; iter != iter_end; ++iter ) {
1184 _tprintf(_T("-- %s %d\n"), iter->first.c_str(), iter->second);
1185 }
1186 }
1187 #endif
1188
1189 while (!visitors.empty()) {
1190 // Find the library with the smallest number of visitors.
1191 // The value should be 0, unless there are circular dependencies.
1192 VisitorMap::const_iterator iter_end = visitors.end();
1193 VisitorMap::const_iterator iter;
1194 int min_visitors = INT_MAX;
1195 String min_libname;
1196 for (iter = visitors.begin(); iter != iter_end; ++iter) {
1197 // Note: Uses <= instead of < to ensure better diagnostics in
1198 // case of circular dependencies. This shall return the latest
1199 // node in the cycle, i.e. the first one where a 'back' edge
1200 // exists.
1201 if (iter->second <= min_visitors) {
1202 min_libname = iter->first;
1203 min_visitors = iter->second;
1204 }
1205 }
1206
1207 if (min_visitors == INT_MAX) {
1208 // Should not happen.
1209 panic(_T("INTERNAL: Could not find minimum visited node!"));
1210 }
1211
1212 // min_visitors should be 0, unless there are circular dependencies.
1213 if (min_visitors != 0) {
1214 // Warn about circular dependencies
1215 _ftprintf(stderr,
1216 _T("WARNING: Circular dependency found from: %s\n"),
1217 min_libname.c_str());
1218 }
1219
1220 // Remove minimum node from the graph, and decrement the visitor
1221 // count of all its needed libraries. This also breaks dependency
1222 // cycles.
1223 result->push_back(min_libname);
1224 const LibNode& node = deps[min_libname];
1225 const std::vector<String> needed_libs = node.needed_libs;
1226 visitors.erase(min_libname);
1227
1228 for (size_t n = 0; n < needed_libs.size(); ++n)
1229 visitors[needed_libs[n]]--;
1230 }
1231 }
1232
1233 // Main function
1234
1235 #define PROGNAME "ndk-depends"
1236
print_usage(int exit_code)1237 void print_usage(int exit_code) {
1238 printf(
1239 "Usage: %s [options] <elf-file>\n\n"
1240
1241 "This program is used to print the dependencies of a given ELF\n"
1242 "binary (shared library or executable). It supports any architecture,\n"
1243 "endianess and bitness.\n\n"
1244
1245 "By default, all dependencies are printed in topological order,\n"
1246 "which means that each item always appear before other items\n"
1247 "it depends on. Except in case of circular dependencies, which will\n"
1248 "print a warning to stderr.\n\n"
1249
1250 "The tool will try to find other libraries in the same directory\n"
1251 "as the input ELF file. It is possible however to provide\n"
1252 "additional search paths with the -L<path>, which adds an explicit path\n"
1253 "or --host-libs which adds host-specific library paths, on ELF-based systems\n"
1254 "only.\n\n"
1255
1256 "Use --print-paths to print the path of each ELF binary.\n\n"
1257
1258 "Use --print-direct to only print the direct dependencies\n"
1259 "of the input ELF binary. All other options except --verbose will be ignored.\n\n"
1260
1261 "Use --print-java to print a Java source fragment that loads the\n"
1262 "libraries with System.loadLibrary() in the correct order. This can\n"
1263 "be useful when copied into your application's Java source code.\n\n"
1264
1265 "Use --print-dot to print the dependency graph as a .dot file that can be\n"
1266 "parsed by the GraphViz tool. For example, to generate a PNG image of the\n"
1267 "graph, use something like:\n\n"
1268
1269 " ndk-depends /path/to/libfoo.so --print-dot | dot -Tpng -o /tmp/graph.png\n\n"
1270
1271 "The --verbose option prints debugging information, which can be useful\n"
1272 "to diagnose problems with malformed ELF binaries.\n\n"
1273
1274 "Valid options:\n"
1275 " --help|-h|-? Print this message.\n"
1276 " --verbose Increase verbosity.\n"
1277 " --print-direct Only print direct dependencies.\n"
1278 " -L<path> Append <path> to the library search path.\n"
1279 " --host-libs Append host library search path.\n"
1280 " --print-paths Print full paths of all libraries.\n"
1281 " --print-java Print Java library load sequence.\n"
1282 " --print-dot Print the dependency graph as a Graphviz .dot file.\n"
1283 "\n", PROGNAME);
1284
1285 exit(exit_code);
1286 }
1287
1288 } // namespace
1289
1290
1291 #ifdef _WIN32
main(void)1292 int main(void) {
1293 int argc = 0;
1294 TCHAR** argv = CommandLineToArgvW(GetCommandLine(), &argc);
1295 #else
1296 int main(int argc, const char** argv) {
1297 #endif
1298
1299 enum PrintFormat {
1300 PRINT_DEFAULT = 0,
1301 PRINT_DIRECT,
1302 PRINT_PATHS,
1303 PRINT_JAVA,
1304 PRINT_DOT_FILE,
1305 };
1306
1307 bool do_help = false;
1308 PrintFormat print_format = PRINT_DEFAULT;
1309 std::vector<String> lib_search_path;
1310 std::vector<String> params;
1311
1312 // Process options.
1313 while (argc > 1) {
1314 if (argv[1][0] == _T('-')) {
1315 const TCHAR* arg = argv[1];
1316 if (!_tcscmp(arg, _T("--help")) ||
1317 !_tcscmp(arg, _T("-h")) ||
1318 !_tcscmp(arg, _T("-?")))
1319 do_help = true;
1320 else if (!_tcscmp(arg, _T("--print-direct"))) {
1321 print_format = PRINT_DIRECT;
1322 } else if (!_tcscmp(arg, _T("-L"))) {
1323 if (argc < 3)
1324 panic(_T("Option -L requires an argument."));
1325 lib_search_path.push_back(String(argv[2]));
1326 argc--;
1327 argv++;
1328 } else if (!_tcsncmp(arg, _T("-L"), 2)) {
1329 lib_search_path.push_back(String(arg+2));
1330 } else if (!_tcscmp(arg, _T("--host-libs"))) {
1331 AddHostLibraryPaths(&lib_search_path);
1332 } else if (!_tcscmp(arg, _T("--print-java"))) {
1333 print_format = PRINT_JAVA;
1334 } else if (!_tcscmp(arg, _T("--print-paths"))) {
1335 print_format = PRINT_PATHS;
1336 } else if (!_tcscmp(arg, _T("--print-dot"))) {
1337 print_format = PRINT_DOT_FILE;
1338 } else if (!_tcscmp(arg, _T("--verbose"))) {
1339 g_verbose++;
1340 } else {
1341 panic(_T("Unsupported option '%s', see --help."), arg);
1342 }
1343 } else {
1344 params.push_back(String(argv[1]));
1345 }
1346 argc--;
1347 argv++;
1348 }
1349
1350 if (do_help)
1351 print_usage(0);
1352
1353 if (params.empty())
1354 panic(_T("Please provide the path of an ELF shared library or executable."
1355 "\nSee --help for usage details."));
1356
1357 // Insert ELF file directory at the head of the search path.
1358 lib_search_path.insert(lib_search_path.begin(), path_dirname(params[0]));
1359
1360 if (g_verbose >= 1) {
1361 _tprintf(_T("Current library search path:\n"));
1362 for (size_t n = 0; n < lib_search_path.size(); ++n)
1363 _tprintf(_T(" %s\n"), lib_search_path[n].c_str());
1364 _tprintf(_T("\n"));
1365 }
1366
1367 // Open main input file.
1368 const TCHAR* libfile_path = params[0].c_str();
1369
1370 ElfFile libfile;
1371 String error;
1372 if (!libfile.Open(libfile_path, &error)) {
1373 panic(_T("Could not open file '%s': %s"), libfile_path, error.c_str());
1374 }
1375
1376 if (print_format == PRINT_DIRECT) {
1377 // Simple dump, one line per dependency. No frills, no recursion.
1378 std::vector<String> needed_libs;
1379 libfile.GetNeededLibs(&needed_libs);
1380
1381 for (size_t i = 0; i < needed_libs.size(); ++i)
1382 _tprintf(_T("%s\n"), needed_libs[i].c_str());
1383
1384 return 0;
1385 }
1386
1387 // Topological sort of all dependencies.
1388 LOG(_T("Building dependency graph...\n"));
1389 DependencyGraph deps = BuildDependencyGraph(
1390 libfile_path, lib_search_path);
1391
1392 if (g_verbose >= 2)
1393 DumpDependencyGraph(deps);
1394
1395 LOG(_T("Building sorted list of binaries:\n"));
1396 std::vector<String> needed_libs;
1397 GetTopologicalSortedLibraries(deps, &needed_libs);
1398
1399 if (print_format == PRINT_JAVA) {
1400 // Print Java libraries in reverse order.
1401 std::reverse(needed_libs.begin(), needed_libs.end());
1402 for (size_t i = 0; i < needed_libs.size(); ++i) {
1403 const String& lib = needed_libs[i];
1404 if (IsAndroidSystemLib(lib)) {
1405 // Skip system libraries.
1406 continue;
1407 }
1408 if (!IsAndroidNdkCompatibleLib(lib)) {
1409 _ftprintf(
1410 stderr,
1411 _T("WARNING: Non-compatible library name ignored: %s\n"),
1412 lib.c_str());
1413 continue;
1414 }
1415 _tprintf(_T("System.loadLibrary(%s);\n"),
1416 lib.substr(3, lib.size() - 6).c_str());
1417 }
1418 return 0;
1419 }
1420
1421 if (print_format == PRINT_DOT_FILE) {
1422 // Using the topological order helps generates a more human-friendly
1423 // directed graph.
1424 _tprintf(_T("digraph {\n"));
1425 for (size_t i = 0; i < needed_libs.size(); ++i) {
1426 const String& libname = needed_libs[i];
1427 const std::vector<String>& libdeps = deps[libname].needed_libs;
1428 for (size_t n = 0; n < libdeps.size(); ++n) {
1429 // Note: Use quoting to deal with special characters like -
1430 // which are not normally part of DOT 'id' tokens.
1431 _tprintf(_T(" \"%s\" -> \"%s\"\n"), libname.c_str(), libdeps[n].c_str());
1432 }
1433 }
1434 _tprintf(_T("}\n"));
1435 return 0;
1436 }
1437
1438 if (print_format == PRINT_PATHS) {
1439 // Print libraries with path.
1440 for (size_t i = 0; i < needed_libs.size(); ++i) {
1441 const String& lib = needed_libs[i];
1442 LibNode& node = deps[lib];
1443 const TCHAR* format;
1444 switch (node.type) {
1445 case LibNode::NODE_PATH:
1446 format = _T("%s -> %s\n");
1447 break;
1448 case LibNode::NODE_SYSTEM:
1449 format = _T("%s -> $ /system/lib/%s\n");
1450 break;
1451 default:
1452 format = _T("%s -> !! %s\n");
1453 }
1454 _tprintf(format, lib.c_str(), deps[lib].value.c_str());
1455 }
1456 return 0;
1457 }
1458
1459 // Print simple library names.
1460 for (size_t i = 0; i < needed_libs.size(); ++i) {
1461 const String& lib = needed_libs[i];
1462 _tprintf(_T("%s\n"), lib.c_str());
1463 }
1464 return 0;
1465 }
1466