File: NativeProcessSoftwareSingleStep.cpp

package info (click to toggle)
llvm-toolchain-16 1%3A16.0.6-15~deb11u2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 1,634,820 kB
  • sloc: cpp: 6,179,261; ansic: 1,216,205; asm: 741,319; python: 196,614; objc: 75,325; f90: 49,640; lisp: 32,396; pascal: 12,286; sh: 9,394; perl: 7,442; ml: 5,494; awk: 3,523; makefile: 2,723; javascript: 1,206; xml: 886; fortran: 581; cs: 573
file content (185 lines) | stat: -rw-r--r-- 7,126 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//===-- NativeProcessSoftwareSingleStep.cpp -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "NativeProcessSoftwareSingleStep.h"

#include "lldb/Core/EmulateInstruction.h"
#include "lldb/Host/common/NativeRegisterContext.h"
#include "lldb/Utility/RegisterValue.h"

#include <unordered_map>

using namespace lldb;
using namespace lldb_private;

namespace {

struct EmulatorBaton {
  NativeProcessProtocol &m_process;
  NativeRegisterContext &m_reg_context;

  // eRegisterKindDWARF -> RegsiterValue
  std::unordered_map<uint32_t, RegisterValue> m_register_values;

  EmulatorBaton(NativeProcessProtocol &process,
                NativeRegisterContext &reg_context)
      : m_process(process), m_reg_context(reg_context) {}
};

} // anonymous namespace

static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
                                 const EmulateInstruction::Context &context,
                                 lldb::addr_t addr, void *dst, size_t length) {
  EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);

  size_t bytes_read;
  emulator_baton->m_process.ReadMemory(addr, dst, length, bytes_read);
  return bytes_read;
}

static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
                                 const RegisterInfo *reg_info,
                                 RegisterValue &reg_value) {
  EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);

  auto it = emulator_baton->m_register_values.find(
      reg_info->kinds[eRegisterKindDWARF]);
  if (it != emulator_baton->m_register_values.end()) {
    reg_value = it->second;
    return true;
  }

  // The emulator only fill in the dwarf regsiter numbers (and in some case the
  // generic register numbers). Get the full register info from the register
  // context based on the dwarf register numbers.
  const RegisterInfo *full_reg_info =
      emulator_baton->m_reg_context.GetRegisterInfo(
          eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);

  Status 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 &reg_value) {
  EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
  emulator_baton->m_register_values[reg_info->kinds[eRegisterKindDWARF]] =
      reg_value;
  return true;
}

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 lldb::addr_t ReadFlags(NativeRegisterContext &regsiter_context) {
  const RegisterInfo *flags_info = regsiter_context.GetRegisterInfo(
      eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
  return regsiter_context.ReadRegisterAsUnsigned(flags_info,
                                                 LLDB_INVALID_ADDRESS);
}

Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
    NativeThreadProtocol &thread) {
  Status error;
  NativeProcessProtocol &process = thread.GetProcess();
  NativeRegisterContext &register_context = thread.GetRegisterContext();
  const ArchSpec &arch = process.GetArchitecture();

  std::unique_ptr<EmulateInstruction> emulator_up(
      EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying,
                                     nullptr));

  if (emulator_up == nullptr)
    return Status("Instruction emulator not found!");

  EmulatorBaton baton(process, register_context);
  emulator_up->SetBaton(&baton);
  emulator_up->SetReadMemCallback(&ReadMemoryCallback);
  emulator_up->SetReadRegCallback(&ReadRegisterCallback);
  emulator_up->SetWriteMemCallback(&WriteMemoryCallback);
  emulator_up->SetWriteRegCallback(&WriteRegisterCallback);

  if (!emulator_up->ReadInstruction())
    return Status("Read instruction failed!");

  bool emulation_result =
      emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);

  const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo(
      eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
  const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo(
      eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);

  auto pc_it =
      baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
  auto flags_it = reg_info_flags == nullptr
                      ? baton.m_register_values.end()
                      : baton.m_register_values.find(
                            reg_info_flags->kinds[eRegisterKindDWARF]);

  lldb::addr_t next_pc;
  lldb::addr_t next_flags;
  if (emulation_result) {
    assert(pc_it != baton.m_register_values.end() &&
           "Emulation was successfull but PC wasn't updated");
    next_pc = pc_it->second.GetAsUInt64();

    if (flags_it != baton.m_register_values.end())
      next_flags = flags_it->second.GetAsUInt64();
    else
      next_flags = ReadFlags(register_context);
  } else if (pc_it == baton.m_register_values.end()) {
    // Emulate instruction failed and it haven't changed PC. Advance PC with
    // the size of the current opcode because the emulation of all
    // PC modifying instruction should be successful. The failure most
    // likely caused by a not supported instruction which don't modify PC.
    next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize();
    next_flags = ReadFlags(register_context);
  } else {
    // The instruction emulation failed after it modified the PC. It is an
    // unknown error where we can't continue because the next instruction is
    // modifying the PC but we don't  know how.
    return Status("Instruction emulation failed unexpectedly.");
  }

  int size_hint = 0;
  if (arch.GetMachine() == llvm::Triple::arm) {
    if (next_flags & 0x20) {
      // Thumb mode
      size_hint = 2;
    } else {
      // Arm mode
      size_hint = 4;
    }
  } else if (arch.IsMIPS() || arch.GetTriple().isPPC64() ||
             arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch())
    size_hint = 4;
  error = process.SetBreakpoint(next_pc, size_hint, /*hardware=*/false);

  // If setting the breakpoint fails because next_pc is out of the address
  // space, ignore it and let the debugee segfault.
  if (error.GetError() == EIO || error.GetError() == EFAULT) {
    return Status();
  } else if (error.Fail())
    return error;

  m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});

  return Status();
}