diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Target/ThreadPlanCallFunction.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Target/ThreadPlanCallFunction.cpp')
-rw-r--r-- | gnu/llvm/lldb/source/Target/ThreadPlanCallFunction.cpp | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Target/ThreadPlanCallFunction.cpp b/gnu/llvm/lldb/source/Target/ThreadPlanCallFunction.cpp new file mode 100644 index 00000000000..23d114e3099 --- /dev/null +++ b/gnu/llvm/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -0,0 +1,473 @@ +//===-- ThreadPlanCallFunction.cpp ------------------------------*- C++ -*-===// +// +// 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/Target/ThreadPlanCallFunction.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/DumpRegisterValue.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" + +#include <memory> + +using namespace lldb; +using namespace lldb_private; + +// ThreadPlanCallFunction: Plan to call a single function +bool ThreadPlanCallFunction::ConstructorSetup( + Thread &thread, ABI *&abi, lldb::addr_t &start_load_addr, + lldb::addr_t &function_load_addr) { + SetIsMasterPlan(true); + SetOkayToDiscard(false); + SetPrivate(true); + + ProcessSP process_sp(thread.GetProcess()); + if (!process_sp) + return false; + + abi = process_sp->GetABI().get(); + + if (!abi) + return false; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP)); + + SetBreakpoints(); + + m_function_sp = thread.GetRegisterContext()->GetSP() - abi->GetRedZoneSize(); + // If we can't read memory at the point of the process where we are planning + // to put our function, we're not going to get any further... + Status error; + process_sp->ReadUnsignedIntegerFromMemory(m_function_sp, 4, 0, error); + if (!error.Success()) { + m_constructor_errors.Printf( + "Trying to put the stack in unreadable memory at: 0x%" PRIx64 ".", + m_function_sp); + LLDB_LOGF(log, "ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this), + m_constructor_errors.GetData()); + return false; + } + + llvm::Expected<Address> start_address = GetTarget().GetEntryPointAddress(); + if (!start_address) { + m_constructor_errors.Printf( + "%s", llvm::toString(start_address.takeError()).c_str()); + LLDB_LOGF(log, "ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this), + m_constructor_errors.GetData()); + return false; + } + + m_start_addr = *start_address; + start_load_addr = m_start_addr.GetLoadAddress(&GetTarget()); + + // Checkpoint the thread state so we can restore it later. + if (log && log->GetVerbose()) + ReportRegisterState("About to checkpoint thread before function call. " + "Original register state was:"); + + if (!thread.CheckpointThreadState(m_stored_thread_state)) { + m_constructor_errors.Printf("Setting up ThreadPlanCallFunction, failed to " + "checkpoint thread state."); + LLDB_LOGF(log, "ThreadPlanCallFunction(%p): %s.", static_cast<void *>(this), + m_constructor_errors.GetData()); + return false; + } + function_load_addr = m_function_addr.GetLoadAddress(&GetTarget()); + + return true; +} + +ThreadPlanCallFunction::ThreadPlanCallFunction( + Thread &thread, const Address &function, const CompilerType &return_type, + llvm::ArrayRef<addr_t> args, const EvaluateExpressionOptions &options) + : ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), m_stop_other_threads(options.GetStopOthers()), + m_unwind_on_error(options.DoesUnwindOnError()), + m_ignore_breakpoints(options.DoesIgnoreBreakpoints()), + m_debug_execution(options.GetDebug()), + m_trap_exceptions(options.GetTrapExceptions()), m_function_addr(function), + m_function_sp(0), m_takedown_done(false), + m_should_clear_objc_exception_bp(false), + m_should_clear_cxx_exception_bp(false), + m_stop_address(LLDB_INVALID_ADDRESS), m_return_type(return_type) { + lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS; + ABI *abi = nullptr; + + if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr)) + return; + + if (!abi->PrepareTrivialCall(thread, m_function_sp, function_load_addr, + start_load_addr, args)) + return; + + ReportRegisterState("Function call was set up. Register state was:"); + + m_valid = true; +} + +ThreadPlanCallFunction::ThreadPlanCallFunction( + Thread &thread, const Address &function, + const EvaluateExpressionOptions &options) + : ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread, + eVoteNoOpinion, eVoteNoOpinion), + m_valid(false), m_stop_other_threads(options.GetStopOthers()), + m_unwind_on_error(options.DoesUnwindOnError()), + m_ignore_breakpoints(options.DoesIgnoreBreakpoints()), + m_debug_execution(options.GetDebug()), + m_trap_exceptions(options.GetTrapExceptions()), m_function_addr(function), + m_function_sp(0), m_takedown_done(false), + m_should_clear_objc_exception_bp(false), + m_should_clear_cxx_exception_bp(false), + m_stop_address(LLDB_INVALID_ADDRESS), m_return_type(CompilerType()) {} + +ThreadPlanCallFunction::~ThreadPlanCallFunction() { + DoTakedown(PlanSucceeded()); +} + +void ThreadPlanCallFunction::ReportRegisterState(const char *message) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + if (log && log->GetVerbose()) { + StreamString strm; + RegisterContext *reg_ctx = m_thread.GetRegisterContext().get(); + + log->PutCString(message); + + RegisterValue reg_value; + + for (uint32_t reg_idx = 0, num_registers = reg_ctx->GetRegisterCount(); + reg_idx < num_registers; ++reg_idx) { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx); + if (reg_ctx->ReadRegister(reg_info, reg_value)) { + DumpRegisterValue(reg_value, &strm, reg_info, true, false, + eFormatDefault); + strm.EOL(); + } + } + log->PutString(strm.GetString()); + } +} + +void ThreadPlanCallFunction::DoTakedown(bool success) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP)); + + if (!m_valid) { + // Don't call DoTakedown if we were never valid to begin with. + LLDB_LOGF(log, + "ThreadPlanCallFunction(%p): Log called on " + "ThreadPlanCallFunction that was never valid.", + static_cast<void *>(this)); + return; + } + + if (!m_takedown_done) { + if (success) { + SetReturnValue(); + } + LLDB_LOGF(log, + "ThreadPlanCallFunction(%p): DoTakedown called for thread " + "0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", + static_cast<void *>(this), m_thread.GetID(), m_valid, + IsPlanComplete()); + m_takedown_done = true; + m_stop_address = + m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC(); + m_real_stop_info_sp = GetPrivateStopInfo(); + if (!m_thread.RestoreRegisterStateFromCheckpoint(m_stored_thread_state)) { + LLDB_LOGF(log, + "ThreadPlanCallFunction(%p): DoTakedown failed to restore " + "register state", + static_cast<void *>(this)); + } + SetPlanComplete(success); + ClearBreakpoints(); + if (log && log->GetVerbose()) + ReportRegisterState("Restoring thread state after function call. " + "Restored register state:"); + } else { + LLDB_LOGF(log, + "ThreadPlanCallFunction(%p): DoTakedown called as no-op for " + "thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", + static_cast<void *>(this), m_thread.GetID(), m_valid, + IsPlanComplete()); + } +} + +void ThreadPlanCallFunction::WillPop() { DoTakedown(PlanSucceeded()); } + +void ThreadPlanCallFunction::GetDescription(Stream *s, DescriptionLevel level) { + if (level == eDescriptionLevelBrief) { + s->Printf("Function call thread plan"); + } else { + TargetSP target_sp(m_thread.CalculateTarget()); + s->Printf("Thread plan to call 0x%" PRIx64, + m_function_addr.GetLoadAddress(target_sp.get())); + } +} + +bool ThreadPlanCallFunction::ValidatePlan(Stream *error) { + if (!m_valid) { + if (error) { + if (m_constructor_errors.GetSize() > 0) + error->PutCString(m_constructor_errors.GetString()); + else + error->PutCString("Unknown error"); + } + return false; + } + + return true; +} + +Vote ThreadPlanCallFunction::ShouldReportStop(Event *event_ptr) { + if (m_takedown_done || IsPlanComplete()) + return eVoteYes; + else + return ThreadPlan::ShouldReportStop(event_ptr); +} + +bool ThreadPlanCallFunction::DoPlanExplainsStop(Event *event_ptr) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP | + LIBLLDB_LOG_PROCESS)); + m_real_stop_info_sp = GetPrivateStopInfo(); + + // If our subplan knows why we stopped, even if it's done (which would + // forward the question to us) we answer yes. + if (m_subplan_sp && m_subplan_sp->PlanExplainsStop(event_ptr)) { + SetPlanComplete(); + return true; + } + + // Check if the breakpoint is one of ours. + + StopReason stop_reason; + if (!m_real_stop_info_sp) + stop_reason = eStopReasonNone; + else + stop_reason = m_real_stop_info_sp->GetStopReason(); + LLDB_LOGF(log, + "ThreadPlanCallFunction::PlanExplainsStop: Got stop reason - %s.", + Thread::StopReasonAsCString(stop_reason)); + + if (stop_reason == eStopReasonBreakpoint && BreakpointsExplainStop()) + return true; + + // One more quirk here. If this event was from Halt interrupting the target, + // then we should not consider ourselves complete. Return true to + // acknowledge the stop. + if (Process::ProcessEventData::GetInterruptedFromEvent(event_ptr)) { + LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop: The event is an " + "Interrupt, returning true."); + return true; + } + // We control breakpoints separately from other "stop reasons." So first, + // check the case where we stopped for an internal breakpoint, in that case, + // continue on. If it is not an internal breakpoint, consult + // m_ignore_breakpoints. + + if (stop_reason == eStopReasonBreakpoint) { + ProcessSP process_sp(m_thread.CalculateProcess()); + uint64_t break_site_id = m_real_stop_info_sp->GetValue(); + BreakpointSiteSP bp_site_sp; + if (process_sp) + bp_site_sp = process_sp->GetBreakpointSiteList().FindByID(break_site_id); + if (bp_site_sp) { + uint32_t num_owners = bp_site_sp->GetNumberOfOwners(); + bool is_internal = true; + for (uint32_t i = 0; i < num_owners; i++) { + Breakpoint &bp = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint(); + LLDB_LOGF(log, + "ThreadPlanCallFunction::PlanExplainsStop: hit " + "breakpoint %d while calling function", + bp.GetID()); + + if (!bp.IsInternal()) { + is_internal = false; + break; + } + } + if (is_internal) { + LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop hit an " + "internal breakpoint, not stopping."); + return false; + } + } + + if (m_ignore_breakpoints) { + LLDB_LOGF(log, + "ThreadPlanCallFunction::PlanExplainsStop: we are ignoring " + "breakpoints, overriding breakpoint stop info ShouldStop, " + "returning true"); + m_real_stop_info_sp->OverrideShouldStop(false); + return true; + } else { + LLDB_LOGF(log, "ThreadPlanCallFunction::PlanExplainsStop: we are not " + "ignoring breakpoints, overriding breakpoint stop info " + "ShouldStop, returning true"); + m_real_stop_info_sp->OverrideShouldStop(true); + return false; + } + } else if (!m_unwind_on_error) { + // If we don't want to discard this plan, than any stop we don't understand + // should be propagated up the stack. + return false; + } else { + // If the subplan is running, any crashes are attributable to us. If we + // want to discard the plan, then we say we explain the stop but if we are + // going to be discarded, let whoever is above us explain the stop. But + // don't discard the plan if the stop would restart itself (for instance if + // it is a signal that is set not to stop. Check that here first. We just + // say we explain the stop but aren't done and everything will continue on + // from there. + + if (m_real_stop_info_sp && + m_real_stop_info_sp->ShouldStopSynchronous(event_ptr)) { + SetPlanComplete(false); + return m_subplan_sp ? m_unwind_on_error : false; + } else + return true; + } +} + +bool ThreadPlanCallFunction::ShouldStop(Event *event_ptr) { + // We do some computation in DoPlanExplainsStop that may or may not set the + // plan as complete. We need to do that here to make sure our state is + // correct. + DoPlanExplainsStop(event_ptr); + + if (IsPlanComplete()) { + ReportRegisterState("Function completed. Register state was:"); + return true; + } else { + return false; + } +} + +bool ThreadPlanCallFunction::StopOthers() { return m_stop_other_threads; } + +StateType ThreadPlanCallFunction::GetPlanRunState() { return eStateRunning; } + +void ThreadPlanCallFunction::DidPush() { + //#define SINGLE_STEP_EXPRESSIONS + + // Now set the thread state to "no reason" so we don't run with whatever + // signal was outstanding... Wait till the plan is pushed so we aren't + // changing the stop info till we're about to run. + + GetThread().SetStopInfoToNothing(); + +#ifndef SINGLE_STEP_EXPRESSIONS + m_subplan_sp = std::make_shared<ThreadPlanRunToAddress>( + m_thread, m_start_addr, m_stop_other_threads); + + m_thread.QueueThreadPlan(m_subplan_sp, false); + m_subplan_sp->SetPrivate(true); +#endif +} + +bool ThreadPlanCallFunction::WillStop() { return true; } + +bool ThreadPlanCallFunction::MischiefManaged() { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); + + if (IsPlanComplete()) { + LLDB_LOGF(log, "ThreadPlanCallFunction(%p): Completed call function plan.", + static_cast<void *>(this)); + + ThreadPlan::MischiefManaged(); + return true; + } else { + return false; + } +} + +void ThreadPlanCallFunction::SetBreakpoints() { + ProcessSP process_sp(m_thread.CalculateProcess()); + if (m_trap_exceptions && process_sp) { + m_cxx_language_runtime = + process_sp->GetLanguageRuntime(eLanguageTypeC_plus_plus); + m_objc_language_runtime = process_sp->GetLanguageRuntime(eLanguageTypeObjC); + + if (m_cxx_language_runtime) { + m_should_clear_cxx_exception_bp = + !m_cxx_language_runtime->ExceptionBreakpointsAreSet(); + m_cxx_language_runtime->SetExceptionBreakpoints(); + } + if (m_objc_language_runtime) { + m_should_clear_objc_exception_bp = + !m_objc_language_runtime->ExceptionBreakpointsAreSet(); + m_objc_language_runtime->SetExceptionBreakpoints(); + } + } +} + +void ThreadPlanCallFunction::ClearBreakpoints() { + if (m_trap_exceptions) { + if (m_cxx_language_runtime && m_should_clear_cxx_exception_bp) + m_cxx_language_runtime->ClearExceptionBreakpoints(); + if (m_objc_language_runtime && m_should_clear_objc_exception_bp) + m_objc_language_runtime->ClearExceptionBreakpoints(); + } +} + +bool ThreadPlanCallFunction::BreakpointsExplainStop() { + StopInfoSP stop_info_sp = GetPrivateStopInfo(); + + if (m_trap_exceptions) { + if ((m_cxx_language_runtime && + m_cxx_language_runtime->ExceptionBreakpointsExplainStop( + stop_info_sp)) || + (m_objc_language_runtime && + m_objc_language_runtime->ExceptionBreakpointsExplainStop( + stop_info_sp))) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_STEP)); + LLDB_LOGF(log, "ThreadPlanCallFunction::BreakpointsExplainStop - Hit an " + "exception breakpoint, setting plan complete."); + + SetPlanComplete(false); + + // If the user has set the ObjC language breakpoint, it would normally + // get priority over our internal catcher breakpoint, but in this case we + // can't let that happen, so force the ShouldStop here. + stop_info_sp->OverrideShouldStop(true); + return true; + } + } + + return false; +} + +void ThreadPlanCallFunction::SetStopOthers(bool new_value) { + m_subplan_sp->SetStopOthers(new_value); +} + +bool ThreadPlanCallFunction::RestoreThreadState() { + return GetThread().RestoreThreadStateFromCheckpoint(m_stored_thread_state); +} + +void ThreadPlanCallFunction::SetReturnValue() { + ProcessSP process_sp(m_thread.GetProcess()); + const ABI *abi = process_sp ? process_sp->GetABI().get() : nullptr; + if (abi && m_return_type.IsValid()) { + const bool persistent = false; + m_return_valobj_sp = + abi->GetReturnValueObject(m_thread, m_return_type, persistent); + } +} |