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