blob: 3afb708073d253a56fbce1b0b57202d5b9c2a4f6 [file] [log] [blame]
// Copyright 2024 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 LIB_C_DLFCN_DL_RUNTIME_MODULE_H_
#define LIB_C_DLFCN_DL_RUNTIME_MODULE_H_
// Avoid symbol conflict between <ld/abi/abi.h> and <link.h>
#pragma push_macro("_r_debug")
#undef _r_debug
#define _r_debug not_using_system_r_debug
#include <link.h>
#pragma pop_macro("_r_debug")
#include <lib/elfldltl/alloc-checker-container.h>
#include <lib/elfldltl/layout.h>
#include <lib/elfldltl/soname.h>
#include <lib/elfldltl/symbol.h>
#include <lib/ld/abi.h>
#include <lib/ld/dl-phdr-info.h>
#include <lib/ld/load.h> // For ld::AbiModule
#include <lib/ld/tls.h>
#include <cstring>
#include <fbl/alloc_checker.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/vector.h>
#include "diagnostics.h"
#include "tls-desc-resolver.h"
namespace dl {
using AbiModule = ld::AbiModule<>;
using Elf = elfldltl::Elf<>;
using Soname = elfldltl::Soname<>;
// Forward declaration; defined below.
class RuntimeModule;
// A list of unique "permanent" RuntimeModule data structures used to represent
// a loaded file in the system image.
using ModuleList = fbl::DoublyLinkedList<std::unique_ptr<RuntimeModule>, fbl::DefaultObjectTag,
fbl::SizeOrder::Constant>;
// Use an AllocCheckerContainer that supports fallible allocations; methods
// return a boolean value to signify allocation success or failure.
template <typename T>
using Vector = elfldltl::AllocCheckerContainer<fbl::Vector>::Container<T>;
// A list of valid non-owning references to RuntimeModules.
using ModuleRefList = Vector<const RuntimeModule*>;
// These are helpers to allow specific ways to access an element.
struct DerefElement {
constexpr decltype(auto) operator()(auto&& ptr) const { return *ptr; }
};
struct AsConstElement {
constexpr decltype(auto) operator()(const auto& elt) const { return elt; }
};
constexpr auto DerefElementsView = std::views::transform(DerefElement());
constexpr auto AsConstElementsView = std::views::transform(AsConstElement());
// TODO(https://fxbug.dev/324136831): comment on how RuntimeModule relates to
// startup modules when the latter is supported.
// TODO(https://fxbug.dev/328135195): comment on the reference counting when
// that gets implemented.
// RuntimeModules are managed by the RuntimeDynamicLinker. A RuntimeModule is
// created for every unique ELF file object loaded either directly or indirectly
// as a dependency of another module. It holds the ld::abi::Abi<...>::Module
// data structure that describes the module in the passive ABI
// (see <sdk/lib/ld/module.h>).
// A RuntimeModule is created along with a LinkingSession::SessionModule (see
// linking-session.h) to represent the ELF file when it is first loaded by
// `dlopen`. Whereas a SessionModule is ephemeral and only lives as long as the
// the LinkingSession in a `dlopen` call, the RuntimeModule is a "permanent"
// data structure that is kept alive in the RuntimeDynamicLinker's `modules_`
// list until it is unloaded.
// While this is an internal API, a RuntimeModule* is the void* handle returned
// by the public <dlfcn.h> API.
class RuntimeModule : public fbl::DoublyLinkedListable<std::unique_ptr<RuntimeModule>> {
public:
using Addr = Elf::Addr;
using SymbolInfo = elfldltl::SymbolInfo<Elf>;
using TlsModule = ld::abi::Abi<>::TlsModule;
using size_type = Elf::size_type;
// Not copyable, but movable.
RuntimeModule(const RuntimeModule&) = delete;
RuntimeModule(RuntimeModule&&) = default;
// See unmap-[posix|zircon].cc for the dtor. On destruction, the module's load
// image is unmapped per the semantics of the OS implementation.
~RuntimeModule();
// The name of the runtime module is set to the filename passed to dlopen() to
// create the runtime module. This is usually the same as the DT_SONAME of the
// AbiModule, but that is not guaranteed. When performing an equality check,
// match against both possible name values.
constexpr bool operator==(const Soname& other) const {
return other == name() || other == abi_module_.soname;
}
constexpr const Soname& name() const { return name_; }
// Translate a void * pointer, as when passed to public-facing <dlfcn.h> APIs,
// into a RuntimeModule reference.
static constexpr const RuntimeModule& FromPtr(const void* ptr) {
return *static_cast<const RuntimeModule*>(ptr);
}
static constexpr RuntimeModule& FromPtr(void* ptr) { return *static_cast<RuntimeModule*>(ptr); }
// TODO(https://fxbug.dev/333920495): pass in the symbolizer_modid.
[[nodiscard]] static std::unique_ptr<RuntimeModule> Create(fbl::AllocChecker& ac, Soname soname) {
auto result = [&ac](std::unique_ptr<RuntimeModule> v) {
ac.arm(sizeof(RuntimeModule), v);
return v;
};
fbl::AllocChecker module_ac;
std::unique_ptr<RuntimeModule> module{new (module_ac) RuntimeModule};
if (!module_ac.check()) [[unlikely]] {
return result(nullptr);
}
fbl::AllocChecker name_ac;
size_t buf_sz = soname.size() + 1;
char* buf = new (name_ac) char[buf_sz];
if (!name_ac.check()) [[unlikely]] {
return result(nullptr);
}
soname.copy(buf, buf_sz);
module->name_ = Soname{buf};
return result(std::move(module));
}
// This is called if the RuntimeModule is created from a startup module (i.e.
// a module that was linked and loaded with the running program). This sets
// the abi, TLS, and direct_deps information onto the RuntimeModule.
void SetStartupModule(const AbiModule& abi_module, const ld::abi::Abi<>& abi) {
abi_module_ = abi_module;
no_delete_ = true;
initialized_ = true;
size_t tls_modid = abi_module_.tls_modid;
if (tls_modid > 0) {
const size_t idx = tls_modid - 1;
tls_module_ = abi.static_tls_modules[idx];
static_tls_bias_ = abi.static_tls_offsets[idx];
}
}
constexpr AbiModule& module() { return abi_module_; }
constexpr const AbiModule& module() const { return abi_module_; }
constexpr void set_tls_module(TlsModule tls_module) { tls_module_ = tls_module; }
constexpr const TlsModule& tls_module() const { return tls_module_; }
size_t vaddr_size() const { return abi_module_.vaddr_end - abi_module_.vaddr_start; }
// The following methods satisfy the Module template API for use with
// elfldltl::ResolverDefinition (see <lib/elfldltl/resolve.h>).
const SymbolInfo& symbol_info() const { return abi_module_.symbols; }
constexpr const ld::abi::Abi<>::LinkMap& link_map() const { return abi_module_.link_map; }
// The ld::abi::Abi<>::LinkMap embedded in the RuntimeModule's ABI module is
// compatible with `struct link_map` (see <lib/elfldltl/svr4-abi.h>). This
// will cast the module's LinkMap into the `struct link_map` type used by
// users of the public-facing <dlfcn.h> API.
const struct link_map* user_link_map() const {
return reinterpret_cast<const struct link_map*>(&link_map());
}
constexpr Addr load_bias() const { return abi_module_.link_map.addr; }
constexpr size_type tls_module_id() const { return abi_module_.tls_modid; }
constexpr bool uses_static_tls() const { return ld::ModuleUsesStaticTls(abi_module_); }
constexpr size_t static_tls_bias() const { return static_tls_bias_; }
constexpr fit::result<bool, const typename Elf::Sym*> Lookup( //
Diagnostics& diag, elfldltl::SymbolName& name) const {
return fit::ok(name.Lookup(symbol_info()));
}
// Return a view of `list` with all of its elements dereferenced and made
// constant (i.e. Vector<const RuntimeModule*> -> View<const RuntimeModule&>).
static constexpr auto const_derefed_element_view(const ModuleRefList& list) {
return AsConstElementsView(DerefElementsView(list));
}
// This is a list of module pointers to this module's DT_NEEDEDs, i.e. the
// first level of dependencies in this module's module tree. If this list is
// empty, the module does not have any dependencies.
constexpr ModuleRefList& direct_deps() { return direct_deps_; }
// Return a view of const references to each direct_deps module.
constexpr auto GetDirectDeps() const { return const_derefed_element_view(direct_deps_); }
// This is the breadth-first ordered list of module references, representing
// this module's tree of modules. A reference to this module (the root) is
// always the first in this list. The other modules in this list are
// non-owning references to modules that were explicitly linked with this
// module; global modules that may have been used for relocations, but are not
// a DT_NEEDED of any dependency, are not included in this list.
// This list is set when dlopen() is called on this module.
constexpr auto module_tree() const {
// RuntimeModule::ReifyModuleTree should have ben called before any callers
// call this accessor.
assert(!module_tree_.is_empty());
return const_derefed_element_view(module_tree_);
}
// Constructs this module's `module_tree` if it has not been set yet.
bool ReifyModuleTree(Diagnostics& diag);
// Whether this module is a global module: either the module was loaded at
// startup or loaded by dlopen() with the RTLD_GLOBAL flag.
constexpr bool is_global() const { return abi_module_.symbols_visible; }
constexpr void set_global() { abi_module_.symbols_visible = true; }
constexpr bool is_local() const { return !is_global(); }
// Prevent this module from being unloaded. Once this is set (usually by
// passing RTLD_NODELETE to dlopen), it cannot be unset. Setting this on a
// module implies that none of its dependencies can be unloaded either: since
// this module will live for the lifetime of the main program, so will its
// dependencies.
constexpr void set_no_delete() { no_delete_ = true; }
// This is the list of TlsDesc objects (see tls-desc-resolver.h) that was set
// during TLS relocation for this module. The RuntimeModule owns this list:
// it will get destroyed with the module.
constexpr TlsdescIndirectList& tls_desc_indirect_list() { return tls_desc_indirect_list_; }
// Run the init functions for this module (as the root module) and the init
// functions for all its dependencies.
void InitializeModuleTree();
// Run only this module's init functions. This is called if this module is
// loaded as a dependency to another module.
void Initialize();
// Construct the `dl_phdr_info` for this module.
dl_phdr_info MakeDlPhdrInfo(void* tls_data, ld::DlPhdrInfoCounts counts) const {
return ld::MakeDlPhdrInfo(module(), tls_data, counts);
}
private:
// A RuntimeModule can only be created with Module::Create...).
RuntimeModule() = default;
static void Unmap(uintptr_t vaddr, size_t len);
Soname name_;
AbiModule abi_module_;
TlsModule tls_module_;
ModuleRefList direct_deps_;
ModuleRefList module_tree_;
size_type static_tls_bias_ = 0;
bool no_delete_ = false;
bool initialized_ = false;
TlsdescIndirectList tls_desc_indirect_list_;
};
// This is the module tree view type returned by RuntimeModule::module_tree.
using ModuleTree = decltype(std::declval<RuntimeModule>().module_tree());
} // namespace dl
#endif // LIB_C_DLFCN_DL_RUNTIME_MODULE_H_