* commit '8d31f51a38f10917b2396412c2dbe45dc9abe864': Add support for hash-style=gnu
This commit is contained in:
		@@ -94,6 +94,9 @@ typedef struct {
 | 
				
			|||||||
#define DT_PREINIT_ARRAY 32
 | 
					#define DT_PREINIT_ARRAY 32
 | 
				
			||||||
#define DT_PREINIT_ARRAYSZ 33
 | 
					#define DT_PREINIT_ARRAYSZ 33
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* gnu hash entry */
 | 
				
			||||||
 | 
					#define DT_GNU_HASH 0x6ffffef5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ELFOSABI_SYSV 0 /* Synonym for ELFOSABI_NONE used by valgrind. */
 | 
					#define ELFOSABI_SYSV 0 /* Synonym for ELFOSABI_NONE used by valgrind. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PT_GNU_RELRO 0x6474e552
 | 
					#define PT_GNU_RELRO 0x6474e552
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -145,7 +145,7 @@ int dladdr(const void* addr, Dl_info* info) {
 | 
				
			|||||||
  info->dli_fbase = reinterpret_cast<void*>(si->base);
 | 
					  info->dli_fbase = reinterpret_cast<void*>(si->base);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Determine if any symbol in the library contains the specified address.
 | 
					  // Determine if any symbol in the library contains the specified address.
 | 
				
			||||||
  ElfW(Sym)* sym = dladdr_find_symbol(si, addr);
 | 
					  ElfW(Sym)* sym = si->find_symbol_by_address(addr);
 | 
				
			||||||
  if (sym != nullptr) {
 | 
					  if (sym != nullptr) {
 | 
				
			||||||
    info->dli_sname = si->get_string(sym->st_name);
 | 
					    info->dli_sname = si->get_string(sym->st_name);
 | 
				
			||||||
    info->dli_saddr = reinterpret_cast<void*>(si->resolve_symbol_address(sym));
 | 
					    info->dli_saddr = reinterpret_cast<void*>(si->resolve_symbol_address(sym));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,7 @@
 | 
				
			|||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
#include <sys/mman.h>
 | 
					#include <sys/mman.h>
 | 
				
			||||||
 | 
					#include <sys/param.h>
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <new>
 | 
					#include <new>
 | 
				
			||||||
@@ -316,6 +317,7 @@ static void soinfo_free(soinfo* si) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    prev = trav;
 | 
					    prev = trav;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (trav == nullptr) {
 | 
					  if (trav == nullptr) {
 | 
				
			||||||
    // si was not in solist
 | 
					    // si was not in solist
 | 
				
			||||||
    DL_ERR("name \"%s\" is not in solist!", si->name);
 | 
					    DL_ERR("name \"%s\" is not in solist!", si->name);
 | 
				
			||||||
@@ -335,7 +337,6 @@ static void soinfo_free(soinfo* si) {
 | 
				
			|||||||
  g_soinfo_allocator.free(si);
 | 
					  g_soinfo_allocator.free(si);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
static void parse_path(const char* path, const char* delimiters,
 | 
					static void parse_path(const char* path, const char* delimiters,
 | 
				
			||||||
                       const char** array, char* buf, size_t buf_size, size_t max_count) {
 | 
					                       const char** array, char* buf, size_t buf_size, size_t max_count) {
 | 
				
			||||||
  if (path == nullptr) {
 | 
					  if (path == nullptr) {
 | 
				
			||||||
@@ -415,39 +416,72 @@ int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void
 | 
				
			|||||||
  return rv;
 | 
					  return rv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static ElfW(Sym)* soinfo_elf_lookup(const soinfo* si, unsigned hash, const char* name) {
 | 
					ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name) {
 | 
				
			||||||
  ElfW(Sym)* symtab = si->symtab;
 | 
					  return is_gnu_hash() ? gnu_lookup(symbol_name) : elf_lookup(symbol_name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p %x %zd",
 | 
					static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
 | 
				
			||||||
             name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket);
 | 
					  if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
 | 
				
			||||||
 | 
					      ELF_ST_BIND(s->st_info) == STB_WEAK) {
 | 
				
			||||||
  for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) {
 | 
					    return s->st_shndx != SHN_UNDEF;
 | 
				
			||||||
    ElfW(Sym)* s = symtab + n;
 | 
					  } else if (ELF_ST_BIND(s->st_info) != STB_LOCAL) {
 | 
				
			||||||
    if (strcmp(si->get_string(s->st_name), name)) continue;
 | 
					    DL_WARN("unexpected ST_BIND value: %d for '%s' in '%s'",
 | 
				
			||||||
 | 
					        ELF_ST_BIND(s->st_info), si->get_string(s->st_name), si->name);
 | 
				
			||||||
    // only concern ourselves with global and weak symbol definitions
 | 
					 | 
				
			||||||
    switch (ELF_ST_BIND(s->st_info)) {
 | 
					 | 
				
			||||||
      case STB_GLOBAL:
 | 
					 | 
				
			||||||
      case STB_WEAK:
 | 
					 | 
				
			||||||
        if (s->st_shndx == SHN_UNDEF) {
 | 
					 | 
				
			||||||
          continue;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
 | 
				
			||||||
 | 
					  uint32_t hash = symbol_name.gnu_hash();
 | 
				
			||||||
 | 
					  uint32_t h2 = hash >> gnu_shift2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t bloom_mask_bits = sizeof(ElfW(Addr))*8;
 | 
				
			||||||
 | 
					  uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords;
 | 
				
			||||||
 | 
					  ElfW(Addr) bloom_word = gnu_bloom_filter[word_num];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // test against bloom filter
 | 
				
			||||||
 | 
					  if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) {
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // bloom test says "probably yes"...
 | 
				
			||||||
 | 
					  uint32_t n = bucket[hash % nbucket];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (n == 0) {
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  do {
 | 
				
			||||||
 | 
					    ElfW(Sym)* s = symtab + n;
 | 
				
			||||||
 | 
					    if (((chain[n] ^ hash) >> 1) == 0 &&
 | 
				
			||||||
 | 
					        strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
 | 
				
			||||||
 | 
					        is_symbol_global_and_defined(this, s)) {
 | 
				
			||||||
 | 
					      return s;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } while ((chain[n++] & 1) == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) {
 | 
				
			||||||
 | 
					  uint32_t hash = symbol_name.elf_hash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
 | 
				
			||||||
 | 
					             symbol_name.get_name(), name, reinterpret_cast<void*>(base), hash, hash % nbucket);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (uint32_t n = bucket[hash % nbucket]; n != 0; n = chain[n]) {
 | 
				
			||||||
 | 
					    ElfW(Sym)* s = symtab + n;
 | 
				
			||||||
 | 
					    if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 && is_symbol_global_and_defined(this, s)) {
 | 
				
			||||||
      TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
 | 
					      TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
 | 
				
			||||||
                 name, si->name, reinterpret_cast<void*>(s->st_value),
 | 
					               symbol_name.get_name(), name, reinterpret_cast<void*>(s->st_value),
 | 
				
			||||||
               static_cast<size_t>(s->st_size));
 | 
					               static_cast<size_t>(s->st_size));
 | 
				
			||||||
      return s;
 | 
					      return s;
 | 
				
			||||||
      case STB_LOCAL:
 | 
					 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      default:
 | 
					 | 
				
			||||||
        __libc_fatal("ERROR: Unexpected ST_BIND value: %d for '%s' in '%s'",
 | 
					 | 
				
			||||||
            ELF_ST_BIND(s->st_info), name, si->name);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
 | 
					  TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
 | 
				
			||||||
             name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket);
 | 
					             symbol_name.get_name(), name, reinterpret_cast<void*>(base), hash, hash % nbucket);
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return nullptr;
 | 
					  return nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -468,9 +502,11 @@ soinfo::soinfo(const char* name, const struct stat* file_stat, off64_t file_offs
 | 
				
			|||||||
  this->rtld_flags = rtld_flags;
 | 
					  this->rtld_flags = rtld_flags;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static unsigned elfhash(const char* _name) {
 | 
					
 | 
				
			||||||
  const unsigned char* name = reinterpret_cast<const unsigned char*>(_name);
 | 
					uint32_t SymbolName::elf_hash() {
 | 
				
			||||||
  unsigned h = 0, g;
 | 
					  if (!has_elf_hash_) {
 | 
				
			||||||
 | 
					    const unsigned char* name = reinterpret_cast<const unsigned char*>(name_);
 | 
				
			||||||
 | 
					    uint32_t h = 0, g;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while (*name) {
 | 
					    while (*name) {
 | 
				
			||||||
      h = (h << 4) + *name++;
 | 
					      h = (h << 4) + *name++;
 | 
				
			||||||
@@ -478,12 +514,32 @@ static unsigned elfhash(const char* _name) {
 | 
				
			|||||||
      h ^= g;
 | 
					      h ^= g;
 | 
				
			||||||
      h ^= g >> 24;
 | 
					      h ^= g >> 24;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  return h;
 | 
					
 | 
				
			||||||
 | 
					    elf_hash_ = h;
 | 
				
			||||||
 | 
					    has_elf_hash_ = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return elf_hash_;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uint32_t SymbolName::gnu_hash() {
 | 
				
			||||||
 | 
					  if (!has_gnu_hash_) {
 | 
				
			||||||
 | 
					    uint32_t h = 5381;
 | 
				
			||||||
 | 
					    const unsigned char* name = reinterpret_cast<const unsigned char*>(name_);
 | 
				
			||||||
 | 
					    while (*name != 0) {
 | 
				
			||||||
 | 
					      h += (h << 5) + *name++; // h*33 + c = h + h * 32 + c = h + h << 5 + c
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gnu_hash_ =  h;
 | 
				
			||||||
 | 
					    has_gnu_hash_ = true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return gnu_hash_;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
 | 
					static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
 | 
				
			||||||
    const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
 | 
					    const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
 | 
				
			||||||
  unsigned elf_hash = elfhash(name);
 | 
					  SymbolName symbol_name(name);
 | 
				
			||||||
  ElfW(Sym)* s = nullptr;
 | 
					  ElfW(Sym)* s = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* "This element's presence in a shared object library alters the dynamic linker's
 | 
					  /* "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
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  if (si_from->has_DT_SYMBOLIC) {
 | 
					  if (si_from->has_DT_SYMBOLIC) {
 | 
				
			||||||
    DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->name, name);
 | 
					    DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->name, name);
 | 
				
			||||||
    s = soinfo_elf_lookup(si_from, elf_hash, name);
 | 
					    s = si_from->find_symbol_by_name(symbol_name);
 | 
				
			||||||
    if (s != nullptr) {
 | 
					    if (s != nullptr) {
 | 
				
			||||||
      *si_found_in = si_from;
 | 
					      *si_found_in = si_from;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -509,7 +565,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** s
 | 
				
			|||||||
  if (s == nullptr) {
 | 
					  if (s == nullptr) {
 | 
				
			||||||
    global_group.visit([&](soinfo* global_si) {
 | 
					    global_group.visit([&](soinfo* global_si) {
 | 
				
			||||||
      DEBUG("%s: looking up %s in %s (from global group)", si_from->name, name, global_si->name);
 | 
					      DEBUG("%s: looking up %s in %s (from global group)", si_from->name, name, global_si->name);
 | 
				
			||||||
      s = soinfo_elf_lookup(global_si, elf_hash, name);
 | 
					      s = global_si->find_symbol_by_name(symbol_name);
 | 
				
			||||||
      if (s != nullptr) {
 | 
					      if (s != nullptr) {
 | 
				
			||||||
        *si_found_in = global_si;
 | 
					        *si_found_in = global_si;
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
@@ -528,7 +584,7 @@ static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** s
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      DEBUG("%s: looking up %s in %s (from local group)", si_from->name, name, local_si->name);
 | 
					      DEBUG("%s: looking up %s in %s (from local group)", si_from->name, name, local_si->name);
 | 
				
			||||||
      s = soinfo_elf_lookup(local_si, elf_hash, name);
 | 
					      s = local_si->find_symbol_by_name(symbol_name);
 | 
				
			||||||
      if (s != nullptr) {
 | 
					      if (s != nullptr) {
 | 
				
			||||||
        *si_found_in = local_si;
 | 
					        *si_found_in = local_si;
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
@@ -665,11 +721,11 @@ static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_s
 | 
				
			|||||||
// specified soinfo object and its dependencies in breadth first order.
 | 
					// specified soinfo object and its dependencies in breadth first order.
 | 
				
			||||||
ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
 | 
					ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
 | 
				
			||||||
  ElfW(Sym)* result = nullptr;
 | 
					  ElfW(Sym)* result = nullptr;
 | 
				
			||||||
  uint32_t elf_hash = elfhash(name);
 | 
					  SymbolName symbol_name(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) {
 | 
					  walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) {
 | 
				
			||||||
    result = soinfo_elf_lookup(current_soinfo, elf_hash, name);
 | 
					    result = current_soinfo->find_symbol_by_name(symbol_name);
 | 
				
			||||||
    if (result != nullptr) {
 | 
					    if (result != nullptr) {
 | 
				
			||||||
      *found = current_soinfo;
 | 
					      *found = current_soinfo;
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
@@ -687,7 +743,7 @@ ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
 | 
				
			|||||||
   specified soinfo (for RTLD_NEXT).
 | 
					   specified soinfo (for RTLD_NEXT).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) {
 | 
					ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start) {
 | 
				
			||||||
  unsigned elf_hash = elfhash(name);
 | 
					  SymbolName symbol_name(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (start == nullptr) {
 | 
					  if (start == nullptr) {
 | 
				
			||||||
    start = solist;
 | 
					    start = solist;
 | 
				
			||||||
@@ -699,7 +755,7 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start)
 | 
				
			|||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    s = soinfo_elf_lookup(si, elf_hash, name);
 | 
					    s = si->find_symbol_by_name(symbol_name);
 | 
				
			||||||
    if (s != nullptr) {
 | 
					    if (s != nullptr) {
 | 
				
			||||||
      *found = si;
 | 
					      *found = si;
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
@@ -724,16 +780,45 @@ soinfo* find_containing_library(const void* p) {
 | 
				
			|||||||
  return nullptr;
 | 
					  return nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Sym)* dladdr_find_symbol(soinfo* si, const void* addr) {
 | 
					ElfW(Sym)* soinfo::find_symbol_by_address(const void* addr) {
 | 
				
			||||||
  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - si->base;
 | 
					  return is_gnu_hash() ? gnu_addr_lookup(addr) : elf_addr_lookup(addr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) {
 | 
				
			||||||
 | 
					  return sym->st_shndx != SHN_UNDEF &&
 | 
				
			||||||
 | 
					      soaddr >= sym->st_value &&
 | 
				
			||||||
 | 
					      soaddr < sym->st_value + sym->st_size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) {
 | 
				
			||||||
 | 
					  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - base;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (size_t i = 0; i < nbucket; ++i) {
 | 
				
			||||||
 | 
					    uint32_t n = bucket[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (n == 0) {
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					      ElfW(Sym)* sym = symtab + n;
 | 
				
			||||||
 | 
					      if (symbol_matches_soaddr(sym, soaddr)) {
 | 
				
			||||||
 | 
					        return sym;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } while ((chain[n++] & 1) == 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) {
 | 
				
			||||||
 | 
					  ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - base;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Search the library's symbol table for any defined symbol which
 | 
					  // Search the library's symbol table for any defined symbol which
 | 
				
			||||||
  // contains this address.
 | 
					  // contains this address.
 | 
				
			||||||
  for (size_t i = 0; i < si->nchain; ++i) {
 | 
					  for (size_t i = 0; i < nchain; ++i) {
 | 
				
			||||||
    ElfW(Sym)* sym = &si->symtab[i];
 | 
					    ElfW(Sym)* sym = symtab + i;
 | 
				
			||||||
    if (sym->st_shndx != SHN_UNDEF &&
 | 
					    if (symbol_matches_soaddr(sym, soaddr)) {
 | 
				
			||||||
        soaddr >= sym->st_value &&
 | 
					 | 
				
			||||||
        soaddr < sym->st_value + sym->st_size) {
 | 
					 | 
				
			||||||
      return sym;
 | 
					      return sym;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -1898,6 +1983,10 @@ const char* soinfo::get_string(ElfW(Word) index) const {
 | 
				
			|||||||
  return strtab + index;
 | 
					  return strtab + index;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool soinfo::is_gnu_hash() const {
 | 
				
			||||||
 | 
					  return (flags & FLAG_GNU_HASH) != 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool soinfo::can_unload() const {
 | 
					bool soinfo::can_unload() const {
 | 
				
			||||||
  return (get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0;
 | 
					  return (get_rtld_flags() & (RTLD_NODELETE | RTLD_GLOBAL)) == 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -2003,12 +2092,42 @@ bool soinfo::PrelinkImage() {
 | 
				
			|||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case DT_HASH:
 | 
					      case DT_HASH:
 | 
				
			||||||
 | 
					        if (nbucket != 0) {
 | 
				
			||||||
 | 
					          // in case of --hash-style=both, we prefer gnu
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
 | 
					        nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
 | 
				
			||||||
        nchain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
 | 
					        nchain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
 | 
				
			||||||
        bucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8);
 | 
					        bucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8);
 | 
				
			||||||
        chain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket * 4);
 | 
					        chain = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr + 8 + nbucket * 4);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      case DT_GNU_HASH:
 | 
				
			||||||
 | 
					        if (nbucket != 0) {
 | 
				
			||||||
 | 
					          // in case of --hash-style=both, we prefer gnu
 | 
				
			||||||
 | 
					          nchain = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nbucket = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[0];
 | 
				
			||||||
 | 
					        // skip symndx
 | 
				
			||||||
 | 
					        gnu_maskwords = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[2];
 | 
				
			||||||
 | 
					        gnu_shift2 = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[3];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gnu_bloom_filter = reinterpret_cast<ElfW(Addr)*>(load_bias + d->d_un.d_ptr + 16);
 | 
				
			||||||
 | 
					        bucket = reinterpret_cast<uint32_t*>(gnu_bloom_filter + gnu_maskwords);
 | 
				
			||||||
 | 
					        // amend chain for symndx = header[1]
 | 
				
			||||||
 | 
					        chain = bucket + nbucket - reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!powerof2(gnu_maskwords)) {
 | 
				
			||||||
 | 
					          DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", gnu_maskwords, name);
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        --gnu_maskwords;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        flags |= FLAG_GNU_HASH;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case DT_STRTAB:
 | 
					      case DT_STRTAB:
 | 
				
			||||||
        strtab = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
 | 
					        strtab = reinterpret_cast<const char*>(load_bias + d->d_un.d_ptr);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
@@ -2023,7 +2142,7 @@ bool soinfo::PrelinkImage() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      case DT_SYMENT:
 | 
					      case DT_SYMENT:
 | 
				
			||||||
        if (d->d_un.d_val != sizeof(ElfW(Sym))) {
 | 
					        if (d->d_un.d_val != sizeof(ElfW(Sym))) {
 | 
				
			||||||
          DL_ERR("invalid DT_SYMENT: %zd", static_cast<size_t>(d->d_un.d_val));
 | 
					          DL_ERR("invalid DT_SYMENT: %zd in \"%s\"", static_cast<size_t>(d->d_un.d_val), name);
 | 
				
			||||||
          return false;
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
@@ -2263,7 +2382,7 @@ bool soinfo::PrelinkImage() {
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (nbucket == 0) {
 | 
					  if (nbucket == 0) {
 | 
				
			||||||
    DL_ERR("empty/missing DT_HASH in \"%s\" (built with --hash-style=gnu?)", name);
 | 
					    DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" (new hash type from the future?)", name);
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (strtab == 0) {
 | 
					  if (strtab == 0) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -87,6 +87,7 @@
 | 
				
			|||||||
#define FLAG_LINKED     0x00000001
 | 
					#define FLAG_LINKED     0x00000001
 | 
				
			||||||
#define FLAG_EXE        0x00000004 // The main executable
 | 
					#define FLAG_EXE        0x00000004 // The main executable
 | 
				
			||||||
#define FLAG_LINKER     0x00000010 // The linker itself
 | 
					#define FLAG_LINKER     0x00000010 // The linker itself
 | 
				
			||||||
 | 
					#define FLAG_GNU_HASH   0x00000040 // uses gnu hash
 | 
				
			||||||
#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format
 | 
					#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE)
 | 
					#define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE)
 | 
				
			||||||
@@ -105,14 +106,38 @@ typedef void (*linker_function_t)();
 | 
				
			|||||||
struct soinfo;
 | 
					struct soinfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SoinfoListAllocator {
 | 
					class SoinfoListAllocator {
 | 
				
			||||||
public:
 | 
					 public:
 | 
				
			||||||
  static LinkedListEntry<soinfo>* alloc();
 | 
					  static LinkedListEntry<soinfo>* alloc();
 | 
				
			||||||
  static void free(LinkedListEntry<soinfo>* entry);
 | 
					  static void free(LinkedListEntry<soinfo>* entry);
 | 
				
			||||||
private:
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
  // unconstructable
 | 
					  // unconstructable
 | 
				
			||||||
  DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator);
 | 
					  DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SymbolName {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  explicit SymbolName(const char* name)
 | 
				
			||||||
 | 
					      : name_(name), has_elf_hash_(false), has_gnu_hash_(false),
 | 
				
			||||||
 | 
					        elf_hash_(0), gnu_hash_(0) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const char* get_name() {
 | 
				
			||||||
 | 
					    return name_;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t elf_hash();
 | 
				
			||||||
 | 
					  uint32_t gnu_hash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  const char* name_;
 | 
				
			||||||
 | 
					  bool has_elf_hash_;
 | 
				
			||||||
 | 
					  bool has_gnu_hash_;
 | 
				
			||||||
 | 
					  uint32_t elf_hash_;
 | 
				
			||||||
 | 
					  uint32_t gnu_hash_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct soinfo {
 | 
					struct soinfo {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
 | 
					  typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
 | 
				
			||||||
@@ -140,7 +165,6 @@ struct soinfo {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
  const char* strtab;
 | 
					  const char* strtab;
 | 
				
			||||||
 public:
 | 
					 | 
				
			||||||
  ElfW(Sym)* symtab;
 | 
					  ElfW(Sym)* symtab;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t nbucket;
 | 
					  size_t nbucket;
 | 
				
			||||||
@@ -148,6 +172,7 @@ struct soinfo {
 | 
				
			|||||||
  uint32_t* bucket;
 | 
					  uint32_t* bucket;
 | 
				
			||||||
  uint32_t* chain;
 | 
					  uint32_t* chain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
#if defined(__mips__) || !defined(__LP64__)
 | 
					#if defined(__mips__) || !defined(__LP64__)
 | 
				
			||||||
  // This is only used by mips and mips64, but needs to be here for
 | 
					  // This is only used by mips and mips64, but needs to be here for
 | 
				
			||||||
  // all 32-bit architectures to preserve binary compatibility.
 | 
					  // all 32-bit architectures to preserve binary compatibility.
 | 
				
			||||||
@@ -225,16 +250,24 @@ struct soinfo {
 | 
				
			|||||||
  soinfo_list_t& get_children();
 | 
					  soinfo_list_t& get_children();
 | 
				
			||||||
  soinfo_list_t& get_parents();
 | 
					  soinfo_list_t& get_parents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ElfW(Sym)* find_symbol_by_name(SymbolName& symbol_name);
 | 
				
			||||||
 | 
					  ElfW(Sym)* find_symbol_by_address(const void* addr);
 | 
				
			||||||
  ElfW(Addr) resolve_symbol_address(ElfW(Sym)* s);
 | 
					  ElfW(Addr) resolve_symbol_address(ElfW(Sym)* s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const char* get_string(ElfW(Word) index) const;
 | 
					  const char* get_string(ElfW(Word) index) const;
 | 
				
			||||||
  bool can_unload() const;
 | 
					  bool can_unload() const;
 | 
				
			||||||
 | 
					  bool is_gnu_hash() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool inline has_min_version(uint32_t min_version) const {
 | 
					  bool inline has_min_version(uint32_t min_version) const {
 | 
				
			||||||
    return (flags & FLAG_NEW_SOINFO) != 0 && version >= min_version;
 | 
					    return (flags & FLAG_NEW_SOINFO) != 0 && version >= min_version;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
 | 
					  ElfW(Sym)* elf_lookup(SymbolName& symbol_name);
 | 
				
			||||||
 | 
					  ElfW(Sym)* elf_addr_lookup(const void* addr);
 | 
				
			||||||
 | 
					  ElfW(Sym)* gnu_lookup(SymbolName& symbol_name);
 | 
				
			||||||
 | 
					  ElfW(Sym)* gnu_addr_lookup(const void* addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
 | 
					  void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
 | 
				
			||||||
  void CallFunction(const char* function_name, linker_function_t function);
 | 
					  void CallFunction(const char* function_name, linker_function_t function);
 | 
				
			||||||
#if defined(USE_RELA)
 | 
					#if defined(USE_RELA)
 | 
				
			||||||
@@ -262,6 +295,12 @@ struct soinfo {
 | 
				
			|||||||
  uint32_t dt_flags_1;
 | 
					  uint32_t dt_flags_1;
 | 
				
			||||||
  size_t strtab_size;
 | 
					  size_t strtab_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // version >= 2
 | 
				
			||||||
 | 
					  uint32_t gnu_maskwords;
 | 
				
			||||||
 | 
					  uint32_t gnu_shift2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ElfW(Addr)* gnu_bloom_filter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  friend soinfo* get_libdl_info();
 | 
					  friend soinfo* get_libdl_info();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -275,7 +314,6 @@ void do_dlclose(soinfo* si);
 | 
				
			|||||||
ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start);
 | 
					ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* start);
 | 
				
			||||||
soinfo* find_containing_library(const void* addr);
 | 
					soinfo* find_containing_library(const void* addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Sym)* dladdr_find_symbol(soinfo* si, const void* addr);
 | 
					 | 
				
			||||||
ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
 | 
					ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void debuggerd_init();
 | 
					void debuggerd_init();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -648,19 +648,32 @@ TEST(dlfcn, dladdr_invalid) {
 | 
				
			|||||||
  ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3).
 | 
					  ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3).
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Our dynamic linker doesn't support GNU hash tables.
 | 
					 | 
				
			||||||
#if defined(__BIONIC__)
 | 
					 | 
				
			||||||
// GNU-style ELF hash tables are incompatible with the MIPS ABI.
 | 
					// GNU-style ELF hash tables are incompatible with the MIPS ABI.
 | 
				
			||||||
// MIPS requires .dynsym to be sorted to match the GOT but GNU-style requires sorting by hash code.
 | 
					// MIPS requires .dynsym to be sorted to match the GOT but GNU-style requires sorting by hash code.
 | 
				
			||||||
#if !defined(__mips__)
 | 
					 | 
				
			||||||
TEST(dlfcn, dlopen_library_with_only_gnu_hash) {
 | 
					TEST(dlfcn, dlopen_library_with_only_gnu_hash) {
 | 
				
			||||||
 | 
					#if !defined(__mips__)
 | 
				
			||||||
  dlerror(); // Clear any pending errors.
 | 
					  dlerror(); // Clear any pending errors.
 | 
				
			||||||
  void* handle = dlopen("no-elf-hash-table-library.so", RTLD_NOW);
 | 
					  void* handle = dlopen("libgnu-hash-table-library.so", RTLD_NOW);
 | 
				
			||||||
  ASSERT_TRUE(handle == NULL);
 | 
					  ASSERT_TRUE(handle != nullptr) << dlerror();
 | 
				
			||||||
  ASSERT_STREQ("dlopen failed: empty/missing DT_HASH in \"no-elf-hash-table-library.so\" (built with --hash-style=gnu?)", dlerror());
 | 
					  auto guard = make_scope_guard([&]() {
 | 
				
			||||||
 | 
					    dlclose(handle);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  void* sym = dlsym(handle, "getRandomNumber");
 | 
				
			||||||
 | 
					  ASSERT_TRUE(sym != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  int (*fn)(void);
 | 
				
			||||||
 | 
					  fn = reinterpret_cast<int (*)(void)>(sym);
 | 
				
			||||||
 | 
					  EXPECT_EQ(4, fn());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Dl_info dlinfo;
 | 
				
			||||||
 | 
					  ASSERT_TRUE(0 != dladdr(reinterpret_cast<void*>(fn), &dlinfo));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ASSERT_TRUE(fn == dlinfo.dli_saddr);
 | 
				
			||||||
 | 
					  ASSERT_STREQ("getRandomNumber", dlinfo.dli_sname);
 | 
				
			||||||
 | 
					  ASSERT_STREQ("libgnu-hash-table-library.so", dlinfo.dli_fname);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					  GTEST_LOG_(INFO) << "This test does nothing for mips/mips64; mips toolchain does not support '--hash-style=gnu'\n";
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST(dlfcn, dlopen_bad_flags) {
 | 
					TEST(dlfcn, dlopen_bad_flags) {
 | 
				
			||||||
  dlerror(); // Clear any pending errors.
 | 
					  dlerror(); // Clear any pending errors.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,13 +31,13 @@ common_additional_dependencies := \
 | 
				
			|||||||
# Library used by dlfcn tests.
 | 
					# Library used by dlfcn tests.
 | 
				
			||||||
# -----------------------------------------------------------------------------
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
ifneq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),mips mips64))
 | 
					ifneq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),mips mips64))
 | 
				
			||||||
no-elf-hash-table-library_src_files := \
 | 
					libgnu-hash-table-library_src_files := \
 | 
				
			||||||
    empty.cpp \
 | 
					    dlext_test_library.cpp \
 | 
				
			||||||
 | 
					
 | 
				
			||||||
no-elf-hash-table-library_ldflags := \
 | 
					libgnu-hash-table-library_ldflags := \
 | 
				
			||||||
    -Wl,--hash-style=gnu \
 | 
					    -Wl,--hash-style=gnu \
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module := no-elf-hash-table-library
 | 
					module := libgnu-hash-table-library
 | 
				
			||||||
module_tag := optional
 | 
					module_tag := optional
 | 
				
			||||||
include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user