1 | //===-- DYLDRendezvous.cpp ------------------------------------------------===// |
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 | #include "lldb/Core/Module.h" |
10 | #include "lldb/Symbol/ObjectFile.h" |
11 | #include "lldb/Symbol/Symbol.h" |
12 | #include "lldb/Symbol/SymbolContext.h" |
13 | #include "lldb/Target/Platform.h" |
14 | #include "lldb/Target/Process.h" |
15 | #include "lldb/Target/Target.h" |
16 | #include "lldb/Utility/ArchSpec.h" |
17 | #include "lldb/Utility/LLDBLog.h" |
18 | #include "lldb/Utility/Log.h" |
19 | #include "lldb/Utility/Status.h" |
20 | |
21 | #include "llvm/Support/Path.h" |
22 | |
23 | #include "DYLDRendezvous.h" |
24 | |
25 | using namespace lldb; |
26 | using namespace lldb_private; |
27 | |
28 | const char *DYLDRendezvous::StateToCStr(RendezvousState state) { |
29 | switch (state) { |
30 | case DYLDRendezvous::eConsistent: |
31 | return "eConsistent" ; |
32 | case DYLDRendezvous::eAdd: |
33 | return "eAdd" ; |
34 | case DYLDRendezvous::eDelete: |
35 | return "eDelete" ; |
36 | } |
37 | return "<invalid RendezvousState>" ; |
38 | } |
39 | |
40 | const char *DYLDRendezvous::ActionToCStr(RendezvousAction action) { |
41 | switch (action) { |
42 | case DYLDRendezvous::RendezvousAction::eTakeSnapshot: |
43 | return "eTakeSnapshot" ; |
44 | case DYLDRendezvous::RendezvousAction::eAddModules: |
45 | return "eAddModules" ; |
46 | case DYLDRendezvous::RendezvousAction::eRemoveModules: |
47 | return "eRemoveModules" ; |
48 | case DYLDRendezvous::RendezvousAction::eNoAction: |
49 | return "eNoAction" ; |
50 | } |
51 | return "<invalid RendezvousAction>" ; |
52 | } |
53 | |
54 | DYLDRendezvous::DYLDRendezvous(Process *process) |
55 | : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), |
56 | m_executable_interpreter(false), m_current(), m_previous(), |
57 | m_loaded_modules(), m_soentries(), m_added_soentries(), |
58 | m_removed_soentries() { |
59 | m_thread_info.valid = false; |
60 | UpdateExecutablePath(); |
61 | } |
62 | |
63 | addr_t DYLDRendezvous::ResolveRendezvousAddress() { |
64 | Log *log = GetLog(mask: LLDBLog::DynamicLoader); |
65 | addr_t info_location; |
66 | addr_t info_addr; |
67 | Status error; |
68 | |
69 | if (!m_process) { |
70 | LLDB_LOGF(log, "%s null process provided" , __FUNCTION__); |
71 | return LLDB_INVALID_ADDRESS; |
72 | } |
73 | |
74 | // Try to get it from our process. This might be a remote process and might |
75 | // grab it via some remote-specific mechanism. |
76 | info_location = m_process->GetImageInfoAddress(); |
77 | LLDB_LOGF(log, "%s info_location = 0x%" PRIx64, __FUNCTION__, info_location); |
78 | |
79 | // If the process fails to return an address, fall back to seeing if the |
80 | // local object file can help us find it. |
81 | if (info_location == LLDB_INVALID_ADDRESS) { |
82 | Target *target = &m_process->GetTarget(); |
83 | if (target) { |
84 | ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); |
85 | Address addr = obj_file->GetImageInfoAddress(target); |
86 | |
87 | if (addr.IsValid()) { |
88 | info_location = addr.GetLoadAddress(target); |
89 | LLDB_LOGF(log, |
90 | "%s resolved via direct object file approach to 0x%" PRIx64, |
91 | __FUNCTION__, info_location); |
92 | } else { |
93 | const Symbol *_r_debug = |
94 | target->GetExecutableModule()->FindFirstSymbolWithNameAndType( |
95 | name: ConstString("_r_debug" )); |
96 | if (_r_debug) { |
97 | info_addr = _r_debug->GetAddress().GetLoadAddress(target); |
98 | if (info_addr != LLDB_INVALID_ADDRESS) { |
99 | LLDB_LOGF(log, |
100 | "%s resolved by finding symbol '_r_debug' whose value is " |
101 | "0x%" PRIx64, |
102 | __FUNCTION__, info_addr); |
103 | m_executable_interpreter = true; |
104 | return info_addr; |
105 | } |
106 | } |
107 | LLDB_LOGF(log, |
108 | "%s FAILED - direct object file approach did not yield a " |
109 | "valid address" , |
110 | __FUNCTION__); |
111 | } |
112 | } |
113 | } |
114 | |
115 | if (info_location == LLDB_INVALID_ADDRESS) { |
116 | LLDB_LOGF(log, "%s FAILED - invalid info address" , __FUNCTION__); |
117 | return LLDB_INVALID_ADDRESS; |
118 | } |
119 | |
120 | LLDB_LOGF(log, "%s reading pointer (%" PRIu32 " bytes) from 0x%" PRIx64, |
121 | __FUNCTION__, m_process->GetAddressByteSize(), info_location); |
122 | |
123 | info_addr = m_process->ReadPointerFromMemory(vm_addr: info_location, error); |
124 | if (error.Fail()) { |
125 | LLDB_LOGF(log, "%s FAILED - could not read from the info location: %s" , |
126 | __FUNCTION__, error.AsCString()); |
127 | return LLDB_INVALID_ADDRESS; |
128 | } |
129 | |
130 | if (info_addr == 0) { |
131 | LLDB_LOGF(log, |
132 | "%s FAILED - the rendezvous address contained at 0x%" PRIx64 |
133 | " returned a null value" , |
134 | __FUNCTION__, info_location); |
135 | return LLDB_INVALID_ADDRESS; |
136 | } |
137 | |
138 | return info_addr; |
139 | } |
140 | |
141 | void DYLDRendezvous::UpdateExecutablePath() { |
142 | if (m_process) { |
143 | Log *log = GetLog(mask: LLDBLog::DynamicLoader); |
144 | Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer(); |
145 | if (exe_mod) { |
146 | m_exe_file_spec = exe_mod->GetPlatformFileSpec(); |
147 | LLDB_LOGF(log, "DYLDRendezvous::%s exe module executable path set: '%s'" , |
148 | __FUNCTION__, m_exe_file_spec.GetPath().c_str()); |
149 | } else { |
150 | LLDB_LOGF(log, |
151 | "DYLDRendezvous::%s cannot cache exe module path: null " |
152 | "executable module pointer" , |
153 | __FUNCTION__); |
154 | } |
155 | } |
156 | } |
157 | |
158 | void DYLDRendezvous::Rendezvous::DumpToLog(Log *log, const char *label) { |
159 | LLDB_LOGF(log, "%s Rendezvous: version = %" PRIu64 ", map_addr = 0x%16.16" |
160 | PRIx64 ", brk = 0x%16.16" PRIx64 ", state = %" PRIu64 |
161 | " (%s), ldbase = 0x%16.16" PRIx64, label ? label : "" , version, |
162 | map_addr, brk, state, StateToCStr((RendezvousState)state), ldbase); |
163 | } |
164 | |
165 | bool DYLDRendezvous::Resolve() { |
166 | Log *log = GetLog(mask: LLDBLog::DynamicLoader); |
167 | |
168 | const size_t word_size = 4; |
169 | Rendezvous info; |
170 | size_t address_size; |
171 | size_t padding; |
172 | addr_t info_addr; |
173 | addr_t cursor; |
174 | |
175 | address_size = m_process->GetAddressByteSize(); |
176 | padding = address_size - word_size; |
177 | LLDB_LOGF(log, |
178 | "DYLDRendezvous::%s address size: %" PRIu64 ", padding %" PRIu64, |
179 | __FUNCTION__, uint64_t(address_size), uint64_t(padding)); |
180 | |
181 | if (m_rendezvous_addr == LLDB_INVALID_ADDRESS) |
182 | cursor = info_addr = |
183 | ResolveRendezvousAddress(); |
184 | else |
185 | cursor = info_addr = m_rendezvous_addr; |
186 | LLDB_LOGF(log, "DYLDRendezvous::%s cursor = 0x%" PRIx64, __FUNCTION__, |
187 | cursor); |
188 | |
189 | if (cursor == LLDB_INVALID_ADDRESS) |
190 | return false; |
191 | |
192 | if (!(cursor = ReadWord(addr: cursor, dst: &info.version, size: word_size))) |
193 | return false; |
194 | |
195 | if (!(cursor = ReadPointer(addr: cursor + padding, dst: &info.map_addr))) |
196 | return false; |
197 | |
198 | if (!(cursor = ReadPointer(addr: cursor, dst: &info.brk))) |
199 | return false; |
200 | |
201 | if (!(cursor = ReadWord(addr: cursor, dst: &info.state, size: word_size))) |
202 | return false; |
203 | |
204 | if (!(cursor = ReadPointer(addr: cursor + padding, dst: &info.ldbase))) |
205 | return false; |
206 | |
207 | // The rendezvous was successfully read. Update our internal state. |
208 | m_rendezvous_addr = info_addr; |
209 | m_previous = m_current; |
210 | m_current = info; |
211 | |
212 | m_previous.DumpToLog(log, label: "m_previous" ); |
213 | m_current.DumpToLog(log, label: "m_current " ); |
214 | |
215 | if (m_current.map_addr == 0) |
216 | return false; |
217 | |
218 | if (UpdateSOEntriesFromRemote()) |
219 | return true; |
220 | |
221 | return UpdateSOEntries(); |
222 | } |
223 | |
224 | bool DYLDRendezvous::IsValid() { |
225 | return m_rendezvous_addr != LLDB_INVALID_ADDRESS; |
226 | } |
227 | |
228 | DYLDRendezvous::RendezvousAction DYLDRendezvous::GetAction() const { |
229 | // If we have a core file, we will read the current rendezvous state |
230 | // from the core file's memory into m_current which can be in an inconsistent |
231 | // state, so we can't rely on its state to determine what we should do. We |
232 | // always need it to load all of the shared libraries one time when we attach |
233 | // to a core file. |
234 | if (IsCoreFile()) |
235 | return eTakeSnapshot; |
236 | |
237 | switch (m_current.state) { |
238 | |
239 | case eConsistent: |
240 | switch (m_previous.state) { |
241 | // When the previous and current states are consistent this is the first |
242 | // time we have been asked to update. Just take a snapshot of the |
243 | // currently loaded modules. |
244 | case eConsistent: |
245 | return eTakeSnapshot; |
246 | // If we are about to add or remove a shared object clear out the current |
247 | // state and take a snapshot of the currently loaded images. |
248 | case eAdd: |
249 | return eAddModules; |
250 | case eDelete: |
251 | return eRemoveModules; |
252 | } |
253 | break; |
254 | |
255 | case eAdd: |
256 | // If the main executable or a shared library defines a publicly visible |
257 | // symbol named "_r_debug", then it will cause problems once the executable |
258 | // that contains the symbol is loaded into the process. The correct |
259 | // "_r_debug" structure is currently found by LLDB by looking through |
260 | // the .dynamic section in the main executable and finding the DT_DEBUG tag |
261 | // entry. |
262 | // |
263 | // An issue comes up if someone defines another publicly visible "_r_debug" |
264 | // struct in their program. Sample code looks like: |
265 | // |
266 | // #include <link.h> |
267 | // r_debug _r_debug; |
268 | // |
269 | // If code like this is in an executable or shared library, this creates a |
270 | // new "_r_debug" structure and it causes problems once the executable is |
271 | // loaded due to the way symbol lookups happen in linux: the shared library |
272 | // list from _r_debug.r_map will be searched for a symbol named "_r_debug" |
273 | // and the first match will be the new version that is used. The dynamic |
274 | // loader is always last in this list. So at some point the dynamic loader |
275 | // will start updating the copy of "_r_debug" that gets found first. The |
276 | // issue is that LLDB will only look at the copy that is pointed to by the |
277 | // DT_DEBUG entry, or the initial version from the ld.so binary. |
278 | // |
279 | // Steps that show the problem are: |
280 | // |
281 | // - LLDB finds the "_r_debug" structure via the DT_DEBUG entry in the |
282 | // .dynamic section and this points to the "_r_debug" in ld.so |
283 | // - ld.so uodates its copy of "_r_debug" with "state = eAdd" before it |
284 | // loads the dependent shared libraries for the main executable and |
285 | // any dependencies of all shared libraries from the executable's list |
286 | // and ld.so code calls the debugger notification function |
287 | // that LLDB has set a breakpoint on. |
288 | // - LLDB hits the breakpoint and the breakpoint has a callback function |
289 | // where we read the _r_debug.state (eAdd) state and we do nothing as the |
290 | // "eAdd" state indicates that the shared libraries are about to be added. |
291 | // - ld.so finishes loading the main executable and any dependent shared |
292 | // libraries and it will update the "_r_debug.state" member with a |
293 | // "eConsistent", but it now updates the "_r_debug" in the a.out program |
294 | // and it calls the debugger notification function. |
295 | // - lldb hits the notification breakpoint and checks the ld.so copy of |
296 | // "_r_debug.state" which still has a state of "eAdd", but LLDB needs to see a |
297 | // "eConsistent" state to trigger the shared libraries to get loaded into |
298 | // the debug session, but LLDB the ld.so _r_debug.state which still |
299 | // contains "eAdd" and doesn't do anyhing and library load is missed. |
300 | // The "_r_debug" in a.out has the state set correctly to "eConsistent" |
301 | // but LLDB is still looking at the "_r_debug" from ld.so. |
302 | // |
303 | // So if we detect two "eAdd" states in a row, we assume this is the issue |
304 | // and we now load shared libraries correctly and will emit a log message |
305 | // in the "log enable lldb dyld" log channel which states there might be |
306 | // multiple "_r_debug" structs causing problems. |
307 | // |
308 | // The correct solution is that no one should be adding a duplicate |
309 | // publicly visible "_r_debug" symbols to their binaries, but we have |
310 | // programs that are doing this already and since it can be done, we should |
311 | // be able to work with this and keep debug sessions working as expected. |
312 | // |
313 | // If a user includes the <link.h> file, they can just use the existing |
314 | // "_r_debug" structure as it is defined in this header file as "extern |
315 | // struct r_debug _r_debug;" and no local copies need to be made. |
316 | if (m_previous.state == eAdd) { |
317 | Log *log = GetLog(mask: LLDBLog::DynamicLoader); |
318 | LLDB_LOG(log, "DYLDRendezvous::GetAction() found two eAdd states in a " |
319 | "row, check process for multiple \"_r_debug\" symbols. " |
320 | "Returning eAddModules to ensure shared libraries get loaded " |
321 | "correctly" ); |
322 | return eAddModules; |
323 | } |
324 | return eNoAction; |
325 | case eDelete: |
326 | return eNoAction; |
327 | } |
328 | |
329 | return eNoAction; |
330 | } |
331 | |
332 | bool DYLDRendezvous::UpdateSOEntriesFromRemote() { |
333 | const auto action = GetAction(); |
334 | Log *log = GetLog(mask: LLDBLog::DynamicLoader); |
335 | LLDB_LOG(log, "{0} action = {1}" , LLVM_PRETTY_FUNCTION, ActionToCStr(action)); |
336 | |
337 | if (action == eNoAction) |
338 | return false; |
339 | |
340 | m_added_soentries.clear(); |
341 | m_removed_soentries.clear(); |
342 | if (action == eTakeSnapshot) { |
343 | // We already have the loaded list from the previous update so no need to |
344 | // find all the modules again. |
345 | if (!m_loaded_modules.m_list.empty()) |
346 | return true; |
347 | } |
348 | |
349 | llvm::Expected<LoadedModuleInfoList> module_list = |
350 | m_process->GetLoadedModuleList(); |
351 | if (!module_list) { |
352 | llvm::consumeError(Err: module_list.takeError()); |
353 | return false; |
354 | } |
355 | |
356 | switch (action) { |
357 | case eTakeSnapshot: |
358 | m_soentries.clear(); |
359 | return SaveSOEntriesFromRemote(module_list: *module_list); |
360 | case eAddModules: |
361 | return AddSOEntriesFromRemote(module_list: *module_list); |
362 | case eRemoveModules: |
363 | return RemoveSOEntriesFromRemote(module_list: *module_list); |
364 | case eNoAction: |
365 | return false; |
366 | } |
367 | llvm_unreachable("Fully covered switch above!" ); |
368 | } |
369 | |
370 | bool DYLDRendezvous::UpdateSOEntries() { |
371 | m_added_soentries.clear(); |
372 | m_removed_soentries.clear(); |
373 | const auto action = GetAction(); |
374 | Log *log = GetLog(mask: LLDBLog::DynamicLoader); |
375 | LLDB_LOG(log, "{0} action = {1}" , LLVM_PRETTY_FUNCTION, ActionToCStr(action)); |
376 | switch (action) { |
377 | case eTakeSnapshot: |
378 | m_soentries.clear(); |
379 | return TakeSnapshot(entry_list&: m_soentries); |
380 | case eAddModules: |
381 | return AddSOEntries(); |
382 | case eRemoveModules: |
383 | return RemoveSOEntries(); |
384 | case eNoAction: |
385 | return false; |
386 | } |
387 | llvm_unreachable("Fully covered switch above!" ); |
388 | } |
389 | |
390 | bool DYLDRendezvous::FillSOEntryFromModuleInfo( |
391 | LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry) { |
392 | addr_t link_map_addr; |
393 | addr_t base_addr; |
394 | addr_t dyn_addr; |
395 | std::string name; |
396 | |
397 | if (!modInfo.get_link_map(out&: link_map_addr) || !modInfo.get_base(out&: base_addr) || |
398 | !modInfo.get_dynamic(out&: dyn_addr) || !modInfo.get_name(out&: name)) |
399 | return false; |
400 | |
401 | entry.link_addr = link_map_addr; |
402 | entry.base_addr = base_addr; |
403 | entry.dyn_addr = dyn_addr; |
404 | |
405 | entry.file_spec.SetFile(path: name, style: FileSpec::Style::native); |
406 | |
407 | UpdateBaseAddrIfNecessary(entry, file_path: name); |
408 | |
409 | // not needed if we're using ModuleInfos |
410 | entry.next = 0; |
411 | entry.prev = 0; |
412 | entry.path_addr = 0; |
413 | |
414 | return true; |
415 | } |
416 | |
417 | bool DYLDRendezvous::SaveSOEntriesFromRemote( |
418 | const LoadedModuleInfoList &module_list) { |
419 | for (auto const &modInfo : module_list.m_list) { |
420 | SOEntry entry; |
421 | if (!FillSOEntryFromModuleInfo(modInfo, entry)) |
422 | return false; |
423 | |
424 | // Only add shared libraries and not the executable. |
425 | if (!SOEntryIsMainExecutable(entry)) { |
426 | UpdateFileSpecIfNecessary(entry); |
427 | m_soentries.push_back(x: entry); |
428 | } |
429 | } |
430 | |
431 | m_loaded_modules = module_list; |
432 | return true; |
433 | } |
434 | |
435 | bool DYLDRendezvous::AddSOEntriesFromRemote( |
436 | const LoadedModuleInfoList &module_list) { |
437 | for (auto const &modInfo : module_list.m_list) { |
438 | bool found = false; |
439 | for (auto const &existing : m_loaded_modules.m_list) { |
440 | if (modInfo == existing) { |
441 | found = true; |
442 | break; |
443 | } |
444 | } |
445 | |
446 | if (found) |
447 | continue; |
448 | |
449 | SOEntry entry; |
450 | if (!FillSOEntryFromModuleInfo(modInfo, entry)) |
451 | return false; |
452 | |
453 | // Only add shared libraries and not the executable. |
454 | if (!SOEntryIsMainExecutable(entry)) { |
455 | UpdateFileSpecIfNecessary(entry); |
456 | m_soentries.push_back(x: entry); |
457 | m_added_soentries.push_back(x: entry); |
458 | } |
459 | } |
460 | |
461 | m_loaded_modules = module_list; |
462 | return true; |
463 | } |
464 | |
465 | bool DYLDRendezvous::RemoveSOEntriesFromRemote( |
466 | const LoadedModuleInfoList &module_list) { |
467 | for (auto const &existing : m_loaded_modules.m_list) { |
468 | bool found = false; |
469 | for (auto const &modInfo : module_list.m_list) { |
470 | if (modInfo == existing) { |
471 | found = true; |
472 | break; |
473 | } |
474 | } |
475 | |
476 | if (found) |
477 | continue; |
478 | |
479 | SOEntry entry; |
480 | if (!FillSOEntryFromModuleInfo(modInfo: existing, entry)) |
481 | return false; |
482 | |
483 | // Only add shared libraries and not the executable. |
484 | if (!SOEntryIsMainExecutable(entry)) { |
485 | auto pos = llvm::find(Range&: m_soentries, Val: entry); |
486 | if (pos == m_soentries.end()) |
487 | return false; |
488 | |
489 | m_soentries.erase(position: pos); |
490 | m_removed_soentries.push_back(x: entry); |
491 | } |
492 | } |
493 | |
494 | m_loaded_modules = module_list; |
495 | return true; |
496 | } |
497 | |
498 | bool DYLDRendezvous::AddSOEntries() { |
499 | SOEntry entry; |
500 | iterator pos; |
501 | |
502 | assert(m_previous.state == eAdd); |
503 | |
504 | if (m_current.map_addr == 0) |
505 | return false; |
506 | |
507 | for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) { |
508 | if (!ReadSOEntryFromMemory(addr: cursor, entry)) |
509 | return false; |
510 | |
511 | // Only add shared libraries and not the executable. |
512 | if (SOEntryIsMainExecutable(entry)) |
513 | continue; |
514 | |
515 | UpdateFileSpecIfNecessary(entry); |
516 | |
517 | if (!llvm::is_contained(Range&: m_soentries, Element: entry)) { |
518 | m_soentries.push_back(x: entry); |
519 | m_added_soentries.push_back(x: entry); |
520 | } |
521 | } |
522 | |
523 | return true; |
524 | } |
525 | |
526 | bool DYLDRendezvous::RemoveSOEntries() { |
527 | SOEntryList entry_list; |
528 | iterator pos; |
529 | |
530 | assert(m_previous.state == eDelete); |
531 | |
532 | if (!TakeSnapshot(entry_list)) |
533 | return false; |
534 | |
535 | for (iterator I = begin(); I != end(); ++I) { |
536 | if (!llvm::is_contained(Range&: entry_list, Element: *I)) |
537 | m_removed_soentries.push_back(x: *I); |
538 | } |
539 | |
540 | m_soentries = entry_list; |
541 | return true; |
542 | } |
543 | |
544 | bool DYLDRendezvous::SOEntryIsMainExecutable(const SOEntry &entry) { |
545 | // On some systes the executable is indicated by an empty path in the entry. |
546 | // On others it is the full path to the executable. |
547 | |
548 | auto triple = m_process->GetTarget().GetArchitecture().GetTriple(); |
549 | switch (triple.getOS()) { |
550 | case llvm::Triple::FreeBSD: |
551 | case llvm::Triple::NetBSD: |
552 | case llvm::Triple::OpenBSD: |
553 | return entry.file_spec == m_exe_file_spec; |
554 | case llvm::Triple::Linux: |
555 | if (triple.isAndroid()) |
556 | return entry.file_spec == m_exe_file_spec; |
557 | // If we are debugging ld.so, then all SOEntries should be treated as |
558 | // libraries, including the "main" one (denoted by an empty string). |
559 | if (!entry.file_spec && m_executable_interpreter) |
560 | return false; |
561 | return !entry.file_spec; |
562 | default: |
563 | return false; |
564 | } |
565 | } |
566 | |
567 | bool DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) { |
568 | SOEntry entry; |
569 | |
570 | if (m_current.map_addr == 0) |
571 | return false; |
572 | |
573 | // Clear previous entries since we are about to obtain an up to date list. |
574 | entry_list.clear(); |
575 | |
576 | for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) { |
577 | if (!ReadSOEntryFromMemory(addr: cursor, entry)) |
578 | return false; |
579 | |
580 | // Only add shared libraries and not the executable. |
581 | if (SOEntryIsMainExecutable(entry)) |
582 | continue; |
583 | |
584 | UpdateFileSpecIfNecessary(entry); |
585 | |
586 | entry_list.push_back(x: entry); |
587 | } |
588 | |
589 | return true; |
590 | } |
591 | |
592 | addr_t DYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst, size_t size) { |
593 | Status error; |
594 | |
595 | *dst = m_process->ReadUnsignedIntegerFromMemory(load_addr: addr, byte_size: size, fail_value: 0, error); |
596 | if (error.Fail()) |
597 | return 0; |
598 | |
599 | return addr + size; |
600 | } |
601 | |
602 | addr_t DYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) { |
603 | Status error; |
604 | |
605 | *dst = m_process->ReadPointerFromMemory(vm_addr: addr, error); |
606 | if (error.Fail()) |
607 | return 0; |
608 | |
609 | return addr + m_process->GetAddressByteSize(); |
610 | } |
611 | |
612 | std::string DYLDRendezvous::ReadStringFromMemory(addr_t addr) { |
613 | std::string str; |
614 | Status error; |
615 | |
616 | if (addr == LLDB_INVALID_ADDRESS) |
617 | return std::string(); |
618 | |
619 | m_process->ReadCStringFromMemory(vm_addr: addr, out_str&: str, error); |
620 | |
621 | return str; |
622 | } |
623 | |
624 | // Returns true if the load bias reported by the linker is incorrect for the |
625 | // given entry. This function is used to handle cases where we want to work |
626 | // around a bug in the system linker. |
627 | static bool isLoadBiasIncorrect(Target &target, const std::string &file_path) { |
628 | // On Android L (API 21, 22) the load address of the "/system/bin/linker" |
629 | // isn't filled in correctly. |
630 | unsigned os_major = target.GetPlatform()->GetOSVersion().getMajor(); |
631 | return target.GetArchitecture().GetTriple().isAndroid() && |
632 | (os_major == 21 || os_major == 22) && |
633 | (file_path == "/system/bin/linker" || |
634 | file_path == "/system/bin/linker64" ); |
635 | } |
636 | |
637 | void DYLDRendezvous::UpdateBaseAddrIfNecessary(SOEntry &entry, |
638 | std::string const &file_path) { |
639 | // If the load bias reported by the linker is incorrect then fetch the load |
640 | // address of the file from the proc file system. |
641 | if (isLoadBiasIncorrect(target&: m_process->GetTarget(), file_path)) { |
642 | lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; |
643 | bool is_loaded = false; |
644 | Status error = |
645 | m_process->GetFileLoadAddress(file: entry.file_spec, is_loaded, load_addr); |
646 | if (error.Success() && is_loaded) |
647 | entry.base_addr = load_addr; |
648 | } |
649 | } |
650 | |
651 | void DYLDRendezvous::UpdateFileSpecIfNecessary(SOEntry &entry) { |
652 | // Updates filename if empty. It is useful while debugging ld.so, |
653 | // when the link map returns empty string for the main executable. |
654 | if (!entry.file_spec) { |
655 | MemoryRegionInfo region; |
656 | Status region_status = |
657 | m_process->GetMemoryRegionInfo(load_addr: entry.dyn_addr, range_info&: region); |
658 | if (!region.GetName().IsEmpty()) |
659 | entry.file_spec.SetFile(path: region.GetName().AsCString(), |
660 | style: FileSpec::Style::native); |
661 | } |
662 | } |
663 | |
664 | bool DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) { |
665 | entry.clear(); |
666 | |
667 | entry.link_addr = addr; |
668 | |
669 | if (!(addr = ReadPointer(addr, dst: &entry.base_addr))) |
670 | return false; |
671 | |
672 | // mips adds an extra load offset field to the link map struct on FreeBSD and |
673 | // NetBSD (need to validate other OSes). |
674 | // http://svnweb.freebsd.org/base/head/sys/sys/link_elf.h?revision=217153&view=markup#l57 |
675 | const ArchSpec &arch = m_process->GetTarget().GetArchitecture(); |
676 | if ((arch.GetTriple().getOS() == llvm::Triple::FreeBSD || |
677 | arch.GetTriple().getOS() == llvm::Triple::NetBSD) && |
678 | arch.IsMIPS()) { |
679 | addr_t mips_l_offs; |
680 | if (!(addr = ReadPointer(addr, dst: &mips_l_offs))) |
681 | return false; |
682 | if (mips_l_offs != 0 && mips_l_offs != entry.base_addr) |
683 | return false; |
684 | } |
685 | |
686 | if (!(addr = ReadPointer(addr, dst: &entry.path_addr))) |
687 | return false; |
688 | |
689 | if (!(addr = ReadPointer(addr, dst: &entry.dyn_addr))) |
690 | return false; |
691 | |
692 | if (!(addr = ReadPointer(addr, dst: &entry.next))) |
693 | return false; |
694 | |
695 | if (!(addr = ReadPointer(addr, dst: &entry.prev))) |
696 | return false; |
697 | |
698 | std::string file_path = ReadStringFromMemory(addr: entry.path_addr); |
699 | entry.file_spec.SetFile(path: file_path, style: FileSpec::Style::native); |
700 | |
701 | UpdateBaseAddrIfNecessary(entry, file_path); |
702 | |
703 | return true; |
704 | } |
705 | |
706 | bool DYLDRendezvous::FindMetadata(const char *name, PThreadField field, |
707 | uint32_t &value) { |
708 | Target &target = m_process->GetTarget(); |
709 | |
710 | SymbolContextList list; |
711 | target.GetImages().FindSymbolsWithNameAndType(name: ConstString(name), |
712 | symbol_type: eSymbolTypeAny, sc_list&: list); |
713 | if (list.IsEmpty()) |
714 | return false; |
715 | |
716 | Address address = list[0].symbol->GetAddress(); |
717 | address.SetOffset(address.GetOffset() + field * sizeof(uint32_t)); |
718 | |
719 | // Read from target memory as this allows us to try process memory and |
720 | // fallback to reading from read only sections from the object files. Here we |
721 | // are reading read only data from libpthread.so to find data in the thread |
722 | // specific area for the data we want and this won't be saved into process |
723 | // memory due to it being read only. |
724 | Status error; |
725 | value = |
726 | target.ReadUnsignedIntegerFromMemory(addr: address, integer_byte_size: sizeof(uint32_t), fail_value: 0, error); |
727 | if (error.Fail()) |
728 | return false; |
729 | |
730 | if (field == eSize) |
731 | value /= 8; // convert bits to bytes |
732 | |
733 | return true; |
734 | } |
735 | |
736 | const DYLDRendezvous::ThreadInfo &DYLDRendezvous::GetThreadInfo() { |
737 | if (!m_thread_info.valid) { |
738 | bool ok = true; |
739 | |
740 | ok &= FindMetadata(name: "_thread_db_pthread_dtvp" , field: eOffset, |
741 | value&: m_thread_info.dtv_offset); |
742 | ok &= |
743 | FindMetadata(name: "_thread_db_dtv_dtv" , field: eSize, value&: m_thread_info.dtv_slot_size); |
744 | ok &= FindMetadata(name: "_thread_db_link_map_l_tls_modid" , field: eOffset, |
745 | value&: m_thread_info.modid_offset); |
746 | ok &= FindMetadata(name: "_thread_db_dtv_t_pointer_val" , field: eOffset, |
747 | value&: m_thread_info.tls_offset); |
748 | |
749 | if (ok) |
750 | m_thread_info.valid = true; |
751 | } |
752 | |
753 | return m_thread_info; |
754 | } |
755 | |
756 | void DYLDRendezvous::DumpToLog(Log *log) const { |
757 | int state = GetState(); |
758 | |
759 | if (!log) |
760 | return; |
761 | |
762 | log->PutCString(cstr: "DYLDRendezvous:" ); |
763 | LLDB_LOGF(log, " Address: %" PRIx64, GetRendezvousAddress()); |
764 | LLDB_LOGF(log, " Version: %" PRIu64, GetVersion()); |
765 | LLDB_LOGF(log, " Link : %" PRIx64, GetLinkMapAddress()); |
766 | LLDB_LOGF(log, " Break : %" PRIx64, GetBreakAddress()); |
767 | LLDB_LOGF(log, " LDBase : %" PRIx64, GetLDBase()); |
768 | LLDB_LOGF(log, " State : %s" , |
769 | (state == eConsistent) |
770 | ? "consistent" |
771 | : (state == eAdd) ? "add" |
772 | : (state == eDelete) ? "delete" : "unknown" ); |
773 | |
774 | iterator I = begin(); |
775 | iterator E = end(); |
776 | |
777 | if (I != E) |
778 | log->PutCString(cstr: "DYLDRendezvous SOEntries:" ); |
779 | |
780 | for (int i = 1; I != E; ++I, ++i) { |
781 | LLDB_LOGF(log, "\n SOEntry [%d] %s" , i, I->file_spec.GetPath().c_str()); |
782 | LLDB_LOGF(log, " Base : %" PRIx64, I->base_addr); |
783 | LLDB_LOGF(log, " Path : %" PRIx64, I->path_addr); |
784 | LLDB_LOGF(log, " Dyn : %" PRIx64, I->dyn_addr); |
785 | LLDB_LOGF(log, " Next : %" PRIx64, I->next); |
786 | LLDB_LOGF(log, " Prev : %" PRIx64, I->prev); |
787 | } |
788 | } |
789 | |
790 | bool DYLDRendezvous::IsCoreFile() const { |
791 | return !m_process->IsLiveDebugSession(); |
792 | } |
793 | |