1 //===-- DNBBreakpoint.cpp ---------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Created by Greg Clayton on 6/29/07.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "DNBBreakpoint.h"
14 #include "DNBLog.h"
15 #include "MachProcess.h"
16 #include <algorithm>
17 #include <assert.h>
18 #include <inttypes.h>
19
20 #pragma mark-- DNBBreakpoint
DNBBreakpoint(nub_addr_t addr,nub_size_t byte_size,bool hardware)21 DNBBreakpoint::DNBBreakpoint(nub_addr_t addr, nub_size_t byte_size,
22 bool hardware)
23 : m_retain_count(1), m_byte_size(static_cast<uint32_t>(byte_size)),
24 m_opcode(), m_addr(addr), m_enabled(0), m_hw_preferred(hardware),
25 m_is_watchpoint(0), m_watch_read(0), m_watch_write(0),
26 m_hw_index(INVALID_NUB_HW_INDEX) {}
27
~DNBBreakpoint()28 DNBBreakpoint::~DNBBreakpoint() {}
29
Dump() const30 void DNBBreakpoint::Dump() const {
31 if (IsBreakpoint()) {
32 DNBLog("DNBBreakpoint addr = 0x%llx state = %s type = %s breakpoint "
33 "hw_index = %i",
34 (uint64_t)m_addr, m_enabled ? "enabled " : "disabled",
35 IsHardware() ? "hardware" : "software", GetHardwareIndex());
36 } else {
37 DNBLog("DNBBreakpoint addr = 0x%llx size = %llu state = %s type = %s "
38 "watchpoint (%s%s) hw_index = %i",
39 (uint64_t)m_addr, (uint64_t)m_byte_size,
40 m_enabled ? "enabled " : "disabled",
41 IsHardware() ? "hardware" : "software", m_watch_read ? "r" : "",
42 m_watch_write ? "w" : "", GetHardwareIndex());
43 }
44 }
45
46 #pragma mark-- DNBBreakpointList
47
DNBBreakpointList()48 DNBBreakpointList::DNBBreakpointList() {}
49
~DNBBreakpointList()50 DNBBreakpointList::~DNBBreakpointList() {}
51
Add(nub_addr_t addr,nub_size_t length,bool hardware)52 DNBBreakpoint *DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length,
53 bool hardware) {
54 m_breakpoints.insert(
55 std::make_pair(addr, DNBBreakpoint(addr, length, hardware)));
56 iterator pos = m_breakpoints.find(addr);
57 return &pos->second;
58 }
59
Remove(nub_addr_t addr)60 bool DNBBreakpointList::Remove(nub_addr_t addr) {
61 iterator pos = m_breakpoints.find(addr);
62 if (pos != m_breakpoints.end()) {
63 m_breakpoints.erase(pos);
64 return true;
65 }
66 return false;
67 }
68
FindByAddress(nub_addr_t addr)69 DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) {
70 iterator pos = m_breakpoints.find(addr);
71 if (pos != m_breakpoints.end())
72 return &pos->second;
73
74 return NULL;
75 }
76
FindByAddress(nub_addr_t addr) const77 const DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) const {
78 const_iterator pos = m_breakpoints.find(addr);
79 if (pos != m_breakpoints.end())
80 return &pos->second;
81
82 return NULL;
83 }
84
85 // Finds the next breakpoint at an address greater than or equal to "addr"
FindBreakpointsThatOverlapRange(nub_addr_t addr,nub_addr_t size,std::vector<DNBBreakpoint * > & bps)86 size_t DNBBreakpointList::FindBreakpointsThatOverlapRange(
87 nub_addr_t addr, nub_addr_t size, std::vector<DNBBreakpoint *> &bps) {
88 bps.clear();
89 iterator end = m_breakpoints.end();
90 // Find the first breakpoint with an address >= to "addr"
91 iterator pos = m_breakpoints.lower_bound(addr);
92 if (pos != end) {
93 if (pos != m_breakpoints.begin()) {
94 // Watch out for a breakpoint at an address less than "addr" that might
95 // still overlap
96 iterator prev_pos = pos;
97 --prev_pos;
98 if (prev_pos->second.IntersectsRange(addr, size, NULL, NULL, NULL))
99 bps.push_back(&pos->second);
100 }
101
102 while (pos != end) {
103 // When we hit a breakpoint whose start address is greater than "addr +
104 // size" we are done.
105 // Do the math in a way that doesn't risk unsigned overflow with bad
106 // input.
107 if ((pos->second.Address() - addr) >= size)
108 break;
109
110 // Check if this breakpoint overlaps, and if it does, add it to the list
111 if (pos->second.IntersectsRange(addr, size, NULL, NULL, NULL)) {
112 bps.push_back(&pos->second);
113 ++pos;
114 }
115 }
116 }
117 return bps.size();
118 }
119
Dump() const120 void DNBBreakpointList::Dump() const {
121 const_iterator pos;
122 const_iterator end = m_breakpoints.end();
123 for (pos = m_breakpoints.begin(); pos != end; ++pos)
124 pos->second.Dump();
125 }
126
DisableAll()127 void DNBBreakpointList::DisableAll() {
128 iterator pos, end = m_breakpoints.end();
129 for (pos = m_breakpoints.begin(); pos != end; ++pos)
130 pos->second.SetEnabled(false);
131 }
132
RemoveTrapsFromBuffer(nub_addr_t addr,nub_size_t size,void * p) const133 void DNBBreakpointList::RemoveTrapsFromBuffer(nub_addr_t addr, nub_size_t size,
134 void *p) const {
135 uint8_t *buf = (uint8_t *)p;
136 const_iterator end = m_breakpoints.end();
137 const_iterator pos = m_breakpoints.lower_bound(addr);
138 while (pos != end && (pos->first < (addr + size))) {
139 nub_addr_t intersect_addr;
140 nub_size_t intersect_size;
141 nub_size_t opcode_offset;
142 const DNBBreakpoint &bp = pos->second;
143 if (bp.IntersectsRange(addr, size, &intersect_addr, &intersect_size,
144 &opcode_offset)) {
145 assert(addr <= intersect_addr && intersect_addr < addr + size);
146 assert(addr < intersect_addr + intersect_size &&
147 intersect_addr + intersect_size <= addr + size);
148 assert(opcode_offset + intersect_size <= bp.ByteSize());
149 nub_size_t buf_offset = intersect_addr - addr;
150 ::memcpy(buf + buf_offset, bp.SavedOpcodeBytes() + opcode_offset,
151 intersect_size);
152 }
153 ++pos;
154 }
155 }
156
DisableAllBreakpoints(MachProcess * process)157 void DNBBreakpointList::DisableAllBreakpoints(MachProcess *process) {
158 iterator pos, end = m_breakpoints.end();
159 for (pos = m_breakpoints.begin(); pos != end; ++pos)
160 process->DisableBreakpoint(pos->second.Address(), false);
161 }
162
DisableAllWatchpoints(MachProcess * process)163 void DNBBreakpointList::DisableAllWatchpoints(MachProcess *process) {
164 iterator pos, end = m_breakpoints.end();
165 for (pos = m_breakpoints.begin(); pos != end; ++pos)
166 process->DisableWatchpoint(pos->second.Address(), false);
167 }
168
RemoveDisabled()169 void DNBBreakpointList::RemoveDisabled() {
170 iterator pos = m_breakpoints.begin();
171 while (pos != m_breakpoints.end()) {
172 if (!pos->second.IsEnabled())
173 pos = m_breakpoints.erase(pos);
174 else
175 ++pos;
176 }
177 }
178