Path: blob/main/contrib/llvm-project/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h
39654 views
//===-- DYLDRendezvous.h ----------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#ifndef LLDB_SOURCE_PLUGINS_DYNAMICLOADER_POSIX_DYLD_DYLDRENDEZVOUS_H9#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_POSIX_DYLD_DYLDRENDEZVOUS_H1011#include <list>12#include <string>1314#include "lldb/Utility/FileSpec.h"15#include "lldb/lldb-defines.h"16#include "lldb/lldb-types.h"1718#include "lldb/Core/LoadedModuleInfoList.h"1920using lldb_private::LoadedModuleInfoList;2122namespace lldb_private {23class Log;24class Process;25}2627/// \class DYLDRendezvous28/// Interface to the runtime linker.29///30/// A structure is present in a processes memory space which is updated by the31/// dynamic linker each time a module is loaded or unloaded. This class32/// provides an interface to this structure and maintains a consistent33/// snapshot of the currently loaded modules.34///35/// In the dynamic loader sources, this structure has a type of "r_debug" and36/// the name of the structure us "_r_debug". The structure looks like:37///38/// struct r_debug {39/// // Version number for this protocol.40/// int r_version;41/// // Head of the chain of loaded objects.42/// struct link_map *r_map;43/// // The address the debugger should set a breakpoint at in order to get44/// // notified when shared libraries are added or removed45/// uintptr_t r_brk;46/// // This state value describes the mapping change taking place when the47/// // 'r_brk' address is called.48/// enum {49/// RT_CONSISTENT, // Mapping change is complete.50/// RT_ADD, // Beginning to add a new object.51/// RT_DELETE, // Beginning to remove an object mapping.52/// } r_state;53/// // Base address the linker is loaded at.54/// uintptr_t r_ldbase;55/// };56///57/// The dynamic linker then defines a global variable using this type named58/// "_r_debug":59///60/// r_debug _r_debug;61///62/// The DYLDRendezvous class defines a local version of this structure named63/// DYLDRendezvous::Rendezvous. See the definition inside the class definition64/// for DYLDRendezvous.65///66/// This structure can be located by looking through the .dynamic section in67/// the main executable and finding the DT_DEBUG tag entry. This value starts68/// out with a value of zero when the program first is initially loaded, but69/// the address of the "_r_debug" structure from ld.so is filled in by the70/// dynamic loader during program initialization code in ld.so prior to loading71/// or unloading and shared libraries.72///73/// The dynamic loader will update this structure as shared libraries are74/// loaded and will call a specific function that LLDB knows to set a75/// breakpoint on (from _r_debug.r_brk) so LLDB will find out when shared76/// libraries are loaded or unloaded. Each time this breakpoint is hit, LLDB77/// looks at the contents of this structure and the contents tell LLDB what78/// needs to be done.79///80/// Currently we expect the "state" in this structure to change as things81/// happen.82///83/// When any shared libraries are loaded the following happens:84/// - _r_debug.r_map is updated with the new shared libraries. This is a85/// doubly linked list of "link_map *" entries.86/// - _r_debug.r_state is set to RT_ADD and the debugger notification87/// function is called notifying the debugger that shared libraries are88/// about to be added, but are not yet ready for use.89/// - Once the the shared libraries are fully loaded, _r_debug.r_state is set90/// to RT_CONSISTENT and the debugger notification function is called again91/// notifying the debugger that shared libraries are ready for use.92/// DYLDRendezvous must remember that the previous state was RT_ADD when it93/// receives a RT_CONSISTENT in order to know to add libraries94///95/// When any shared libraries are unloaded the following happens:96/// - _r_debug.r_map is updated and the unloaded libraries are removed.97/// - _r_debug.r_state is set to RT_DELETE and the debugger notification98/// function is called notifying the debugger that shared libraries are99/// about to be removed.100/// - Once the the shared libraries are removed _r_debug.r_state is set to101/// RT_CONSISTENT and the debugger notification function is called again102/// notifying the debugger that shared libraries have been removed.103/// DYLDRendezvous must remember that the previous state was RT_DELETE when104/// it receives a RT_CONSISTENT in order to know to remove libraries105///106class DYLDRendezvous {107108// This structure is used to hold the contents of the debug rendezvous109// information (struct r_debug) as found in the inferiors memory. Note that110// the layout of this struct is not binary compatible, it is simply large111// enough to hold the information on both 32 and 64 bit platforms.112struct Rendezvous {113uint64_t version = 0;114lldb::addr_t map_addr = 0;115lldb::addr_t brk = 0;116uint64_t state = 0;117lldb::addr_t ldbase = 0;118119Rendezvous() = default;120121void DumpToLog(lldb_private::Log *log, const char *label);122};123124/// Locates the address of the rendezvous structure. It updates125/// m_executable_interpreter if address is extracted from _r_debug.126///127/// \returns address on success and LLDB_INVALID_ADDRESS on failure.128lldb::addr_t ResolveRendezvousAddress();129130public:131// Various metadata supplied by the inferior's threading library to describe132// the per-thread state.133struct ThreadInfo {134bool valid; // whether we read valid metadata135uint32_t dtv_offset; // offset of DTV pointer within pthread136uint32_t dtv_slot_size; // size of one DTV slot137uint32_t modid_offset; // offset of module ID within link_map138uint32_t tls_offset; // offset of TLS pointer within DTV slot139};140141DYLDRendezvous(lldb_private::Process *process);142143/// Update the cached executable path.144void UpdateExecutablePath();145146/// Update the internal snapshot of runtime linker rendezvous and recompute147/// the currently loaded modules.148///149/// This method should be called once one start up, then once each time the150/// runtime linker enters the function given by GetBreakAddress().151///152/// \returns true on success and false on failure.153///154/// \see GetBreakAddress().155bool Resolve();156157/// \returns true if this rendezvous has been located in the inferiors158/// address space and false otherwise.159bool IsValid();160161/// \returns the address of the rendezvous structure in the inferiors162/// address space.163lldb::addr_t GetRendezvousAddress() const { return m_rendezvous_addr; }164165/// \returns the version of the rendezvous protocol being used.166uint64_t GetVersion() const { return m_current.version; }167168/// \returns address in the inferiors address space containing the linked169/// list of shared object descriptors.170lldb::addr_t GetLinkMapAddress() const { return m_current.map_addr; }171172/// A breakpoint should be set at this address and Resolve called on each173/// hit.174///175/// \returns the address of a function called by the runtime linker each176/// time a module is loaded/unloaded, or about to be loaded/unloaded.177///178/// \see Resolve()179lldb::addr_t GetBreakAddress() const { return m_current.brk; }180181/// Returns the current state of the rendezvous structure.182uint64_t GetState() const { return m_current.state; }183184/// \returns the base address of the runtime linker in the inferiors address185/// space.186lldb::addr_t GetLDBase() const { return m_current.ldbase; }187188/// \returns the thread layout metadata from the inferiors thread library.189const ThreadInfo &GetThreadInfo();190191/// \returns true if modules have been loaded into the inferior since the192/// last call to Resolve().193bool ModulesDidLoad() const { return !m_added_soentries.empty(); }194195/// \returns true if modules have been unloaded from the inferior since the196/// last call to Resolve().197bool ModulesDidUnload() const { return !m_removed_soentries.empty(); }198199void DumpToLog(lldb_private::Log *log) const;200201/// Constants describing the state of the rendezvous.202///203/// These values are defined to match the r_debug.r_state enum from the204/// actual dynamic loader sources.205///206/// \see GetState().207enum RendezvousState {208eConsistent, // RT_CONSISTENT209eAdd, // RT_ADD210eDelete // RT_DELETE211};212213/// Structure representing the shared objects currently loaded into the214/// inferior process.215///216/// This object is a rough analogue to the struct link_map object which217/// actually lives in the inferiors memory.218struct SOEntry {219lldb::addr_t link_addr; ///< Address of this link_map.220lldb::addr_t base_addr; ///< Base address of the loaded object.221lldb::addr_t path_addr; ///< String naming the shared object.222lldb::addr_t dyn_addr; ///< Dynamic section of shared object.223lldb::addr_t next; ///< Address of next so_entry.224lldb::addr_t prev; ///< Address of previous so_entry.225lldb_private::FileSpec file_spec; ///< File spec of shared object.226227SOEntry() { clear(); }228229bool operator==(const SOEntry &entry) {230return file_spec == entry.file_spec;231}232233void clear() {234link_addr = 0;235base_addr = 0;236path_addr = 0;237dyn_addr = 0;238next = 0;239prev = 0;240file_spec.Clear();241}242};243244protected:245typedef std::list<SOEntry> SOEntryList;246247public:248typedef SOEntryList::const_iterator iterator;249250/// Iterators over all currently loaded modules.251iterator begin() const { return m_soentries.begin(); }252iterator end() const { return m_soentries.end(); }253254/// Iterators over all modules loaded into the inferior since the last call255/// to Resolve().256iterator loaded_begin() const { return m_added_soentries.begin(); }257iterator loaded_end() const { return m_added_soentries.end(); }258259/// Iterators over all modules unloaded from the inferior since the last260/// call to Resolve().261iterator unloaded_begin() const { return m_removed_soentries.begin(); }262iterator unloaded_end() const { return m_removed_soentries.end(); }263264protected:265lldb_private::Process *m_process;266267// Cached copy of executable file spec268lldb_private::FileSpec m_exe_file_spec;269270/// Location of the r_debug structure in the inferiors address space.271lldb::addr_t m_rendezvous_addr;272273// True if the main program is the dynamic linker/loader/program interpreter.274bool m_executable_interpreter;275276/// Current and previous snapshots of the rendezvous structure.277Rendezvous m_current;278Rendezvous m_previous;279280/// List of currently loaded SO modules281LoadedModuleInfoList m_loaded_modules;282283/// List of SOEntry objects corresponding to the current link map state.284SOEntryList m_soentries;285286/// List of SOEntry's added to the link map since the last call to287/// Resolve().288SOEntryList m_added_soentries;289290/// List of SOEntry's removed from the link map since the last call to291/// Resolve().292SOEntryList m_removed_soentries;293294/// Threading metadata read from the inferior.295ThreadInfo m_thread_info;296297/// Reads an unsigned integer of \p size bytes from the inferior's address298/// space starting at \p addr.299///300/// \returns addr + size if the read was successful and false otherwise.301lldb::addr_t ReadWord(lldb::addr_t addr, uint64_t *dst, size_t size);302303/// Reads an address from the inferior's address space starting at \p addr.304///305/// \returns addr + target address size if the read was successful and306/// 0 otherwise.307lldb::addr_t ReadPointer(lldb::addr_t addr, lldb::addr_t *dst);308309/// Reads a null-terminated C string from the memory location starting at @p310/// addr.311std::string ReadStringFromMemory(lldb::addr_t addr);312313/// Reads an SOEntry starting at \p addr.314bool ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry);315316/// Updates the current set of SOEntries, the set of added entries, and the317/// set of removed entries.318bool UpdateSOEntries();319320/// Same as UpdateSOEntries but it gets the list of loaded modules from the321/// remote debug server (faster when supported).322bool UpdateSOEntriesFromRemote();323324bool FillSOEntryFromModuleInfo(325LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry);326327bool SaveSOEntriesFromRemote(const LoadedModuleInfoList &module_list);328329bool AddSOEntriesFromRemote(const LoadedModuleInfoList &module_list);330331bool RemoveSOEntriesFromRemote(const LoadedModuleInfoList &module_list);332333bool AddSOEntries();334335bool RemoveSOEntries();336337void UpdateBaseAddrIfNecessary(SOEntry &entry, std::string const &file_path);338339void UpdateFileSpecIfNecessary(SOEntry &entry);340341bool SOEntryIsMainExecutable(const SOEntry &entry);342343/// Reads the current list of shared objects according to the link map344/// supplied by the runtime linker.345bool TakeSnapshot(SOEntryList &entry_list);346347enum PThreadField { eSize, eNElem, eOffset };348349bool FindMetadata(const char *name, PThreadField field, uint32_t &value);350351bool IsCoreFile() const;352353enum RendezvousAction {354eNoAction,355eTakeSnapshot,356eAddModules,357eRemoveModules358};359360static const char *StateToCStr(RendezvousState state);361static const char *ActionToCStr(RendezvousAction action);362363/// Returns the current action to be taken given the current and previous364/// state365RendezvousAction GetAction() const;366};367368#endif369370371