blob: 0e0be6efe45b3a9ae2d5badde0b52a32755ea178 [file] [log] [blame]
// Copyright 2023 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_SVR4_ABI_H_
#define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_SVR4_ABI_H_
#include "abi-ptr.h"
#include "layout.h"
namespace elfldltl {
// These type layouts are not formally parts of the ELF format, but rather
// de facto standard ABI types from the original SVR4 implementation that
// introduced ELF that have been kept compatible in other implementations
// historically. In SVR4 and other systems <link.h> declares these types.
// Expected value for RDebug::version field.
inline constexpr uint32_t kRDebugVersion = 1;
// Recognized values for RDebug::state field. See below.
enum class RDebugState : uint32_t {
// This indicates that no loading or unloading is in progress, so
// everything is in a consistent state.
kConsistent = 0,
// This indicates that a new module is about to be loaded. At the next
// stop when state is kConsistent, the map list can be expected to have
// an additional module (usually on the end of the list).
kAdd = 1,
// This indicates that a new module is about to be unloaded. At the next
// stop when state is kConsistent, the map list can be expected to have
// fewer modules.
kDelete = 2,
};
// This is the traditional `struct r_debug` that SVR4 and other systems declare
// in <link.h>. In the original scheme, the DT_DEBUG element in the PT_DYNAMIC
// (.dynamic) data would be updated to point to this in memory so that a
// debugger could examine that in memory to find it. However, debuggers also
// began looking for known symbol names like `_r_debug` in the dynamic linker
// to find this, so the de facto expectation is that it has that symbol. The
// scheme of modifying DT_DEBUG is not feasible when .dynamic is read-only, so
// it's not used on systems like Fuchsia with `-z rodynamic` on by default.
template <ElfClass Class, ElfData Data>
template <class AbiTraits>
struct Elf<Class, Data>::RDebug {
// A version number > 0. The kRDebugVersion indicates this layout.
// Higher version numbers would indicate a layout that's compatible with
// this but has additional fields.
Word version;
// The list of modules currently loaded. This is a doubly-linked list in
// load order, with a null prev at the beginning and a null next at the
// end. For the initial-exec set of modules, the main executable is
// first in the list and the remainder matches a breadth-first pre-order
// traversal of the DT_NEEDED graph.
AbiPtr<const LinkMap<AbiTraits>, Elf, AbiTraits> map;
// This is a PC location where a breakpoint can be set. When a dynamic
// linker does loading or unloading, it executes this PC both before and
// after the operation with different `.state` values. A debugger is
// expected to read out this address, set a breakpoint there, and then
// when that breakpoint fires to examine `.state` and `.map` to assess
// the new state of what modules are now loaded.
Addr brk;
// This is normally kConsistent. When a dynamic linker is loading new
// modules, it sets `.state` to kAdd before executing the `.brk` PC; when
// unloading modules, it sets `.state` to kRemove before executing the
// `.brk` PC. When the operation is finished, it sets `.state` back to
// kConsistent before executing the `.brk` PC for a second time.
EnumField<RDebugState, kSwap> state;
// This is the load bias of the dynamic linker itself.
Addr ldbase;
// <lib/ld/remote-abi-transcriber.h> introspection API.
using AbiLocal = RDebug<LocalAbiTraits>;
template <template <class...> class Template>
using AbiBases = Template<>;
template <template <auto...> class Template>
using AbiMembers =
Template<&RDebug::version, &RDebug::map, &RDebug::brk, &RDebug::state, &RDebug::ldbase>;
};
// This is the traditional `struct link_map` that SVR4 and other systems
// declare in <link.h>. One of these describes each module loaded, forming a
// doubly-linked list in load order: first the main executable, then its
// DT_NEEDED dependency graph, then modules loaded later at runtime.
template <ElfClass Class, ElfData Data>
template <class AbiTraits>
struct Elf<Class, Data>::LinkMap {
using ElfLayout = Elf;
// This is the load bias, meaning the difference between runtime addresses in
// this process and the link-timeaddress that they appear in this module's
// ELF metadata (program headers, symbol table, etc.). More precisely, it's
// the difference between the runtime address of the module's load image and
// the first PT_LOAD segment's p_vaddr rounded down to the runtime page size.
// In most ELF modules the latter is zero and so the load bias is usually
// equal to the load address; but this does not have to be so.
Addr addr;
// This points to a NUL-terminated C string. For the main executable, it's
// usually the empty string "". For others, it's the "name" of the module.
// What that means exactly depends on how the module was loaded and differs
// on different systems. On traditional POSIX systems, it's usually an
// absolute path name. On Fuchsia, it's just the name that was presented to
// the loader service protocol, usually just the plain DT_NEEDED string
// (which in turn usually matches the DT_SONAME string in the module itself).
AbiPtr<const char, Elf, AbiTraits> name;
// This is the runtime address of the PT_DYNAMIC segment (.dynamic) section
// of this module. The dynamic linker has already processed the data, so
// it's guaranteed that there is a proper DT_NULL element terminating the
// array, though there is no other indication here of the number of elements.
//
// Some dynamic linkers on some systems will apply the load bias to address
// values in .dynamic entries and update them in place, while others do not;
// on systems like Fuchsia where `-z rodynamic` is on by default, this points
// to read-only space that cannot have been modified to apply the load bias.
// A reasonable heuristic is to check whether an address value in an entry
// falls within the bounds of the load image based on its PT_LOAD headers and
// its load bias; if it does, it's probably already relocated and if not it's
// probably unrelocated.
AbiPtr<const Dyn, Elf, AbiTraits> ld;
// These members form a doubly-linked list. The `prev` of the first element
// in the list and the `next` of the last element in the list are both null.
// At startup, this lists the main executable and its dependencies in load
// order, which is also symbol resolution order for that set of modules
// (known as the "initial-exec set"). If more modules are loaded after
// startup, they are added on the end of the list and may be removed later
// from anywhere in the list after the initial-exec set, depending on the
// order of adding and removing modules and how they may share dependencies.
AbiPtr<LinkMap, Elf, AbiTraits> next, prev;
// <lib/ld/remote-abi-transcriber.h> introspection API.
using AbiLocal = LinkMap<LocalAbiTraits>;
template <template <class...> class Template>
using AbiBases = Template<>;
template <template <auto...> class Template>
using AbiMembers =
Template<&LinkMap::addr, &LinkMap::name, &LinkMap::ld, &LinkMap::next, &LinkMap::prev>;
};
} // namespace elfldltl
#endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_SVR4_ABI_H_