diff options
author | Dmitriy Ivanov | 2014-11-09 21:27:20 -0600 |
---|---|---|
committer | Dmitriy Ivanov | 2014-11-12 18:38:12 -0600 |
commit | ec18ce06f2d007be40ad6f043058f5a4c7236573 (patch) | |
tree | 0e22f0d9ad23e06303584d05483ff5a881afe20b /linker | |
parent | e5cabca516252addb5e305c8e1e0f35cafbcafbe (diff) | |
download | platform-bionic-ec18ce06f2d007be40ad6f043058f5a4c7236573.tar.gz platform-bionic-ec18ce06f2d007be40ad6f043058f5a4c7236573.tar.xz platform-bionic-ec18ce06f2d007be40ad6f043058f5a4c7236573.zip |
Add support for hash-style=gnu
Change-Id: I171434a587420895feac8a9b1ad2342087197568
Diffstat (limited to 'linker')
-rw-r--r-- | linker/dlfcn.cpp | 2 | ||||
-rw-r--r-- | linker/linker.cpp | 223 | ||||
-rw-r--r-- | linker/linker.h | 46 |
3 files changed, 214 insertions, 57 deletions
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 367179d8..799284ee 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp | |||
@@ -145,7 +145,7 @@ int dladdr(const void* addr, Dl_info* info) { | |||
145 | info->dli_fbase = reinterpret_cast<void*>(si->base); | 145 | info->dli_fbase = reinterpret_cast<void*>(si->base); |
146 | 146 | ||
147 | // Determine if any symbol in the library contains the specified address. | 147 | // Determine if any symbol in the library contains the specified address. |
148 | ElfW(Sym)* sym = dladdr_find_symbol(si, addr); | 148 | ElfW(Sym)* sym = si->find_symbol_by_address(addr); |
149 | if (sym != nullptr) { | 149 | if (sym != nullptr) { |
150 | info->dli_sname = si->get_string(sym->st_name); | 150 | info->dli_sname = si->get_string(sym->st_name); |
151 | info->dli_saddr = reinterpret_cast<void*>(si->resolve_symbol_address(sym)); | 151 | info->dli_saddr = reinterpret_cast<void*>(si->resolve_symbol_address(sym)); |
diff --git a/linker/linker.cpp b/linker/linker.cpp index b2911b8c..cefbc063 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <stdlib.h> | 35 | #include <stdlib.h> |
36 | #include <string.h> | 36 | #include <string.h> |
37 | #include <sys/mman.h> | 37 | #include <sys/mman.h> |
38 | #include <sys/param.h> | ||
38 | #include <unistd.h> | 39 | #include <unistd.h> |
39 | 40 | ||
40 | #include <new> | 41 | #include <new> |
@@ -316,6 +317,7 @@ static void soinfo_free(soinfo* si) { | |||
316 | } | 317 | } |
317 | prev = trav; | 318 | prev = trav; |
318 | } | 319 | } |
320 | |||
319 | if (trav == nullptr) { | 321 | if (trav == nullptr) { |
320 | // si was not in solist | 322 | // si was not in solist |
321 | DL_ERR("name \"%s\" is not in solist!", si->name); | 323 | DL_ERR("name \"%s\" is not in solist!", si->name); |
@@ -335,7 +337,6 @@ static void soinfo_free(soinfo* si) { | |||
335 | g_soinfo_allocator.free(si); | 337 | g_soinfo_allocator.free(si); |
336 | } | 338 | } |
337 | 339 | ||
338 | |||
339 | static void parse_path(const char* path, const char* delimiters, | 340 | static void parse_path(const char* path, const char* delimiters, |
340 | const char** array, char* buf, size_t buf_size, size_t max_count) { | 341 | const char** array, char* buf, size_t buf_size, size_t max_count) { |
341 | if (path == nullptr) { | 342 | if (path == nullptr) { |
@@ -415,39 +416,72 @@ int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void | |||
415 | return rv; | 416 | return rv; |
416 | } | 417 | } |
417 | 418 | ||
418 | static ElfW(Sym)* soinfo_elf_lookup(const soinfo* si, unsigned hash, const char* name) { | 419 | ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name) { |
419 | ElfW(Sym)* symtab = si->symtab; | 420 | return is_gnu_hash() ? gnu_lookup(symbol_name) : elf_lookup(symbol_name); |
421 | } | ||
422 | |||
423 | static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) { | ||
424 | if (ELF_ST_BIND(s->st_info) == STB_GLOBAL || | ||
425 | ELF_ST_BIND(s->st_info) == STB_WEAK) { | ||
426 | return s->st_shndx != SHN_UNDEF; | ||
427 | } else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) { | ||
428 | DL_WARN("unexpected ST_BIND value: %d for '%s' in '%s'", | ||
429 | ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->name); | ||
430 | } | ||
431 | |||
432 | return false; | ||
433 | } | ||
434 | |||
435 | ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) { | ||
436 | uint32_t hash = symbol_name.gnu_hash(); | ||
437 | uint32_t h2 = hash >> gnu_shift2; | ||
420 | 438 | ||
421 | TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p %x %zd", | 439 | uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8; |
422 | name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket); | 440 | uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords; |
441 | ElfW(Addr) bloom_word = gnu_bloom_filter[word_num]; | ||
423 | 442 | ||
424 | for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) { | 443 | // test against bloom filter |
444 | if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) { | ||
445 | return nullptr; | ||
446 | } | ||
447 | |||
448 | // bloom test says "probably yes"... | ||
449 | uint32_t n = bucket[hash % nbucket]; | ||
450 | |||
451 | if (n == 0) { | ||
452 | return nullptr; | ||
453 | } | ||
454 | |||
455 | do { | ||
425 | ElfW(Sym)* s = symtab + n; | 456 | ElfW(Sym)* s = symtab + n; |
426 | if (strcmp(si->get_string(s->st_name), name)) continue; | 457 | if (((chain[n] ^ hash) >> 1) == 0 && |
427 | 458 | strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && | |
428 | // only concern ourselves with global and weak symbol definitions | 459 | is_symbol_global_and_defined(this, s)) { |
429 | switch (ELF_ST_BIND(s->st_info)) { | 460 | return s; |
430 | case STB_GLOBAL: | 461 | } |
431 | case STB_WEAK: | 462 | } while ((chain[n++] & 1) == 0); |
432 | if (s->st_shndx == SHN_UNDEF) { | ||
433 | continue; | ||
434 | } | ||
435 | 463 | ||
436 | TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", | 464 | return nullptr; |
437 | name, si->name, reinterpret_cast<void*>(s->st_value), | 465 | } |
438 | static_cast<size_t>(s->st_size)); | 466 | |
439 | return s; | 467 | ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) { |
440 | case STB_LOCAL: | 468 | uint32_t hash = symbol_name.elf_hash(); |
441 | continue; | 469 | |
442 | default: | 470 | TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd", |
443 | __libc_fatal("ERROR: Unexpected ST_BIND value: %d for '%s' in '%s'", | 471 | symbol_name.get_name(), name, reinterpret_cast<void*>(base), hash, hash % nbucket); |
444 | ELF_ST_BIND(s->st_info), name, si->name); | 472 | |
473 | for (uint32_t n = bucket[hash % nbucket]; n != 0; n = chain[n]) { | ||
474 | ElfW(Sym)* s = symtab + n; | ||
475 | if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && is_symbol_global_and_defined(this, s)) { | ||
476 | TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", | ||
477 | symbol_name.get_name(), name, reinterpret_cast<void*>(s->st_value), | ||
478 | static_cast<size_t>(s->st_size)); | ||
479 | return s; | ||
445 | } | 480 | } |
446 | } | 481 | } |
447 | 482 | ||
448 | TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd", | 483 | TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd", |
449 | name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket); | 484 | symbol_name.get_name(), name, reinterpret_cast<void*>(base), hash, hash % nbucket); |
450 | |||
451 | 485 | ||
452 | return nullptr; | 486 | return nullptr; |
453 | } | 487 | } |
@@ -468,22 +502,44 @@ soinfo::soinfo(const char* name, const struct stat* file_stat, off64_t file_offs | |||
468 | this->rtld_flags = rtld_flags; | 502 | this->rtld_flags = rtld_flags; |
469 | } | 503 | } |
470 | 504 | ||
471 | static unsigned elfhash(const char* _name) { | ||
472 | const unsigned char* name = reinterpret_cast<const unsigned char*>(_name); | ||
473 | unsigned h = 0, g; | ||
474 | 505 | ||
475 | while (*name) { | 506 | uint32_t SymbolName::elf_hash() { |
476 | h = (h << 4) + *name++; | 507 | if (!has_elf_hash_) { |
477 | g = h & 0xf0000000; | 508 | const unsigned char* name = reinterpret_cast<const unsigned char*>(name_); |
478 | h ^= g; | 509 | uint32_t h = 0, g; |
479 | h ^= g >> 24; | 510 | |
511 | while (*name) { | ||
512 | h = (h << 4) + *name++; | ||
513 | g = h & 0xf0000000; | ||
514 | h ^= g; | ||
515 | h ^= g >> 24; | ||
516 | } | ||
517 | |||
518 | elf_hash_ = h; | ||
519 | has_elf_hash_ = true; | ||
480 | } | 520 | } |
481 | return h; | 521 | |
522 | return elf_hash_; | ||
523 | } | ||
524 | |||
525 | uint32_t SymbolName::gnu_hash() { | ||
526 | if (!has_gnu_hash_) { | ||
527 | uint32_t h = 5381; | ||
528 | const unsigned char* name = reinterpret_cast<const unsigned char*>(name_); | ||
529 | while (*name != 0) { | ||
530 | h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c | ||
531 | } | ||
532 | |||
533 | gnu_hash_ = h; | ||
534 | has_gnu_hash_ = true; | ||
535 | } | ||
536 | |||
537 | return gnu_hash_; | ||
482 | } | 538 | } |
483 | 539 | ||
484 | static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in, | 540 | static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in, |
485 | const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) { | 541 | const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) { |
486 | unsigned elf_hash = elfhash(name); | 542 | SymbolName symbol_name(name); |
487 | ElfW(Sym)* s = nullptr; | 543 | ElfW(Sym)* s = nullptr; |
488 | 544 | ||
489 | /* "This element's presence in a shared object library alters the dynamic linker's | 545 | /* "This element's presence in a shared object library alters the dynamic linker's |
@@ -499,7 +555,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** s | |||
499 | */ | 555 | */ |
500 | if (si_from->has_DT_SYMBOLIC) { | 556 | if (si_from->has_DT_SYMBOLIC) { |
501 | DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->name, name); | 557 | DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->name, name); |
502 | s = soinfo_elf_lookup(si_from, elf_hash, name); | 558 | s = si_from->find_symbol_by_name(symbol_name); |
503 | if (s != nullptr) { | 559 | if (s != nullptr) { |
504 | *si_found_in = si_from; | 560 | *si_found_in = si_from; |
505 | } | 561 | } |
@@ -509,7 +565,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** s | |||
509 | if (s == nullptr) { | 565 | if (s == nullptr) { |
510 | global_group.visit([&](soinfo* global_si) { | 566 | global_group.visit([&](soinfo* global_si) { |
511 | DEBUG("%s: looking up %s in %s (from global group)", si_from->name, name, global_si->name); | 567 | DEBUG("%s: looking up %s in %s (from global group)", si_from->name, name, global_si->name); |
512 | s = soinfo_elf_lookup(global_si, elf_hash, name); | 568 | s = global_si->find_symbol_by_name(symbol_name); |
513 | if (s != nullptr) { | 569 | if (s != nullptr) { |
514 | *si_found_in = global_si; | 570 | *si_found_in = global_si; |
515 | return false; | 571 | return false; |
@@ -528,7 +584,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** s | |||
528 | } | 584 | } |
529 | 585 | ||
530 | DEBUG("%s: looking up %s in %s (from local group)", si_from->name, name, local_si->name); | 586 | DEBUG("%s: looking up %s in %s (from local group)", si_from->name, name, local_si->name); |
531 | s = soinfo_elf_lookup(local_si, elf_hash, name); | 587 | s = local_si->find_symbol_by_name(symbol_name); |
532 | if (s != nullptr) { | 588 | if (s != nullptr) { |
533 | *si_found_in = local_si; | 589 | *si_found_in = local_si; |
534 | return false; | 590 | return false; |
@@ -665,11 +721,11 @@ static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_s | |||
665 | // specified soinfo object and its dependencies in breadth first order. | 721 | // specified soinfo object and its dependencies in breadth first order. |
666 | ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) { | 722 | ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) { |
667 | ElfW(Sym)* result = nullptr; | 723 | ElfW(Sym)* result = nullptr; |
668 | uint32_t elf_hash = elfhash(name); | 724 | SymbolName symbol_name(name); |
669 | 725 | ||
670 | 726 | ||
671 | walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) { | 727 | walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) { |
672 | result = soinfo_elf_lookup(current_soinfo, elf_hash, name); | 728 | result = current_soinfo->find_symbol_by_name(symbol_name); |
673 | if (result != nullptr) { | 729 | if (result != nullptr) { |
674 | *found = current_soinfo; | 730 | *found = current_soinfo; |
675 | return false; | 731 | return false; |
@@ -687,7 +743,7 @@ ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) { | |||
687 | specified soinfo (for RTLD_NEXT). | 743 | specified soinfo (for RTLD_NEXT). |
688 | */ | 744 | */ |
689 | ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) { | 745 | ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) { |
690 | unsigned elf_hash = elfhash(name); | 746 | SymbolName symbol_name(name); |
691 | 747 | ||
692 | if (start == nullptr) { | 748 | if (start == nullptr) { |
693 | start = solist; | 749 | start = solist; |
@@ -699,7 +755,7 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) | |||
699 | continue; | 755 | continue; |
700 | } | 756 | } |
701 | 757 | ||
702 | s = soinfo_elf_lookup(si, elf_hash, name); | 758 | s = si->find_symbol_by_name(symbol_name); |
703 | if (s != nullptr) { | 759 | if (s != nullptr) { |
704 | *found = si; | 760 | *found = si; |
705 | break; | 761 | break; |
@@ -724,16 +780,45 @@ soinfo* find_containing_library(const void* p) { | |||
724 | return nullptr; | 780 | return nullptr; |
725 | } | 781 | } |
726 | 782 | ||
727 | ElfW(Sym)* dladdr_find_symbol(soinfo* si, const void* addr) { | 783 | ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) { |
728 | ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - si->base; | 784 | return is_gnu_hash() ? gnu_addr_lookup(addr) : elf_addr_lookup(addr); |
785 | } | ||
786 | |||
787 | static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) { | ||
788 | return sym->st_shndx != SHN_UNDEF && | ||
789 | soaddr >= sym->st_value && | ||
790 | soaddr < sym->st_value + sym->st_size; | ||
791 | } | ||
792 | |||
793 | ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) { | ||
794 | ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - base; | ||
795 | |||
796 | for (size_t i = 0; i < nbucket; ++i) { | ||
797 | uint32_t n = bucket[i]; | ||
798 | |||
799 | if (n == 0) { | ||
800 | continue; | ||
801 | } | ||
802 | |||
803 | do { | ||
804 | ElfW(Sym)* sym = symtab + n; | ||
805 | if (symbol_matches_soaddr(sym, soaddr)) { | ||
806 | return sym; | ||
807 | } | ||
808 | } while ((chain[n++] & 1) == 0); | ||
809 | } | ||
810 | |||
811 | return nullptr; | ||
812 | } | ||
813 | |||
814 | ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) { | ||
815 | ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - base; | ||
729 | 816 | ||
730 | // Search the library's symbol table for any defined symbol which | 817 | // Search the library's symbol table for any defined symbol which |
731 | // contains this address. | 818 | // contains this address. |
732 | for (size_t i = 0; i < si->nchain; ++i) { | 819 | for (size_t i = 0; i < nchain; ++i) { |
733 | ElfW(Sym)* sym = &si->symtab[i]; | 820 | ElfW(Sym)* sym = symtab + i; |
734 | if (sym->st_shndx != SHN_UNDEF && | 821 | if (symbol_matches_soaddr(sym, soaddr)) { |
735 | soaddr >= sym->st_value && | ||
736 | soaddr < sym->st_value + sym->st_size) { | ||
737 | return sym; | 822 | return sym; |
738 | } | 823 | } |
739 | } | 824 | } |
@@ -1898,6 +1983,10 @@ const char* soinfo::get_string(ElfW(Word) index) const { | |||
1898 | return strtab + index; | 1983 | return strtab + index; |
1899 | } | 1984 | } |
1900 | 1985 | ||
1986 | bool soinfo::is_gnu_hash() const { | ||
1987 | return (flags & FLAG_GNU_HASH) != 0; | ||
1988 | } | ||
1989 | |||
1901 | bool soinfo::can_unload() const { | 1990 | bool soinfo::can_unload() const { |
1902 | return (get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0; | 1991 | return (get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0; |
1903 | } | 1992 | } |
@@ -2003,12 +2092,42 @@ bool soinfo::PrelinkImage() { | |||
2003 | break; | 2092 | break; |
2004 | 2093 | ||
2005 | case DT_HASH: | 2094 | case DT_HASH: |
2095 | if (nbucket != 0) { | ||
2096 | // in case of --hash-style=both, we prefer gnu | ||
2097 | break; | ||
2098 | } | ||
2099 | |||
2006 | nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0]; | 2100 | nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0]; |
2007 | nchain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1]; | 2101 | nchain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1]; |
2008 | bucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8); | 2102 | bucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8); |
2009 | chain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket * 4); | 2103 | chain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket * 4); |
2010 | break; | 2104 | break; |
2011 | 2105 | ||
2106 | case DT_GNU_HASH: | ||
2107 | if (nbucket != 0) { | ||
2108 | // in case of --hash-style=both, we prefer gnu | ||
2109 | nchain = 0; | ||
2110 | } | ||
2111 | |||
2112 | nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0]; | ||
2113 | // skip symndx | ||
2114 | gnu_maskwords = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[2]; | ||
2115 | gnu_shift2 = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[3]; | ||
2116 | |||
2117 | gnu_bloom_filter = reinterpret_cast<ElfW(Addr)*>(load_bias + d->d_un.d_ptr + 16); | ||
2118 | bucket = reinterpret_cast<uint32_t*>(gnu_bloom_filter + gnu_maskwords); | ||
2119 | // amend chain for symndx = header[1] | ||
2120 | chain = bucket + nbucket - reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1]; | ||
2121 | |||
2122 | if (!powerof2(gnu_maskwords)) { | ||
2123 | DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", gnu_maskwords, name); | ||
2124 | return false; | ||
2125 | } | ||
2126 | --gnu_maskwords; | ||
2127 | |||
2128 | flags |= FLAG_GNU_HASH; | ||
2129 | break; | ||
2130 | |||
2012 | case DT_STRTAB: | 2131 | case DT_STRTAB: |
2013 | strtab = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr); | 2132 | strtab = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr); |
2014 | break; | 2133 | break; |
@@ -2023,7 +2142,7 @@ bool soinfo::PrelinkImage() { | |||
2023 | 2142 | ||
2024 | case DT_SYMENT: | 2143 | case DT_SYMENT: |
2025 | if (d->d_un.d_val != sizeof(ElfW(Sym))) { | 2144 | if (d->d_un.d_val != sizeof(ElfW(Sym))) { |
2026 | DL_ERR("invalid DT_SYMENT: %zd", static_cast<size_t>(d->d_un.d_val)); | 2145 | DL_ERR("invalid DT_SYMENT: %zd in \"%s\"", static_cast<size_t>(d->d_un.d_val), name); |
2027 | return false; | 2146 | return false; |
2028 | } | 2147 | } |
2029 | break; | 2148 | break; |
@@ -2263,7 +2382,7 @@ bool soinfo::PrelinkImage() { | |||
2263 | return false; | 2382 | return false; |
2264 | } | 2383 | } |
2265 | if (nbucket == 0) { | 2384 | if (nbucket == 0) { |
2266 | DL_ERR("empty/missing DT_HASH in \"%s\" (built with --hash-style=gnu?)", name); | 2385 | DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" (new hash type from the future?)", name); |
2267 | return false; | 2386 | return false; |
2268 | } | 2387 | } |
2269 | if (strtab == 0) { | 2388 | if (strtab == 0) { |
diff --git a/linker/linker.h b/linker/linker.h index 0a98b40d..69d8c7b5 100644 --- a/linker/linker.h +++ b/linker/linker.h | |||
@@ -87,6 +87,7 @@ | |||
87 | #define FLAG_LINKED 0x00000001 | 87 | #define FLAG_LINKED 0x00000001 |
88 | #define FLAG_EXE 0x00000004 // The main executable | 88 | #define FLAG_EXE 0x00000004 // The main executable |
89 | #define FLAG_LINKER 0x00000010 // The linker itself | 89 | #define FLAG_LINKER 0x00000010 // The linker itself |
90 | #define FLAG_GNU_HASH 0x00000040 // uses gnu hash | ||
90 | #define FLAG_NEW_SOINFO 0x40000000 // new soinfo format | 91 | #define FLAG_NEW_SOINFO 0x40000000 // new soinfo format |
91 | 92 | ||
92 | #define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE) | 93 | #define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE) |
@@ -105,14 +106,38 @@ typedef void (*linker_function_t)(); | |||
105 | struct soinfo; | 106 | struct soinfo; |
106 | 107 | ||
107 | class SoinfoListAllocator { | 108 | class SoinfoListAllocator { |
108 | public: | 109 | public: |
109 | static LinkedListEntry<soinfo>* alloc(); | 110 | static LinkedListEntry<soinfo>* alloc(); |
110 | static void free(LinkedListEntry<soinfo>* entry); | 111 | static void free(LinkedListEntry<soinfo>* entry); |
111 | private: | 112 | |
113 | private: | ||
112 | // unconstructable | 114 | // unconstructable |
113 | DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator); | 115 | DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator); |
114 | }; | 116 | }; |
115 | 117 | ||
118 | class SymbolName { | ||
119 | public: | ||
120 | explicit SymbolName(const char* name) | ||
121 | : name_(name), has_elf_hash_(false), has_gnu_hash_(false), | ||
122 | elf_hash_(0), gnu_hash_(0) { } | ||
123 | |||
124 | const char* get_name() { | ||
125 | return name_; | ||
126 | } | ||
127 | |||
128 | uint32_t elf_hash(); | ||
129 | uint32_t gnu_hash(); | ||
130 | |||
131 | private: | ||
132 | const char* name_; | ||
133 | bool has_elf_hash_; | ||
134 | bool has_gnu_hash_; | ||
135 | uint32_t elf_hash_; | ||
136 | uint32_t gnu_hash_; | ||
137 | |||
138 | DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName); | ||
139 | }; | ||
140 | |||
116 | struct soinfo { | 141 | struct soinfo { |
117 | public: | 142 | public: |
118 | typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t; | 143 | typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t; |
@@ -140,7 +165,6 @@ struct soinfo { | |||
140 | 165 | ||
141 | private: | 166 | private: |
142 | const char* strtab; | 167 | const char* strtab; |
143 | public: | ||
144 | ElfW(Sym)* symtab; | 168 | ElfW(Sym)* symtab; |
145 | 169 | ||
146 | size_t nbucket; | 170 | size_t nbucket; |
@@ -148,6 +172,7 @@ struct soinfo { | |||
148 | uint32_t* bucket; | 172 | uint32_t* bucket; |
149 | uint32_t* chain; | 173 | uint32_t* chain; |
150 | 174 | ||
175 | public: | ||
151 | #if defined(__mips__) || !defined(__LP64__) | 176 | #if defined(__mips__) || !defined(__LP64__) |
152 | // This is only used by mips and mips64, but needs to be here for | 177 | // This is only used by mips and mips64, but needs to be here for |
153 | // all 32-bit architectures to preserve binary compatibility. | 178 | // all 32-bit architectures to preserve binary compatibility. |
@@ -225,16 +250,24 @@ struct soinfo { | |||
225 | soinfo_list_t& get_children(); | 250 | soinfo_list_t& get_children(); |
226 | soinfo_list_t& get_parents(); | 251 | soinfo_list_t& get_parents(); |
227 | 252 | ||
253 | ElfW(Sym)* find_symbol_by_name(SymbolName& symbol_name); | ||
254 | ElfW(Sym)* find_symbol_by_address(const void* addr); | ||
228 | ElfW(Addr) resolve_symbol_address(ElfW(Sym)* s); | 255 | ElfW(Addr) resolve_symbol_address(ElfW(Sym)* s); |
229 | 256 | ||
230 | const char* get_string(ElfW(Word) index) const; | 257 | const char* get_string(ElfW(Word) index) const; |
231 | bool can_unload() const; | 258 | bool can_unload() const; |
259 | bool is_gnu_hash() const; | ||
232 | 260 | ||
233 | bool inline has_min_version(uint32_t min_version) const { | 261 | bool inline has_min_version(uint32_t min_version) const { |
234 | return (flags & FLAG_NEW_SOINFO) != 0 && version >= min_version; | 262 | return (flags & FLAG_NEW_SOINFO) != 0 && version >= min_version; |
235 | } | 263 | } |
236 | 264 | ||
237 | private: | 265 | private: |
266 | ElfW(Sym)* elf_lookup(SymbolName& symbol_name); | ||
267 | ElfW(Sym)* elf_addr_lookup(const void* addr); | ||
268 | ElfW(Sym)* gnu_lookup(SymbolName& symbol_name); | ||
269 | ElfW(Sym)* gnu_addr_lookup(const void* addr); | ||
270 | |||
238 | void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse); | 271 | void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse); |
239 | void CallFunction(const char* function_name, linker_function_t function); | 272 | void CallFunction(const char* function_name, linker_function_t function); |
240 | #if defined(USE_RELA) | 273 | #if defined(USE_RELA) |
@@ -262,6 +295,12 @@ struct soinfo { | |||
262 | uint32_t dt_flags_1; | 295 | uint32_t dt_flags_1; |
263 | size_t strtab_size; | 296 | size_t strtab_size; |
264 | 297 | ||
298 | // version >= 2 | ||
299 | uint32_t gnu_maskwords; | ||
300 | uint32_t gnu_shift2; | ||
301 | |||
302 | ElfW(Addr)* gnu_bloom_filter; | ||
303 | |||
265 | friend soinfo* get_libdl_info(); | 304 | friend soinfo* get_libdl_info(); |
266 | }; | 305 | }; |
267 | 306 | ||
@@ -275,7 +314,6 @@ void do_dlclose(soinfo* si); | |||
275 | ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start); | 314 | ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start); |
276 | soinfo* find_containing_library(const void* addr); | 315 | soinfo* find_containing_library(const void* addr); |
277 | 316 | ||
278 | ElfW(Sym)* dladdr_find_symbol(soinfo* si, const void* addr); | ||
279 | ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name); | 317 | ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name); |
280 | 318 | ||
281 | void debuggerd_init(); | 319 | void debuggerd_init(); |