blob: ac7b8cdf03a54694c0fa7f803a4d0ecb2f3beeac [file] [log] [blame]
// 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_