Store fields for gnu_hash separately

Kindle app relies on soinfo's sysv hash
  fields while linking native libraries.

  This change allows to keep sysv hash fields
  intact for the libraries linked with --hash-style=both.

Bug: 19059885
Change-Id: I12528652955638f1a6586bda99e111bb1c8aa7a3
This commit is contained in:
Dmitriy Ivanov 2015-03-09 12:02:02 -07:00 committed by Dimitry Ivanov
parent edc1d3e3c6
commit 3597b8055d
2 changed files with 29 additions and 20 deletions

View File

@ -421,26 +421,41 @@ 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];
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p (gnu)",
symbol_name.get_name(), name, reinterpret_cast<void*>(base));
// test against bloom filter // test against bloom filter
if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) { if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) {
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
symbol_name.get_name(), name, reinterpret_cast<void*>(base));
return nullptr; return nullptr;
} }
// bloom test says "probably yes"... // bloom test says "probably yes"...
uint32_t n = bucket_[hash % nbucket_]; uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
if (n == 0) { if (n == 0) {
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
symbol_name.get_name(), name, reinterpret_cast<void*>(base));
return nullptr; return nullptr;
} }
do { do {
ElfW(Sym)* s = symtab_ + n; ElfW(Sym)* s = symtab_ + n;
if (((chain_[n] ^ hash) >> 1) == 0 && if (((gnu_chain_[n] ^ hash) >> 1) == 0 &&
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",
symbol_name.get_name(), name, reinterpret_cast<void*>(s->st_value),
static_cast<size_t>(s->st_size));
return s; return s;
} }
} while ((chain_[n++] & 1) == 0); } while ((gnu_chain_[n++] & 1) == 0);
TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p",
symbol_name.get_name(), name, reinterpret_cast<void*>(base));
return nullptr; return nullptr;
} }
@ -802,8 +817,8 @@ static bool symbol_matches_soaddr(const ElfW(Sym)* sym, ElfW(Addr) soaddr) {
ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) { ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) {
ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias; ElfW(Addr) soaddr = reinterpret_cast<ElfW(Addr)>(addr) - load_bias;
for (size_t i = 0; i < nbucket_; ++i) { for (size_t i = 0; i < gnu_nbucket_; ++i) {
uint32_t n = bucket_[i]; uint32_t n = gnu_bucket_[i];
if (n == 0) { if (n == 0) {
continue; continue;
@ -814,7 +829,7 @@ ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) {
if (symbol_matches_soaddr(sym, soaddr)) { if (symbol_matches_soaddr(sym, soaddr)) {
return sym; return sym;
} }
} while ((chain_[n++] & 1) == 0); } while ((gnu_chain_[n++] & 1) == 0);
} }
return nullptr; return nullptr;
@ -1941,11 +1956,6 @@ bool soinfo::prelink_image() {
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);
@ -1953,20 +1963,15 @@ bool soinfo::prelink_image() {
break; break;
case DT_GNU_HASH: case DT_GNU_HASH:
if (nbucket_ != 0) { gnu_nbucket_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[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 // skip symndx
gnu_maskwords_ = reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[2]; 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_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); 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_); gnu_bucket_ = reinterpret_cast<uint32_t*>(gnu_bloom_filter_ + gnu_maskwords_);
// amend chain for symndx = header[1] // amend chain for symndx = header[1]
chain_ = bucket_ + nbucket_ - reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1]; gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - reinterpret_cast<uint32_t*>(load_bias + d->d_un.d_ptr)[1];
if (!powerof2(gnu_maskwords_)) { if (!powerof2(gnu_maskwords_)) {
DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", gnu_maskwords_, name); DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", gnu_maskwords_, name);
@ -2278,7 +2283,7 @@ bool soinfo::prelink_image() {
DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries"); DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries");
return false; return false;
} }
if (nbucket_ == 0) { if (nbucket_ == 0 && gnu_nbucket_ == 0) {
DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" (new hash type from the future?)", name); DL_ERR("empty/missing DT_HASH/DT_GNU_HASH in \"%s\" (new hash type from the future?)", name);
return false; return false;
} }

View File

@ -309,6 +309,10 @@ struct soinfo {
size_t strtab_size_; size_t strtab_size_;
// version >= 2 // version >= 2
size_t gnu_nbucket_;
uint32_t* gnu_bucket_;
uint32_t* gnu_chain_;
uint32_t gnu_maskwords_; uint32_t gnu_maskwords_;
uint32_t gnu_shift2_; uint32_t gnu_shift2_;
ElfW(Addr)* gnu_bloom_filter_; ElfW(Addr)* gnu_bloom_filter_;