blob: 7191abd089d417ec0856d7b1fcfa392c6e2553c9 [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_INIT_FINI_H_
#define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_INIT_FINI_H_
#include <lib/stdcompat/span.h>
#include <optional>
#include "abi-span.h"
namespace elfldltl {
// A module initializer or finalizer function has type void().
using InitFiniFunction = void();
// This represents the information about either initializers or finalizers for
// one ELF module. Two separate InitFiniInfo objects are used for a module's
// initializers and finalizers.
//
// This is normally populated by a call to elfldltl::DecodeDynamic using an
// elfldltl::DynamicInitObserver or elfldltl::DynamicFiniObserver observer.
//
// The VisitInit or VisitFini methods provide general ordered access to the
// function addresses in each list. The correct method should be used for each
// kind of list to get the appropriate ordering of elements.
//
// The CallInit and CallFini methods directly call each function in order, for
// immediate in-process uses.
template <class Elf, class AbiTraits = LocalAbiTraits>
struct InitFiniInfo {
public:
using Addr = typename Elf::Addr;
using size_type = typename Elf::size_type;
// An array of function pointers, in the .init_array or .fini_array section,
// which is normally part of the RELRO segment. So the pointers here are
// unrelocated in the file, but dynamic relocation records apply simple
// fixup. As this points directly into the load image in the Memory object,
// if that image is being relocated in place, then these values will be
// absolute function pointers after relocation. If the original file data
// (or the load image before relocation) is being read, the these addresses
// need the load bias added.
constexpr cpp20::span<const Addr> array() const { return array_; }
// A single function pointer, from the legacy DT_INIT or DT_FINI entry. This
// is not contiguous with the array and is stored separately in the ELF
// headers where no relocation records apply. So this address always needs
// the load bias added to yield a runtime function pointer.
constexpr std::optional<Addr> legacy() const {
if (legacy_ != 0) {
return legacy_;
}
return std::nullopt;
}
constexpr InitFiniInfo& set_array(cpp20::span<const Addr> array) {
array_ = array;
return *this;
}
constexpr InitFiniInfo& set_legacy(Addr legacy) {
legacy_ = legacy;
return *this;
}
// Return the number of function pointers present.
constexpr size_t size() const { return array_.size() + (legacy_ != 0 ? 1 : 0); }
constexpr bool empty() const { return size() == 0; }
// Call init(Addr, bool) exactly size() times. The flag in each callback is
// true iff Addr has already been relocated. The argument flag should be
// true iff relocations affecting RELRO data have already been applied.
template <typename T>
constexpr void VisitInit(T&& init, bool relocated) const {
if (legacy_ != 0) {
init(legacy_, false);
}
for (const auto& addr : array_) {
init(addr, relocated);
}
}
// Same as VisitInit, but in the reverse order.
template <typename T>
constexpr void VisitFini(T&& fini, bool relocated) const {
for (auto it = array_.rbegin(); it != array_.rend(); ++it) {
fini(*it, relocated);
}
if (legacy_ != 0) {
fini(legacy_, false);
}
}
// This returns a callback suitable to pass to VisitInit or VisitFini to
// directly call the functions right here.
static constexpr auto RelocatedCall(size_type bias) {
return [bias](const Addr& addr, bool relocated) {
uintptr_t fnaddr = addr;
if (!relocated) {
fnaddr += bias;
}
auto fn = reinterpret_cast<InitFiniFunction*>(fnaddr);
(*fn)();
};
}
// Call all the functions in initialization order.
void CallInit(size_type bias, bool relocated = true) const {
VisitInit(RelocatedCall(bias), relocated);
}
// Call all the functions in finalization order.
void CallFini(size_type bias, bool relocated = true) const {
VisitFini(RelocatedCall(bias), relocated);
}
private:
AbiSpan<const Addr, cpp20::dynamic_extent, Elf, AbiTraits> array_;
Addr legacy_ = 0;
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 = InitFiniInfo<Elf, LocalAbiTraits>;
template <template <class...> class Template>
using AbiBases = Template<>;
template <template <auto...> class Template>
using AbiMembers = Template<&InitFiniInfo::array_, &InitFiniInfo::legacy_>;
};
} // namespace elfldltl
#endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_INIT_FINI_H_