blob: 617337a6ca5faa8d7ccae7f912add811182d5def [file] [edit]
//===-- DynamicLoaderWindowsDYLD.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 "DynamicLoaderWindowsDYLD.h"
#include "MSVCRTCFrameRecognizer.h"
#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Platform.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadPlanStepInstruction.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "llvm/TargetParser/Triple.h"
using namespace lldb;
using namespace lldb_private;
LLDB_PLUGIN_DEFINE(DynamicLoaderWindowsDYLD)
DynamicLoaderWindowsDYLD::DynamicLoaderWindowsDYLD(Process *process)
: DynamicLoader(process) {}
DynamicLoaderWindowsDYLD::~DynamicLoaderWindowsDYLD() = default;
void DynamicLoaderWindowsDYLD::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
GetPluginDescriptionStatic(), CreateInstance);
}
void DynamicLoaderWindowsDYLD::Terminate() {
PluginManager::UnregisterPlugin(CreateInstance);
}
llvm::StringRef DynamicLoaderWindowsDYLD::GetPluginDescriptionStatic() {
return "Dynamic loader plug-in that watches for shared library "
"loads/unloads in Windows processes.";
}
DynamicLoader *DynamicLoaderWindowsDYLD::CreateInstance(Process *process,
bool force) {
bool should_create = force;
if (!should_create) {
const llvm::Triple &triple_ref =
process->GetTarget().GetArchitecture().GetTriple();
if (triple_ref.getOS() == llvm::Triple::Win32)
should_create = true;
}
if (should_create)
return new DynamicLoaderWindowsDYLD(process);
return nullptr;
}
void DynamicLoaderWindowsDYLD::OnLoadModule(lldb::ModuleSP module_sp,
const ModuleSpec module_spec,
lldb::addr_t module_addr) {
// Resolve the module unless we already have one.
if (!module_sp) {
Status error;
module_sp = m_process->GetTarget().GetOrCreateModule(
module_spec, true /* notify */, &error);
if (error.Fail())
return;
}
m_loaded_modules.insert({module_addr, lldb::ModuleWP(module_sp)});
UpdateLoadedSectionsCommon(module_sp, module_addr, false);
ModuleList module_list;
module_list.Append(module_sp);
m_process->GetTarget().ModulesDidLoad(module_list);
}
void DynamicLoaderWindowsDYLD::OnUnloadModule(lldb::addr_t module_addr) {
auto it = m_loaded_modules.find(module_addr);
if (it == m_loaded_modules.end())
return;
ModuleSP module_sp = it->second.lock();
m_loaded_modules.erase(it);
if (!module_sp)
return;
bool full_unload = true;
for (const auto &entry : m_loaded_modules) {
ModuleSP other_sp = entry.second.lock();
if (other_sp == module_sp) {
UpdateLoadedSectionsCommon(module_sp, entry.first, false);
full_unload = false;
}
}
if (full_unload) {
UnloadSectionsCommon(module_sp);
ModuleList module_list;
module_list.Append(module_sp);
m_process->GetTarget().ModulesDidUnload(module_list, false);
}
}
lldb::addr_t DynamicLoaderWindowsDYLD::GetLoadAddress(ModuleSP executable) {
// First, see if the load address is already cached.
for (const auto &entry : m_loaded_modules) {
ModuleSP mod = entry.second.lock();
if (mod == executable && entry.first != LLDB_INVALID_ADDRESS)
return entry.first;
}
lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
// Second, try to get it through the process plugins. For a remote process,
// the remote platform will be responsible for providing it.
FileSpec file_spec(executable->GetPlatformFileSpec());
bool is_loaded = false;
Status status =
m_process->GetFileLoadAddress(file_spec, is_loaded, load_addr);
// Servers other than lldb server could respond with a bogus address.
if (status.Success() && is_loaded && load_addr != LLDB_INVALID_ADDRESS) {
m_loaded_modules.insert({load_addr, lldb::ModuleWP(executable)});
return load_addr;
}
return LLDB_INVALID_ADDRESS;
}
void DynamicLoaderWindowsDYLD::DidAttach() {
Log *log = GetLog(LLDBLog::DynamicLoader);
LLDB_LOGF(log, "DynamicLoaderWindowsDYLD::%s()", __FUNCTION__);
RegisterMSVCRTCFrameRecognizer(m_process->GetTarget());
ModuleSP executable = GetTargetExecutable();
if (!executable)
return;
// Try to fetch the load address of the file from the process, since there
// could be randomization of the load address.
lldb::addr_t load_addr = GetLoadAddress(executable);
if (load_addr == LLDB_INVALID_ADDRESS) {
// The process does not know the executable under the target's path: it
// was started through another path to the same file, or runs a different
// one. Take the modules it reports as loaded, which replaces the target's
// executable with the process's.
auto error = m_process->LoadModules();
if (!error)
return;
LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}");
// If the load address couldn't be determined (e.g. for a remote gdbstub
// target), fall back to the preferred load address/image base from the
// file if the process is live.
// This is a best-effort guess, if the image isn't loaded there the
// sections will have incorrect addresses and the slide will have to be
// specified manually.
if (!m_process->IsLiveDebugSession())
return;
ObjectFile *object_file = executable->GetObjectFile();
if (!object_file)
return;
load_addr = object_file->GetBaseAddress().GetFileAddress();
if (load_addr == LLDB_INVALID_ADDRESS)
return;
executable->ReportWarning(
"failed to get load address, assuming image base at {0:x}", load_addr);
} else {
// Request the process base address.
lldb::addr_t image_base = m_process->GetImageInfoAddress();
if (image_base == load_addr)
return;
}
// Rebase the process's modules if there is a mismatch.
UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false);
ModuleList module_list;
module_list.Append(executable);
m_process->GetTarget().ModulesDidLoad(module_list);
auto error = m_process->LoadModules();
LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}");
}
void DynamicLoaderWindowsDYLD::DidLaunch() {
Log *log = GetLog(LLDBLog::DynamicLoader);
LLDB_LOGF(log, "DynamicLoaderWindowsDYLD::%s()", __FUNCTION__);
RegisterMSVCRTCFrameRecognizer(m_process->GetTarget());
ModuleSP executable = GetTargetExecutable();
if (!executable)
return;
lldb::addr_t load_addr = GetLoadAddress(executable);
if (load_addr != LLDB_INVALID_ADDRESS) {
// Update the loaded sections so that the breakpoints can be resolved.
UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_addr, false);
ModuleList module_list;
module_list.Append(executable);
m_process->GetTarget().ModulesDidLoad(module_list);
auto error = m_process->LoadModules();
LLDB_LOG_ERROR(log, std::move(error), "failed to load modules: {0}");
}
}
Status DynamicLoaderWindowsDYLD::CanLoadImage() { return Status(); }
ThreadPlanSP
DynamicLoaderWindowsDYLD::GetStepThroughTrampolinePlan(Thread &thread,
bool stop) {
auto arch = m_process->GetTarget().GetArchitecture();
if (arch.GetMachine() != llvm::Triple::x86) {
return ThreadPlanSP();
}
RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
if (!reg_ctx_sp)
return ThreadPlanSP();
uint64_t pc = reg_ctx_sp->GetPC();
// Max size of an instruction in x86 is 15 bytes.
AddressRange range(pc, 2 * 15);
DisassemblerSP disassembler_sp = Disassembler::DisassembleRange(
arch, nullptr, nullptr, nullptr, nullptr, m_process->GetTarget(), range);
if (!disassembler_sp) {
return ThreadPlanSP();
}
InstructionList &insn_list = disassembler_sp->GetInstructionList();
// First instruction in a x86 Windows trampoline is going to be an indirect
// jump through the IAT and the next one will be a nop (usually there for
// alignment purposes). e.g.:
// 0x70ff4cfc <+956>: jmpl *0x7100c2a8
// 0x70ff4d02 <+962>: nop
auto first_insn = insn_list.GetInstructionAtIndex(0);
auto second_insn = insn_list.GetInstructionAtIndex(1);
ExecutionContext exe_ctx(m_process->GetTarget());
if (first_insn == nullptr || second_insn == nullptr)
return ThreadPlanSP();
const char *first_mnemonic = first_insn->GetMnemonic(&exe_ctx);
const char *second_mnemonic = second_insn->GetMnemonic(&exe_ctx);
if (!first_mnemonic || !second_mnemonic ||
strcmp(first_mnemonic, "jmpl") != 0 ||
strcmp(second_mnemonic, "nop") != 0)
return ThreadPlanSP();
assert(first_insn->DoesBranch() && !second_insn->DoesBranch());
return ThreadPlanSP(new ThreadPlanStepInstruction(
thread, false, false, eVoteNoOpinion, eVoteNoOpinion));
}
lldb::addr_t
DynamicLoaderWindowsDYLD::GetThreadLocalData(const lldb::ModuleSP module,
lldb::ThreadSP thread,
lldb::addr_t tls_file_addr) {
Log *log = GetLog(LLDBLog::DynamicLoader);
ObjectFile *obj = module->GetObjectFile();
if (!obj) {
LLDB_LOG(log, "no object file for module");
return LLDB_INVALID_ADDRESS;
}
std::string object_name;
if (log)
object_name = obj->GetObjectName();
StructuredData::ObjectSP extended_info = thread->GetExtendedInfo();
if (!extended_info) {
LLDB_LOG(log, "missing extended info of thread");
return LLDB_INVALID_ADDRESS;
}
StructuredData::Dictionary *dict = extended_info->GetAsDictionary();
if (!dict) {
LLDB_LOG(log, "extended info of thread is not a dictionary");
return LLDB_INVALID_ADDRESS;
}
addr_t teb_base = 0;
if (!dict->GetValueForKeyAsInteger("teb_address", teb_base) || !teb_base ||
teb_base == LLDB_INVALID_ADDRESS) {
LLDB_LOG(log, "missing 'teb_address' in extended thread info");
return LLDB_INVALID_ADDRESS;
}
ProcessSP process = thread->GetProcess();
if (!process) {
LLDB_LOG(log, "missing process");
return LLDB_INVALID_ADDRESS;
}
uint32_t addr_byte_size = process->GetAddressByteSize();
addr_t tls_array_addr = teb_base + 11 * addr_byte_size;
llvm::Expected<addr_t> tls_array =
process->ReadPointerFromMemory(tls_array_addr);
if (!tls_array || *tls_array == LLDB_INVALID_ADDRESS) {
LLDB_LOG_ERROR(log, tls_array.takeError(),
"failed to read TLS array from {1:x16}: {0}",
tls_array_addr);
return LLDB_INVALID_ADDRESS;
}
Address tls_index_addr;
if (!GetTlsIndexAddressForModule(module, tls_index_addr)) {
LLDB_LOG(log, "failed to resolve tls index for {0}", object_name);
return LLDB_INVALID_ADDRESS;
}
Target &target = process->GetTarget();
addr_t tls_index_load_addr = tls_index_addr.GetLoadAddress(&target);
if (tls_index_load_addr == LLDB_INVALID_ADDRESS) {
LLDB_LOG(log, "failed to resolve load address of '_tls_index' in {0}",
object_name);
return LLDB_INVALID_ADDRESS;
}
Status error;
uint64_t tls_index =
process->ReadUnsignedIntegerFromMemory(tls_index_load_addr, 4, 0, error);
if (error.Fail()) {
LLDB_LOG_ERROR(log, error.takeError(),
"failed to read '_tls_index' in {1} from {2:x8}: {0}",
object_name, tls_index_load_addr);
return LLDB_INVALID_ADDRESS;
}
addr_t tls_index_item = *tls_array + tls_index * addr_byte_size;
LLDB_LOG(log, "tls base address for {0} is at {1:x8} + {2:x8} * {3} = {4:x8}",
object_name, *tls_array, tls_index, addr_byte_size, tls_index_item);
llvm::Expected<addr_t> tls_base =
process->ReadPointerFromMemory(tls_index_item);
if (!tls_base || *tls_base == LLDB_INVALID_ADDRESS || *tls_base == 0) {
LLDB_LOG_ERROR(log, tls_base.takeError(),
"failed to read tls array in {1} at index {2} ({3:x8}): {0}",
object_name, tls_index, tls_index_item);
return LLDB_INVALID_ADDRESS;
}
return *tls_base + tls_file_addr;
}
bool DynamicLoaderWindowsDYLD::GetTlsIndexAddressForModule(
const ModuleSP &module, Address &addr) const {
ObjectFilePECOFF *obj =
llvm::dyn_cast_or_null<ObjectFilePECOFF>(module->GetObjectFile());
if (obj) {
addr_t tls_index_file_addr = obj->GetTlsIndexAddress();
if (tls_index_file_addr != LLDB_INVALID_ADDRESS &&
module->ResolveFileAddress(tls_index_file_addr, addr))
return true;
}
// If we can't get the tls index from the object file, try the _tls_index
// symbol.
Symtab *symtab = module->GetSymtab();
if (!symtab)
return false;
Symbol *tls_index_sym = symtab->FindFirstSymbolWithNameAndType(
ConstString("_tls_index"), eSymbolTypeAny, Symtab::eDebugAny,
Symtab::eVisibilityAny);
if (!tls_index_sym)
return false;
addr = tls_index_sym->GetAddress();
return addr.IsValid();
}