am 3875744f: Merge "Support symbol versioning"
				
					
				
			* commit '3875744f89600027c69ea68650fff1eeb4b29723': Support symbol versioning
This commit is contained in:
		@@ -194,4 +194,10 @@ typedef struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#define NT_GNU_BUILD_ID 3
 | 
					#define NT_GNU_BUILD_ID 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define VER_FLG_BASE 0x1
 | 
				
			||||||
 | 
					#define VER_FLG_WEAK 0x2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define VER_NDX_LOCAL  0
 | 
				
			||||||
 | 
					#define VER_NDX_GLOBAL 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* _ELF_H */
 | 
					#endif /* _ELF_H */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -100,7 +100,7 @@ void* dlsym(void* handle, const char* symbol) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  soinfo* found = nullptr;
 | 
					  soinfo* found = nullptr;
 | 
				
			||||||
  ElfW(Sym)* sym = nullptr;
 | 
					  const ElfW(Sym)* sym = nullptr;
 | 
				
			||||||
  void* caller_addr = __builtin_return_address(0);
 | 
					  void* caller_addr = __builtin_return_address(0);
 | 
				
			||||||
  soinfo* caller = find_containing_library(caller_addr);
 | 
					  soinfo* caller = find_containing_library(caller_addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -136,6 +136,17 @@ class LinkedList {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  template<typename F>
 | 
				
			||||||
 | 
					  T* find_if(F predicate) const {
 | 
				
			||||||
 | 
					    for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) {
 | 
				
			||||||
 | 
					      if (predicate(e->element)) {
 | 
				
			||||||
 | 
					        return e->element;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t copy_to_array(T* array[], size_t array_length) const {
 | 
					  size_t copy_to_array(T* array[], size_t array_length) const {
 | 
				
			||||||
    size_t sz = 0;
 | 
					    size_t sz = 0;
 | 
				
			||||||
    for (LinkedListEntry<T>* e = head_; sz < array_length && e != nullptr; e = e->next) {
 | 
					    for (LinkedListEntry<T>* e = head_; sz < array_length && e != nullptr; e = e->next) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * Copyright (C) 2008, 2009 The Android Open Source Project
 | 
					 * Copyright (C) 2008 The Android Open Source Project
 | 
				
			||||||
 * All rights reserved.
 | 
					 * All rights reserved.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Redistribution and use in source and binary forms, with or without
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
@@ -100,6 +100,9 @@ static const char* const kDefaultLdPaths[] = {
 | 
				
			|||||||
  nullptr
 | 
					  nullptr
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const ElfW(Versym) kVersymNotNeeded = 0;
 | 
				
			||||||
 | 
					static const ElfW(Versym) kVersymGlobal = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static std::vector<std::string> g_ld_library_paths;
 | 
					static std::vector<std::string> g_ld_library_paths;
 | 
				
			||||||
static std::vector<std::string> g_ld_preload_names;
 | 
					static std::vector<std::string> g_ld_preload_names;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -379,8 +382,128 @@ int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void
 | 
				
			|||||||
  return rv;
 | 
					  return rv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Sym)* soinfo::find_symbol_by_name(SymbolName& symbol_name) {
 | 
					const ElfW(Versym)* soinfo::get_versym(size_t n) const {
 | 
				
			||||||
  return is_gnu_hash() ? gnu_lookup(symbol_name) : elf_lookup(symbol_name);
 | 
					  if (has_min_version(2) && versym_ != nullptr) {
 | 
				
			||||||
 | 
					    return versym_ + n;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ElfW(Addr) soinfo::get_verneed_ptr() const {
 | 
				
			||||||
 | 
					  if (has_min_version(2)) {
 | 
				
			||||||
 | 
					    return verneed_ptr_;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t soinfo::get_verneed_cnt() const {
 | 
				
			||||||
 | 
					  if (has_min_version(2)) {
 | 
				
			||||||
 | 
					    return verneed_cnt_;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ElfW(Addr) soinfo::get_verdef_ptr() const {
 | 
				
			||||||
 | 
					  if (has_min_version(2)) {
 | 
				
			||||||
 | 
					    return verdef_ptr_;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t soinfo::get_verdef_cnt() const {
 | 
				
			||||||
 | 
					  if (has_min_version(2)) {
 | 
				
			||||||
 | 
					    return verdef_cnt_;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template<typename F>
 | 
				
			||||||
 | 
					static bool for_each_verdef(const soinfo* si, F functor) {
 | 
				
			||||||
 | 
					  if (!si->has_min_version(2)) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uintptr_t verdef_ptr = si->get_verdef_ptr();
 | 
				
			||||||
 | 
					  if (verdef_ptr == 0) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  size_t offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  size_t verdef_cnt = si->get_verdef_cnt();
 | 
				
			||||||
 | 
					  for (size_t i = 0; i<verdef_cnt; ++i) {
 | 
				
			||||||
 | 
					    const ElfW(Verdef)* verdef = reinterpret_cast<ElfW(Verdef)*>(verdef_ptr + offset);
 | 
				
			||||||
 | 
					    size_t verdaux_offset = offset + verdef->vd_aux;
 | 
				
			||||||
 | 
					    offset += verdef->vd_next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (verdef->vd_version != 1) {
 | 
				
			||||||
 | 
					      DL_ERR("unsupported verdef[%zd] vd_version: %d (expected 1)", i, verdef->vd_version);
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((verdef->vd_flags & VER_FLG_BASE) != 0) {
 | 
				
			||||||
 | 
					      // "this is the version of the file itself.  It must not be used for
 | 
				
			||||||
 | 
					      //  matching a symbol. It can be used to match references."
 | 
				
			||||||
 | 
					      //
 | 
				
			||||||
 | 
					      // http://www.akkadia.org/drepper/symbol-versioning
 | 
				
			||||||
 | 
					      continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (verdef->vd_cnt == 0) {
 | 
				
			||||||
 | 
					      DL_ERR("invalid verdef[%zd] vd_cnt == 0 (version without a name)", i);
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const ElfW(Verdaux)* verdaux = reinterpret_cast<ElfW(Verdaux)*>(verdef_ptr + verdaux_offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (functor(i, verdef, verdaux) == true) {
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool soinfo::find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const {
 | 
				
			||||||
 | 
					  if (vi == nullptr) {
 | 
				
			||||||
 | 
					    *versym = kVersymNotNeeded;
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  *versym = kVersymGlobal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return for_each_verdef(this,
 | 
				
			||||||
 | 
					    [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
 | 
				
			||||||
 | 
					      if (verdef->vd_hash == vi->elf_hash &&
 | 
				
			||||||
 | 
					          strcmp(vi->name, get_string(verdaux->vda_name)) == 0) {
 | 
				
			||||||
 | 
					        *versym = verdef->vd_ndx;
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool soinfo::find_symbol_by_name(SymbolName& symbol_name,
 | 
				
			||||||
 | 
					                                 const version_info* vi,
 | 
				
			||||||
 | 
					                                 const ElfW(Sym)** symbol) const {
 | 
				
			||||||
 | 
					  uint32_t symbol_index;
 | 
				
			||||||
 | 
					  bool success =
 | 
				
			||||||
 | 
					      is_gnu_hash() ?
 | 
				
			||||||
 | 
					      gnu_lookup(symbol_name, vi, &symbol_index) :
 | 
				
			||||||
 | 
					      elf_lookup(symbol_name, vi, &symbol_index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (success) {
 | 
				
			||||||
 | 
					    *symbol = symbol_index == 0 ? nullptr : symtab_ + symbol_index;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return success;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
 | 
					static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
 | 
				
			||||||
@@ -395,7 +518,23 @@ static bool is_symbol_global_and_defined(const soinfo* si, const ElfW(Sym)* s) {
 | 
				
			|||||||
  return false;
 | 
					  return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
 | 
					static const ElfW(Versym) kVersymHiddenBit = 0x8000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool is_versym_hidden(const ElfW(Versym)* versym) {
 | 
				
			||||||
 | 
					  // the symbol is hidden if bit 15 of versym is set.
 | 
				
			||||||
 | 
					  return versym != nullptr && (*versym & kVersymHiddenBit) != 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool check_symbol_version(const ElfW(Versym) verneed,
 | 
				
			||||||
 | 
					                                        const ElfW(Versym)* verdef) {
 | 
				
			||||||
 | 
					  return verneed == kVersymNotNeeded ||
 | 
				
			||||||
 | 
					      verdef == nullptr ||
 | 
				
			||||||
 | 
					      verneed == (*verdef & ~kVersymHiddenBit);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool soinfo::gnu_lookup(SymbolName& symbol_name,
 | 
				
			||||||
 | 
					                        const version_info* vi,
 | 
				
			||||||
 | 
					                        uint32_t* symbol_index) const {
 | 
				
			||||||
  uint32_t hash = symbol_name.gnu_hash();
 | 
					  uint32_t hash = symbol_name.gnu_hash();
 | 
				
			||||||
  uint32_t h2 = hash >> gnu_shift2_;
 | 
					  uint32_t h2 = hash >> gnu_shift2_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -403,6 +542,8 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
 | 
				
			|||||||
  uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
 | 
					  uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
 | 
				
			||||||
  ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
 | 
					  ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  *symbol_index = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
 | 
					  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
 | 
				
			||||||
      symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
 | 
					      symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -411,7 +552,7 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
 | 
				
			|||||||
    TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
 | 
					    TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
 | 
				
			||||||
        symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
 | 
					        symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return nullptr;
 | 
					    return true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // bloom test says "probably yes"...
 | 
					  // bloom test says "probably yes"...
 | 
				
			||||||
@@ -421,43 +562,77 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) {
 | 
				
			|||||||
    TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
 | 
					    TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
 | 
				
			||||||
        symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
 | 
					        symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return nullptr;
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // lookup versym for the version definition in this library
 | 
				
			||||||
 | 
					  // note the difference between "version is not requested" (vi == nullptr)
 | 
				
			||||||
 | 
					  // and "version not found". In the first case verneed is kVersymNotNeeded
 | 
				
			||||||
 | 
					  // which implies that the default version can be accepted; the second case results in
 | 
				
			||||||
 | 
					  // verneed = 1 (kVersymGlobal) and implies that we should ignore versioned symbols
 | 
				
			||||||
 | 
					  // for this library and consider only *global* ones.
 | 
				
			||||||
 | 
					  ElfW(Versym) verneed = 0;
 | 
				
			||||||
 | 
					  if (!find_verdef_version_index(vi, &verneed)) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  do {
 | 
					  do {
 | 
				
			||||||
    ElfW(Sym)* s = symtab_ + n;
 | 
					    ElfW(Sym)* s = symtab_ + n;
 | 
				
			||||||
 | 
					    const ElfW(Versym)* verdef = get_versym(n);
 | 
				
			||||||
 | 
					    // skip hidden versions when verneed == kVersymNotNeeded (0)
 | 
				
			||||||
 | 
					    if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
 | 
					    if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
 | 
				
			||||||
 | 
					        check_symbol_version(verneed, verdef) &&
 | 
				
			||||||
        strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
 | 
					        strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
 | 
				
			||||||
        is_symbol_global_and_defined(this, s)) {
 | 
					        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",
 | 
				
			||||||
          symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(s->st_value),
 | 
					          symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(s->st_value),
 | 
				
			||||||
          static_cast<size_t>(s->st_size));
 | 
					          static_cast<size_t>(s->st_size));
 | 
				
			||||||
      return s;
 | 
					      *symbol_index = n;
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } while ((gnu_chain_[n++] & 1) == 0);
 | 
					  } while ((gnu_chain_[n++] & 1) == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
 | 
					  TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
 | 
				
			||||||
             symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
 | 
					             symbol_name.get_name(), get_soname(), reinterpret_cast<void*>(base));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return nullptr;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) {
 | 
					bool soinfo::elf_lookup(SymbolName& symbol_name,
 | 
				
			||||||
 | 
					                        const version_info* vi,
 | 
				
			||||||
 | 
					                        uint32_t* symbol_index) const {
 | 
				
			||||||
  uint32_t hash = symbol_name.elf_hash();
 | 
					  uint32_t hash = symbol_name.elf_hash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
 | 
					  TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p h=%x(elf) %zd",
 | 
				
			||||||
             symbol_name.get_name(), get_soname(),
 | 
					             symbol_name.get_name(), get_soname(),
 | 
				
			||||||
             reinterpret_cast<void*>(base), hash, hash % nbucket_);
 | 
					             reinterpret_cast<void*>(base), hash, hash % nbucket_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ElfW(Versym) verneed = 0;
 | 
				
			||||||
 | 
					  if (!find_verdef_version_index(vi, &verneed)) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
 | 
					  for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
 | 
				
			||||||
    ElfW(Sym)* s = symtab_ + n;
 | 
					    ElfW(Sym)* s = symtab_ + n;
 | 
				
			||||||
    if (strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
 | 
					    const ElfW(Versym)* verdef = get_versym(n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // skip hidden versions when verneed == 0
 | 
				
			||||||
 | 
					    if (verneed == kVersymNotNeeded && is_versym_hidden(verdef)) {
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (check_symbol_version(verneed, verdef) &&
 | 
				
			||||||
 | 
					        strcmp(get_string(s->st_name), symbol_name.get_name()) == 0 &&
 | 
				
			||||||
        is_symbol_global_and_defined(this, s)) {
 | 
					        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",
 | 
				
			||||||
                 symbol_name.get_name(), get_soname(),
 | 
					                 symbol_name.get_name(), get_soname(),
 | 
				
			||||||
                 reinterpret_cast<void*>(s->st_value),
 | 
					                 reinterpret_cast<void*>(s->st_value),
 | 
				
			||||||
                 static_cast<size_t>(s->st_size));
 | 
					                 static_cast<size_t>(s->st_size));
 | 
				
			||||||
      return s;
 | 
					      *symbol_index = n;
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -465,7 +640,8 @@ ElfW(Sym)* soinfo::elf_lookup(SymbolName& symbol_name) {
 | 
				
			|||||||
             symbol_name.get_name(), get_soname(),
 | 
					             symbol_name.get_name(), get_soname(),
 | 
				
			||||||
             reinterpret_cast<void*>(base), hash, hash % nbucket_);
 | 
					             reinterpret_cast<void*>(base), hash, hash % nbucket_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return nullptr;
 | 
					  *symbol_index = 0;
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
soinfo::soinfo(const char* realpath, const struct stat* file_stat,
 | 
					soinfo::soinfo(const char* realpath, const struct stat* file_stat,
 | 
				
			||||||
@@ -523,10 +699,11 @@ uint32_t SymbolName::gnu_hash() {
 | 
				
			|||||||
  return gnu_hash_;
 | 
					  return gnu_hash_;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
 | 
					bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
 | 
				
			||||||
    const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
 | 
					                      soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
 | 
				
			||||||
 | 
					                      const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol) {
 | 
				
			||||||
  SymbolName symbol_name(name);
 | 
					  SymbolName symbol_name(name);
 | 
				
			||||||
  ElfW(Sym)* s = nullptr;
 | 
					  const 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
 | 
				
			||||||
   * symbol resolution algorithm for references within the library. Instead of starting
 | 
					   * symbol resolution algorithm for references within the library. Instead of starting
 | 
				
			||||||
@@ -541,7 +718,10 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  if (si_from->has_DT_SYMBOLIC) {
 | 
					  if (si_from->has_DT_SYMBOLIC) {
 | 
				
			||||||
    DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_soname(), name);
 | 
					    DEBUG("%s: looking up %s in local scope (DT_SYMBOLIC)", si_from->get_soname(), name);
 | 
				
			||||||
    s = si_from->find_symbol_by_name(symbol_name);
 | 
					    if (!si_from->find_symbol_by_name(symbol_name, vi, &s)) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (s != nullptr) {
 | 
					    if (s != nullptr) {
 | 
				
			||||||
      *si_found_in = si_from;
 | 
					      *si_found_in = si_from;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -549,10 +729,15 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // 1. Look for it in global_group
 | 
					  // 1. Look for it in global_group
 | 
				
			||||||
  if (s == nullptr) {
 | 
					  if (s == nullptr) {
 | 
				
			||||||
 | 
					    bool error = false;
 | 
				
			||||||
    global_group.visit([&](soinfo* global_si) {
 | 
					    global_group.visit([&](soinfo* global_si) {
 | 
				
			||||||
      DEBUG("%s: looking up %s in %s (from global group)",
 | 
					      DEBUG("%s: looking up %s in %s (from global group)",
 | 
				
			||||||
          si_from->get_soname(), name, global_si->get_soname());
 | 
					          si_from->get_soname(), name, global_si->get_soname());
 | 
				
			||||||
      s = global_si->find_symbol_by_name(symbol_name);
 | 
					      if (!global_si->find_symbol_by_name(symbol_name, vi, &s)) {
 | 
				
			||||||
 | 
					        error = true;
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (s != nullptr) {
 | 
					      if (s != nullptr) {
 | 
				
			||||||
        *si_found_in = global_si;
 | 
					        *si_found_in = global_si;
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
@@ -560,10 +745,15 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (error) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 2. Look for it in the local group
 | 
					  // 2. Look for it in the local group
 | 
				
			||||||
  if (s == nullptr) {
 | 
					  if (s == nullptr) {
 | 
				
			||||||
 | 
					    bool error = false;
 | 
				
			||||||
    local_group.visit([&](soinfo* local_si) {
 | 
					    local_group.visit([&](soinfo* local_si) {
 | 
				
			||||||
      if (local_si == si_from && si_from->has_DT_SYMBOLIC) {
 | 
					      if (local_si == si_from && si_from->has_DT_SYMBOLIC) {
 | 
				
			||||||
        // we already did this - skip
 | 
					        // we already did this - skip
 | 
				
			||||||
@@ -572,7 +762,11 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      DEBUG("%s: looking up %s in %s (from local group)",
 | 
					      DEBUG("%s: looking up %s in %s (from local group)",
 | 
				
			||||||
          si_from->get_soname(), name, local_si->get_soname());
 | 
					          si_from->get_soname(), name, local_si->get_soname());
 | 
				
			||||||
      s = local_si->find_symbol_by_name(symbol_name);
 | 
					      if (!local_si->find_symbol_by_name(symbol_name, vi, &s)) {
 | 
				
			||||||
 | 
					        error = true;
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (s != nullptr) {
 | 
					      if (s != nullptr) {
 | 
				
			||||||
        *si_found_in = local_si;
 | 
					        *si_found_in = local_si;
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
@@ -580,6 +774,10 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (error) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (s != nullptr) {
 | 
					  if (s != nullptr) {
 | 
				
			||||||
@@ -590,7 +788,8 @@ ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found
 | 
				
			|||||||
               reinterpret_cast<void*>((*si_found_in)->load_bias));
 | 
					               reinterpret_cast<void*>((*si_found_in)->load_bias));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return s;
 | 
					  *symbol = s;
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProtectedDataGuard {
 | 
					class ProtectedDataGuard {
 | 
				
			||||||
@@ -735,13 +934,16 @@ static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_s
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// This is used by dlsym(3).  It performs symbol lookup only within the
 | 
					// This is used by dlsym(3).  It performs symbol lookup only within the
 | 
				
			||||||
// 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) {
 | 
					const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
 | 
				
			||||||
  ElfW(Sym)* result = nullptr;
 | 
					  const ElfW(Sym)* result = nullptr;
 | 
				
			||||||
  SymbolName symbol_name(name);
 | 
					  SymbolName symbol_name(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) {
 | 
					  walk_dependencies_tree(&si, 1, [&](soinfo* current_soinfo) {
 | 
				
			||||||
    result = current_soinfo->find_symbol_by_name(symbol_name);
 | 
					    if (!current_soinfo->find_symbol_by_name(symbol_name, nullptr, &result)) {
 | 
				
			||||||
 | 
					      result = nullptr;
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (result != nullptr) {
 | 
					    if (result != nullptr) {
 | 
				
			||||||
      *found = current_soinfo;
 | 
					      *found = current_soinfo;
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
@@ -758,7 +960,10 @@ ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name) {
 | 
				
			|||||||
   beginning of the global solist. Otherwise the search starts at the
 | 
					   beginning of the global solist. Otherwise the search starts at the
 | 
				
			||||||
   specified soinfo (for RTLD_NEXT).
 | 
					   specified soinfo (for RTLD_NEXT).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle) {
 | 
					const ElfW(Sym)* dlsym_linear_lookup(const char* name,
 | 
				
			||||||
 | 
					                                     soinfo** found,
 | 
				
			||||||
 | 
					                                     soinfo* caller,
 | 
				
			||||||
 | 
					                                     void* handle) {
 | 
				
			||||||
  SymbolName symbol_name(name);
 | 
					  SymbolName symbol_name(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  soinfo* start = solist;
 | 
					  soinfo* start = solist;
 | 
				
			||||||
@@ -771,13 +976,16 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ElfW(Sym)* s = nullptr;
 | 
					  const ElfW(Sym)* s = nullptr;
 | 
				
			||||||
  for (soinfo* si = start; si != nullptr; si = si->next) {
 | 
					  for (soinfo* si = start; si != nullptr; si = si->next) {
 | 
				
			||||||
    if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0) {
 | 
					    if ((si->get_rtld_flags() & RTLD_GLOBAL) == 0) {
 | 
				
			||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    s = si->find_symbol_by_name(symbol_name);
 | 
					    if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) {
 | 
				
			||||||
 | 
					      return nullptr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (s != nullptr) {
 | 
					    if (s != nullptr) {
 | 
				
			||||||
      *found = si;
 | 
					      *found = si;
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
@@ -800,7 +1008,10 @@ ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller,
 | 
				
			|||||||
        break;
 | 
					        break;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      s = si->find_symbol_by_name(symbol_name);
 | 
					      if (!si->find_symbol_by_name(symbol_name, nullptr, &s)) {
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (s != nullptr) {
 | 
					      if (s != nullptr) {
 | 
				
			||||||
        *found = si;
 | 
					        *found = si;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
@@ -1444,6 +1655,93 @@ static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
 | 
				
			|||||||
  return ifunc_addr;
 | 
					  return ifunc_addr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const version_info* VersionTracker::get_version_info(ElfW(Versym) source_symver) const {
 | 
				
			||||||
 | 
					  if (source_symver < 2 ||
 | 
				
			||||||
 | 
					      source_symver >= version_infos.size() ||
 | 
				
			||||||
 | 
					      version_infos[source_symver].name == nullptr) {
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return &version_infos[source_symver];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void VersionTracker::add_version_info(size_t source_index,
 | 
				
			||||||
 | 
					                                      ElfW(Word) elf_hash,
 | 
				
			||||||
 | 
					                                      const char* ver_name,
 | 
				
			||||||
 | 
					                                      const soinfo* target_si) {
 | 
				
			||||||
 | 
					  if (source_index >= version_infos.size()) {
 | 
				
			||||||
 | 
					    version_infos.resize(source_index+1);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  version_infos[source_index].elf_hash = elf_hash;
 | 
				
			||||||
 | 
					  version_infos[source_index].name = ver_name;
 | 
				
			||||||
 | 
					  version_infos[source_index].target_si = target_si;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool VersionTracker::init_verneed(const soinfo* si_from) {
 | 
				
			||||||
 | 
					  uintptr_t verneed_ptr = si_from->get_verneed_ptr();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (verneed_ptr == 0) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  size_t verneed_cnt = si_from->get_verneed_cnt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (size_t i = 0, offset = 0; i<verneed_cnt; ++i) {
 | 
				
			||||||
 | 
					    const ElfW(Verneed)* verneed = reinterpret_cast<ElfW(Verneed)*>(verneed_ptr + offset);
 | 
				
			||||||
 | 
					    size_t vernaux_offset = offset + verneed->vn_aux;
 | 
				
			||||||
 | 
					    offset += verneed->vn_next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (verneed->vn_version != 1) {
 | 
				
			||||||
 | 
					      DL_ERR("unsupported verneed[%zd] vn_version: %d (expected 1)", i, verneed->vn_version);
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const char* target_soname = si_from->get_string(verneed->vn_file);
 | 
				
			||||||
 | 
					    // find it in dependencies
 | 
				
			||||||
 | 
					    soinfo* target_si = si_from->get_children().find_if([&](const soinfo* si) {
 | 
				
			||||||
 | 
					      return strcmp(si->get_soname(), target_soname) == 0;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (target_si == nullptr) {
 | 
				
			||||||
 | 
					      DL_ERR("cannot find \"%s\" from verneed[%zd] in DT_NEEDED list for \"%s\"",
 | 
				
			||||||
 | 
					          target_soname, i, si_from->get_soname());
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (size_t j = 0; j<verneed->vn_cnt; ++j) {
 | 
				
			||||||
 | 
					      const ElfW(Vernaux)* vernaux = reinterpret_cast<ElfW(Vernaux)*>(verneed_ptr + vernaux_offset);
 | 
				
			||||||
 | 
					      vernaux_offset += vernaux->vna_next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const ElfW(Word) elf_hash = vernaux->vna_hash;
 | 
				
			||||||
 | 
					      const char* ver_name = si_from->get_string(vernaux->vna_name);
 | 
				
			||||||
 | 
					      ElfW(Half) source_index = vernaux->vna_other;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      add_version_info(source_index, elf_hash, ver_name, target_si);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool VersionTracker::init_verdef(const soinfo* si_from) {
 | 
				
			||||||
 | 
					  return for_each_verdef(si_from,
 | 
				
			||||||
 | 
					    [&](size_t, const ElfW(Verdef)* verdef, const ElfW(Verdaux)* verdaux) {
 | 
				
			||||||
 | 
					      add_version_info(verdef->vd_ndx, verdef->vd_hash,
 | 
				
			||||||
 | 
					          si_from->get_string(verdaux->vda_name), si_from);
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool VersionTracker::init(const soinfo* si_from) {
 | 
				
			||||||
 | 
					  if (!si_from->has_min_version(2)) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return init_verneed(si_from) && init_verdef(si_from);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if !defined(__mips__)
 | 
					#if !defined(__mips__)
 | 
				
			||||||
#if defined(USE_RELA)
 | 
					#if defined(USE_RELA)
 | 
				
			||||||
static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) {
 | 
					static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) {
 | 
				
			||||||
@@ -1462,6 +1760,12 @@ static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) {
 | 
				
			|||||||
template<typename ElfRelIteratorT>
 | 
					template<typename ElfRelIteratorT>
 | 
				
			||||||
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group,
 | 
					bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group,
 | 
				
			||||||
                      const soinfo_list_t& local_group) {
 | 
					                      const soinfo_list_t& local_group) {
 | 
				
			||||||
 | 
					  VersionTracker version_tracker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!version_tracker.init(this)) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
 | 
					  for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
 | 
				
			||||||
    const auto rel = rel_iterator.next();
 | 
					    const auto rel = rel_iterator.next();
 | 
				
			||||||
    if (rel == nullptr) {
 | 
					    if (rel == nullptr) {
 | 
				
			||||||
@@ -1481,12 +1785,32 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator, const soinfo_list_t& globa
 | 
				
			|||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ElfW(Sym)* s = nullptr;
 | 
					    const ElfW(Sym)* s = nullptr;
 | 
				
			||||||
    soinfo* lsi = nullptr;
 | 
					    soinfo* lsi = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (sym != 0) {
 | 
					    if (sym != 0) {
 | 
				
			||||||
      sym_name = get_string(symtab_[sym].st_name);
 | 
					      sym_name = get_string(symtab_[sym].st_name);
 | 
				
			||||||
      s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group);
 | 
					      const ElfW(Versym)* sym_ver_ptr = get_versym(sym);
 | 
				
			||||||
 | 
					      ElfW(Versym) sym_ver = sym_ver_ptr == nullptr ? 0 : *sym_ver_ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (sym_ver == VER_NDX_LOCAL || sym_ver == VER_NDX_GLOBAL) {
 | 
				
			||||||
 | 
					        // there is no version info for this one
 | 
				
			||||||
 | 
					        if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) {
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        const version_info* vi = version_tracker.get_version_info(sym_ver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (vi == nullptr) {
 | 
				
			||||||
 | 
					          DL_ERR("cannot find verneed/verdef for version index=%d "
 | 
				
			||||||
 | 
					              "referenced by symbol \"%s\" at \"%s\"", sym_ver, sym_name, get_soname());
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) {
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      if (s == nullptr) {
 | 
					      if (s == nullptr) {
 | 
				
			||||||
        // We only allow an undefined symbol if this is a weak reference...
 | 
					        // We only allow an undefined symbol if this is a weak reference...
 | 
				
			||||||
        s = &symtab_[sym];
 | 
					        s = &symtab_[sym];
 | 
				
			||||||
@@ -1977,6 +2301,14 @@ soinfo::soinfo_list_t& soinfo::get_children() {
 | 
				
			|||||||
  return g_empty_list;
 | 
					  return g_empty_list;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const soinfo::soinfo_list_t& soinfo::get_children() const {
 | 
				
			||||||
 | 
					  if (has_min_version(0)) {
 | 
				
			||||||
 | 
					    return children_;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return g_empty_list;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
soinfo::soinfo_list_t& soinfo::get_parents() {
 | 
					soinfo::soinfo_list_t& soinfo::get_parents() {
 | 
				
			||||||
  if (has_min_version(0)) {
 | 
					  if (has_min_version(0)) {
 | 
				
			||||||
    return parents_;
 | 
					    return parents_;
 | 
				
			||||||
@@ -1985,7 +2317,7 @@ soinfo::soinfo_list_t& soinfo::get_parents() {
 | 
				
			|||||||
  return g_empty_list;
 | 
					  return g_empty_list;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Addr) soinfo::resolve_symbol_address(ElfW(Sym)* s) {
 | 
					ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const {
 | 
				
			||||||
  if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
 | 
					  if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
 | 
				
			||||||
    return call_ifunc_resolver(s->st_value + load_bias);
 | 
					    return call_ifunc_resolver(s->st_value + load_bias);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -2452,12 +2784,23 @@ bool soinfo::prelink_image() {
 | 
				
			|||||||
      case DT_BIND_NOW:
 | 
					      case DT_BIND_NOW:
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Ignore: bionic does not support symbol versioning...
 | 
					 | 
				
			||||||
      case DT_VERSYM:
 | 
					      case DT_VERSYM:
 | 
				
			||||||
 | 
					        versym_ = reinterpret_cast<ElfW(Versym)*>(load_bias + d->d_un.d_ptr);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case DT_VERDEF:
 | 
					      case DT_VERDEF:
 | 
				
			||||||
 | 
					        verdef_ptr_ = load_bias + d->d_un.d_ptr;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
      case DT_VERDEFNUM:
 | 
					      case DT_VERDEFNUM:
 | 
				
			||||||
 | 
					        verdef_cnt_ = d->d_un.d_val;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case DT_VERNEED:
 | 
					      case DT_VERNEED:
 | 
				
			||||||
 | 
					        verneed_ptr_ = load_bias + d->d_un.d_ptr;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case DT_VERNEEDNUM:
 | 
					      case DT_VERNEEDNUM:
 | 
				
			||||||
 | 
					        verneed_cnt_ = d->d_un.d_val;
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,7 @@
 | 
				
			|||||||
#include "linked_list.h"
 | 
					#include "linked_list.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DL_ERR(fmt, x...) \
 | 
					#define DL_ERR(fmt, x...) \
 | 
				
			||||||
    do { \
 | 
					    do { \
 | 
				
			||||||
@@ -142,6 +143,32 @@ class SymbolName {
 | 
				
			|||||||
  DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName);
 | 
					  DISALLOW_IMPLICIT_CONSTRUCTORS(SymbolName);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct version_info {
 | 
				
			||||||
 | 
					  version_info() : elf_hash(0), name(nullptr), target_si(nullptr) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  uint32_t elf_hash;
 | 
				
			||||||
 | 
					  const char* name;
 | 
				
			||||||
 | 
					  const soinfo* target_si;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Class used construct version dependency graph.
 | 
				
			||||||
 | 
					class VersionTracker {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  VersionTracker() = default;
 | 
				
			||||||
 | 
					  bool init(const soinfo* si_from);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const version_info* get_version_info(ElfW(Versym) source_symver) const;
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  bool init_verneed(const soinfo* si_from);
 | 
				
			||||||
 | 
					  bool init_verdef(const soinfo* si_from);
 | 
				
			||||||
 | 
					  void add_version_info(size_t source_index, ElfW(Word) elf_hash,
 | 
				
			||||||
 | 
					      const char* ver_name, const soinfo* target_si);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::vector<version_info> version_infos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DISALLOW_COPY_AND_ASSIGN(VersionTracker);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct soinfo {
 | 
					struct soinfo {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
 | 
					  typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
 | 
				
			||||||
@@ -260,11 +287,16 @@ struct soinfo {
 | 
				
			|||||||
  void set_dt_flags_1(uint32_t dt_flags_1);
 | 
					  void set_dt_flags_1(uint32_t dt_flags_1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  soinfo_list_t& get_children();
 | 
					  soinfo_list_t& get_children();
 | 
				
			||||||
 | 
					  const soinfo_list_t& get_children() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  soinfo_list_t& get_parents();
 | 
					  soinfo_list_t& get_parents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ElfW(Sym)* find_symbol_by_name(SymbolName& symbol_name);
 | 
					  bool find_symbol_by_name(SymbolName& symbol_name,
 | 
				
			||||||
 | 
					                           const version_info* vi,
 | 
				
			||||||
 | 
					                           const ElfW(Sym)** symbol) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ElfW(Sym)* find_symbol_by_address(const void* addr);
 | 
					  ElfW(Sym)* find_symbol_by_address(const void* addr);
 | 
				
			||||||
  ElfW(Addr) resolve_symbol_address(ElfW(Sym)* s);
 | 
					  ElfW(Addr) resolve_symbol_address(const ElfW(Sym)* s) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const char* get_string(ElfW(Word) index) const;
 | 
					  const char* get_string(ElfW(Word) index) const;
 | 
				
			||||||
  bool can_unload() const;
 | 
					  bool can_unload() const;
 | 
				
			||||||
@@ -292,11 +324,18 @@ struct soinfo {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const char* get_soname() const;
 | 
					  const char* get_soname() const;
 | 
				
			||||||
  const char* get_realpath() const;
 | 
					  const char* get_realpath() const;
 | 
				
			||||||
 | 
					  const ElfW(Versym)* get_versym(size_t n) const;
 | 
				
			||||||
 | 
					  ElfW(Addr) get_verneed_ptr() const;
 | 
				
			||||||
 | 
					  size_t get_verneed_cnt() const;
 | 
				
			||||||
 | 
					  ElfW(Addr) get_verdef_ptr() const;
 | 
				
			||||||
 | 
					  size_t get_verdef_cnt() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool find_verdef_version_index(const version_info* vi, ElfW(Versym)* versym) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
  ElfW(Sym)* elf_lookup(SymbolName& symbol_name);
 | 
					  bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
 | 
				
			||||||
  ElfW(Sym)* elf_addr_lookup(const void* addr);
 | 
					  ElfW(Sym)* elf_addr_lookup(const void* addr);
 | 
				
			||||||
  ElfW(Sym)* gnu_lookup(SymbolName& symbol_name);
 | 
					  bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
 | 
				
			||||||
  ElfW(Sym)* gnu_addr_lookup(const void* addr);
 | 
					  ElfW(Sym)* gnu_addr_lookup(const void* addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
 | 
					  void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
 | 
				
			||||||
@@ -341,11 +380,20 @@ struct soinfo {
 | 
				
			|||||||
  const char* soname_;
 | 
					  const char* soname_;
 | 
				
			||||||
  std::string realpath_;
 | 
					  std::string realpath_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const ElfW(Versym)* versym_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ElfW(Addr) verdef_ptr_;
 | 
				
			||||||
 | 
					  size_t verdef_cnt_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ElfW(Addr) verneed_ptr_;
 | 
				
			||||||
 | 
					  size_t verneed_cnt_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  friend soinfo* get_libdl_info();
 | 
					  friend soinfo* get_libdl_info();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
 | 
					bool soinfo_do_lookup(soinfo* si_from, const char* name, const version_info* vi,
 | 
				
			||||||
    const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group);
 | 
					                      soinfo** si_found_in, const soinfo::soinfo_list_t& global_group,
 | 
				
			||||||
 | 
					                      const soinfo::soinfo_list_t& local_group, const ElfW(Sym)** symbol);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum RelocationKind {
 | 
					enum RelocationKind {
 | 
				
			||||||
  kRelocAbsolute = 0,
 | 
					  kRelocAbsolute = 0,
 | 
				
			||||||
@@ -364,10 +412,10 @@ void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path);
 | 
				
			|||||||
soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo);
 | 
					soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo);
 | 
				
			||||||
void do_dlclose(soinfo* si);
 | 
					void do_dlclose(soinfo* si);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle);
 | 
					const ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle);
 | 
				
			||||||
soinfo* find_containing_library(const void* addr);
 | 
					soinfo* find_containing_library(const void* addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
 | 
					const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void debuggerd_init();
 | 
					void debuggerd_init();
 | 
				
			||||||
extern "C" abort_msg_t* g_abort_message;
 | 
					extern "C" abort_msg_t* g_abort_message;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,6 +50,12 @@ template <typename ElfRelIteratorT>
 | 
				
			|||||||
bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
 | 
					bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
 | 
				
			||||||
                      const soinfo_list_t& global_group,
 | 
					                      const soinfo_list_t& global_group,
 | 
				
			||||||
                      const soinfo_list_t& local_group) {
 | 
					                      const soinfo_list_t& local_group) {
 | 
				
			||||||
 | 
					  VersionTracker version_tracker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!version_tracker.init(this)) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
 | 
					  for (size_t idx = 0; rel_iterator.has_next(); ++idx) {
 | 
				
			||||||
    const auto rel = rel_iterator.next();
 | 
					    const auto rel = rel_iterator.next();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,12 +75,33 @@ bool soinfo::relocate(ElfRelIteratorT&& rel_iterator,
 | 
				
			|||||||
      continue;
 | 
					      continue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ElfW(Sym)* s = nullptr;
 | 
					    const ElfW(Sym)* s = nullptr;
 | 
				
			||||||
    soinfo* lsi = nullptr;
 | 
					    soinfo* lsi = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (sym != 0) {
 | 
					    if (sym != 0) {
 | 
				
			||||||
      sym_name = get_string(symtab_[sym].st_name);
 | 
					      sym_name = get_string(symtab_[sym].st_name);
 | 
				
			||||||
      s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group);
 | 
					      const ElfW(Versym)* sym_ver_ptr = get_versym(sym);
 | 
				
			||||||
 | 
					      ElfW(Versym) sym_ver = sym_ver_ptr == nullptr ? 0 : *sym_ver_ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (sym_ver == VER_NDX_LOCAL || sym_ver == VER_NDX_GLOBAL) {
 | 
				
			||||||
 | 
					        // there is no version info for this one
 | 
				
			||||||
 | 
					        if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) {
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        const version_info* vi = version_tracker.get_version_info(sym_ver);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (vi == nullptr) {
 | 
				
			||||||
 | 
					          DL_ERR("cannot find verneed/verdef for version index=%d "
 | 
				
			||||||
 | 
					              "referenced by symbol \"%s\" at \"%s\"", sym_ver, sym_name, get_soname());
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!soinfo_do_lookup(this, sym_name, vi, &lsi, global_group, local_group, &s)) {
 | 
				
			||||||
 | 
					          return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (s == nullptr) {
 | 
					      if (s == nullptr) {
 | 
				
			||||||
        // mips does not support relocation with weak-undefined symbols
 | 
					        // mips does not support relocation with weak-undefined symbols
 | 
				
			||||||
        DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_soname());
 | 
					        DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, get_soname());
 | 
				
			||||||
@@ -147,7 +174,11 @@ bool soinfo::mips_relocate_got(const soinfo_list_t& global_group,
 | 
				
			|||||||
    // This is an undefined reference... try to locate it.
 | 
					    // This is an undefined reference... try to locate it.
 | 
				
			||||||
    const char* sym_name = get_string(sym->st_name);
 | 
					    const char* sym_name = get_string(sym->st_name);
 | 
				
			||||||
    soinfo* lsi = nullptr;
 | 
					    soinfo* lsi = nullptr;
 | 
				
			||||||
    ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
 | 
					    const ElfW(Sym)* s = nullptr;
 | 
				
			||||||
 | 
					    if (!soinfo_do_lookup(this, sym_name, nullptr, &lsi, global_group, local_group, &s)) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (s == nullptr) {
 | 
					    if (s == nullptr) {
 | 
				
			||||||
      // We only allow an undefined symbol if this is a weak reference.
 | 
					      // We only allow an undefined symbol if this is a weak reference.
 | 
				
			||||||
      s = &symtab_[g];
 | 
					      s = &symtab_[g];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -925,3 +925,63 @@ TEST(dlfcn, dlopen_dlopen_from_ctor) {
 | 
				
			|||||||
  GTEST_LOG_(INFO) << "This test is disabled for glibc (glibc segfaults if you try to call dlopen from a constructor).\n";
 | 
					  GTEST_LOG_(INFO) << "This test is disabled for glibc (glibc segfaults if you try to call dlopen from a constructor).\n";
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST(dlfcn, symbol_versioning_use_v1) {
 | 
				
			||||||
 | 
					  void* handle = dlopen("libtest_versioned_uselibv1.so", RTLD_NOW);
 | 
				
			||||||
 | 
					  ASSERT_TRUE(handle != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  typedef int (*fn_t)();
 | 
				
			||||||
 | 
					  fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
 | 
				
			||||||
 | 
					  ASSERT_TRUE(fn != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  ASSERT_EQ(1, fn());
 | 
				
			||||||
 | 
					  dlclose(handle);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST(dlfcn, symbol_versioning_use_v2) {
 | 
				
			||||||
 | 
					  void* handle = dlopen("libtest_versioned_uselibv2.so", RTLD_NOW);
 | 
				
			||||||
 | 
					  ASSERT_TRUE(handle != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  typedef int (*fn_t)();
 | 
				
			||||||
 | 
					  fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
 | 
				
			||||||
 | 
					  ASSERT_TRUE(fn != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  ASSERT_EQ(2, fn());
 | 
				
			||||||
 | 
					  dlclose(handle);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST(dlfcn, symbol_versioning_use_other_v2) {
 | 
				
			||||||
 | 
					  void* handle = dlopen("libtest_versioned_uselibv2_other.so", RTLD_NOW);
 | 
				
			||||||
 | 
					  ASSERT_TRUE(handle != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  typedef int (*fn_t)();
 | 
				
			||||||
 | 
					  fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
 | 
				
			||||||
 | 
					  ASSERT_TRUE(fn != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  ASSERT_EQ(20, fn());
 | 
				
			||||||
 | 
					  dlclose(handle);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST(dlfcn, symbol_versioning_use_other_v3) {
 | 
				
			||||||
 | 
					  void* handle = dlopen("libtest_versioned_uselibv3_other.so", RTLD_NOW);
 | 
				
			||||||
 | 
					  ASSERT_TRUE(handle != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  typedef int (*fn_t)();
 | 
				
			||||||
 | 
					  fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "get_function_version"));
 | 
				
			||||||
 | 
					  ASSERT_TRUE(fn != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  ASSERT_EQ(3, fn());
 | 
				
			||||||
 | 
					  dlclose(handle);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST(dlfcn, symbol_versioning_default_via_dlsym) {
 | 
				
			||||||
 | 
					  void* handle = dlopen("libtest_versioned_lib.so", RTLD_NOW);
 | 
				
			||||||
 | 
					  ASSERT_TRUE(handle != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  typedef int (*fn_t)();
 | 
				
			||||||
 | 
					  fn_t fn = reinterpret_cast<fn_t>(dlsym(handle, "versioned_function"));
 | 
				
			||||||
 | 
					  ASSERT_TRUE(fn != nullptr) << dlerror();
 | 
				
			||||||
 | 
					  ASSERT_EQ(3, fn()); // the default version is 3
 | 
				
			||||||
 | 
					  dlclose(handle);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This preempts the implementation from libtest_versioned_lib.so
 | 
				
			||||||
 | 
					extern "C" int version_zero_function() {
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This preempts the implementation from libtest_versioned_uselibv*.so
 | 
				
			||||||
 | 
					extern "C" int version_zero_function2() {
 | 
				
			||||||
 | 
					  return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										120
									
								
								tests/libs/Android.build.versioned_lib.mk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								tests/libs/Android.build.versioned_lib.mk
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
				
			|||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (C) 2015 The Android Open Source Project
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					# you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					# You may obtain a copy of the License at
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					# distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					# See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					# limitations under the License.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					# Libraries used to test versioned symbols
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					libtest_versioned_uselibv1_src_files := versioned_uselib.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_uselibv1_shared_libraries := \
 | 
				
			||||||
 | 
					    libtest_versioned_libv1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module := libtest_versioned_uselibv1
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					libtest_versioned_uselibv2_src_files := \
 | 
				
			||||||
 | 
					    versioned_uselib.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_uselibv2_shared_libraries := \
 | 
				
			||||||
 | 
					    libtest_versioned_libv2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_uselibv2_ldflags := \
 | 
				
			||||||
 | 
					    -Wl,--version-script,$(LOCAL_PATH)/versioned_uselib.map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module := libtest_versioned_uselibv2
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					libtest_versioned_uselibv2_other_src_files := \
 | 
				
			||||||
 | 
					    versioned_uselib.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_uselibv2_other_shared_libraries := \
 | 
				
			||||||
 | 
					    libtest_versioned_otherlib_empty libtest_versioned_libv2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module := libtest_versioned_uselibv2_other
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					libtest_versioned_uselibv3_other_src_files := \
 | 
				
			||||||
 | 
					    versioned_uselib.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_uselibv3_other_shared_libraries := \
 | 
				
			||||||
 | 
					    libtest_versioned_otherlib_empty libtest_versioned_lib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module := libtest_versioned_uselibv3_other
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					# lib v1 - this one used during static linking but never used at runtime
 | 
				
			||||||
 | 
					# which forces libtest_versioned_uselibv1 to use function v1 from
 | 
				
			||||||
 | 
					# libtest_versioned_lib.so
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					libtest_versioned_libv1_src_files := \
 | 
				
			||||||
 | 
					    versioned_lib_v1.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_libv1_ldflags := \
 | 
				
			||||||
 | 
					    -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v1.map \
 | 
				
			||||||
 | 
					    -Wl,-soname,libtest_versioned_lib.so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module := libtest_versioned_libv1
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					# lib v2 - to make libtest_versioned_uselibv2.so use version 2 of versioned_function()
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					libtest_versioned_libv2_src_files := \
 | 
				
			||||||
 | 
					    versioned_lib_v2.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_libv2_ldflags := \
 | 
				
			||||||
 | 
					    -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v2.map \
 | 
				
			||||||
 | 
					    -Wl,-soname,libtest_versioned_lib.so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module := libtest_versioned_libv2
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					# last version - this one is used at the runtime and exports 3 versions
 | 
				
			||||||
 | 
					# of versioned_symbol().
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					libtest_versioned_lib_src_files := \
 | 
				
			||||||
 | 
					    versioned_lib_v3.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_lib_ldflags := \
 | 
				
			||||||
 | 
					    -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_v3.map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module := libtest_versioned_lib
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					# This library is empty, the actual implementation will provide an unversioned
 | 
				
			||||||
 | 
					# symbol for versioned_function().
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					libtest_versioned_otherlib_empty_src_files := empty.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_otherlib_empty_ldflags := -Wl,-soname,libtest_versioned_otherlib.so
 | 
				
			||||||
 | 
					module := libtest_versioned_otherlib_empty
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					libtest_versioned_otherlib_src_files := versioned_lib_other.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libtest_versioned_otherlib_ldflags := \
 | 
				
			||||||
 | 
					    -Wl,--version-script,$(LOCAL_PATH)/versioned_lib_other.map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module := libtest_versioned_otherlib
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.testlib.mk
 | 
				
			||||||
@@ -26,6 +26,7 @@ common_additional_dependencies := \
 | 
				
			|||||||
    $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \
 | 
					    $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \
 | 
				
			||||||
    $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \
 | 
					    $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \
 | 
				
			||||||
    $(LOCAL_PATH)/Android.build.testlib.mk \
 | 
					    $(LOCAL_PATH)/Android.build.testlib.mk \
 | 
				
			||||||
 | 
					    $(LOCAL_PATH)/Android.build.versioned_lib.mk \
 | 
				
			||||||
    $(TEST_PATH)/Android.build.mk
 | 
					    $(TEST_PATH)/Android.build.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# -----------------------------------------------------------------------------
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
@@ -197,6 +198,11 @@ include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk
 | 
				
			|||||||
# -----------------------------------------------------------------------------
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk
 | 
					include $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					# Build libtest_versioned_lib.so with its dependencies.
 | 
				
			||||||
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					include $(LOCAL_PATH)/Android.build.versioned_lib.mk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# -----------------------------------------------------------------------------
 | 
					# -----------------------------------------------------------------------------
 | 
				
			||||||
# Library with dependency loop used by dlfcn tests
 | 
					# Library with dependency loop used by dlfcn tests
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								tests/libs/versioned_lib_other.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/libs/versioned_lib_other.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2015 The Android Open Source Project
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" int versioned_function_v2() {
 | 
				
			||||||
 | 
					  return 20;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2");
 | 
				
			||||||
							
								
								
									
										9
									
								
								tests/libs/versioned_lib_other.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/libs/versioned_lib_other.map
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					TESTLIB_V0 {
 | 
				
			||||||
 | 
					  local:
 | 
				
			||||||
 | 
					    versioned_function_v*;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TESTLIB_V2 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    versioned_function;
 | 
				
			||||||
 | 
					} TESTLIB_V0;
 | 
				
			||||||
							
								
								
									
										30
									
								
								tests/libs/versioned_lib_v1.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								tests/libs/versioned_lib_v1.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2015 The Android Open Source Project
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					  int versioned_function_v1(); // __attribute__((visibility("hidden")));
 | 
				
			||||||
 | 
					  int version_zero_function();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int versioned_function_v1() {
 | 
				
			||||||
 | 
					  return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int version_zero_function() {
 | 
				
			||||||
 | 
					  return 100;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__asm__(".symver versioned_function_v1,versioned_function@@TESTLIB_V1");
 | 
				
			||||||
							
								
								
									
										12
									
								
								tests/libs/versioned_lib_v1.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/libs/versioned_lib_v1.map
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					TESTLIB_V0 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    version_zero_function;
 | 
				
			||||||
 | 
					  local:
 | 
				
			||||||
 | 
					    versioned_function_v*;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TESTLIB_V1 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    versioned_function;
 | 
				
			||||||
 | 
					} TESTLIB_V0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										35
									
								
								tests/libs/versioned_lib_v2.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								tests/libs/versioned_lib_v2.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2015 The Android Open Source Project
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					  int versioned_function_v1(); // __attribute__((visibility("hidden")));
 | 
				
			||||||
 | 
					  int versioned_function_v2(); // __attribute__((visibility("hidden")));
 | 
				
			||||||
 | 
					  int version_zero_function();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int versioned_function_v1() {
 | 
				
			||||||
 | 
					  return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int versioned_function_v2() {
 | 
				
			||||||
 | 
					  return 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int version_zero_function() {
 | 
				
			||||||
 | 
					  return 200;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1");
 | 
				
			||||||
 | 
					__asm__(".symver versioned_function_v2,versioned_function@@TESTLIB_V2");
 | 
				
			||||||
							
								
								
									
										16
									
								
								tests/libs/versioned_lib_v2.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/libs/versioned_lib_v2.map
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					TESTLIB_V0 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    version_zero_function;
 | 
				
			||||||
 | 
					  local:
 | 
				
			||||||
 | 
					    versioned_function_v*;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TESTLIB_V1 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    versioned_function;
 | 
				
			||||||
 | 
					} TESTLIB_V0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TESTLIB_V2 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    versioned_function;
 | 
				
			||||||
 | 
					} TESTLIB_V1;
 | 
				
			||||||
							
								
								
									
										42
									
								
								tests/libs/versioned_lib_v3.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								tests/libs/versioned_lib_v3.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2015 The Android Open Source Project
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					  int versioned_function_v1(); // __attribute__((visibility("hidden")));
 | 
				
			||||||
 | 
					  int versioned_function_v2(); // __attribute__((visibility("hidden")));
 | 
				
			||||||
 | 
					  int versioned_function_v3(); // __attribute__((visibility("hidden")));
 | 
				
			||||||
 | 
					  int version_zero_function();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int versioned_function_v1() {
 | 
				
			||||||
 | 
					  return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int versioned_function_v2() {
 | 
				
			||||||
 | 
					  return 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int versioned_function_v3() {
 | 
				
			||||||
 | 
					  return 3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int version_zero_function() {
 | 
				
			||||||
 | 
					  return 1000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__asm__(".symver versioned_function_v1,versioned_function@TESTLIB_V1");
 | 
				
			||||||
 | 
					__asm__(".symver versioned_function_v2,versioned_function@TESTLIB_V2");
 | 
				
			||||||
 | 
					__asm__(".symver versioned_function_v3,versioned_function@@TESTLIB_V3");
 | 
				
			||||||
							
								
								
									
										21
									
								
								tests/libs/versioned_lib_v3.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/libs/versioned_lib_v3.map
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					TESTLIB_V0 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    version_zero_function;
 | 
				
			||||||
 | 
					  local:
 | 
				
			||||||
 | 
					    versioned_function_v*;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TESTLIB_V1 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    versioned_function;
 | 
				
			||||||
 | 
					} TESTLIB_V0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TESTLIB_V2 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    versioned_function;
 | 
				
			||||||
 | 
					} TESTLIB_V1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TESTLIB_V3 {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    versioned_function;
 | 
				
			||||||
 | 
					} TESTLIB_V2;
 | 
				
			||||||
							
								
								
									
										32
									
								
								tests/libs/versioned_uselib.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								tests/libs/versioned_uselib.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2015 The Android Open Source Project
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					  int versioned_function();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int get_function_version();
 | 
				
			||||||
 | 
					  int version_zero_function();
 | 
				
			||||||
 | 
					  int version_zero_function2() __attribute__((weak));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int get_function_version() {
 | 
				
			||||||
 | 
					  return version_zero_function2() + version_zero_function() + versioned_function();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// we expect this function to be preempted by main executable.
 | 
				
			||||||
 | 
					int version_zero_function2() {
 | 
				
			||||||
 | 
					  return 40000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										9
									
								
								tests/libs/versioned_uselib.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/libs/versioned_uselib.map
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					TESTLIB_NONE {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    get_function_version;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TESTLIB_ZERO {
 | 
				
			||||||
 | 
					  global:
 | 
				
			||||||
 | 
					    version_zero_function2;
 | 
				
			||||||
 | 
					} TESTLIB_NONE;
 | 
				
			||||||
		Reference in New Issue
	
	Block a user