370 lines · c
1//===-- DYLDRendezvous.h ----------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLDB_SOURCE_PLUGINS_DYNAMICLOADER_POSIX_DYLD_DYLDRENDEZVOUS_H10#define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_POSIX_DYLD_DYLDRENDEZVOUS_H11 12#include <list>13#include <string>14 15#include "lldb/Utility/FileSpec.h"16#include "lldb/lldb-defines.h"17#include "lldb/lldb-types.h"18 19#include "lldb/Core/LoadedModuleInfoList.h"20 21using lldb_private::LoadedModuleInfoList;22 23namespace lldb_private {24class Log;25class Process;26}27 28/// \class DYLDRendezvous29/// Interface to the runtime linker.30///31/// A structure is present in a processes memory space which is updated by the32/// dynamic linker each time a module is loaded or unloaded. This class33/// provides an interface to this structure and maintains a consistent34/// snapshot of the currently loaded modules.35///36/// In the dynamic loader sources, this structure has a type of "r_debug" and37/// the name of the structure us "_r_debug". The structure looks like:38///39/// struct r_debug {40/// // Version number for this protocol.41/// int r_version;42/// // Head of the chain of loaded objects.43/// struct link_map *r_map;44/// // The address the debugger should set a breakpoint at in order to get45/// // notified when shared libraries are added or removed46/// uintptr_t r_brk;47/// // This state value describes the mapping change taking place when the48/// // 'r_brk' address is called.49/// enum {50/// RT_CONSISTENT, // Mapping change is complete.51/// RT_ADD, // Beginning to add a new object.52/// RT_DELETE, // Beginning to remove an object mapping.53/// } r_state;54/// // Base address the linker is loaded at.55/// uintptr_t r_ldbase;56/// };57///58/// The dynamic linker then defines a global variable using this type named59/// "_r_debug":60///61/// r_debug _r_debug;62///63/// The DYLDRendezvous class defines a local version of this structure named64/// DYLDRendezvous::Rendezvous. See the definition inside the class definition65/// for DYLDRendezvous.66///67/// This structure can be located by looking through the .dynamic section in68/// the main executable and finding the DT_DEBUG tag entry. This value starts69/// out with a value of zero when the program first is initially loaded, but70/// the address of the "_r_debug" structure from ld.so is filled in by the71/// dynamic loader during program initialization code in ld.so prior to loading72/// or unloading and shared libraries.73///74/// The dynamic loader will update this structure as shared libraries are75/// loaded and will call a specific function that LLDB knows to set a76/// breakpoint on (from _r_debug.r_brk) so LLDB will find out when shared77/// libraries are loaded or unloaded. Each time this breakpoint is hit, LLDB78/// looks at the contents of this structure and the contents tell LLDB what79/// needs to be done.80///81/// Currently we expect the "state" in this structure to change as things82/// happen.83///84/// When any shared libraries are loaded the following happens:85/// - _r_debug.r_map is updated with the new shared libraries. This is a86/// doubly linked list of "link_map *" entries.87/// - _r_debug.r_state is set to RT_ADD and the debugger notification88/// function is called notifying the debugger that shared libraries are89/// about to be added, but are not yet ready for use.90/// - Once the the shared libraries are fully loaded, _r_debug.r_state is set91/// to RT_CONSISTENT and the debugger notification function is called again92/// notifying the debugger that shared libraries are ready for use.93/// DYLDRendezvous must remember that the previous state was RT_ADD when it94/// receives a RT_CONSISTENT in order to know to add libraries95///96/// When any shared libraries are unloaded the following happens:97/// - _r_debug.r_map is updated and the unloaded libraries are removed.98/// - _r_debug.r_state is set to RT_DELETE and the debugger notification99/// function is called notifying the debugger that shared libraries are100/// about to be removed.101/// - Once the the shared libraries are removed _r_debug.r_state is set to102/// RT_CONSISTENT and the debugger notification function is called again103/// notifying the debugger that shared libraries have been removed.104/// DYLDRendezvous must remember that the previous state was RT_DELETE when105/// it receives a RT_CONSISTENT in order to know to remove libraries106///107class DYLDRendezvous {108 109 // This structure is used to hold the contents of the debug rendezvous110 // information (struct r_debug) as found in the inferiors memory. Note that111 // the layout of this struct is not binary compatible, it is simply large112 // enough to hold the information on both 32 and 64 bit platforms.113 struct Rendezvous {114 uint64_t version = 0;115 lldb::addr_t map_addr = 0;116 lldb::addr_t brk = 0;117 uint64_t state = 0;118 lldb::addr_t ldbase = 0;119 120 Rendezvous() = default;121 122 void DumpToLog(lldb_private::Log *log, const char *label);123 };124 125 /// Locates the address of the rendezvous structure. It updates126 /// m_executable_interpreter if address is extracted from _r_debug.127 ///128 /// \returns address on success and LLDB_INVALID_ADDRESS on failure.129 lldb::addr_t ResolveRendezvousAddress();130 131public:132 // Various metadata supplied by the inferior's threading library to describe133 // the per-thread state.134 struct ThreadInfo {135 bool valid; // whether we read valid metadata136 uint32_t dtv_offset; // offset of DTV pointer within pthread137 uint32_t dtv_slot_size; // size of one DTV slot138 uint32_t modid_offset; // offset of module ID within link_map139 uint32_t tls_offset; // offset of TLS pointer within DTV slot140 };141 142 DYLDRendezvous(lldb_private::Process *process);143 144 /// Update the cached executable path.145 void UpdateExecutablePath();146 147 /// Update the internal snapshot of runtime linker rendezvous and recompute148 /// the currently loaded modules.149 ///150 /// This method should be called once one start up, then once each time the151 /// runtime linker enters the function given by GetBreakAddress().152 ///153 /// \returns true on success and false on failure.154 ///155 /// \see GetBreakAddress().156 bool Resolve();157 158 /// \returns true if this rendezvous has been located in the inferiors159 /// address space and false otherwise.160 bool IsValid();161 162 /// \returns the address of the rendezvous structure in the inferiors163 /// address space.164 lldb::addr_t GetRendezvousAddress() const { return m_rendezvous_addr; }165 166 /// \returns the version of the rendezvous protocol being used.167 uint64_t GetVersion() const { return m_current.version; }168 169 /// \returns address in the inferiors address space containing the linked170 /// list of shared object descriptors.171 lldb::addr_t GetLinkMapAddress() const { return m_current.map_addr; }172 173 /// A breakpoint should be set at this address and Resolve called on each174 /// hit.175 ///176 /// \returns the address of a function called by the runtime linker each177 /// time a module is loaded/unloaded, or about to be loaded/unloaded.178 ///179 /// \see Resolve()180 lldb::addr_t GetBreakAddress() const { return m_current.brk; }181 182 /// Returns the current state of the rendezvous structure.183 uint64_t GetState() const { return m_current.state; }184 185 /// \returns the base address of the runtime linker in the inferiors address186 /// space.187 lldb::addr_t GetLDBase() const { return m_current.ldbase; }188 189 /// \returns the thread layout metadata from the inferiors thread library.190 const ThreadInfo &GetThreadInfo();191 192 /// \returns true if modules have been loaded into the inferior since the193 /// last call to Resolve().194 bool ModulesDidLoad() const { return !m_added_soentries.empty(); }195 196 /// \returns true if modules have been unloaded from the inferior since the197 /// last call to Resolve().198 bool ModulesDidUnload() const { return !m_removed_soentries.empty(); }199 200 void DumpToLog(lldb_private::Log *log) const;201 202 /// Constants describing the state of the rendezvous.203 ///204 /// These values are defined to match the r_debug.r_state enum from the205 /// actual dynamic loader sources.206 ///207 /// \see GetState().208 enum RendezvousState {209 eConsistent, // RT_CONSISTENT210 eAdd, // RT_ADD211 eDelete // RT_DELETE212 };213 214 /// Structure representing the shared objects currently loaded into the215 /// inferior process.216 ///217 /// This object is a rough analogue to the struct link_map object which218 /// actually lives in the inferiors memory.219 struct SOEntry {220 lldb::addr_t link_addr; ///< Address of this link_map.221 lldb::addr_t base_addr; ///< Base address of the loaded object.222 lldb::addr_t path_addr; ///< String naming the shared object.223 lldb::addr_t dyn_addr; ///< Dynamic section of shared object.224 lldb::addr_t next; ///< Address of next so_entry.225 lldb::addr_t prev; ///< Address of previous so_entry.226 lldb_private::FileSpec file_spec; ///< File spec of shared object.227 228 SOEntry() { clear(); }229 230 bool operator==(const SOEntry &entry) {231 return file_spec == entry.file_spec;232 }233 234 void clear() {235 link_addr = 0;236 base_addr = 0;237 path_addr = 0;238 dyn_addr = 0;239 next = 0;240 prev = 0;241 file_spec.Clear();242 }243 };244 245protected:246 typedef std::list<SOEntry> SOEntryList;247 248public:249 typedef SOEntryList::const_iterator iterator;250 251 /// Iterators over all currently loaded modules.252 iterator begin() const { return m_soentries.begin(); }253 iterator end() const { return m_soentries.end(); }254 255 /// Iterators over all modules loaded into the inferior since the last call256 /// to Resolve().257 iterator loaded_begin() const { return m_added_soentries.begin(); }258 iterator loaded_end() const { return m_added_soentries.end(); }259 260 /// Iterators over all modules unloaded from the inferior since the last261 /// call to Resolve().262 iterator unloaded_begin() const { return m_removed_soentries.begin(); }263 iterator unloaded_end() const { return m_removed_soentries.end(); }264 265protected:266 lldb_private::Process *m_process;267 268 // Cached copy of executable file spec269 lldb_private::FileSpec m_exe_file_spec;270 271 /// Location of the r_debug structure in the inferiors address space.272 lldb::addr_t m_rendezvous_addr;273 274 // True if the main program is the dynamic linker/loader/program interpreter.275 bool m_executable_interpreter;276 277 /// Current and previous snapshots of the rendezvous structure.278 Rendezvous m_current;279 Rendezvous m_previous;280 281 /// List of currently loaded SO modules282 LoadedModuleInfoList m_loaded_modules;283 284 /// List of SOEntry objects corresponding to the current link map state.285 SOEntryList m_soentries;286 287 /// List of SOEntry's added to the link map since the last call to288 /// Resolve().289 SOEntryList m_added_soentries;290 291 /// List of SOEntry's removed from the link map since the last call to292 /// Resolve().293 SOEntryList m_removed_soentries;294 295 /// Threading metadata read from the inferior.296 ThreadInfo m_thread_info;297 298 /// Reads an unsigned integer of \p size bytes from the inferior's address299 /// space starting at \p addr.300 ///301 /// \returns addr + size if the read was successful and false otherwise.302 lldb::addr_t ReadWord(lldb::addr_t addr, uint64_t *dst, size_t size);303 304 /// Reads an address from the inferior's address space starting at \p addr.305 ///306 /// \returns addr + target address size if the read was successful and307 /// 0 otherwise.308 lldb::addr_t ReadPointer(lldb::addr_t addr, lldb::addr_t *dst);309 310 /// Reads a null-terminated C string from the memory location starting at @p311 /// addr.312 std::string ReadStringFromMemory(lldb::addr_t addr);313 314 /// Reads an SOEntry starting at \p addr.315 bool ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry);316 317 /// Updates the current set of SOEntries, the set of added entries, and the318 /// set of removed entries.319 bool UpdateSOEntries();320 321 /// Same as UpdateSOEntries but it gets the list of loaded modules from the322 /// remote debug server (faster when supported).323 bool UpdateSOEntriesFromRemote();324 325 bool FillSOEntryFromModuleInfo(326 LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry);327 328 bool SaveSOEntriesFromRemote(const LoadedModuleInfoList &module_list);329 330 bool AddSOEntriesFromRemote(const LoadedModuleInfoList &module_list);331 332 bool RemoveSOEntriesFromRemote(const LoadedModuleInfoList &module_list);333 334 bool AddSOEntries();335 336 bool RemoveSOEntries();337 338 void UpdateBaseAddrIfNecessary(SOEntry &entry, std::string const &file_path);339 340 void UpdateFileSpecIfNecessary(SOEntry &entry);341 342 bool SOEntryIsMainExecutable(const SOEntry &entry);343 344 /// Reads the current list of shared objects according to the link map345 /// supplied by the runtime linker.346 bool TakeSnapshot(SOEntryList &entry_list);347 348 enum PThreadField { eSize, eNElem, eOffset };349 350 bool FindMetadata(const char *name, PThreadField field, uint32_t &value);351 352 bool IsCoreFile() const;353 354 enum RendezvousAction {355 eNoAction,356 eTakeSnapshot,357 eAddModules,358 eRemoveModules359 };360 361 static const char *StateToCStr(RendezvousState state);362 static const char *ActionToCStr(RendezvousAction action);363 364 /// Returns the current action to be taken given the current and previous365 /// state366 RendezvousAction GetAction() const;367};368 369#endif370