Index: include/lldb/Host/common/NativeRegisterContext.h =================================================================== --- include/lldb/Host/common/NativeRegisterContext.h +++ include/lldb/Host/common/NativeRegisterContext.h @@ -111,6 +111,9 @@ virtual lldb::addr_t GetWatchpointAddress (uint32_t wp_index); + virtual lldb::addr_t + GetWatchpointHitAddress (uint32_t wp_index); + virtual bool HardwareSingleStep (bool enable); Index: include/lldb/Target/StopInfo.h =================================================================== --- include/lldb/Target/StopInfo.h +++ include/lldb/Target/StopInfo.h @@ -161,7 +161,7 @@ CreateStopReasonWithBreakpointSiteID (Thread &thread, lldb::break_id_t break_id, bool should_stop); static lldb::StopInfoSP - CreateStopReasonWithWatchpointID (Thread &thread, lldb::break_id_t watch_id); + CreateStopReasonWithWatchpointID (Thread &thread, lldb::break_id_t watch_id, lldb::addr_t watch_hit_addr = LLDB_INVALID_ADDRESS); static lldb::StopInfoSP CreateStopReasonWithSignal (Thread &thread, int signo, const char *description = nullptr); Index: source/Host/common/NativeRegisterContext.cpp =================================================================== --- source/Host/common/NativeRegisterContext.cpp +++ source/Host/common/NativeRegisterContext.cpp @@ -334,6 +334,12 @@ return LLDB_INVALID_ADDRESS; } +lldb::addr_t +NativeRegisterContext::GetWatchpointHitAddress (uint32_t wp_index) +{ + return LLDB_INVALID_ADDRESS; +} + bool NativeRegisterContext::HardwareSingleStep (bool enable) { Index: source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h =================================================================== --- source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h +++ source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.h @@ -128,6 +128,12 @@ Emulate_SD (llvm::MCInst& insn); bool + Emulate_SW (llvm::MCInst& insn); + + bool + Emulate_LW (llvm::MCInst& insn); + + bool Emulate_LD (llvm::MCInst& insn); bool Index: source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp =================================================================== --- source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp +++ source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp @@ -410,6 +410,9 @@ { "SD", &EmulateInstructionMIPS64::Emulate_SD, "SD rt,offset(rs)" }, { "LD", &EmulateInstructionMIPS64::Emulate_LD, "LD rt,offset(base)" }, + { "SW", &EmulateInstructionMIPS64::Emulate_SW, "SW rt,offset(rs)" }, + { "LW", &EmulateInstructionMIPS64::Emulate_LW, "LW rt,offset(rs)" }, + //---------------------------------------------------------------------- // Branch instructions //---------------------------------------------------------------------- @@ -657,35 +660,95 @@ } bool +EmulateInstructionMIPS64::Emulate_SW (llvm::MCInst& insn) +{ + bool success = false; + uint32_t base; + int64_t imm, address; + Context bad_vaddr_context; + + base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); + imm = insn.getOperand(2).getImm(); + + RegisterInfo reg_info_base; + if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, reg_info_base)) + return false; + + /* read base register */ + address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, 0, &success); + if (!success) + return false; + + /* destination address */ + address = address + imm; + + /* Set the bad_vaddr register with base address used in the instruction */ + bad_vaddr_context.type = eContextInvalid; + WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, gcc_dwarf_bad_mips64, address); + + return true; +} + +bool +EmulateInstructionMIPS64::Emulate_LW (llvm::MCInst& insn) +{ + bool success = false; + uint32_t base; + int64_t imm, address; + Context bad_vaddr_context; + + base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); + imm = insn.getOperand(2).getImm(); + + RegisterInfo reg_info_base; + if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, reg_info_base)) + return false; + + /* read base register */ + address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, 0, &success); + if (!success) + return false; + + /* destination address */ + address = address + imm; + + /* Set the bad_vaddr register with base address used in the instruction */ + bad_vaddr_context.type = eContextInvalid; + WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, gcc_dwarf_bad_mips64, address); + + return true; +} + +bool EmulateInstructionMIPS64::Emulate_SD (llvm::MCInst& insn) { + uint64_t address; + RegisterInfo reg_info_base; + RegisterInfo reg_info_src; bool success = false; uint32_t imm16 = insn.getOperand(2).getImm(); uint64_t imm = SignedBits(imm16, 15, 0); uint32_t src, base; + Context bad_vaddr_context; src = m_reg_info->getEncodingValue (insn.getOperand(0).getReg()); base = m_reg_info->getEncodingValue (insn.getOperand(1).getReg()); - /* We look for sp based non-volatile register stores */ - if (base == gcc_dwarf_sp_mips64 && nonvolatile_reg_p (src)) - { - uint64_t address; - RegisterInfo reg_info_base; - RegisterInfo reg_info_src; - - if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, reg_info_base) - || !GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + src, reg_info_src)) - return false; + if (!GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, reg_info_base) + || !GetRegisterInfo (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + src, reg_info_src)) + return false; - /* read SP */ - address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, 0, &success); - if (!success) - return false; + /* read SP */ + address = ReadRegisterUnsigned (eRegisterKindDWARF, gcc_dwarf_zero_mips64 + base, 0, &success); + if (!success) + return false; - /* destination address */ - address = address + imm; + /* destination address */ + address = address + imm; + /* We look for sp based non-volatile register stores */ + if (base == gcc_dwarf_sp_mips64 && nonvolatile_reg_p (src)) + { Context context; RegisterValue data_src; context.type = eContextPushRegisterOnStack; @@ -702,11 +765,13 @@ if (!WriteMemory (context, address, buffer, reg_info_src.byte_size)) return false; - - return true; } - return false; + /* Set the bad_vaddr register with base address used in the instruction */ + bad_vaddr_context.type = eContextInvalid; + WriteRegisterUnsigned (bad_vaddr_context, eRegisterKindDWARF, gcc_dwarf_bad_mips64, address); + + return true; } bool Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.h @@ -36,6 +36,9 @@ lldb::addr_t GetPCfromBreakpointLocation (lldb::addr_t fail_value = LLDB_INVALID_ADDRESS) override; + lldb::addr_t + GetWatchpointHitAddress (uint32_t wp_index) override; + const RegisterSet * GetRegisterSet (uint32_t set_index) const override; Index: source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp =================================================================== --- source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp +++ source/Plugins/Process/Linux/NativeRegisterContextLinux_mips64.cpp @@ -20,7 +20,9 @@ #include "lldb/Core/Log.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Host/HostInfo.h" - +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-private-enumerations.h" #include "Plugins/Process/Linux/NativeProcessLinux.h" #include "Plugins/Process/Linux/Procfs.h" #include "Plugins/Process/Utility/RegisterContextLinux_mips64.h" @@ -997,6 +999,106 @@ return hw_addr_map[wp_index]; } +struct EmulatorBaton +{ + lldb::addr_t m_watch_hit_addr; + NativeProcessLinux* m_process; + NativeRegisterContext* m_reg_context; + + EmulatorBaton(NativeProcessLinux* process, NativeRegisterContext* reg_context) : + m_watch_hit_addr(LLDB_INVALID_ADDRESS), + m_process(process), + m_reg_context(reg_context) + {} +}; + +static size_t +ReadMemoryCallback (EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, lldb::addr_t addr, + void *dst, size_t length) +{ + size_t bytes_read; + EmulatorBaton* emulator_baton = static_cast(baton); + emulator_baton->m_process->ReadMemory(addr, dst, length, bytes_read); + return bytes_read; +} + +static size_t +WriteMemoryCallback (EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + lldb::addr_t addr, const void *dst, size_t length) +{ + return length; +} + +static bool +ReadRegisterCallback (EmulateInstruction *instruction, void *baton, + const RegisterInfo *reg_info, RegisterValue ®_value) +{ + EmulatorBaton* emulator_baton = static_cast(baton); + + const RegisterInfo* full_reg_info = emulator_baton->m_reg_context->GetRegisterInfo( + lldb::eRegisterKindDWARF, reg_info->kinds[lldb::eRegisterKindDWARF]); + + Error error = emulator_baton->m_reg_context->ReadRegister(full_reg_info, reg_value); + if (error.Success()) + return true; + + return false; +} + +static bool +WriteRegisterCallback (EmulateInstruction *instruction, void *baton, + const EmulateInstruction::Context &context, + const RegisterInfo *reg_info, const RegisterValue ®_value) +{ + if (reg_info->kinds[lldb::eRegisterKindDWARF] == gcc_dwarf_bad_mips64) + { + EmulatorBaton* emulator_baton = static_cast(baton); + emulator_baton->m_watch_hit_addr = reg_value.GetAsUInt64 (); + } + + return true; +} + +/* + * MIPS Linux kernel returns a masked address (last 3bits are masked) + * when a HW watchpoint is hit. However user may not have set a watchpoint + * on this address. Emulate instruction at PC and find the base address of + * the load/store instruction. This will give the exact address used to + * read/write the variable. Send this exact address to client so that + * it can decide to stop or continue the thread. +*/ +lldb::addr_t +NativeRegisterContextLinux_mips64::GetWatchpointHitAddress (uint32_t wp_index) +{ + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + + lldb_private::ArchSpec arch; + arch = GetRegisterInfoInterface().GetTargetArchitecture(); + std::unique_ptr emulator_ap( + EmulateInstruction::FindPlugin(arch, lldb_private::eInstructionTypeAny, nullptr)); + + if (emulator_ap == nullptr) + return LLDB_INVALID_ADDRESS; + + EmulatorBaton baton(static_cast(m_thread.GetProcess().get()), this); + emulator_ap->SetBaton (&baton); + emulator_ap->SetReadMemCallback (&ReadMemoryCallback); + emulator_ap->SetReadRegCallback (&ReadRegisterCallback); + emulator_ap->SetWriteMemCallback (&WriteMemoryCallback); + emulator_ap->SetWriteRegCallback (&WriteRegisterCallback); + + if (!emulator_ap->ReadInstruction()) + return LLDB_INVALID_ADDRESS; + + if (emulator_ap->EvaluateInstruction(lldb::eEmulateInstructionOptionNone)) + return baton.m_watch_hit_addr; + + return LLDB_INVALID_ADDRESS; +} + uint32_t NativeRegisterContextLinux_mips64::NumSupportedHardwareWatchpoints () { Index: source/Plugins/Process/Linux/NativeThreadLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -333,6 +333,16 @@ std::ostringstream ostr; ostr << GetRegisterContext()->GetWatchpointAddress(wp_index) << " "; ostr << wp_index; + + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at 'm', then + * watch exception is generated even when 'n' is read/written. To handle this case, + * find the base address of the load/store instruction and append it in the stop-info + * packet. + */ + ostr << " " << GetRegisterContext()->GetWatchpointHitAddress(wp_index); + m_stop_description = ostr.str(); m_stop_info.reason = StopReason::eStopReasonWatchpoint; Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -2020,6 +2020,7 @@ StringExtractor desc_extractor(description.c_str()); addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32); + addr_t wp_hit_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS); watch_id_t watch_id = LLDB_INVALID_WATCH_ID; if (wp_addr != LLDB_INVALID_ADDRESS) { @@ -2035,7 +2036,7 @@ Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS)); if (log) log->Printf ("failed to find watchpoint"); } - thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id)); + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id, wp_hit_addr)); handled = true; } else if (reason.compare("exception") == 0) Index: source/Target/StopInfo.cpp =================================================================== --- source/Target/StopInfo.cpp +++ source/Target/StopInfo.cpp @@ -614,10 +614,11 @@ Watchpoint *watchpoint; }; - StopInfoWatchpoint (Thread &thread, break_id_t watch_id) : + StopInfoWatchpoint (Thread &thread, break_id_t watch_id, lldb::addr_t watch_hit_addr) : StopInfo(thread, watch_id), m_should_stop(false), - m_should_stop_is_valid(false) + m_should_stop_is_valid(false), + m_watch_hit_addr(watch_hit_addr) { } @@ -744,6 +745,21 @@ } } + /* + * MIPS: Last 3bits of the watchpoint address are masked by the kernel. For example: + * 'n' is at 0x120010d00 and 'm' is 0x120010d04. When a watchpoint is set at 'm', then + * watch exception is generated even when 'n' is read/written. To handle this case, + * server emulates the instruction at PC and finds the base address of the load/store + * instruction and appends it in the description of the stop-info packet. If watchpoint + * is not set on this address by user then this do not stop. + */ + if (m_watch_hit_addr != LLDB_INVALID_ADDRESS) + { + WatchpointSP wp_hit_sp = thread_sp->CalculateTarget()->GetWatchpointList().FindByAddress(m_watch_hit_addr); + if (!wp_hit_sp) + m_should_stop = false; + } + if (m_should_stop && wp_sp->GetConditionText() != NULL) { // We need to make sure the user sees any parse errors in their condition, so we'll hook the @@ -856,6 +872,7 @@ private: bool m_should_stop; bool m_should_stop_is_valid; + lldb::addr_t m_watch_hit_addr; }; @@ -1153,9 +1170,9 @@ } StopInfoSP -StopInfo::CreateStopReasonWithWatchpointID (Thread &thread, break_id_t watch_id) +StopInfo::CreateStopReasonWithWatchpointID (Thread &thread, break_id_t watch_id, lldb::addr_t watch_hit_addr) { - return StopInfoSP (new StopInfoWatchpoint (thread, watch_id)); + return StopInfoSP (new StopInfoWatchpoint (thread, watch_id, watch_hit_addr)); } StopInfoSP