Files
clang-p2996/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp
Raphael Isemann 808142876c [lldb][NFC] Fix all formatting errors in .cpp file headers
Summary:
A *.cpp file header in LLDB (and in LLDB) should like this:
```
//===-- TestUtilities.cpp -------------------------------------------------===//
```
However in LLDB most of our source files have arbitrary changes to this format and
these changes are spreading through LLDB as folks usually just use the existing
source files as templates for their new files (most notably the unnecessary
editor language indicator `-*- C++ -*-` is spreading and in every review
someone is pointing out that this is wrong, resulting in people pointing out that this
is done in the same way in other files).

This patch removes most of these inconsistencies including the editor language indicators,
all the different missing/additional '-' characters, files that center the file name, missing
trailing `===//` (mostly caused by clang-format breaking the line).

Reviewers: aprantl, espindola, jfb, shafik, JDevlieghere

Reviewed By: JDevlieghere

Subscribers: dexonsmith, wuzish, emaste, sdardis, nemanjai, kbarton, MaskRay, atanasyan, arphaman, jfb, abidh, jsji, JDevlieghere, usaxena95, lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D73258
2020-01-24 08:52:55 +01:00

636 lines
18 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/Log.h"
#include "lldb/Utility/Status.h"
#include "llvm/Support/Path.h"
#include "DYLDRendezvous.h"
using namespace lldb;
using namespace lldb_private;
/// Locates the address of the rendezvous structure. Returns the address on
/// success and LLDB_INVALID_ADDRESS on failure.
static addr_t ResolveRendezvousAddress(Process *process) {
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
addr_t info_location;
addr_t info_addr;
Status error;
if (!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 = 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 = &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 {
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__, process->GetAddressByteSize(), info_location);
info_addr = 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;
}
DYLDRendezvous::DYLDRendezvous(Process *process)
: m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(),
m_previous(), m_loaded_modules(), m_soentries(), m_added_soentries(),
m_removed_soentries() {
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
m_thread_info.valid = false;
// Cache a copy of the executable path
if (m_process) {
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(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
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(m_process);
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:
// Some versions of the android dynamic linker might send two
// notifications with state == eAdd back to back. Ignore them until we
// get an eConsistent notification.
if (!(m_previous.state == eConsistent ||
(m_previous.state == eAdd && m_current.state == eDelete)))
return eNoAction;
return eTakeSnapshot;
}
return eNoAction;
}
bool DYLDRendezvous::UpdateSOEntriesFromRemote() {
auto action = GetAction();
if (action == eNoAction)
return false;
if (action == eTakeSnapshot) {
m_added_soentries.clear();
m_removed_soentries.clear();
// 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() {
switch (GetAction()) {
case eTakeSnapshot:
m_soentries.clear();
m_added_soentries.clear();
m_removed_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))
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)) {
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;
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;
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;
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;
}
}
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);
}
}