New glibc versions (since 2.34 or including this
<ed3ce71f5c>
patch) trigger the rendezvous breakpoint after they have already added
some modules to the list. This did not play well with our dynamic
loader plugin which was doing a diff of the the reported modules in the
before (RT_ADD) and after (RT_CONSISTENT) states. Specifically, it
caused us to miss some of the modules.
While I think the old behavior makes more sense, I don't think that lldb
is doing the right thing either, as the documentation states that we
should not be expecting a consistent view in the RT_ADD (and RT_DELETE)
states.
Therefore, this patch changes the lldb algorithm to compare the module
list against the previous consistent snapshot. This fixes the previous
issue, and I believe it is more correct in general. It also reduces the
number of times we are fetching the module info, which should speed up
the debugging of processes with many shared libraries.
The change in RefreshModules ensures we don't broadcast the loaded
notification for the dynamic loader (ld.so) module more than once.
Differential Revision: https://reviews.llvm.org/D128264
669 lines
19 KiB
C++
669 lines
19 KiB
C++
//===-- DYLDRendezvous.cpp ------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Symbol/Symbol.h"
|
|
#include "lldb/Symbol/SymbolContext.h"
|
|
#include "lldb/Target/Platform.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Utility/ArchSpec.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/Status.h"
|
|
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#include "DYLDRendezvous.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
DYLDRendezvous::DYLDRendezvous(Process *process)
|
|
: m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS),
|
|
m_executable_interpreter(false), m_current(), m_previous(),
|
|
m_loaded_modules(), m_soentries(), m_added_soentries(),
|
|
m_removed_soentries() {
|
|
m_thread_info.valid = false;
|
|
UpdateExecutablePath();
|
|
}
|
|
|
|
addr_t DYLDRendezvous::ResolveRendezvousAddress() {
|
|
Log *log = GetLog(LLDBLog::DynamicLoader);
|
|
addr_t info_location;
|
|
addr_t info_addr;
|
|
Status error;
|
|
|
|
if (!m_process) {
|
|
LLDB_LOGF(log, "%s null process provided", __FUNCTION__);
|
|
return LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
// Try to get it from our process. This might be a remote process and might
|
|
// grab it via some remote-specific mechanism.
|
|
info_location = m_process->GetImageInfoAddress();
|
|
LLDB_LOGF(log, "%s info_location = 0x%" PRIx64, __FUNCTION__, info_location);
|
|
|
|
// If the process fails to return an address, fall back to seeing if the
|
|
// local object file can help us find it.
|
|
if (info_location == LLDB_INVALID_ADDRESS) {
|
|
Target *target = &m_process->GetTarget();
|
|
if (target) {
|
|
ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile();
|
|
Address addr = obj_file->GetImageInfoAddress(target);
|
|
|
|
if (addr.IsValid()) {
|
|
info_location = addr.GetLoadAddress(target);
|
|
LLDB_LOGF(log,
|
|
"%s resolved via direct object file approach to 0x%" PRIx64,
|
|
__FUNCTION__, info_location);
|
|
} else {
|
|
const Symbol *_r_debug =
|
|
target->GetExecutableModule()->FindFirstSymbolWithNameAndType(
|
|
ConstString("_r_debug"));
|
|
if (_r_debug) {
|
|
info_addr = _r_debug->GetAddress().GetLoadAddress(target);
|
|
if (info_addr != LLDB_INVALID_ADDRESS) {
|
|
LLDB_LOGF(log,
|
|
"%s resolved by finding symbol '_r_debug' whose value is "
|
|
"0x%" PRIx64,
|
|
__FUNCTION__, info_addr);
|
|
m_executable_interpreter = true;
|
|
return info_addr;
|
|
}
|
|
}
|
|
LLDB_LOGF(log,
|
|
"%s FAILED - direct object file approach did not yield a "
|
|
"valid address",
|
|
__FUNCTION__);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info_location == LLDB_INVALID_ADDRESS) {
|
|
LLDB_LOGF(log, "%s FAILED - invalid info address", __FUNCTION__);
|
|
return LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
LLDB_LOGF(log, "%s reading pointer (%" PRIu32 " bytes) from 0x%" PRIx64,
|
|
__FUNCTION__, m_process->GetAddressByteSize(), info_location);
|
|
|
|
info_addr = m_process->ReadPointerFromMemory(info_location, error);
|
|
if (error.Fail()) {
|
|
LLDB_LOGF(log, "%s FAILED - could not read from the info location: %s",
|
|
__FUNCTION__, error.AsCString());
|
|
return LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
if (info_addr == 0) {
|
|
LLDB_LOGF(log,
|
|
"%s FAILED - the rendezvous address contained at 0x%" PRIx64
|
|
" returned a null value",
|
|
__FUNCTION__, info_location);
|
|
return LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
return info_addr;
|
|
}
|
|
|
|
void DYLDRendezvous::UpdateExecutablePath() {
|
|
if (m_process) {
|
|
Log *log = GetLog(LLDBLog::DynamicLoader);
|
|
Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
|
|
if (exe_mod) {
|
|
m_exe_file_spec = exe_mod->GetPlatformFileSpec();
|
|
LLDB_LOGF(log, "DYLDRendezvous::%s exe module executable path set: '%s'",
|
|
__FUNCTION__, m_exe_file_spec.GetCString());
|
|
} else {
|
|
LLDB_LOGF(log,
|
|
"DYLDRendezvous::%s cannot cache exe module path: null "
|
|
"executable module pointer",
|
|
__FUNCTION__);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DYLDRendezvous::Resolve() {
|
|
Log *log = GetLog(LLDBLog::DynamicLoader);
|
|
|
|
const size_t word_size = 4;
|
|
Rendezvous info;
|
|
size_t address_size;
|
|
size_t padding;
|
|
addr_t info_addr;
|
|
addr_t cursor;
|
|
|
|
address_size = m_process->GetAddressByteSize();
|
|
padding = address_size - word_size;
|
|
LLDB_LOGF(log,
|
|
"DYLDRendezvous::%s address size: %" PRIu64 ", padding %" PRIu64,
|
|
__FUNCTION__, uint64_t(address_size), uint64_t(padding));
|
|
|
|
if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
|
|
cursor = info_addr =
|
|
ResolveRendezvousAddress();
|
|
else
|
|
cursor = info_addr = m_rendezvous_addr;
|
|
LLDB_LOGF(log, "DYLDRendezvous::%s cursor = 0x%" PRIx64, __FUNCTION__,
|
|
cursor);
|
|
|
|
if (cursor == LLDB_INVALID_ADDRESS)
|
|
return false;
|
|
|
|
if (!(cursor = ReadWord(cursor, &info.version, word_size)))
|
|
return false;
|
|
|
|
if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
|
|
return false;
|
|
|
|
if (!(cursor = ReadPointer(cursor, &info.brk)))
|
|
return false;
|
|
|
|
if (!(cursor = ReadWord(cursor, &info.state, word_size)))
|
|
return false;
|
|
|
|
if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
|
|
return false;
|
|
|
|
// The rendezvous was successfully read. Update our internal state.
|
|
m_rendezvous_addr = info_addr;
|
|
m_previous = m_current;
|
|
m_current = info;
|
|
|
|
if (m_current.map_addr == 0)
|
|
return false;
|
|
|
|
if (UpdateSOEntriesFromRemote())
|
|
return true;
|
|
|
|
return UpdateSOEntries();
|
|
}
|
|
|
|
bool DYLDRendezvous::IsValid() {
|
|
return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
|
|
}
|
|
|
|
DYLDRendezvous::RendezvousAction DYLDRendezvous::GetAction() const {
|
|
switch (m_current.state) {
|
|
|
|
case eConsistent:
|
|
switch (m_previous.state) {
|
|
// When the previous and current states are consistent this is the first
|
|
// time we have been asked to update. Just take a snapshot of the
|
|
// currently loaded modules.
|
|
case eConsistent:
|
|
return eTakeSnapshot;
|
|
// If we are about to add or remove a shared object clear out the current
|
|
// state and take a snapshot of the currently loaded images.
|
|
case eAdd:
|
|
return eAddModules;
|
|
case eDelete:
|
|
return eRemoveModules;
|
|
}
|
|
break;
|
|
|
|
case eAdd:
|
|
case eDelete:
|
|
return eNoAction;
|
|
}
|
|
|
|
return eNoAction;
|
|
}
|
|
|
|
bool DYLDRendezvous::UpdateSOEntriesFromRemote() {
|
|
auto action = GetAction();
|
|
|
|
if (action == eNoAction)
|
|
return false;
|
|
|
|
m_added_soentries.clear();
|
|
m_removed_soentries.clear();
|
|
if (action == eTakeSnapshot) {
|
|
// We already have the loaded list from the previous update so no need to
|
|
// find all the modules again.
|
|
if (!m_loaded_modules.m_list.empty())
|
|
return true;
|
|
}
|
|
|
|
llvm::Expected<LoadedModuleInfoList> module_list =
|
|
m_process->GetLoadedModuleList();
|
|
if (!module_list) {
|
|
llvm::consumeError(module_list.takeError());
|
|
return false;
|
|
}
|
|
|
|
switch (action) {
|
|
case eTakeSnapshot:
|
|
m_soentries.clear();
|
|
return SaveSOEntriesFromRemote(*module_list);
|
|
case eAddModules:
|
|
return AddSOEntriesFromRemote(*module_list);
|
|
case eRemoveModules:
|
|
return RemoveSOEntriesFromRemote(*module_list);
|
|
case eNoAction:
|
|
return false;
|
|
}
|
|
llvm_unreachable("Fully covered switch above!");
|
|
}
|
|
|
|
bool DYLDRendezvous::UpdateSOEntries() {
|
|
m_added_soentries.clear();
|
|
m_removed_soentries.clear();
|
|
switch (GetAction()) {
|
|
case eTakeSnapshot:
|
|
m_soentries.clear();
|
|
return TakeSnapshot(m_soentries);
|
|
case eAddModules:
|
|
return AddSOEntries();
|
|
case eRemoveModules:
|
|
return RemoveSOEntries();
|
|
case eNoAction:
|
|
return false;
|
|
}
|
|
llvm_unreachable("Fully covered switch above!");
|
|
}
|
|
|
|
bool DYLDRendezvous::FillSOEntryFromModuleInfo(
|
|
LoadedModuleInfoList::LoadedModuleInfo const &modInfo, SOEntry &entry) {
|
|
addr_t link_map_addr;
|
|
addr_t base_addr;
|
|
addr_t dyn_addr;
|
|
std::string name;
|
|
|
|
if (!modInfo.get_link_map(link_map_addr) || !modInfo.get_base(base_addr) ||
|
|
!modInfo.get_dynamic(dyn_addr) || !modInfo.get_name(name))
|
|
return false;
|
|
|
|
entry.link_addr = link_map_addr;
|
|
entry.base_addr = base_addr;
|
|
entry.dyn_addr = dyn_addr;
|
|
|
|
entry.file_spec.SetFile(name, FileSpec::Style::native);
|
|
|
|
UpdateBaseAddrIfNecessary(entry, name);
|
|
|
|
// not needed if we're using ModuleInfos
|
|
entry.next = 0;
|
|
entry.prev = 0;
|
|
entry.path_addr = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DYLDRendezvous::SaveSOEntriesFromRemote(
|
|
const LoadedModuleInfoList &module_list) {
|
|
for (auto const &modInfo : module_list.m_list) {
|
|
SOEntry entry;
|
|
if (!FillSOEntryFromModuleInfo(modInfo, entry))
|
|
return false;
|
|
|
|
// Only add shared libraries and not the executable.
|
|
if (!SOEntryIsMainExecutable(entry)) {
|
|
UpdateFileSpecIfNecessary(entry);
|
|
m_soentries.push_back(entry);
|
|
}
|
|
}
|
|
|
|
m_loaded_modules = module_list;
|
|
return true;
|
|
}
|
|
|
|
bool DYLDRendezvous::AddSOEntriesFromRemote(
|
|
const LoadedModuleInfoList &module_list) {
|
|
for (auto const &modInfo : module_list.m_list) {
|
|
bool found = false;
|
|
for (auto const &existing : m_loaded_modules.m_list) {
|
|
if (modInfo == existing) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
continue;
|
|
|
|
SOEntry entry;
|
|
if (!FillSOEntryFromModuleInfo(modInfo, entry))
|
|
return false;
|
|
|
|
// Only add shared libraries and not the executable.
|
|
if (!SOEntryIsMainExecutable(entry)) {
|
|
UpdateFileSpecIfNecessary(entry);
|
|
m_soentries.push_back(entry);
|
|
m_added_soentries.push_back(entry);
|
|
}
|
|
}
|
|
|
|
m_loaded_modules = module_list;
|
|
return true;
|
|
}
|
|
|
|
bool DYLDRendezvous::RemoveSOEntriesFromRemote(
|
|
const LoadedModuleInfoList &module_list) {
|
|
for (auto const &existing : m_loaded_modules.m_list) {
|
|
bool found = false;
|
|
for (auto const &modInfo : module_list.m_list) {
|
|
if (modInfo == existing) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
continue;
|
|
|
|
SOEntry entry;
|
|
if (!FillSOEntryFromModuleInfo(existing, entry))
|
|
return false;
|
|
|
|
// Only add shared libraries and not the executable.
|
|
if (!SOEntryIsMainExecutable(entry)) {
|
|
auto pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
|
|
if (pos == m_soentries.end())
|
|
return false;
|
|
|
|
m_soentries.erase(pos);
|
|
m_removed_soentries.push_back(entry);
|
|
}
|
|
}
|
|
|
|
m_loaded_modules = module_list;
|
|
return true;
|
|
}
|
|
|
|
bool DYLDRendezvous::AddSOEntries() {
|
|
SOEntry entry;
|
|
iterator pos;
|
|
|
|
assert(m_previous.state == eAdd);
|
|
|
|
if (m_current.map_addr == 0)
|
|
return false;
|
|
|
|
for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
|
|
if (!ReadSOEntryFromMemory(cursor, entry))
|
|
return false;
|
|
|
|
// Only add shared libraries and not the executable.
|
|
if (SOEntryIsMainExecutable(entry))
|
|
continue;
|
|
|
|
UpdateFileSpecIfNecessary(entry);
|
|
|
|
pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
|
|
if (pos == m_soentries.end()) {
|
|
m_soentries.push_back(entry);
|
|
m_added_soentries.push_back(entry);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DYLDRendezvous::RemoveSOEntries() {
|
|
SOEntryList entry_list;
|
|
iterator pos;
|
|
|
|
assert(m_previous.state == eDelete);
|
|
|
|
if (!TakeSnapshot(entry_list))
|
|
return false;
|
|
|
|
for (iterator I = begin(); I != end(); ++I) {
|
|
pos = std::find(entry_list.begin(), entry_list.end(), *I);
|
|
if (pos == entry_list.end())
|
|
m_removed_soentries.push_back(*I);
|
|
}
|
|
|
|
m_soentries = entry_list;
|
|
return true;
|
|
}
|
|
|
|
bool DYLDRendezvous::SOEntryIsMainExecutable(const SOEntry &entry) {
|
|
// On some systes the executable is indicated by an empty path in the entry.
|
|
// On others it is the full path to the executable.
|
|
|
|
auto triple = m_process->GetTarget().GetArchitecture().GetTriple();
|
|
switch (triple.getOS()) {
|
|
case llvm::Triple::FreeBSD:
|
|
case llvm::Triple::NetBSD:
|
|
return entry.file_spec == m_exe_file_spec;
|
|
case llvm::Triple::Linux:
|
|
if (triple.isAndroid())
|
|
return entry.file_spec == m_exe_file_spec;
|
|
// If we are debugging ld.so, then all SOEntries should be treated as
|
|
// libraries, including the "main" one (denoted by an empty string).
|
|
if (!entry.file_spec && m_executable_interpreter)
|
|
return false;
|
|
return !entry.file_spec;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
|
|
SOEntry entry;
|
|
|
|
if (m_current.map_addr == 0)
|
|
return false;
|
|
|
|
// Clear previous entries since we are about to obtain an up to date list.
|
|
entry_list.clear();
|
|
|
|
for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
|
|
if (!ReadSOEntryFromMemory(cursor, entry))
|
|
return false;
|
|
|
|
// Only add shared libraries and not the executable.
|
|
if (SOEntryIsMainExecutable(entry))
|
|
continue;
|
|
|
|
UpdateFileSpecIfNecessary(entry);
|
|
|
|
entry_list.push_back(entry);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
addr_t DYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst, size_t size) {
|
|
Status error;
|
|
|
|
*dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
|
|
if (error.Fail())
|
|
return 0;
|
|
|
|
return addr + size;
|
|
}
|
|
|
|
addr_t DYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) {
|
|
Status error;
|
|
|
|
*dst = m_process->ReadPointerFromMemory(addr, error);
|
|
if (error.Fail())
|
|
return 0;
|
|
|
|
return addr + m_process->GetAddressByteSize();
|
|
}
|
|
|
|
std::string DYLDRendezvous::ReadStringFromMemory(addr_t addr) {
|
|
std::string str;
|
|
Status error;
|
|
|
|
if (addr == LLDB_INVALID_ADDRESS)
|
|
return std::string();
|
|
|
|
m_process->ReadCStringFromMemory(addr, str, error);
|
|
|
|
return str;
|
|
}
|
|
|
|
// Returns true if the load bias reported by the linker is incorrect for the
|
|
// given entry. This function is used to handle cases where we want to work
|
|
// around a bug in the system linker.
|
|
static bool isLoadBiasIncorrect(Target &target, const std::string &file_path) {
|
|
// On Android L (API 21, 22) the load address of the "/system/bin/linker"
|
|
// isn't filled in correctly.
|
|
unsigned os_major = target.GetPlatform()->GetOSVersion().getMajor();
|
|
return target.GetArchitecture().GetTriple().isAndroid() &&
|
|
(os_major == 21 || os_major == 22) &&
|
|
(file_path == "/system/bin/linker" ||
|
|
file_path == "/system/bin/linker64");
|
|
}
|
|
|
|
void DYLDRendezvous::UpdateBaseAddrIfNecessary(SOEntry &entry,
|
|
std::string const &file_path) {
|
|
// If the load bias reported by the linker is incorrect then fetch the load
|
|
// address of the file from the proc file system.
|
|
if (isLoadBiasIncorrect(m_process->GetTarget(), file_path)) {
|
|
lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
|
|
bool is_loaded = false;
|
|
Status error =
|
|
m_process->GetFileLoadAddress(entry.file_spec, is_loaded, load_addr);
|
|
if (error.Success() && is_loaded)
|
|
entry.base_addr = load_addr;
|
|
}
|
|
}
|
|
|
|
void DYLDRendezvous::UpdateFileSpecIfNecessary(SOEntry &entry) {
|
|
// Updates filename if empty. It is useful while debugging ld.so,
|
|
// when the link map returns empty string for the main executable.
|
|
if (!entry.file_spec) {
|
|
MemoryRegionInfo region;
|
|
Status region_status =
|
|
m_process->GetMemoryRegionInfo(entry.dyn_addr, region);
|
|
if (!region.GetName().IsEmpty())
|
|
entry.file_spec.SetFile(region.GetName().AsCString(),
|
|
FileSpec::Style::native);
|
|
}
|
|
}
|
|
|
|
bool DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) {
|
|
entry.clear();
|
|
|
|
entry.link_addr = addr;
|
|
|
|
if (!(addr = ReadPointer(addr, &entry.base_addr)))
|
|
return false;
|
|
|
|
// mips adds an extra load offset field to the link map struct on FreeBSD and
|
|
// NetBSD (need to validate other OSes).
|
|
// http://svnweb.freebsd.org/base/head/sys/sys/link_elf.h?revision=217153&view=markup#l57
|
|
const ArchSpec &arch = m_process->GetTarget().GetArchitecture();
|
|
if ((arch.GetTriple().getOS() == llvm::Triple::FreeBSD ||
|
|
arch.GetTriple().getOS() == llvm::Triple::NetBSD) &&
|
|
arch.IsMIPS()) {
|
|
addr_t mips_l_offs;
|
|
if (!(addr = ReadPointer(addr, &mips_l_offs)))
|
|
return false;
|
|
if (mips_l_offs != 0 && mips_l_offs != entry.base_addr)
|
|
return false;
|
|
}
|
|
|
|
if (!(addr = ReadPointer(addr, &entry.path_addr)))
|
|
return false;
|
|
|
|
if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
|
|
return false;
|
|
|
|
if (!(addr = ReadPointer(addr, &entry.next)))
|
|
return false;
|
|
|
|
if (!(addr = ReadPointer(addr, &entry.prev)))
|
|
return false;
|
|
|
|
std::string file_path = ReadStringFromMemory(entry.path_addr);
|
|
entry.file_spec.SetFile(file_path, FileSpec::Style::native);
|
|
|
|
UpdateBaseAddrIfNecessary(entry, file_path);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DYLDRendezvous::FindMetadata(const char *name, PThreadField field,
|
|
uint32_t &value) {
|
|
Target &target = m_process->GetTarget();
|
|
|
|
SymbolContextList list;
|
|
target.GetImages().FindSymbolsWithNameAndType(ConstString(name),
|
|
eSymbolTypeAny, list);
|
|
if (list.IsEmpty())
|
|
return false;
|
|
|
|
Address address = list[0].symbol->GetAddress();
|
|
addr_t addr = address.GetLoadAddress(&target);
|
|
if (addr == LLDB_INVALID_ADDRESS)
|
|
return false;
|
|
|
|
Status error;
|
|
value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(
|
|
addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error);
|
|
if (error.Fail())
|
|
return false;
|
|
|
|
if (field == eSize)
|
|
value /= 8; // convert bits to bytes
|
|
|
|
return true;
|
|
}
|
|
|
|
const DYLDRendezvous::ThreadInfo &DYLDRendezvous::GetThreadInfo() {
|
|
if (!m_thread_info.valid) {
|
|
bool ok = true;
|
|
|
|
ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
|
|
m_thread_info.dtv_offset);
|
|
ok &=
|
|
FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
|
|
ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset,
|
|
m_thread_info.modid_offset);
|
|
ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset,
|
|
m_thread_info.tls_offset);
|
|
|
|
if (ok)
|
|
m_thread_info.valid = true;
|
|
}
|
|
|
|
return m_thread_info;
|
|
}
|
|
|
|
void DYLDRendezvous::DumpToLog(Log *log) const {
|
|
int state = GetState();
|
|
|
|
if (!log)
|
|
return;
|
|
|
|
log->PutCString("DYLDRendezvous:");
|
|
LLDB_LOGF(log, " Address: %" PRIx64, GetRendezvousAddress());
|
|
LLDB_LOGF(log, " Version: %" PRIu64, GetVersion());
|
|
LLDB_LOGF(log, " Link : %" PRIx64, GetLinkMapAddress());
|
|
LLDB_LOGF(log, " Break : %" PRIx64, GetBreakAddress());
|
|
LLDB_LOGF(log, " LDBase : %" PRIx64, GetLDBase());
|
|
LLDB_LOGF(log, " State : %s",
|
|
(state == eConsistent)
|
|
? "consistent"
|
|
: (state == eAdd) ? "add"
|
|
: (state == eDelete) ? "delete" : "unknown");
|
|
|
|
iterator I = begin();
|
|
iterator E = end();
|
|
|
|
if (I != E)
|
|
log->PutCString("DYLDRendezvous SOEntries:");
|
|
|
|
for (int i = 1; I != E; ++I, ++i) {
|
|
LLDB_LOGF(log, "\n SOEntry [%d] %s", i, I->file_spec.GetCString());
|
|
LLDB_LOGF(log, " Base : %" PRIx64, I->base_addr);
|
|
LLDB_LOGF(log, " Path : %" PRIx64, I->path_addr);
|
|
LLDB_LOGF(log, " Dyn : %" PRIx64, I->dyn_addr);
|
|
LLDB_LOGF(log, " Next : %" PRIx64, I->next);
|
|
LLDB_LOGF(log, " Prev : %" PRIx64, I->prev);
|
|
}
|
|
}
|