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 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() {} 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() {} 49 50 DNBBreakpointList::~DNBBreakpointList() {} 51 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 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 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 77 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" 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 120 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 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 133 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 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 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 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