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 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 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 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 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. 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. 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. 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 239 uint16_t get_u16_le(const uint8_t* bytes) { 240 return static_cast<uint16_t>(bytes[0] | (bytes[1] << 8)); 241 } 242 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 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 254 uint16_t get_u16_be(const uint8_t* bytes) { 255 return static_cast<uint16_t>((bytes[0] << 8) | bytes[1]); 256 } 257 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 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: 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 280 virtual ~Reader() {} 281 }; 282 283 class FileReader : public Reader { 284 public: 285 FileReader(FILE* file) : file_(file) {} 286 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: 304 FileLittleEndianReader(FILE* file) : FileReader(file) {} 305 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 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 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: 324 FileBigEndianReader(FILE* file) : FileReader(file) {} 325 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 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 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 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 362 bool elf_ident_is_big_endian(const uint8_t* ident) { 363 return ident[EI_DATA] == ELFDATA2MSB; 364 } 365 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: 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. 469 Word GetWordAt(off_t pos) { 470 return reader_.GetU32At(pos); 471 } 472 473 // Read an ELF::Half at a given position. 474 Half GetHalfAt(off_t pos) { 475 return reader_.GetU16At(pos); 476 } 477 478 // Read an ELF::Sword at a given position. 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. 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: 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 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 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. 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. 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). 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. 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 <> 605 Elf32::Addr ElfParser<Elf32>::GetAddrAt(off_t pos) { 606 return reader_.GetU32At(pos); 607 } 608 609 template <> 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: 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 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 650 typename ELF::Off GetTag() { 651 return parser_.GetOffAt(dyn_offset_); 652 } 653 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> 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> 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: 708 ElfFile() 709 : file_(NULL), big_endian_(false), is_64bits_(false), reader_(NULL) {} 710 711 virtual ~ElfFile() { 712 delete reader_; 713 Close(); 714 } 715 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 745 bool IsOk() { return file_ != NULL; } 746 747 bool IsBigEndian() { return big_endian_; } 748 749 const Reader& GetReader() { return *reader_; }; 750 751 // Returns the embedded library name, extracted from the dynamic table. 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. 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: 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__ 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' 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| 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. 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). 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. 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 1003 LibNode() : type(NODE_NONE), value(), needed_libs() {} 1004 1005 explicit LibNode(const String& path) 1006 : type(NODE_PATH), value(path), needed_libs() {} 1007 1008 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(). 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. 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. 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. 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 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 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