diff options
Diffstat (limited to 'lldb/source/Core/ArchSpec.cpp')
-rw-r--r-- | lldb/source/Core/ArchSpec.cpp | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/lldb/source/Core/ArchSpec.cpp b/lldb/source/Core/ArchSpec.cpp index 70f53d91efb..e7a5e489af1 100644 --- a/lldb/source/Core/ArchSpec.cpp +++ b/lldb/source/Core/ArchSpec.cpp @@ -24,6 +24,11 @@ #include "lldb/Host/Endian.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "Plugins/Process/Utility/ARMDefines.h" +#include "Plugins/Process/Utility/InstructionUtils.h" using namespace lldb; using namespace lldb_private; @@ -1081,3 +1086,108 @@ lldb_private::operator<(const ArchSpec& lhs, const ArchSpec& rhs) const ArchSpec::Core rhs_core = rhs.GetCore (); return lhs_core < rhs_core; } + +static void +StopInfoOverrideCallbackTypeARM(lldb_private::Thread &thread) +{ + // We need to check if we are stopped in Thumb mode in a IT instruction + // and detect if the condition doesn't pass. If this is the case it means + // we won't actually execute this instruction. If this happens we need to + // clear the stop reason to no thread plans think we are stopped for a + // reason and the plans should keep going. + // + // We do this because when single stepping many ARM processes, debuggers + // often use the BVR/BCR registers that says "stop when the PC is not + // equal to its current value". This method of stepping means we can end + // up stopping on instructions inside an if/then block that wouldn't get + // executed. By fixing this we can stop the debugger from seeming like + // you stepped through both the "if" _and_ the "else" clause when source + // level stepping because the debugger stops regardless due to the BVR/BCR + // triggering a stop. + // + // It also means we can set breakpoints on instructions inside an an + // if/then block and correctly skip them if we use the BKPT instruction. + // The ARM and Thumb BKPT instructions are unconditional even when executed + // in a Thumb IT block. + // + // If your debugger inserts software traps in ARM/Thumb code, it will + // need to use 16 and 32 bit instruction for 16 and 32 bit thumb + // instructions respectively. If your debugger inserts a 16 bit thumb + // trap on top of a 32 bit thumb instruction for an opcode that is inside + // an if/then, it will change the it/then to conditionally execute your + // 16 bit trap and then cause your program to crash if it executes the + // trailing 16 bits (the second half of the 32 bit thumb instruction you + // partially overwrote). + + RegisterContextSP reg_ctx_sp (thread.GetRegisterContext()); + if (reg_ctx_sp) + { + const uint32_t cpsr = reg_ctx_sp->GetFlags(0); + if (cpsr != 0) + { + // Read the J and T bits to get the ISETSTATE + const uint32_t J = Bit32(cpsr, 24); + const uint32_t T = Bit32(cpsr, 5); + const uint32_t ISETSTATE = J << 1 | T; + if (ISETSTATE == 0) + { + // NOTE: I am pretty sure we want to enable the code below + // that detects when we stop on an instruction in ARM mode + // that is conditional and the condition doesn't pass. This + // can happen if you set a breakpoint on an instruction that + // is conditional. We currently will _always_ stop on the + // instruction which is bad. You can also run into this while + // single stepping and you could appear to run code in the "if" + // and in the "else" clause because it would stop at all of the + // conditional instructions in both. + // In such cases, we really don't want to stop at this location. + // I will check with the lldb-dev list first before I enable this. +#if 0 + // ARM mode: check for condition on intsruction + const addr_t pc = reg_ctx_sp->GetPC(); + Error error; + // If we fail to read the opcode we will get UINT64_MAX as the + // result in "opcode" which we can use to detect if we read a + // valid opcode. + const uint64_t opcode = thread.GetProcess()->ReadUnsignedIntegerFromMemory(pc, 4, UINT64_MAX, error); + if (opcode <= UINT32_MAX) + { + const uint32_t condition = Bits32((uint32_t)opcode, 31, 28); + if (ARMConditionPassed(condition, cpsr) == false) + { + // We ARE stopped on an ARM instruction whose condition doesn't + // pass so this instruction won't get executed. + // Regardless of why it stopped, we need to clear the stop info + thread.SetStopInfo (StopInfoSP()); + } + } +#endif + } + else if (ISETSTATE == 1) + { + // Thumb mode + const uint32_t ITSTATE = Bits32 (cpsr, 15, 10) << 2 | Bits32 (cpsr, 26, 25); + if (ITSTATE != 0) + { + const uint32_t condition = Bits32(ITSTATE, 7, 4); + if (ARMConditionPassed(condition, cpsr) == false) + { + // We ARE stopped in a Thumb IT instruction on an instruction whose + // condition doesn't pass so this instruction won't get executed. + // Regardless of why it stopped, we need to clear the stop info + thread.SetStopInfo (StopInfoSP()); + } + } + } + } + } +} + +ArchSpec::StopInfoOverrideCallbackType +ArchSpec::GetStopInfoOverrideCallback () const +{ + const llvm::Triple::ArchType machine = GetMachine(); + if (machine == llvm::Triple::arm) + return StopInfoOverrideCallbackTypeARM; + return NULL; +} |