From 3597b8055da090ef3f1ee662e96dcb952bba2c30 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Mon, 9 Mar 2015 12:02:02 -0700 Subject: [PATCH] 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 --- linker/linker.cpp | 45 +++++++++++++++++++++++++-------------------- linker/linker.h | 4 ++++ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index 9ba83ecc2..593785b79 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -421,26 +421,41 @@ ElfW(Sym)* soinfo::gnu_lookup(SymbolName& symbol_name) { uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_; 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(base)); + // test against bloom filter 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(base)); + return nullptr; } // bloom test says "probably yes"... - uint32_t n = bucket_[hash % nbucket_]; + uint32_t n = gnu_bucket_[hash % gnu_nbucket_]; if (n == 0) { + TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p", + symbol_name.get_name(), name, reinterpret_cast(base)); + return nullptr; } do { 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 && is_symbol_global_and_defined(this, s)) { + TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd", + symbol_name.get_name(), name, reinterpret_cast(s->st_value), + static_cast(s->st_size)); 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(base)); 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(Addr) soaddr = reinterpret_cast(addr) - load_bias; - for (size_t i = 0; i < nbucket_; ++i) { - uint32_t n = bucket_[i]; + for (size_t i = 0; i < gnu_nbucket_; ++i) { + uint32_t n = gnu_bucket_[i]; if (n == 0) { continue; @@ -814,7 +829,7 @@ ElfW(Sym)* soinfo::gnu_addr_lookup(const void* addr) { if (symbol_matches_soaddr(sym, soaddr)) { return sym; } - } while ((chain_[n++] & 1) == 0); + } while ((gnu_chain_[n++] & 1) == 0); } return nullptr; @@ -1941,11 +1956,6 @@ bool soinfo::prelink_image() { break; case DT_HASH: - if (nbucket_ != 0) { - // in case of --hash-style=both, we prefer gnu - break; - } - nbucket_ = reinterpret_cast(load_bias + d->d_un.d_ptr)[0]; nchain_ = reinterpret_cast(load_bias + d->d_un.d_ptr)[1]; bucket_ = reinterpret_cast(load_bias + d->d_un.d_ptr + 8); @@ -1953,20 +1963,15 @@ bool soinfo::prelink_image() { break; case DT_GNU_HASH: - if (nbucket_ != 0) { - // in case of --hash-style=both, we prefer gnu - nchain_ = 0; - } - - nbucket_ = reinterpret_cast(load_bias + d->d_un.d_ptr)[0]; + gnu_nbucket_ = reinterpret_cast(load_bias + d->d_un.d_ptr)[0]; // skip symndx gnu_maskwords_ = reinterpret_cast(load_bias + d->d_un.d_ptr)[2]; gnu_shift2_ = reinterpret_cast(load_bias + d->d_un.d_ptr)[3]; gnu_bloom_filter_ = reinterpret_cast(load_bias + d->d_un.d_ptr + 16); - bucket_ = reinterpret_cast(gnu_bloom_filter_ + gnu_maskwords_); + gnu_bucket_ = reinterpret_cast(gnu_bloom_filter_ + gnu_maskwords_); // amend chain for symndx = header[1] - chain_ = bucket_ + nbucket_ - reinterpret_cast(load_bias + d->d_un.d_ptr)[1]; + gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - reinterpret_cast(load_bias + d->d_un.d_ptr)[1]; if (!powerof2(gnu_maskwords_)) { DL_ERR("invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", gnu_maskwords_, name); @@ -2278,7 +2283,7 @@ bool soinfo::prelink_image() { DL_ERR("linker cannot have DT_NEEDED dependencies on other libraries"); 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); return false; } diff --git a/linker/linker.h b/linker/linker.h index f8640a0e5..e4681ebe5 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -309,6 +309,10 @@ struct soinfo { size_t strtab_size_; // version >= 2 + + size_t gnu_nbucket_; + uint32_t* gnu_bucket_; + uint32_t* gnu_chain_; uint32_t gnu_maskwords_; uint32_t gnu_shift2_; ElfW(Addr)* gnu_bloom_filter_;