[Lldb-commits] [lldb] r246015 - [MIPS] Avoid breakpoint in delay slot

Bhushan D. Attarde via lldb-commits lldb-commits at lists.llvm.org
Tue Aug 25 23:04:55 PDT 2015


Author: bhushan.attarde
Date: Wed Aug 26 01:04:54 2015
New Revision: 246015

URL: http://llvm.org/viewvc/llvm-project?rev=246015&view=rev
Log:
[MIPS] Avoid breakpoint in delay slot
    
    SUMMARY:
    This patch implements Target::GetBreakableLoadAddress() method that takes an address
    and checks for any reason there is a better address than this to put a breakpoint on.
    If there is then return that address.
    MIPS uses this method to avoid breakpoint in delay slot.
    
    Reviewers: clayborg, jingham
    Subscribers: jingham, mohit.bhakkad, sagar, jaydeep, nitesh.jain, lldb-commits
    Differential Revision: http://http://reviews.llvm.org/D12184

Modified:
    lldb/trunk/include/lldb/Core/Disassembler.h
    lldb/trunk/include/lldb/Target/Target.h
    lldb/trunk/source/Core/Disassembler.cpp
    lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
    lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
    lldb/trunk/source/Target/Target.cpp

Modified: lldb/trunk/include/lldb/Core/Disassembler.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Core/Disassembler.h?rev=246015&r1=246014&r2=246015&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Core/Disassembler.h (original)
+++ lldb/trunk/include/lldb/Core/Disassembler.h Wed Aug 26 01:04:54 2015
@@ -142,6 +142,9 @@ public:
     virtual bool
     DoesBranch () = 0;
 
+    virtual bool
+    HasDelaySlot ();
+
     virtual size_t
     Decode (const Disassembler &disassembler, 
             const DataExtractor& data,
@@ -266,6 +269,9 @@ public:
     virtual bool
     DoesBranch ();
 
+    virtual bool
+    HasDelaySlot ();
+
     virtual void
     CalculateMnemonicOperandsAndComment (const ExecutionContext* exe_ctx)
     {

Modified: lldb/trunk/include/lldb/Target/Target.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Target.h?rev=246015&r1=246014&r2=246015&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/Target.h (original)
+++ lldb/trunk/include/lldb/Target/Target.h Wed Aug 26 01:04:54 2015
@@ -920,6 +920,14 @@ public:
     lldb::addr_t
     GetOpcodeLoadAddress (lldb::addr_t load_addr, lldb::AddressClass addr_class = lldb::eAddressClassInvalid) const;
 
+    // Get load_addr as breakable load address for this target.
+    // Take a addr and check if for any reason there is a better address than this to put a breakpoint on.
+    // If there is then return that address.
+    // For MIPS, if instruction at addr is a delay slot instruction then this method will find the address of its
+    // previous instruction and return that address.
+    lldb::addr_t
+    GetBreakableLoadAddress (lldb::addr_t addr);
+
 protected:
     //------------------------------------------------------------------
     /// Implementing of ModuleList::Notifier.

Modified: lldb/trunk/source/Core/Disassembler.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Core/Disassembler.cpp?rev=246015&r1=246014&r2=246015&view=diff
==============================================================================
--- lldb/trunk/source/Core/Disassembler.cpp (original)
+++ lldb/trunk/source/Core/Disassembler.cpp Wed Aug 26 01:04:54 2015
@@ -674,6 +674,13 @@ Instruction::DumpEmulation (const ArchSp
     return false;
 }
 
+bool
+Instruction::HasDelaySlot ()
+{
+    // Default is false.
+    return false;
+}
+
 OptionValueSP
 Instruction::ReadArray (FILE *in_file, Stream *out_stream, OptionValue::Type data_type)
 {
@@ -1318,6 +1325,13 @@ PseudoInstruction::DoesBranch ()
     return false;
 }
     
+bool
+PseudoInstruction::HasDelaySlot ()
+{
+    // This is NOT a valid question for a pseudo instruction.
+    return false;
+}
+
 size_t
 PseudoInstruction::Decode (const lldb_private::Disassembler &disassembler,
                            const lldb_private::DataExtractor &data,

Modified: lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp?rev=246015&r1=246014&r2=246015&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp (original)
+++ lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp Wed Aug 26 01:04:54 2015
@@ -52,6 +52,7 @@ public:
         Instruction (address, addr_class),
         m_disasm_sp (disasm.shared_from_this()),
         m_does_branch (eLazyBoolCalculate),
+        m_has_delay_slot (eLazyBoolCalculate),
         m_is_valid (false),
         m_using_file_addr (false)
     {
@@ -99,6 +100,43 @@ public:
         return m_does_branch == eLazyBoolYes;
     }
 
+    virtual bool
+    HasDelaySlot ()
+    {
+        if (m_has_delay_slot == eLazyBoolCalculate)
+        {
+            GetDisassemblerLLVMC().Lock(this, NULL);
+            DataExtractor data;
+            if (m_opcode.GetData(data))
+            {
+                bool is_alternate_isa;
+                lldb::addr_t pc = m_address.GetFileAddress();
+
+                DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa);
+                const uint8_t *opcode_data = data.GetDataStart();
+                const size_t opcode_data_len = data.GetByteSize();
+                llvm::MCInst inst;
+                const size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data,
+                                                                   opcode_data_len,
+                                                                   pc,
+                                                                   inst);
+                // if we didn't understand the instruction, say it doesn't have a delay slot...
+                if (inst_size == 0)
+                    m_has_delay_slot = eLazyBoolNo;
+                else
+                {
+                    const bool has_delay_slot = mc_disasm_ptr->HasDelaySlot(inst);
+                    if (has_delay_slot)
+                        m_has_delay_slot = eLazyBoolYes;
+                    else
+                        m_has_delay_slot = eLazyBoolNo;
+                }
+            }
+            GetDisassemblerLLVMC().Unlock();
+        }
+        return m_has_delay_slot == eLazyBoolYes;
+    }
+
     DisassemblerLLVMC::LLVMCDisassembler *
     GetDisasmToUse (bool &is_alternate_isa)
     {
@@ -409,6 +447,7 @@ protected:
 
     DisassemblerSP          m_disasm_sp; // for ownership
     LazyBool                m_does_branch;
+    LazyBool                m_has_delay_slot;
     bool                    m_is_valid;
     bool                    m_using_file_addr;
 };
@@ -543,6 +582,12 @@ DisassemblerLLVMC::LLVMCDisassembler::Ca
 }
 
 bool
+DisassemblerLLVMC::LLVMCDisassembler::HasDelaySlot (llvm::MCInst &mc_inst)
+{
+    return m_instr_info_ap->get(mc_inst.getOpcode()).hasDelaySlot();
+}
+
+bool
 DisassemblerLLVMC::FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor)
 {
     llvm::Triple triple = arch.GetTriple();

Modified: lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h?rev=246015&r1=246014&r2=246015&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h (original)
+++ lldb/trunk/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h Wed Aug 26 01:04:54 2015
@@ -49,6 +49,7 @@ class DisassemblerLLVMC : public lldb_pr
         uint64_t PrintMCInst (llvm::MCInst &mc_inst, char *output_buffer, size_t out_buffer_len);
         void     SetStyle (bool use_hex_immed, HexImmediateStyle hex_style);
         bool     CanBranch (llvm::MCInst &mc_inst);
+        bool     HasDelaySlot (llvm::MCInst &mc_inst);
         bool     IsValid()
         {
             return m_is_valid;

Modified: lldb/trunk/source/Target/Target.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Target.cpp?rev=246015&r1=246014&r2=246015&view=diff
==============================================================================
--- lldb/trunk/source/Target/Target.cpp (original)
+++ lldb/trunk/source/Target/Target.cpp Wed Aug 26 01:04:54 2015
@@ -44,6 +44,8 @@
 #include "lldb/Interpreter/Property.h"
 #include "lldb/Symbol/ClangASTContext.h"
 #include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
 #include "lldb/Target/LanguageRuntime.h"
 #include "lldb/Target/ObjCLanguageRuntime.h"
 #include "lldb/Target/Process.h"
@@ -344,6 +346,10 @@ BreakpointSP
 Target::CreateBreakpoint (lldb::addr_t addr, bool internal, bool hardware)
 {
     Address so_addr;
+    
+    // Check for any reason we want to move this breakpoint to other address.
+    addr = GetBreakableLoadAddress(addr);
+
     // Attempt to resolve our load address if possible, though it is ok if
     // it doesn't resolve to section/offset.
 
@@ -2134,6 +2140,170 @@ Target::GetOpcodeLoadAddress (lldb::addr
     return opcode_addr;
 }
 
+lldb::addr_t
+Target::GetBreakableLoadAddress (lldb::addr_t addr)
+{
+    addr_t breakable_addr = addr;
+    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+
+    switch (m_arch.GetMachine())
+    {
+    default:
+        break;
+    case llvm::Triple::mips:
+    case llvm::Triple::mipsel:
+    case llvm::Triple::mips64:
+    case llvm::Triple::mips64el:
+    {
+        addr_t function_start = 0;
+        addr_t current_offset = 0;
+        uint32_t loop_count = 0;
+        Address resolved_addr;
+        uint32_t arch_flags = m_arch.GetFlags ();
+        bool IsMips16 = arch_flags & ArchSpec::eMIPSAse_mips16;
+        bool IsMicromips = arch_flags & ArchSpec::eMIPSAse_micromips;
+        SectionLoadList &section_load_list = GetSectionLoadList();
+
+        if (section_load_list.IsEmpty())
+            // No sections are loaded, so we must assume we are not running yet
+            // and need to operate only on file address.
+            m_images.ResolveFileAddress (addr, resolved_addr); 
+        else
+            section_load_list.ResolveLoadAddress(addr, resolved_addr);
+
+        // Get the function boundaries to make sure we don't scan back before the beginning of the current function.
+        ModuleSP temp_addr_module_sp (resolved_addr.GetModule());
+        if (temp_addr_module_sp)
+        {
+            SymbolContext sc;
+            uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol;
+            uint32_t resolved_mask = temp_addr_module_sp->ResolveSymbolContextForAddress(resolved_addr, resolve_scope, sc);
+            if (sc.function)
+            {
+                function_start = sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress(this);
+                if (function_start == LLDB_INVALID_ADDRESS)
+                    function_start = sc.function->GetAddressRange().GetBaseAddress().GetFileAddress();
+            }
+            else if (sc.symbol)
+            {
+                Address sym_addr = sc.symbol->GetAddress();
+                function_start = sym_addr.GetFileAddress();
+            }
+            current_offset = addr - function_start;
+        }
+
+        // If breakpoint address is start of function then we dont have to do anything.
+        if (current_offset == 0)
+            return breakable_addr;
+        else
+            loop_count = current_offset / 2;
+
+        if (loop_count > 3)
+        {
+            // Scan previous 6 bytes
+            if (IsMips16 | IsMicromips)
+                loop_count = 3;
+            // For mips-only, instructions are always 4 bytes, so scan previous 4 bytes only.
+            else
+                loop_count = 2;
+        }
+
+        // Create Disassembler Instance
+        lldb::DisassemblerSP disasm_sp (Disassembler::FindPlugin(m_arch, NULL, NULL));
+
+        ExecutionContext exe_ctx;
+        CalculateExecutionContext(exe_ctx);
+        InstructionList instruction_list;
+        InstructionSP prev_insn;
+        bool prefer_file_cache = true; // Read from file
+        uint32_t inst_to_choose = 0;
+
+        for (uint32_t i = 1; i <= loop_count; i++)
+        {
+            // Adjust the address to read from.
+            resolved_addr.Slide (-2);
+            AddressRange range(resolved_addr, i*2);
+            uint32_t insn_size = 0;
+
+            uint32_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, NULL, prefer_file_cache);
+            
+            uint32_t num_insns = disasm_sp->GetInstructionList().GetSize();
+            if (num_insns)
+            {
+                prev_insn = disasm_sp->GetInstructionList().GetInstructionAtIndex(0);
+                insn_size = prev_insn->GetOpcode().GetByteSize();
+                if (i == 1 && insn_size == 2)
+                {
+                    // This looks like a valid 2-byte instruction (but it could be a part of upper 4 byte instruction).
+                    instruction_list.Append(prev_insn);
+                    inst_to_choose = 1;
+                }
+                else if (i == 2)
+                {
+                    // Here we may get one 4-byte instruction or two 2-byte instructions.
+                    if (num_insns == 2)
+                    {
+                        // Looks like there are two 2-byte instructions above our breakpoint target address.
+                        // Now the upper 2-byte instruction is either a valid 2-byte instruction or could be a part of it's upper 4-byte instruction.
+                        // In both cases we don't care because in this case lower 2-byte instruction is definitely a valid instruction
+                        // and whatever i=1 iteration has found out is true.
+                        inst_to_choose = 1;
+                        break;
+                    }
+                    else if (insn_size == 4)
+                    {
+                        // This instruction claims its a valid 4-byte instruction. But it could be a part of it's upper 4-byte instruction.
+                        // Lets try scanning upper 2 bytes to verify this.
+                        instruction_list.Append(prev_insn);
+                        inst_to_choose = 2;
+                    }
+                }
+                else if (i == 3)
+                {
+                    if (insn_size == 4)
+                        // FIXME: We reached here that means instruction at [target - 4] has already claimed to be a 4-byte instruction,
+                        // and now instruction at [target - 6] is also claiming that it's a 4-byte instruction. This can not be true.
+                        // In this case we can not decide the valid previous instruction so we let lldb set the breakpoint at the address given by user.
+                        inst_to_choose = 0;
+                    else
+                        // This is straight-forward 
+                        inst_to_choose = 2;
+                    break;
+                }
+            }
+            else
+            {
+                // Decode failed, bytes do not form a valid instruction. So whatever previous iteration has found out is true.
+                if (i > 1)
+                {
+                    inst_to_choose = i - 1;
+                    break;
+                }
+            }
+        }
+
+        // Check if we are able to find any valid instruction.
+        if (inst_to_choose)
+        {
+            if (inst_to_choose > instruction_list.GetSize())
+                inst_to_choose--;
+            prev_insn = instruction_list.GetInstructionAtIndex(inst_to_choose - 1);
+
+            if (prev_insn->HasDelaySlot())
+            {
+                uint32_t shift_size = prev_insn->GetOpcode().GetByteSize();
+                // Adjust the breakable address
+                breakable_addr = addr - shift_size;
+                if (log)
+                    log->Printf ("Target::%s Breakpoint at 0x%8.8" PRIx64 " is adjusted to 0x%8.8" PRIx64 " due to delay slot\n", __FUNCTION__, addr, breakable_addr);
+            }
+        }
+        break;
+    }
+    }
+    return breakable_addr;
+}
+
 SourceManager &
 Target::GetSourceManager ()
 {




More information about the lldb-commits mailing list