xref: /llvm-project/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp (revision 0642cd768b80665585c8500bed2933a3b99123dc)
18244fc50SMichał Górny //===-- NativeProcessSoftwareSingleStep.cpp -------------------------------===//
28244fc50SMichał Górny //
38244fc50SMichał Górny // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48244fc50SMichał Górny // See https://llvm.org/LICENSE.txt for license information.
58244fc50SMichał Górny // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68244fc50SMichał Górny //
78244fc50SMichał Górny //===----------------------------------------------------------------------===//
88244fc50SMichał Górny 
98244fc50SMichał Górny #include "NativeProcessSoftwareSingleStep.h"
108244fc50SMichał Górny 
118244fc50SMichał Górny #include "lldb/Core/EmulateInstruction.h"
128244fc50SMichał Górny #include "lldb/Host/common/NativeRegisterContext.h"
138244fc50SMichał Górny #include "lldb/Utility/RegisterValue.h"
148244fc50SMichał Górny 
158244fc50SMichał Górny #include <unordered_map>
168244fc50SMichał Górny 
178244fc50SMichał Górny using namespace lldb;
188244fc50SMichał Górny using namespace lldb_private;
198244fc50SMichał Górny 
208244fc50SMichał Górny namespace {
218244fc50SMichał Górny 
228244fc50SMichał Górny struct EmulatorBaton {
238244fc50SMichał Górny   NativeProcessProtocol &m_process;
248244fc50SMichał Górny   NativeRegisterContext &m_reg_context;
258244fc50SMichał Górny 
268244fc50SMichał Górny   // eRegisterKindDWARF -> RegsiterValue
278244fc50SMichał Górny   std::unordered_map<uint32_t, RegisterValue> m_register_values;
288244fc50SMichał Górny 
298244fc50SMichał Górny   EmulatorBaton(NativeProcessProtocol &process,
308244fc50SMichał Górny                 NativeRegisterContext &reg_context)
318244fc50SMichał Górny       : m_process(process), m_reg_context(reg_context) {}
328244fc50SMichał Górny };
338244fc50SMichał Górny 
348244fc50SMichał Górny } // anonymous namespace
358244fc50SMichał Górny 
368244fc50SMichał Górny static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
378244fc50SMichał Górny                                  const EmulateInstruction::Context &context,
388244fc50SMichał Górny                                  lldb::addr_t addr, void *dst, size_t length) {
398244fc50SMichał Górny   EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
408244fc50SMichał Górny 
418244fc50SMichał Górny   size_t bytes_read;
428244fc50SMichał Górny   emulator_baton->m_process.ReadMemory(addr, dst, length, bytes_read);
438244fc50SMichał Górny   return bytes_read;
448244fc50SMichał Górny }
458244fc50SMichał Górny 
468244fc50SMichał Górny static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
478244fc50SMichał Górny                                  const RegisterInfo *reg_info,
488244fc50SMichał Górny                                  RegisterValue &reg_value) {
498244fc50SMichał Górny   EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
508244fc50SMichał Górny 
518244fc50SMichał Górny   auto it = emulator_baton->m_register_values.find(
528244fc50SMichał Górny       reg_info->kinds[eRegisterKindDWARF]);
538244fc50SMichał Górny   if (it != emulator_baton->m_register_values.end()) {
548244fc50SMichał Górny     reg_value = it->second;
558244fc50SMichał Górny     return true;
568244fc50SMichał Górny   }
578244fc50SMichał Górny 
588244fc50SMichał Górny   // The emulator only fill in the dwarf regsiter numbers (and in some case the
598244fc50SMichał Górny   // generic register numbers). Get the full register info from the register
608244fc50SMichał Górny   // context based on the dwarf register numbers.
618244fc50SMichał Górny   const RegisterInfo *full_reg_info =
628244fc50SMichał Górny       emulator_baton->m_reg_context.GetRegisterInfo(
638244fc50SMichał Górny           eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);
648244fc50SMichał Górny 
658244fc50SMichał Górny   Status error =
668244fc50SMichał Górny       emulator_baton->m_reg_context.ReadRegister(full_reg_info, reg_value);
678244fc50SMichał Górny   if (error.Success())
688244fc50SMichał Górny     return true;
698244fc50SMichał Górny 
708244fc50SMichał Górny   return false;
718244fc50SMichał Górny }
728244fc50SMichał Górny 
738244fc50SMichał Górny static bool WriteRegisterCallback(EmulateInstruction *instruction, void *baton,
748244fc50SMichał Górny                                   const EmulateInstruction::Context &context,
758244fc50SMichał Górny                                   const RegisterInfo *reg_info,
768244fc50SMichał Górny                                   const RegisterValue &reg_value) {
778244fc50SMichał Górny   EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
788244fc50SMichał Górny   emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] =
798244fc50SMichał Górny       reg_value;
808244fc50SMichał Górny   return true;
818244fc50SMichał Górny }
828244fc50SMichał Górny 
838244fc50SMichał Górny static size_t WriteMemoryCallback(EmulateInstruction *instruction, void *baton,
848244fc50SMichał Górny                                   const EmulateInstruction::Context &context,
858244fc50SMichał Górny                                   lldb::addr_t addr, const void *dst,
868244fc50SMichał Górny                                   size_t length) {
878244fc50SMichał Górny   return length;
888244fc50SMichał Górny }
898244fc50SMichał Górny 
908244fc50SMichał Górny static lldb::addr_t ReadFlags(NativeRegisterContext &regsiter_context) {
918244fc50SMichał Górny   const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo(
928244fc50SMichał Górny       eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
938244fc50SMichał Górny   return regsiter_context.ReadRegisterAsUnsigned(flags_info,
948244fc50SMichał Górny                                                  LLDB_INVALID_ADDRESS);
958244fc50SMichał Górny }
968244fc50SMichał Górny 
97a1ffabc4Sita-sc static int GetSoftwareBreakpointSize(const ArchSpec &arch,
98a1ffabc4Sita-sc                                      lldb::addr_t next_flags) {
99a1ffabc4Sita-sc   if (arch.GetMachine() == llvm::Triple::arm) {
100a1ffabc4Sita-sc     if (next_flags & 0x20)
101a1ffabc4Sita-sc       // Thumb mode
102a1ffabc4Sita-sc       return 2;
103a1ffabc4Sita-sc     // Arm mode
104a1ffabc4Sita-sc     return 4;
105a1ffabc4Sita-sc   }
106a1ffabc4Sita-sc   if (arch.IsMIPS() || arch.GetTriple().isPPC64() ||
107a1ffabc4Sita-sc       arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch())
108a1ffabc4Sita-sc     return 4;
109a1ffabc4Sita-sc   return 0;
110a1ffabc4Sita-sc }
111a1ffabc4Sita-sc 
112a1ffabc4Sita-sc static Status SetSoftwareBreakpointOnPC(const ArchSpec &arch, lldb::addr_t pc,
113a1ffabc4Sita-sc                                         lldb::addr_t next_flags,
114a1ffabc4Sita-sc                                         NativeProcessProtocol &process) {
115a1ffabc4Sita-sc   int size_hint = GetSoftwareBreakpointSize(arch, next_flags);
116a1ffabc4Sita-sc   Status error;
117a1ffabc4Sita-sc   error = process.SetBreakpoint(pc, size_hint, /*hardware=*/false);
118a1ffabc4Sita-sc 
119a1ffabc4Sita-sc   // If setting the breakpoint fails because pc is out of the address
120a1ffabc4Sita-sc   // space, ignore it and let the debugee segfault.
121a1ffabc4Sita-sc   if (error.GetError() == EIO || error.GetError() == EFAULT)
122a1ffabc4Sita-sc     return Status();
123a1ffabc4Sita-sc   if (error.Fail())
124a1ffabc4Sita-sc     return error;
125a1ffabc4Sita-sc 
126a1ffabc4Sita-sc   return Status();
127a1ffabc4Sita-sc }
128a1ffabc4Sita-sc 
1298244fc50SMichał Górny Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
1308244fc50SMichał Górny     NativeThreadProtocol &thread) {
1318244fc50SMichał Górny   Status error;
1328244fc50SMichał Górny   NativeProcessProtocol &process = thread.GetProcess();
1338244fc50SMichał Górny   NativeRegisterContext &register_context = thread.GetRegisterContext();
1348244fc50SMichał Górny   const ArchSpec &arch = process.GetArchitecture();
1358244fc50SMichał Górny 
1368244fc50SMichał Górny   std::unique_ptr<EmulateInstruction> emulator_up(
1378244fc50SMichał Górny       EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying,
1388244fc50SMichał Górny                                      nullptr));
1398244fc50SMichał Górny 
1408244fc50SMichał Górny   if (emulator_up == nullptr)
141*0642cd76SAdrian Prantl     return Status::FromErrorString("Instruction emulator not found!");
1428244fc50SMichał Górny 
1438244fc50SMichał Górny   EmulatorBaton baton(process, register_context);
1448244fc50SMichał Górny   emulator_up->SetBaton(&baton);
1458244fc50SMichał Górny   emulator_up->SetReadMemCallback(&ReadMemoryCallback);
1468244fc50SMichał Górny   emulator_up->SetReadRegCallback(&ReadRegisterCallback);
1478244fc50SMichał Górny   emulator_up->SetWriteMemCallback(&WriteMemoryCallback);
1488244fc50SMichał Górny   emulator_up->SetWriteRegCallback(&WriteRegisterCallback);
1498244fc50SMichał Górny 
150a1ffabc4Sita-sc   if (!emulator_up->ReadInstruction()) {
151a1ffabc4Sita-sc     // try to get at least the size of next instruction to set breakpoint.
152a1ffabc4Sita-sc     auto instr_size = emulator_up->GetLastInstrSize();
153a1ffabc4Sita-sc     if (!instr_size)
154*0642cd76SAdrian Prantl       return Status::FromErrorString("Read instruction failed!");
155a1ffabc4Sita-sc     bool success = false;
156a1ffabc4Sita-sc     auto pc = emulator_up->ReadRegisterUnsigned(eRegisterKindGeneric,
157a1ffabc4Sita-sc                                                 LLDB_REGNUM_GENERIC_PC,
158a1ffabc4Sita-sc                                                 LLDB_INVALID_ADDRESS, &success);
159a1ffabc4Sita-sc     if (!success)
160*0642cd76SAdrian Prantl       return Status::FromErrorString("Reading pc failed!");
161a1ffabc4Sita-sc     lldb::addr_t next_pc = pc + *instr_size;
162a1ffabc4Sita-sc     auto result =
163a1ffabc4Sita-sc         SetSoftwareBreakpointOnPC(arch, next_pc, /* next_flags */ 0x0, process);
164a1ffabc4Sita-sc     m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
165a1ffabc4Sita-sc     return result;
166a1ffabc4Sita-sc   }
1678244fc50SMichał Górny 
1688244fc50SMichał Górny   bool emulation_result =
1698244fc50SMichał Górny       emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
1708244fc50SMichał Górny 
1718244fc50SMichał Górny   const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo(
1728244fc50SMichał Górny       eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
1738244fc50SMichał Górny   const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo(
1748244fc50SMichał Górny       eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
1758244fc50SMichał Górny 
1768244fc50SMichał Górny   auto pc_it =
1778244fc50SMichał Górny       baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
17895e2949aSEmmmer   auto flags_it = reg_info_flags == nullptr
17995e2949aSEmmmer                       ? baton.m_register_values.end()
18095e2949aSEmmmer                       : baton.m_register_values.find(
18195e2949aSEmmmer                             reg_info_flags->kinds[eRegisterKindDWARF]);
1828244fc50SMichał Górny 
1838244fc50SMichał Górny   lldb::addr_t next_pc;
1848244fc50SMichał Górny   lldb::addr_t next_flags;
1858244fc50SMichał Górny   if (emulation_result) {
1868244fc50SMichał Górny     assert(pc_it != baton.m_register_values.end() &&
1878244fc50SMichał Górny            "Emulation was successfull but PC wasn't updated");
1888244fc50SMichał Górny     next_pc = pc_it->second.GetAsUInt64();
1898244fc50SMichał Górny 
1908244fc50SMichał Górny     if (flags_it != baton.m_register_values.end())
1918244fc50SMichał Górny       next_flags = flags_it->second.GetAsUInt64();
1928244fc50SMichał Górny     else
1938244fc50SMichał Górny       next_flags = ReadFlags(register_context);
1948244fc50SMichał Górny   } else if (pc_it == baton.m_register_values.end()) {
1958244fc50SMichał Górny     // Emulate instruction failed and it haven't changed PC. Advance PC with
1968244fc50SMichał Górny     // the size of the current opcode because the emulation of all
1978244fc50SMichał Górny     // PC modifying instruction should be successful. The failure most
1988244fc50SMichał Górny     // likely caused by a not supported instruction which don't modify PC.
1998244fc50SMichał Górny     next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize();
2008244fc50SMichał Górny     next_flags = ReadFlags(register_context);
2018244fc50SMichał Górny   } else {
2028244fc50SMichał Górny     // The instruction emulation failed after it modified the PC. It is an
2038244fc50SMichał Górny     // unknown error where we can't continue because the next instruction is
2048244fc50SMichał Górny     // modifying the PC but we don't  know how.
205*0642cd76SAdrian Prantl     return Status::FromErrorString(
206*0642cd76SAdrian Prantl         "Instruction emulation failed unexpectedly.");
2078244fc50SMichał Górny   }
208a1ffabc4Sita-sc   auto result = SetSoftwareBreakpointOnPC(arch, next_pc, next_flags, process);
2098244fc50SMichał Górny   m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
210a1ffabc4Sita-sc   return result;
2118244fc50SMichał Górny }
212