1343181Sdim//===-- ArchitectureMips.cpp -------------------------------------*- C++ -*-===//
2343181Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6343181Sdim//
7343181Sdim//===----------------------------------------------------------------------===//
8343181Sdim
9343181Sdim#include "Plugins/Architecture/Mips/ArchitectureMips.h"
10343181Sdim#include "lldb/Core/Address.h"
11343181Sdim#include "lldb/Core/Disassembler.h"
12343181Sdim#include "lldb/Core/Module.h"
13343181Sdim#include "lldb/Core/PluginManager.h"
14343181Sdim#include "lldb/Symbol/Function.h"
15343181Sdim#include "lldb/Symbol/SymbolContext.h"
16343181Sdim#include "lldb/Target/SectionLoadList.h"
17343181Sdim#include "lldb/Target/Target.h"
18343181Sdim#include "lldb/Utility/ArchSpec.h"
19343181Sdim#include "lldb/Utility/Log.h"
20343181Sdim
21343181Sdimusing namespace lldb_private;
22343181Sdimusing namespace lldb;
23343181Sdim
24343181SdimConstString ArchitectureMips::GetPluginNameStatic() {
25343181Sdim  return ConstString("mips");
26343181Sdim}
27343181Sdim
28343181Sdimvoid ArchitectureMips::Initialize() {
29343181Sdim  PluginManager::RegisterPlugin(GetPluginNameStatic(),
30343181Sdim                                "Mips-specific algorithms",
31343181Sdim                                &ArchitectureMips::Create);
32343181Sdim}
33343181Sdim
34343181Sdimvoid ArchitectureMips::Terminate() {
35343181Sdim  PluginManager::UnregisterPlugin(&ArchitectureMips::Create);
36343181Sdim}
37343181Sdim
38343181Sdimstd::unique_ptr<Architecture> ArchitectureMips::Create(const ArchSpec &arch) {
39343181Sdim  return arch.IsMIPS() ?
40343181Sdim      std::unique_ptr<Architecture>(new ArchitectureMips(arch)) : nullptr;
41343181Sdim}
42343181Sdim
43343181SdimConstString ArchitectureMips::GetPluginName() { return GetPluginNameStatic(); }
44343181Sdimuint32_t ArchitectureMips::GetPluginVersion() { return 1; }
45343181Sdim
46343181Sdimaddr_t ArchitectureMips::GetCallableLoadAddress(addr_t code_addr,
47343181Sdim                                                AddressClass addr_class) const {
48343181Sdim  bool is_alternate_isa = false;
49343181Sdim
50343181Sdim  switch (addr_class) {
51343181Sdim  case AddressClass::eData:
52343181Sdim  case AddressClass::eDebug:
53343181Sdim    return LLDB_INVALID_ADDRESS;
54343181Sdim  case AddressClass::eCodeAlternateISA:
55343181Sdim    is_alternate_isa = true;
56343181Sdim    break;
57343181Sdim  default: break;
58343181Sdim  }
59343181Sdim
60343181Sdim  if ((code_addr & 2ull) || is_alternate_isa)
61343181Sdim    return code_addr | 1u;
62343181Sdim  return code_addr;
63343181Sdim}
64343181Sdim
65343181Sdimaddr_t ArchitectureMips::GetOpcodeLoadAddress(addr_t opcode_addr,
66343181Sdim                                              AddressClass addr_class) const {
67343181Sdim  switch (addr_class) {
68343181Sdim  case AddressClass::eData:
69343181Sdim  case AddressClass::eDebug:
70343181Sdim    return LLDB_INVALID_ADDRESS;
71343181Sdim  default: break;
72343181Sdim  }
73343181Sdim  return opcode_addr & ~(1ull);
74343181Sdim}
75343181Sdim
76343181Sdimlldb::addr_t ArchitectureMips::GetBreakableLoadAddress(lldb::addr_t addr,
77343181Sdim                                                       Target &target) const {
78343181Sdim
79343181Sdim  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
80343181Sdim
81343181Sdim  Address resolved_addr;
82343181Sdim
83343181Sdim  SectionLoadList &section_load_list = target.GetSectionLoadList();
84343181Sdim  if (section_load_list.IsEmpty())
85343181Sdim    // No sections are loaded, so we must assume we are not running yet and
86343181Sdim    // need to operate only on file address.
87343181Sdim    target.ResolveFileAddress(addr, resolved_addr);
88343181Sdim  else
89343181Sdim    target.ResolveLoadAddress(addr, resolved_addr);
90343181Sdim
91343181Sdim  addr_t current_offset = 0;
92343181Sdim
93343181Sdim  // Get the function boundaries to make sure we don't scan back before the
94343181Sdim  // beginning of the current function.
95343181Sdim  ModuleSP temp_addr_module_sp(resolved_addr.GetModule());
96343181Sdim  if (temp_addr_module_sp) {
97343181Sdim    SymbolContext sc;
98343181Sdim    SymbolContextItem resolve_scope =
99343181Sdim        eSymbolContextFunction | eSymbolContextSymbol;
100343181Sdim    temp_addr_module_sp->ResolveSymbolContextForAddress(resolved_addr,
101343181Sdim      resolve_scope, sc);
102343181Sdim    Address sym_addr;
103343181Sdim    if (sc.function)
104343181Sdim      sym_addr = sc.function->GetAddressRange().GetBaseAddress();
105343181Sdim    else if (sc.symbol)
106343181Sdim      sym_addr = sc.symbol->GetAddress();
107343181Sdim
108343181Sdim    addr_t function_start = sym_addr.GetLoadAddress(&target);
109343181Sdim    if (function_start == LLDB_INVALID_ADDRESS)
110343181Sdim      function_start = sym_addr.GetFileAddress();
111343181Sdim
112343181Sdim    if (function_start)
113343181Sdim      current_offset = addr - function_start;
114343181Sdim  }
115343181Sdim
116343181Sdim  // If breakpoint address is start of function then we dont have to do
117343181Sdim  // anything.
118343181Sdim  if (current_offset == 0)
119343181Sdim    return addr;
120343181Sdim
121343181Sdim  ExecutionContext ctx;
122343181Sdim  target.CalculateExecutionContext(ctx);
123343181Sdim  auto insn = GetInstructionAtAddress(ctx, current_offset, addr);
124343181Sdim
125343181Sdim  if (nullptr == insn || !insn->HasDelaySlot())
126343181Sdim    return addr;
127343181Sdim
128343181Sdim  // Adjust the breakable address
129343181Sdim  uint64_t breakable_addr = addr - insn->GetOpcode().GetByteSize();
130360784Sdim  LLDB_LOGF(log,
131360784Sdim            "Target::%s Breakpoint at 0x%8.8" PRIx64
132360784Sdim            " is adjusted to 0x%8.8" PRIx64 " due to delay slot\n",
133360784Sdim            __FUNCTION__, addr, breakable_addr);
134343181Sdim
135343181Sdim  return breakable_addr;
136343181Sdim}
137343181Sdim
138343181SdimInstruction *ArchitectureMips::GetInstructionAtAddress(
139343181Sdim    const ExecutionContext &exe_ctx, const Address &resolved_addr,
140343181Sdim    addr_t symbol_offset) const {
141343181Sdim
142343181Sdim  auto loop_count = symbol_offset / 2;
143343181Sdim
144343181Sdim  uint32_t arch_flags = m_arch.GetFlags();
145343181Sdim  bool IsMips16 = arch_flags & ArchSpec::eMIPSAse_mips16;
146343181Sdim  bool IsMicromips = arch_flags & ArchSpec::eMIPSAse_micromips;
147343181Sdim
148343181Sdim  if (loop_count > 3) {
149343181Sdim    // Scan previous 6 bytes
150343181Sdim    if (IsMips16 | IsMicromips)
151343181Sdim      loop_count = 3;
152343181Sdim    // For mips-only, instructions are always 4 bytes, so scan previous 4
153343181Sdim    // bytes only.
154343181Sdim    else
155343181Sdim      loop_count = 2;
156343181Sdim  }
157343181Sdim
158343181Sdim  // Create Disassembler Instance
159343181Sdim  lldb::DisassemblerSP disasm_sp(
160343181Sdim    Disassembler::FindPlugin(m_arch, nullptr, nullptr));
161343181Sdim
162343181Sdim  InstructionList instruction_list;
163343181Sdim  InstructionSP prev_insn;
164343181Sdim  bool prefer_file_cache = true; // Read from file
165343181Sdim  uint32_t inst_to_choose = 0;
166343181Sdim
167343181Sdim  Address addr = resolved_addr;
168343181Sdim
169343181Sdim  for (uint32_t i = 1; i <= loop_count; i++) {
170343181Sdim    // Adjust the address to read from.
171343181Sdim    addr.Slide(-2);
172343181Sdim    AddressRange range(addr, i * 2);
173343181Sdim    uint32_t insn_size = 0;
174343181Sdim
175343181Sdim    disasm_sp->ParseInstructions(&exe_ctx, range, nullptr, prefer_file_cache);
176343181Sdim
177343181Sdim    uint32_t num_insns = disasm_sp->GetInstructionList().GetSize();
178343181Sdim    if (num_insns) {
179343181Sdim      prev_insn = disasm_sp->GetInstructionList().GetInstructionAtIndex(0);
180343181Sdim      insn_size = prev_insn->GetOpcode().GetByteSize();
181343181Sdim      if (i == 1 && insn_size == 2) {
182343181Sdim        // This looks like a valid 2-byte instruction (but it could be a part
183343181Sdim        // of upper 4 byte instruction).
184343181Sdim        instruction_list.Append(prev_insn);
185343181Sdim        inst_to_choose = 1;
186343181Sdim      }
187343181Sdim      else if (i == 2) {
188343181Sdim        // Here we may get one 4-byte instruction or two 2-byte instructions.
189343181Sdim        if (num_insns == 2) {
190343181Sdim          // Looks like there are two 2-byte instructions above our
191343181Sdim          // breakpoint target address. Now the upper 2-byte instruction is
192343181Sdim          // either a valid 2-byte instruction or could be a part of it's
193343181Sdim          // upper 4-byte instruction. In both cases we don't care because in
194343181Sdim          // this case lower 2-byte instruction is definitely a valid
195343181Sdim          // instruction and whatever i=1 iteration has found out is true.
196343181Sdim          inst_to_choose = 1;
197343181Sdim          break;
198343181Sdim        }
199343181Sdim        else if (insn_size == 4) {
200343181Sdim          // This instruction claims its a valid 4-byte instruction. But it
201343181Sdim          // could be a part of it's upper 4-byte instruction. Lets try
202343181Sdim          // scanning upper 2 bytes to verify this.
203343181Sdim          instruction_list.Append(prev_insn);
204343181Sdim          inst_to_choose = 2;
205343181Sdim        }
206343181Sdim      }
207343181Sdim      else if (i == 3) {
208343181Sdim        if (insn_size == 4)
209343181Sdim          // FIXME: We reached here that means instruction at [target - 4] has
210343181Sdim          // already claimed to be a 4-byte instruction, and now instruction
211343181Sdim          // at [target - 6] is also claiming that it's a 4-byte instruction.
212343181Sdim          // This can not be true. In this case we can not decide the valid
213343181Sdim          // previous instruction so we let lldb set the breakpoint at the
214343181Sdim          // address given by user.
215343181Sdim          inst_to_choose = 0;
216343181Sdim        else
217343181Sdim          // This is straight-forward
218343181Sdim          inst_to_choose = 2;
219343181Sdim        break;
220343181Sdim      }
221343181Sdim    }
222343181Sdim    else {
223343181Sdim      // Decode failed, bytes do not form a valid instruction. So whatever
224343181Sdim      // previous iteration has found out is true.
225343181Sdim      if (i > 1) {
226343181Sdim        inst_to_choose = i - 1;
227343181Sdim        break;
228343181Sdim      }
229343181Sdim    }
230343181Sdim  }
231343181Sdim
232343181Sdim  // Check if we are able to find any valid instruction.
233343181Sdim  if (inst_to_choose) {
234343181Sdim    if (inst_to_choose > instruction_list.GetSize())
235343181Sdim      inst_to_choose--;
236343181Sdim    return instruction_list.GetInstructionAtIndex(inst_to_choose - 1).get();
237343181Sdim  }
238343181Sdim
239343181Sdim  return nullptr;
240343181Sdim}
241