| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_COMPAT_HASH_H_ |
| #define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_COMPAT_HASH_H_ |
| |
| #include <lib/stdcompat/bit.h> |
| #include <lib/stdcompat/span.h> |
| |
| #include <cstdint> |
| #include <string_view> |
| |
| #include "abi-span.h" |
| |
| namespace elfldltl { |
| |
| // This handles the DT_HASH format, which is mostly obsolete but is the |
| // official ELF standard format. This interface matches GnuHash (gnu-hash.h). |
| // See SymbolInfo (symbol.h) for details. |
| |
| inline constexpr uint32_t CompatHashString(std::string_view name) { |
| uint_fast32_t hash = 0; |
| for (char c : name) { |
| hash = (hash << 4) + cpp20::bit_cast<unsigned char>(c); |
| hash ^= (hash >> 24) & 0xf0; |
| } |
| return hash & 0x0fffffff; |
| } |
| |
| constexpr uint32_t kCompatNoHash = ~uint32_t{}; |
| |
| // In DT_HASH format, there is a table mapping hash buckets to indices of the |
| // first symbol table entry in the bucket. A second "chain" table maps the |
| // symbol table index of each symbol to the next symbol in the same bucket. |
| // Empty buckets and the end of a chain are identified by index 0 (STN_UNDEF), |
| // which is always a null entry. The first two words of the DT_HASH data are |
| // the number of buckets and the number of chain entries (i.e. the number of |
| // symbol table entries). Then the bucket words follow, then the chain words. |
| |
| template <class Elf, class AbiTraits = LocalAbiTraits> |
| class CompatHash { |
| public: |
| using Word = typename Elf::Word; |
| |
| class iterator; |
| |
| class BucketIterator; |
| |
| constexpr explicit CompatHash(cpp20::span<const Word> table) |
| : buckets_(table.subspan(2, table[0])), chain_(table.subspan(2 + table[0], table[1])) {} |
| |
| static constexpr bool Valid(cpp20::span<const Word> table) { |
| if (table.size() < 2) { |
| return false; |
| } |
| const uint32_t nbucket = table[0], nchain = table[1]; |
| return table.size() - 2 > nbucket && table.size() - 2 - nbucket >= nchain; |
| } |
| |
| constexpr uint32_t symtab_size() const { return static_cast<uint32_t>(chain_.size()); } |
| |
| constexpr uint32_t Bucket(uint32_t hash) const { |
| if (buckets_.empty()) [[unlikely]] { |
| return 0; |
| } |
| return buckets_[hash % buckets_.size()]; |
| } |
| |
| constexpr iterator begin() const; |
| constexpr iterator end() const; |
| |
| constexpr size_t size() const { return buckets_.size(); } |
| |
| private: |
| AbiSpan<const Word, cpp20::dynamic_extent, Elf, AbiTraits> buckets_, chain_; |
| |
| public: |
| // <lib/ld/remote-abi-transcriber.h> introspection API. These aliases must |
| // be public, but can't be defined lexically before the private: section that |
| // declares the members; so this special public: section is at the end. |
| |
| using AbiLocal = CompatHash<Elf, LocalAbiTraits>; |
| |
| template <template <class...> class Template> |
| using AbiBases = Template<>; |
| |
| template <template <auto...> class Template> |
| using AbiMembers = Template<&CompatHash::buckets_, &CompatHash::chain_>; |
| }; |
| |
| // This is only actually used when AbiTraits supports direct memory access, so |
| // it doesn't need to work with other instantiations. |
| template <class Elf, class AbiTraits> |
| class CompatHash<Elf, AbiTraits>::BucketIterator { |
| public: |
| constexpr BucketIterator() = default; |
| constexpr BucketIterator(const BucketIterator&) = default; |
| constexpr BucketIterator& operator=(const BucketIterator&) = default; |
| |
| // This creates a begin() iterator for the bucket and hash value. |
| constexpr explicit BucketIterator(const CompatHash& table, uint32_t bucket, uint32_t hash) |
| : chain_(table.chain_), i_(BucketIndex(bucket)) {} |
| |
| // This creates an end() iterator. |
| constexpr explicit BucketIterator(const CompatHash& table) : chain_(table.chain_) {} |
| |
| constexpr bool operator==(const BucketIterator& other) const { return i_ == other.i_; } |
| |
| constexpr bool operator!=(const BucketIterator& other) const { return !(*this == other); } |
| |
| constexpr BucketIterator& operator++() { // prefix |
| // The chain table might encode an infinite loop here. So cut short |
| // iteration when the total number of entries has been enumerated. In |
| // corrupt data, this may not have covered all the entries because it hit a |
| // loop. In valid data, the natural end will always be reached first. |
| if (++count_ > chain_.size()) [[unlikely]] { |
| i_ = 0; |
| } else { |
| i_ = BucketIndex(chain_[i_]); |
| } |
| return *this; |
| } |
| |
| constexpr BucketIterator& operator++(int) { // postfix |
| auto old = *this; |
| ++*this; |
| return old; |
| } |
| |
| constexpr uint32_t operator*() const { return i_; } |
| |
| private: |
| // If a bogus index came out of the table, reset to end() state. |
| constexpr uint32_t BucketIndex(uint32_t symndx) { |
| if (symndx < chain_.size()) [[likely]] { |
| return symndx; |
| } |
| return 0; |
| } |
| |
| cpp20::span<const typename Elf::Word> chain_; |
| uint32_t i_ = 0; |
| uint32_t count_ = 0; |
| }; |
| |
| // Iterate over the hash buckets to yield a BucketIerator for each bucket. |
| // Together this allows for exhaustive iteration over the whole table. |
| template <class Elf, class AbiTraits> |
| class CompatHash<Elf, AbiTraits>::iterator { |
| public: |
| constexpr bool operator==(const iterator& other) const { return it_ == other.it_; } |
| constexpr bool operator!=(const iterator& other) const { return it_ != other.it_; } |
| |
| constexpr iterator& operator++() { // prefix |
| ++it_; |
| return *this; |
| } |
| |
| constexpr iterator operator++(int) { // postfix |
| iterator old = *this; |
| ++*this; |
| return old; |
| } |
| |
| constexpr BucketIterator operator*() const { return BucketIterator(*table_, *it_, 0); } |
| |
| private: |
| friend CompatHash<Elf, AbiTraits>; |
| |
| const CompatHash<Elf, AbiTraits>* table_ = nullptr; |
| typename cpp20::span<const typename Elf::Word>::iterator it_; |
| }; |
| |
| template <class Elf, class AbiTraits> |
| constexpr auto CompatHash<Elf, AbiTraits>::begin() const -> iterator { |
| iterator it; |
| it.table_ = this; |
| it.it_ = buckets_.begin(); |
| return it; |
| } |
| |
| template <class Elf, class AbiTraits> |
| constexpr auto CompatHash<Elf, AbiTraits>::end() const -> iterator { |
| iterator it; |
| it.table_ = this; |
| it.it_ = buckets_.end(); |
| return it; |
| } |
| |
| } // namespace elfldltl |
| |
| #endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_COMPAT_HASH_H_ |