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 | |