blob: a1d27b2ba3d67b4103ab96bdbe501ad548736e95 [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_DYNAMIC_H_
#define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_DYNAMIC_H_
#include <lib/stdcompat/span.h>
#include <zircon/compiler.h>
#include <optional>
#include <string_view>
#include <type_traits>
#include <utility>
#include "init-fini.h"
#include "internal/dynamic-tag-error.h"
#include "layout.h"
#include "relocation.h"
#include "symbol.h"
namespace elfldltl {
using namespace std::literals::string_view_literals;
// elfldltl::DecodeDynamic does a single-pass decoding of the PT_DYNAMIC data
// by statically combining multiple "observer" objects. Various observers are
// defined to collect different subsets of the dynamic linking metadata needed
// for different purposes.
// This represents a PT_DYNAMIC entry that's been matched to a specific tag.
// These types are used in the arguments to Observe callbacks; see below.
template <ElfDynTag Tag>
struct DynamicTagMatch {};
// This is the base class for Dynamic*Observer classes.
// Each Observer subclass should define these two methods:
// * template <class DiagnosticsType, class Memory>
// bool Observe(DiagnosticsType& diagnostics, Memory& memory,
// DynamicTagMatch<Tag> tag, Addr val);
// * template <class DiagnosticsType, class Memory>
// bool Finish(DiagnosticsType& diagnostics, Memory& memory);
// Observe will be called with each entry matching any Tag of the Tags... list.
// Then Finish will be called at the end of all entries unless processing was
// terminated early for some reason, in which case the observer object is
// usually going to be destroyed without checking its results. Both return
// false if processing the dynamic section should be terminated early.
template <class Observer, ElfDynTag... Tags>
struct DynamicTagObserver {};
// This decodes a dynamic section by matching each entry against a list of
// observers. Each observer should be of a subclass of DynamicTagObserver that
// indicates the tags it matches. If any matching observer returns false then
// this stops processing early and returns false. Otherwise, each observer's
// Finish method is called, stopping early if one returns false.
template <class DiagnosticsType, class Memory, class Dyn, size_t N, class... Observers>
constexpr bool DecodeDynamic(DiagnosticsType&& diagnostics, Memory&& memory,
cpp20::span<const Dyn, N> dyn, Observers&&... observers) {
// The span is an upper bound but the section is terminated by a null entry.
for (const auto& entry : dyn) {
// At the terminator entry, call each observer's Finish() method.
if (entry.tag == ElfDynTag::kNull) {
return (observers.Finish(diagnostics, memory) && ...);
}
// Present each entry to each matching observer (see below).
if ((!DecodeDynamic(diagnostics, memory, entry, observers) || ...)) {
return false;
}
}
// This should never be reached.
return diagnostics.FormatError("missing DT_NULL terminator in PT_DYNAMIC"sv);
}
// Match a single dynamic section entry against a single observer. If the
// observer matches, its Observe overload for the matching tag is called.
// Returns the value of that call, or true if this observer didn't match.
template <class DiagnosticsType, class Memory, class Dyn, class Observer, ElfDynTag... Tags>
constexpr bool DecodeDynamic(DiagnosticsType&& diagnostics, Memory&& memory, const Dyn& entry,
DynamicTagObserver<Observer, Tags...>& observer) {
bool ok = true;
auto call_observer = [&](auto dt) {
ok = static_cast<Observer&>(observer).Observe(diagnostics, memory, dt, entry.val);
return true;
};
((entry.tag == Tags && call_observer(DynamicTagMatch<Tags>{})) || ...);
return ok;
}
// This is a very simple observer that rejects DT_TEXTREL and DF_TEXTREL.
class DynamicTextrelRejectObserver
: public DynamicTagObserver<DynamicTextrelRejectObserver, ElfDynTag::kTextRel,
ElfDynTag::kFlags> {
public:
static constexpr std::string_view kMessage = "DT_TEXTREL and DF_TEXTREL not supported";
template <class DiagnosticsType, class Memory, typename ValueType>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kTextRel> tag, ValueType val) {
// If this is called at all, that's an error.
return diagnostics.FormatError(kMessage);
}
template <class DiagnosticsType, class Memory, typename ValueType>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kFlags> tag, ValueType val) {
if (val & ElfDynFlags::kTextRel) {
return diagnostics.FormatError(kMessage);
}
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Finish(DiagnosticsType& diagnostics, Memory& memory) {
// There is no state kept aside from in the diagnostics object, so nothing
// to do.
return true;
}
};
// This is a base class for Dynamic*Observer classes in the common pattern
// where an Info object will be filled in with data observed in dynamic
// entries. A subclass can do `using Base::Base;` to get the constructor that
// takes an Info reference, and has the info() accessor for it. Then it must
// define its own `Observe` and `Finish` methods.
template <class Observer, class Info, class Elf, ElfDynTag... Tags>
class DynamicInfoObserver : public DynamicTagObserver<Observer, Tags...> {
public:
DynamicInfoObserver() = delete;
constexpr DynamicInfoObserver(const DynamicInfoObserver&) = default;
constexpr explicit DynamicInfoObserver(Info& info) : info_(info) {}
constexpr Info& info() { return info_; }
constexpr const Info& info() const { return info_; }
protected:
// This is a utility class for the common pattern of a pair of dynamic tags
// for a table address and its size in bytes. The SizedArray object's
// set_address and set_size_bytes methods should be called from Observe
// methods for the respective dynamic tags. Then a Finish method calls
// SizedArray::Finish<T, Setter, Tag1, Tag2> using explicit template
// arguments giving the type of table elements, &Info::set_foo for the setter
// in info() that takes a span<T>, and the two tags, passing it the
// diagnostics, memory and info objects by reference. This calls the setter
// only if both tags were present and the table was successfully fetched from
// memory. It diagnoses all the partial and invalid cases in detail with
// calls to diagnostics.FormatError, and does nothing at all if neither tag
// is present.
//
// The optional third tag is for a related count that also may be present.
// If this tag is supplied, the setter takes a second argument. If the
// CountTag was not present at runtime, that second argument will be zero.
//
// The SizedArray object is contextually convertible to bool to test whether
// either tag was present at all.
class SizedArray {
public:
template <typename T, auto Setter, ElfDynTag AddressTag, ElfDynTag SizeBytesTag,
ElfDynTag CountTag = ElfDynTag::kNull, class DiagnosticsType, class Memory>
constexpr bool Finish(DiagnosticsType&& diagnostics, Memory&& memory, Info& info,
typename Elf::size_type count = 0) {
if (!address_ && !size_bytes_) {
// No corresponding entries were found.
return true;
}
// Check invariants.
using Error = internal::DynamicTagError<AddressTag, SizeBytesTag, CountTag>;
if (!address_) [[unlikely]] {
return diagnostics.FormatError(Error::kMissingAddress);
}
if (!size_bytes_) [[unlikely]] {
return diagnostics.FormatError(Error::kMissingSize);
}
if (*address_ % alignof(T) != 0) [[unlikely]] {
// Don't store the bad address so that no misaligned fetches will be
// attempted later if we keep going to look for more errors.
*address_ = 0;
return diagnostics.FormatError(Error::kMisalignedAddress);
}
if (*size_bytes_ % sizeof(T) != 0) [[unlikely]] {
return diagnostics.FormatError(Error::kMisalignedSize);
}
// Fetch the table.
if (auto table = memory.template ReadArray<T>(*address_, *size_bytes_ / sizeof(T))) {
if constexpr (CountTag != ElfDynTag::kNull) {
if (count > table->size()) [[unlikely]] {
return diagnostics.FormatError(Error::kInvalidCount);
}
(info.*Setter)(*table, count);
} else {
(info.*Setter)(*table);
}
return true;
}
return diagnostics.FormatError(Error::kRead);
}
constexpr explicit operator bool() const { return address_ || size_bytes_; }
constexpr void set_address(typename Elf::size_type val) { address_ = val; }
constexpr void set_size_bytes(typename Elf::size_type val) { size_bytes_ = val; }
private:
std::optional<typename Elf::size_type> address_, size_bytes_;
};
private:
Info& info_;
};
// This is an observer to fill in an elfldltl::RelocationInfo<Elf> object.
// Its constructor takes (elfldltl::RelocationInfo<Elf>&, Memory&).
template <class Elf>
class DynamicRelocationInfoObserver;
// This is just a shorthand to avoid repeating the long list of parameters.
template <class Elf>
using DynamicRelocationInfoObserverBase =
DynamicInfoObserver<DynamicRelocationInfoObserver<Elf>, RelocationInfo<Elf>, Elf,
ElfDynTag::kJmpRel, ElfDynTag::kPltRel, ElfDynTag::kPltRelSz,
ElfDynTag::kRelr, ElfDynTag::kRelrSz, ElfDynTag::kRelrEnt, ElfDynTag::kRel,
ElfDynTag::kRelCount, ElfDynTag::kRelEnt, ElfDynTag::kRelSz,
ElfDynTag::kRela, ElfDynTag::kRelaCount, ElfDynTag::kRelaEnt,
ElfDynTag::kRelaSz>;
template <class Elf>
class DynamicRelocationInfoObserver : public DynamicRelocationInfoObserverBase<Elf> {
public:
using Base = DynamicRelocationInfoObserverBase<Elf>;
using Info = RelocationInfo<Elf>;
using size_type = typename Elf::size_type;
using Base::Base;
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kJmpRel> tag, size_type val) {
jmprel_.set_address(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kPltRelSz> tag, size_type val) {
jmprel_.set_size_bytes(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kPltRel> tag, size_type val) {
pltrel_ = val;
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRelr> tag, size_type val) {
relr_.set_address(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRelrSz> tag, size_type val) {
relr_.set_size_bytes(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRel> tag, size_type val) {
rel_.set_address(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRelSz> tag, size_type val) {
rel_.set_size_bytes(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRelCount> tag, size_type val) {
relcount_ = val;
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRela> tag, size_type val) {
rela_.set_address(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRelaSz> tag, size_type val) {
rela_.set_size_bytes(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRelaCount> tag, size_type val) {
relacount_ = val;
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRelEnt> tag, size_type val) {
return val == sizeof(typename Elf::Rel) ||
diagnostics.FormatError("incorrect DT_RELENT value"sv);
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRelaEnt> tag, size_type val) {
return val == sizeof(typename Elf::Rela) ||
diagnostics.FormatError("incorrect DT_RELAENT value"sv);
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kRelrEnt> tag, size_type val) {
return val == sizeof(typename Elf::Addr) ||
diagnostics.FormatError("incorrect DT_RELRENT value"sv);
}
template <class DiagnosticsType, class Memory>
constexpr bool Finish(DiagnosticsType& diagnostics, Memory& memory) {
// DT_PLTREL says which format DT_JMPREL uses: DT_REL or DT_RELA.
if (pltrel_ == static_cast<uint32_t>(ElfDynTag::kRel)) {
if (!jmprel_.template Finish<typename Elf::Rel, &Info::set_jmprel, ElfDynTag::kJmpRel,
ElfDynTag::kPltRelSz>(diagnostics, memory, this->info())) {
return false;
}
} else if (pltrel_ == static_cast<uint32_t>(ElfDynTag::kRela)) {
if (!jmprel_.template Finish<typename Elf::Rela, &Info::set_jmprel, ElfDynTag::kJmpRel,
ElfDynTag::kPltRelSz>(diagnostics, memory, this->info())) {
return false;
}
} else if (jmprel_ && !diagnostics.FormatError(pltrel_ ? "missing DT_PLTREL entry"sv
: "invalid DT_PLTREL entry"sv)) {
return false;
}
return relr_.template Finish<typename Elf::Addr, &Info::set_relr, ElfDynTag::kRelr,
ElfDynTag::kRelrSz>(diagnostics, memory, this->info()) &&
rel_.template Finish<typename Elf::Rel, &Info::set_rel, ElfDynTag::kRel,
ElfDynTag::kRelSz, ElfDynTag::kRelCount>(diagnostics, memory,
this->info(), relcount_) &&
rela_.template Finish<typename Elf::Rela, &Info::set_rela, ElfDynTag::kRela,
ElfDynTag::kRelaSz, ElfDynTag::kRelaCount>(
diagnostics, memory, this->info(), relacount_);
}
private:
typename Base::SizedArray relr_, rel_, rela_, jmprel_;
typename Elf::size_type relcount_ = 0, relacount_ = 0;
std::optional<typename Elf::size_type> pltrel_;
};
// Deduction guide.
template <class Elf>
DynamicRelocationInfoObserver(RelocationInfo<Elf>& info) -> DynamicRelocationInfoObserver<Elf>;
// This is an observer to fill in an elfldltl::SymbolInfo<Elf> object.
// Its constructor takes (elfldltl::SymbolInfo<Elf>&, Memory&).
template <class Elf>
class DynamicSymbolInfoObserver;
// This is just a shorthand to avoid repeating the long list of parameters.
template <class Elf>
using DynamicSymbolInfoObserverBase =
DynamicInfoObserver<DynamicSymbolInfoObserver<Elf>, SymbolInfo<Elf>, Elf, ElfDynTag::kSymTab,
ElfDynTag::kSymEnt, ElfDynTag::kHash, ElfDynTag::kGnuHash,
ElfDynTag::kStrTab, ElfDynTag::kStrSz, ElfDynTag::kSoname,
ElfDynTag::kFlags, ElfDynTag::kFlags1>;
template <class Elf>
class DynamicSymbolInfoObserver : public DynamicSymbolInfoObserverBase<Elf> {
public:
using Base = DynamicSymbolInfoObserverBase<Elf>;
using Info = SymbolInfo<Elf>;
using size_type = typename Elf::size_type;
using Base::Base;
// There's one Observe overload for each dynamic tag the observer handles.
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kStrTab> tag, size_type val) {
strtab_.set_address(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kStrSz> tag, size_type val) {
strtab_.set_size_bytes(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kSymTab> tag, size_type val) {
if (val % sizeof(size_type)) [[unlikely]] {
// Mark that it was present so we don't diagnose a second error.
// But don't use a bogus value so no misaligned fetches will be tried.
symtab_ = 0;
return diagnostics.FormatError("DT_SYMTAB has misaligned address"sv);
}
symtab_ = val;
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kHash> tag, size_type val) {
if (val % sizeof(uint32_t)) [[unlikely]] {
return diagnostics.FormatError("DT_HASH has misaligned address"sv);
}
hash_ = val;
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kGnuHash> tag, size_type val) {
if (val % sizeof(size_type)) [[unlikely]] {
return diagnostics.FormatError("DT_GNU_HASH has misaligned address"sv);
}
gnu_hash_ = val;
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kSoname> tag, size_type val) {
soname_ = val;
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kSymEnt> tag, size_type val) {
return val == sizeof(typename Elf::Sym) ||
diagnostics.FormatError("incorrect DT_SYMENT value "sv, val);
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kFlags> tag, size_type val) {
this->info().set_flags(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory,
DynamicTagMatch<ElfDynTag::kFlags1> tag, size_type val) {
this->info().set_flags1(val);
return true;
}
// Check and finalize what's been observed.
template <class DiagnosticsType, class Memory>
constexpr bool Finish(DiagnosticsType& diagnostics, Memory& memory) {
if (hash_) {
if (auto table = memory.template ReadArray<typename Elf::Word>(*hash_)) {
this->info().set_compat_hash(*table);
} else {
return false;
}
}
if (gnu_hash_) {
if (auto table = memory.template ReadArray<typename Elf::Addr>(*gnu_hash_)) {
this->info().set_gnu_hash(*table);
} else {
return false;
}
}
if (!symtab_) {
return !strtab_ || diagnostics.FormatError("DT_STRTAB with no DT_SYMTAB"sv);
}
if (auto symtab = memory.template ReadArray<typename Elf::Sym>(*symtab_)) {
this->info().set_symtab(*symtab);
} else {
return false;
}
if (!strtab_.template Finish<char, &Info::set_strtab_as_span, ElfDynTag::kStrTab,
ElfDynTag::kStrSz>(diagnostics, memory, this->info())) {
return false;
}
if (soname_) {
this->info().set_soname(*soname_);
if (this->info().soname().empty()) [[unlikely]] {
return diagnostics.FormatError("DT_SONAME does not fit in DT_STRTAB"sv);
}
}
return true;
}
private:
typename Base::SizedArray strtab_;
std::optional<typename Elf::size_type> symtab_, hash_, gnu_hash_, soname_;
};
// Deduction guide.
template <class Elf>
DynamicSymbolInfoObserver(SymbolInfo<Elf>& info) -> DynamicSymbolInfoObserver<Elf>;
// These observers fill the same simple result structure.
// Their constructors take (elfldltl::InitFiniInfo<Elf>&, Memory&).
template <class Elf>
class DynamicInitObserver;
template <class Elf>
class DynamicFiniObserver;
template <class Elf, ElfDynTag Array, ElfDynTag ArraySz, ElfDynTag Legacy>
class DynamicInitFiniObserver;
// This is just a shorthand to avoid repeating the long list of parameters.
template <class Elf, ElfDynTag Array, ElfDynTag ArraySz, ElfDynTag Legacy>
using DynamicInitFiniObserverBase =
DynamicInfoObserver<DynamicInitFiniObserver<Elf, Array, ArraySz, Legacy>, InitFiniInfo<Elf>,
Elf, Array, ArraySz, Legacy>;
template <class Elf, ElfDynTag Array, ElfDynTag ArraySz, ElfDynTag Legacy>
class DynamicInitFiniObserver : public DynamicInitFiniObserverBase<Elf, Array, ArraySz, Legacy> {
public:
using Base = DynamicInitFiniObserverBase<Elf, Array, ArraySz, Legacy>;
using Info = InitFiniInfo<Elf>;
using size_type = typename Elf::size_type;
using Base::Base;
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory, DynamicTagMatch<Array> tag,
size_type val) {
array_.set_address(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory, DynamicTagMatch<ArraySz> tag,
size_type val) {
array_.set_size_bytes(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType& diagnostics, Memory& memory, DynamicTagMatch<Legacy> tag,
typename Elf::Addr val) {
this->info().set_legacy(val);
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Finish(DiagnosticsType& diagnostics, Memory& memory) {
return array_.template Finish<typename Elf::Addr, &Info::set_array, //
Array, ArraySz>(diagnostics, memory, this->info());
}
private:
typename Base::SizedArray array_;
};
template <class Elf>
using DynamicInitObserverBase =
DynamicInitFiniObserver<Elf, ElfDynTag::kInitArray, ElfDynTag::kInitArraySz, ElfDynTag::kInit>;
template <class Elf>
using DynamicFiniObserverBase =
DynamicInitFiniObserver<Elf, ElfDynTag::kFiniArray, ElfDynTag::kFiniArraySz, ElfDynTag::kFini>;
template <class Elf>
class DynamicInitObserver : public DynamicInitObserverBase<Elf> {
public:
using DynamicInitObserverBase<Elf>::DynamicInitObserverBase;
};
template <class Elf>
class DynamicFiniObserver : public DynamicFiniObserverBase<Elf> {
public:
using DynamicFiniObserverBase<Elf>::DynamicFiniObserverBase;
};
// Deduction guides.
template <class Elf>
DynamicInitObserver(InitFiniInfo<Elf>& info) -> DynamicInitObserver<Elf>;
template <class Elf>
DynamicFiniObserver(InitFiniInfo<Elf>& info) -> DynamicFiniObserver<Elf>;
// This can be used for invoking a callback over every DT_NEEDED tag, passed
// as a string. Getting the string value requires that the SymbolInfo object
// be already set up. In practice this means that DynamicNeededObserver
// cannot safely be passed to DecodeDynamic with DynamicSymbolInfoObserver
// because there is no expectation that DT_STRTAB will appear before DT_NEEDED.
// It's the expectation that this would be used only once, just when loading
// dependencies, which is likely only going to happen after getting
// the symbol and relocation info of the current dso, so the cost of this
// restricition isn't high.
template <class Elf, class SymbolInfo, class Callback>
class DynamicNeededObserver
: public DynamicTagObserver<DynamicNeededObserver<Elf, SymbolInfo, Callback>,
ElfDynTag::kNeeded> {
using size_type = typename Elf::size_type;
public:
DynamicNeededObserver(const SymbolInfo& si, Callback callback) : si(si), callback(callback) {}
template <class DiagnosticsType, class Memory>
constexpr bool Observe(DiagnosticsType&, Memory&, DynamicTagMatch<ElfDynTag::kNeeded>,
size_type val) {
return callback(si.string(val));
}
template <class DiagnosticsType, class Memory>
constexpr bool Finish(DiagnosticsType&, Memory&) {
return true;
}
private:
const SymbolInfo& si;
__NO_UNIQUE_ADDRESS Callback callback;
};
// Deduction guides.
template <class Elf, class AbiTraits, template <class, class> class SymbolInfo, class Callback>
DynamicNeededObserver(const SymbolInfo<Elf, AbiTraits>& info,
Callback) -> DynamicNeededObserver<Elf, SymbolInfo<Elf, AbiTraits>, Callback>;
// This provides a trivial observer that simply counts how many occurrences of
// any of the given tags appear.
template <class Elf, ElfDynTag... Tag>
class DynamicTagCountObserver
: public DynamicTagObserver<DynamicTagCountObserver<Elf, Tag...>, Tag...> {
public:
explicit DynamicTagCountObserver(size_t& count) : count_(count) {}
template <class DiagnosticsType, class Memory, ElfDynTag Match>
constexpr bool Observe(DiagnosticsType&, Memory&, DynamicTagMatch<Match>,
typename Elf::size_type val) {
static_assert(((Match == Tag) || ...));
++count_;
return true;
}
template <class DiagnosticsType, class Memory>
constexpr bool Finish(DiagnosticsType&, Memory&) {
return true;
}
private:
size_t& count_;
};
// An observer that will match the given tag and add its value to the provided
// container. This observer will call the `push_back` method from the
// container.h API with `Elf::size_type` values onto the container.
template <class Elf, ElfDynTag Tag, class Container, const std::string_view& ErrorString>
class DynamicValueCollectionObserver
: public DynamicTagObserver<DynamicValueCollectionObserver<Elf, Tag, Container, ErrorString>,
Tag> {
public:
explicit DynamicValueCollectionObserver(Container& values) : values_(values) {}
template <class DiagnosticsType, class Memory, ElfDynTag Match>
constexpr bool Observe(DiagnosticsType& diag, Memory&, DynamicTagMatch<Match>,
typename Elf::size_type val) {
static_assert(Match == Tag);
return values_.push_back(diag, ErrorString, val);
}
template <class DiagnosticsType, class Memory>
constexpr bool Finish(DiagnosticsType&, Memory&) {
return true;
}
private:
Container& values_;
};
} // namespace elfldltl
#endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_DYNAMIC_H_