| 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-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLDB_SOURCE_PLUGINS_DYNAMICLOADER_POSIX_DYLD_DYLDRENDEZVOUS_H |
| 10 | #define LLDB_SOURCE_PLUGINS_DYNAMICLOADER_POSIX_DYLD_DYLDRENDEZVOUS_H |
| 11 | |
| 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 | |
| 21 | using lldb_private::LoadedModuleInfoList; |
| 22 | |
| 23 | namespace lldb_private { |
| 24 | class Log; |
| 25 | class Process; |
| 26 | } |
| 27 | |
| 28 | /// \class DYLDRendezvous |
| 29 | /// Interface to the runtime linker. |
| 30 | /// |
| 31 | /// A structure is present in a processes memory space which is updated by the |
| 32 | /// dynamic linker each time a module is loaded or unloaded. This class |
| 33 | /// provides an interface to this structure and maintains a consistent |
| 34 | /// snapshot of the currently loaded modules. |
| 35 | /// |
| 36 | /// In the dynamic loader sources, this structure has a type of "r_debug" and |
| 37 | /// 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 get |
| 45 | /// // notified when shared libraries are added or removed |
| 46 | /// uintptr_t r_brk; |
| 47 | /// // This state value describes the mapping change taking place when the |
| 48 | /// // '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 named |
| 59 | /// "_r_debug": |
| 60 | /// |
| 61 | /// r_debug _r_debug; |
| 62 | /// |
| 63 | /// The DYLDRendezvous class defines a local version of this structure named |
| 64 | /// DYLDRendezvous::Rendezvous. See the definition inside the class definition |
| 65 | /// for DYLDRendezvous. |
| 66 | /// |
| 67 | /// This structure can be located by looking through the .dynamic section in |
| 68 | /// the main executable and finding the DT_DEBUG tag entry. This value starts |
| 69 | /// out with a value of zero when the program first is initially loaded, but |
| 70 | /// the address of the "_r_debug" structure from ld.so is filled in by the |
| 71 | /// dynamic loader during program initialization code in ld.so prior to loading |
| 72 | /// or unloading and shared libraries. |
| 73 | /// |
| 74 | /// The dynamic loader will update this structure as shared libraries are |
| 75 | /// loaded and will call a specific function that LLDB knows to set a |
| 76 | /// breakpoint on (from _r_debug.r_brk) so LLDB will find out when shared |
| 77 | /// libraries are loaded or unloaded. Each time this breakpoint is hit, LLDB |
| 78 | /// looks at the contents of this structure and the contents tell LLDB what |
| 79 | /// needs to be done. |
| 80 | /// |
| 81 | /// Currently we expect the "state" in this structure to change as things |
| 82 | /// 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 a |
| 86 | /// doubly linked list of "link_map *" entries. |
| 87 | /// - _r_debug.r_state is set to RT_ADD and the debugger notification |
| 88 | /// function is called notifying the debugger that shared libraries are |
| 89 | /// 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 set |
| 91 | /// to RT_CONSISTENT and the debugger notification function is called again |
| 92 | /// notifying the debugger that shared libraries are ready for use. |
| 93 | /// DYLDRendezvous must remember that the previous state was RT_ADD when it |
| 94 | /// receives a RT_CONSISTENT in order to know to add libraries |
| 95 | /// |
| 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 notification |
| 99 | /// function is called notifying the debugger that shared libraries are |
| 100 | /// about to be removed. |
| 101 | /// - Once the the shared libraries are removed _r_debug.r_state is set to |
| 102 | /// RT_CONSISTENT and the debugger notification function is called again |
| 103 | /// notifying the debugger that shared libraries have been removed. |
| 104 | /// DYLDRendezvous must remember that the previous state was RT_DELETE when |
| 105 | /// it receives a RT_CONSISTENT in order to know to remove libraries |
| 106 | /// |
| 107 | class DYLDRendezvous { |
| 108 | |
| 109 | // This structure is used to hold the contents of the debug rendezvous |
| 110 | // information (struct r_debug) as found in the inferiors memory. Note that |
| 111 | // the layout of this struct is not binary compatible, it is simply large |
| 112 | // 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 updates |
| 126 | /// 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 | |
| 131 | public: |
| 132 | // Various metadata supplied by the inferior's threading library to describe |
| 133 | // the per-thread state. |
| 134 | struct ThreadInfo { |
| 135 | bool valid; // whether we read valid metadata |
| 136 | uint32_t dtv_offset; // offset of DTV pointer within pthread |
| 137 | uint32_t dtv_slot_size; // size of one DTV slot |
| 138 | uint32_t modid_offset; // offset of module ID within link_map |
| 139 | uint32_t tls_offset; // offset of TLS pointer within DTV slot |
| 140 | }; |
| 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 recompute |
| 148 | /// the currently loaded modules. |
| 149 | /// |
| 150 | /// This method should be called once one start up, then once each time the |
| 151 | /// 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 inferiors |
| 159 | /// address space and false otherwise. |
| 160 | bool IsValid(); |
| 161 | |
| 162 | /// \returns the address of the rendezvous structure in the inferiors |
| 163 | /// 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 linked |
| 170 | /// 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 each |
| 174 | /// hit. |
| 175 | /// |
| 176 | /// \returns the address of a function called by the runtime linker each |
| 177 | /// 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 address |
| 186 | /// 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 the |
| 193 | /// 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 the |
| 197 | /// 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 the |
| 205 | /// actual dynamic loader sources. |
| 206 | /// |
| 207 | /// \see GetState(). |
| 208 | enum RendezvousState { |
| 209 | eConsistent, // RT_CONSISTENT |
| 210 | eAdd, // RT_ADD |
| 211 | eDelete // RT_DELETE |
| 212 | }; |
| 213 | |
| 214 | /// Structure representing the shared objects currently loaded into the |
| 215 | /// inferior process. |
| 216 | /// |
| 217 | /// This object is a rough analogue to the struct link_map object which |
| 218 | /// 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 | |
| 245 | protected: |
| 246 | typedef std::list<SOEntry> SOEntryList; |
| 247 | |
| 248 | public: |
| 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 call |
| 256 | /// 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 last |
| 261 | /// call to Resolve(). |
| 262 | iterator unloaded_begin() const { return m_removed_soentries.begin(); } |
| 263 | iterator unloaded_end() const { return m_removed_soentries.end(); } |
| 264 | |
| 265 | protected: |
| 266 | lldb_private::Process *m_process; |
| 267 | |
| 268 | // Cached copy of executable file spec |
| 269 | 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 modules |
| 282 | 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 to |
| 288 | /// Resolve(). |
| 289 | SOEntryList m_added_soentries; |
| 290 | |
| 291 | /// List of SOEntry's removed from the link map since the last call to |
| 292 | /// 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 address |
| 299 | /// 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 and |
| 307 | /// 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 @p |
| 311 | /// 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 the |
| 318 | /// set of removed entries. |
| 319 | bool UpdateSOEntries(); |
| 320 | |
| 321 | /// Same as UpdateSOEntries but it gets the list of loaded modules from the |
| 322 | /// 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 map |
| 345 | /// 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 | eRemoveModules |
| 359 | }; |
| 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 previous |
| 365 | /// state |
| 366 | RendezvousAction GetAction() const; |
| 367 | }; |
| 368 | |
| 369 | #endif |
| 370 | |