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 <cassert> |
18 | #include <cinttypes> |
19 | |
20 | #pragma mark-- DNBBreakpoint |
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 | |
28 | DNBBreakpoint::~DNBBreakpoint() = default; |
29 | |
30 | 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 | |
48 | DNBBreakpointList::DNBBreakpointList() = default; |
49 | |
50 | DNBBreakpointList::~DNBBreakpointList() = default; |
51 | |
52 | DNBBreakpoint *DNBBreakpointList::Add(nub_addr_t addr, nub_size_t length, |
53 | bool hardware) { |
54 | m_breakpoints.insert( |
55 | x: std::make_pair(x&: addr, y: DNBBreakpoint(addr, length, hardware))); |
56 | iterator pos = m_breakpoints.find(x: addr); |
57 | return &pos->second; |
58 | } |
59 | |
60 | bool DNBBreakpointList::Remove(nub_addr_t addr) { |
61 | iterator pos = m_breakpoints.find(x: addr); |
62 | if (pos != m_breakpoints.end()) { |
63 | m_breakpoints.erase(position: pos); |
64 | return true; |
65 | } |
66 | return false; |
67 | } |
68 | |
69 | DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) { |
70 | iterator pos = m_breakpoints.find(x: addr); |
71 | if (pos != m_breakpoints.end()) |
72 | return &pos->second; |
73 | |
74 | return NULL; |
75 | } |
76 | |
77 | const DNBBreakpoint *DNBBreakpointList::FindByAddress(nub_addr_t addr) const { |
78 | const_iterator pos = m_breakpoints.find(x: addr); |
79 | if (pos != m_breakpoints.end()) |
80 | return &pos->second; |
81 | |
82 | return NULL; |
83 | } |
84 | |
85 | const DNBBreakpoint * |
86 | DNBBreakpointList::FindByHardwareIndex(uint32_t idx) const { |
87 | for (const auto &pos : m_breakpoints) |
88 | if (pos.second.GetHardwareIndex() == idx) |
89 | return &pos.second; |
90 | |
91 | return nullptr; |
92 | } |
93 | |
94 | const DNBBreakpoint * |
95 | DNBBreakpointList::FindNearestWatchpoint(nub_addr_t addr) const { |
96 | // Exact match |
97 | for (const auto &pos : m_breakpoints) { |
98 | if (pos.second.IsEnabled()) { |
99 | nub_addr_t start_addr = pos.second.Address(); |
100 | nub_addr_t end_addr = start_addr + pos.second.ByteSize(); |
101 | if (addr >= start_addr && addr <= end_addr) |
102 | return &pos.second; |
103 | } |
104 | } |
105 | |
106 | // Find watchpoint nearest to this address |
107 | // before or after the watched region of memory |
108 | const DNBBreakpoint *closest = nullptr; |
109 | uint32_t best_match = UINT32_MAX; |
110 | for (const auto &pos : m_breakpoints) { |
111 | if (pos.second.IsEnabled()) { |
112 | nub_addr_t start_addr = pos.second.Address(); |
113 | nub_addr_t end_addr = start_addr + pos.second.ByteSize(); |
114 | uint32_t delta = addr < start_addr ? start_addr - addr : addr - end_addr; |
115 | if (delta < best_match) { |
116 | closest = &pos.second; |
117 | best_match = delta; |
118 | } |
119 | } |
120 | } |
121 | return closest; |
122 | } |
123 | |
124 | // Finds the next breakpoint at an address greater than or equal to "addr" |
125 | size_t DNBBreakpointList::FindBreakpointsThatOverlapRange( |
126 | nub_addr_t addr, nub_addr_t size, std::vector<DNBBreakpoint *> &bps) { |
127 | bps.clear(); |
128 | iterator end = m_breakpoints.end(); |
129 | // Find the first breakpoint with an address >= to "addr" |
130 | iterator pos = m_breakpoints.lower_bound(x: addr); |
131 | if (pos != end) { |
132 | if (pos != m_breakpoints.begin()) { |
133 | // Watch out for a breakpoint at an address less than "addr" that might |
134 | // still overlap |
135 | iterator prev_pos = pos; |
136 | --prev_pos; |
137 | if (prev_pos->second.IntersectsRange(addr, size, NULL, NULL, NULL)) |
138 | bps.push_back(x: &pos->second); |
139 | } |
140 | |
141 | while (pos != end) { |
142 | // When we hit a breakpoint whose start address is greater than "addr + |
143 | // size" we are done. |
144 | // Do the math in a way that doesn't risk unsigned overflow with bad |
145 | // input. |
146 | if ((pos->second.Address() - addr) >= size) |
147 | break; |
148 | |
149 | // Check if this breakpoint overlaps, and if it does, add it to the list |
150 | if (pos->second.IntersectsRange(addr, size, NULL, NULL, NULL)) { |
151 | bps.push_back(x: &pos->second); |
152 | ++pos; |
153 | } |
154 | } |
155 | } |
156 | return bps.size(); |
157 | } |
158 | |
159 | void DNBBreakpointList::Dump() const { |
160 | const_iterator pos; |
161 | const_iterator end = m_breakpoints.end(); |
162 | for (pos = m_breakpoints.begin(); pos != end; ++pos) |
163 | pos->second.Dump(); |
164 | } |
165 | |
166 | void DNBBreakpointList::DisableAll() { |
167 | iterator pos, end = m_breakpoints.end(); |
168 | for (pos = m_breakpoints.begin(); pos != end; ++pos) |
169 | pos->second.SetEnabled(false); |
170 | } |
171 | |
172 | void DNBBreakpointList::RemoveTrapsFromBuffer(nub_addr_t addr, nub_size_t size, |
173 | void *p) const { |
174 | uint8_t *buf = (uint8_t *)p; |
175 | const_iterator end = m_breakpoints.end(); |
176 | const_iterator pos = m_breakpoints.lower_bound(x: addr); |
177 | while (pos != end && (pos->first < (addr + size))) { |
178 | nub_addr_t intersect_addr; |
179 | nub_size_t intersect_size; |
180 | nub_size_t opcode_offset; |
181 | const DNBBreakpoint &bp = pos->second; |
182 | if (bp.IntersectsRange(addr, size, intersect_addr: &intersect_addr, intersect_size: &intersect_size, |
183 | opcode_offset: &opcode_offset)) { |
184 | assert(addr <= intersect_addr && intersect_addr < addr + size); |
185 | assert(addr < intersect_addr + intersect_size && |
186 | intersect_addr + intersect_size <= addr + size); |
187 | assert(opcode_offset + intersect_size <= bp.ByteSize()); |
188 | nub_size_t buf_offset = intersect_addr - addr; |
189 | ::memcpy(buf + buf_offset, bp.SavedOpcodeBytes() + opcode_offset, |
190 | intersect_size); |
191 | } |
192 | ++pos; |
193 | } |
194 | } |
195 | |
196 | void DNBBreakpointList::DisableAllBreakpoints(MachProcess *process) { |
197 | iterator pos, end = m_breakpoints.end(); |
198 | for (pos = m_breakpoints.begin(); pos != end; ++pos) |
199 | process->DisableBreakpoint(pos->second.Address(), false); |
200 | } |
201 | |
202 | void DNBBreakpointList::DisableAllWatchpoints(MachProcess *process) { |
203 | iterator pos, end = m_breakpoints.end(); |
204 | for (pos = m_breakpoints.begin(); pos != end; ++pos) |
205 | process->DisableWatchpoint(pos->second.Address(), false); |
206 | } |
207 | |
208 | void DNBBreakpointList::RemoveDisabled() { |
209 | iterator pos = m_breakpoints.begin(); |
210 | while (pos != m_breakpoints.end()) { |
211 | if (!pos->second.IsEnabled()) |
212 | pos = m_breakpoints.erase(position: pos); |
213 | else |
214 | ++pos; |
215 | } |
216 | } |
217 | |