xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick //===-- MachVMRegion.cpp ----------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick //  Created by Greg Clayton on 6/26/07.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick 
13061da546Spatrick #include "MachVMRegion.h"
14061da546Spatrick #include "DNBLog.h"
15be691f3bSpatrick #include <cassert>
16061da546Spatrick #include <mach/mach_vm.h>
17061da546Spatrick 
MachVMRegion(task_t task)18061da546Spatrick MachVMRegion::MachVMRegion(task_t task)
19061da546Spatrick     : m_task(task), m_addr(INVALID_NUB_ADDRESS), m_err(),
20061da546Spatrick       m_start(INVALID_NUB_ADDRESS), m_size(0), m_depth(-1),
21061da546Spatrick       m_curr_protection(0), m_protection_addr(INVALID_NUB_ADDRESS),
22061da546Spatrick       m_protection_size(0) {
23061da546Spatrick   memset(&m_data, 0, sizeof(m_data));
24061da546Spatrick }
25061da546Spatrick 
~MachVMRegion()26061da546Spatrick MachVMRegion::~MachVMRegion() {
27061da546Spatrick   // Restore any original protections and clear our vars
28061da546Spatrick   Clear();
29061da546Spatrick }
30061da546Spatrick 
Clear()31061da546Spatrick void MachVMRegion::Clear() {
32061da546Spatrick   RestoreProtections();
33061da546Spatrick   m_addr = INVALID_NUB_ADDRESS;
34061da546Spatrick   m_err.Clear();
35061da546Spatrick   m_start = INVALID_NUB_ADDRESS;
36061da546Spatrick   m_size = 0;
37061da546Spatrick   m_depth = -1;
38061da546Spatrick   memset(&m_data, 0, sizeof(m_data));
39061da546Spatrick   m_curr_protection = 0;
40061da546Spatrick   m_protection_addr = INVALID_NUB_ADDRESS;
41061da546Spatrick   m_protection_size = 0;
42061da546Spatrick }
43061da546Spatrick 
SetProtections(mach_vm_address_t addr,mach_vm_size_t size,vm_prot_t prot)44061da546Spatrick bool MachVMRegion::SetProtections(mach_vm_address_t addr, mach_vm_size_t size,
45061da546Spatrick                                   vm_prot_t prot) {
46061da546Spatrick   if (ContainsAddress(addr)) {
47061da546Spatrick     mach_vm_size_t prot_size = size;
48061da546Spatrick     mach_vm_address_t end_addr = EndAddress();
49061da546Spatrick     if (prot_size > (end_addr - addr))
50061da546Spatrick       prot_size = end_addr - addr;
51061da546Spatrick 
52061da546Spatrick     if (prot_size > 0) {
53061da546Spatrick       if (prot == (m_curr_protection & VM_PROT_ALL)) {
54061da546Spatrick         DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE,
55061da546Spatrick                          "MachVMRegion::%s: protections (%u) already "
56061da546Spatrick                          "sufficient for task 0x%4.4x at address 0x%8.8llx) ",
57061da546Spatrick                          __FUNCTION__, prot, m_task, (uint64_t)addr);
58061da546Spatrick         // Protections are already set as requested...
59061da546Spatrick         return true;
60061da546Spatrick       } else {
61061da546Spatrick         m_err = ::mach_vm_protect(m_task, addr, prot_size, 0, prot);
62061da546Spatrick         if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS))
63061da546Spatrick           m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = "
64061da546Spatrick                             "0x%8.8llx, size = %llu, set_max = %i, prot = %u )",
65061da546Spatrick                             m_task, (uint64_t)addr, (uint64_t)prot_size, 0,
66061da546Spatrick                             prot);
67061da546Spatrick         if (m_err.Fail()) {
68061da546Spatrick           // Try again with the ability to create a copy on write region
69061da546Spatrick           m_err = ::mach_vm_protect(m_task, addr, prot_size, 0,
70061da546Spatrick                                     prot | VM_PROT_COPY);
71061da546Spatrick           if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail())
72061da546Spatrick             m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = "
73061da546Spatrick                               "0x%8.8llx, size = %llu, set_max = %i, prot = %u "
74061da546Spatrick                               ")",
75061da546Spatrick                               m_task, (uint64_t)addr, (uint64_t)prot_size, 0,
76061da546Spatrick                               prot | VM_PROT_COPY);
77061da546Spatrick         }
78061da546Spatrick         if (m_err.Success()) {
79061da546Spatrick           m_curr_protection = prot;
80061da546Spatrick           m_protection_addr = addr;
81061da546Spatrick           m_protection_size = prot_size;
82061da546Spatrick           return true;
83061da546Spatrick         }
84061da546Spatrick       }
85061da546Spatrick     } else {
86061da546Spatrick       DNBLogThreadedIf(LOG_MEMORY_PROTECTIONS | LOG_VERBOSE,
87061da546Spatrick                        "%s: Zero size for task 0x%4.4x at address 0x%8.8llx) ",
88061da546Spatrick                        __FUNCTION__, m_task, (uint64_t)addr);
89061da546Spatrick     }
90061da546Spatrick   }
91061da546Spatrick   return false;
92061da546Spatrick }
93061da546Spatrick 
RestoreProtections()94061da546Spatrick bool MachVMRegion::RestoreProtections() {
95061da546Spatrick   if (m_curr_protection != m_data.protection && m_protection_size > 0) {
96061da546Spatrick     m_err = ::mach_vm_protect(m_task, m_protection_addr, m_protection_size, 0,
97061da546Spatrick                               m_data.protection);
98061da546Spatrick     if (DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS) || m_err.Fail())
99061da546Spatrick       m_err.LogThreaded("::mach_vm_protect ( task = 0x%4.4x, addr = 0x%8.8llx, "
100061da546Spatrick                         "size = %llu, set_max = %i, prot = %u )",
101061da546Spatrick                         m_task, (uint64_t)m_protection_addr,
102061da546Spatrick                         (uint64_t)m_protection_size, 0, m_data.protection);
103061da546Spatrick     if (m_err.Success()) {
104061da546Spatrick       m_protection_size = 0;
105061da546Spatrick       m_protection_addr = INVALID_NUB_ADDRESS;
106061da546Spatrick       m_curr_protection = m_data.protection;
107061da546Spatrick       return true;
108061da546Spatrick     }
109061da546Spatrick   } else {
110061da546Spatrick     m_err.Clear();
111061da546Spatrick     return true;
112061da546Spatrick   }
113061da546Spatrick 
114061da546Spatrick   return false;
115061da546Spatrick }
116061da546Spatrick 
GetRegionForAddress(nub_addr_t addr)117061da546Spatrick bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) {
118061da546Spatrick   // Restore any original protections and clear our vars
119061da546Spatrick   Clear();
120061da546Spatrick   m_err.Clear();
121061da546Spatrick   m_addr = addr;
122061da546Spatrick   m_start = addr;
123061da546Spatrick   m_depth = 1024;
124061da546Spatrick   mach_msg_type_number_t info_size = kRegionInfoSize;
125*f6aab3d8Srobert   static_assert(sizeof(info_size) == 4);
126061da546Spatrick   m_err =
127061da546Spatrick       ::mach_vm_region_recurse(m_task, &m_start, &m_size, &m_depth,
128061da546Spatrick                                (vm_region_recurse_info_t)&m_data, &info_size);
129061da546Spatrick 
130061da546Spatrick   const bool failed = m_err.Fail();
131061da546Spatrick   const bool log_protections = DNBLogCheckLogBit(LOG_MEMORY_PROTECTIONS);
132061da546Spatrick 
133061da546Spatrick   if (log_protections || failed)
134061da546Spatrick     m_err.LogThreaded("::mach_vm_region_recurse ( task = 0x%4.4x, address => "
135061da546Spatrick                       "0x%8.8llx, size => %llu, nesting_depth => %d, info => "
136061da546Spatrick                       "%p, infoCnt => %d) addr = 0x%8.8llx ",
137061da546Spatrick                       m_task, (uint64_t)m_start, (uint64_t)m_size, m_depth,
138061da546Spatrick                       &m_data, info_size, (uint64_t)addr);
139061da546Spatrick 
140061da546Spatrick   if (failed)
141061da546Spatrick     return false;
142061da546Spatrick   if (log_protections) {
143061da546Spatrick     DNBLogThreaded("info = { prot = %u, "
144061da546Spatrick                    "max_prot = %u, "
145061da546Spatrick                    "inheritance = 0x%8.8x, "
146061da546Spatrick                    "offset = 0x%8.8llx, "
147061da546Spatrick                    "user_tag = 0x%8.8x, "
148061da546Spatrick                    "ref_count = %u, "
149061da546Spatrick                    "shadow_depth = %u, "
150061da546Spatrick                    "ext_pager = %u, "
151061da546Spatrick                    "share_mode = %u, "
152061da546Spatrick                    "is_submap = %d, "
153061da546Spatrick                    "behavior = %d, "
154061da546Spatrick                    "object_id = 0x%8.8x, "
155061da546Spatrick                    "user_wired_count = 0x%4.4x }",
156061da546Spatrick                    m_data.protection, m_data.max_protection, m_data.inheritance,
157061da546Spatrick                    (uint64_t)m_data.offset, m_data.user_tag, m_data.ref_count,
158061da546Spatrick                    m_data.shadow_depth, m_data.external_pager,
159061da546Spatrick                    m_data.share_mode, m_data.is_submap, m_data.behavior,
160061da546Spatrick                    m_data.object_id, m_data.user_wired_count);
161061da546Spatrick   }
162061da546Spatrick   m_curr_protection = m_data.protection;
163061da546Spatrick 
164061da546Spatrick   // We make a request for an address and got no error back, but this
165061da546Spatrick   // doesn't mean that "addr" is in the range. The data in this object will
166061da546Spatrick   // be valid though, so you could see where the next region begins. So we
167061da546Spatrick   // return false, yet leave "m_err" with a successfull return code.
168061da546Spatrick   return !((addr < m_start) || (addr >= (m_start + m_size)));
169061da546Spatrick }
170061da546Spatrick 
GetDNBPermissions() const171061da546Spatrick uint32_t MachVMRegion::GetDNBPermissions() const {
172061da546Spatrick   if (m_addr == INVALID_NUB_ADDRESS || m_start == INVALID_NUB_ADDRESS ||
173061da546Spatrick       m_size == 0)
174061da546Spatrick     return 0;
175061da546Spatrick   uint32_t dnb_permissions = 0;
176061da546Spatrick 
177061da546Spatrick   if ((m_data.protection & VM_PROT_READ) == VM_PROT_READ)
178061da546Spatrick     dnb_permissions |= eMemoryPermissionsReadable;
179061da546Spatrick   if ((m_data.protection & VM_PROT_WRITE) == VM_PROT_WRITE)
180061da546Spatrick     dnb_permissions |= eMemoryPermissionsWritable;
181061da546Spatrick   if ((m_data.protection & VM_PROT_EXECUTE) == VM_PROT_EXECUTE)
182061da546Spatrick     dnb_permissions |= eMemoryPermissionsExecutable;
183061da546Spatrick   return dnb_permissions;
184061da546Spatrick }
185*f6aab3d8Srobert 
GetMemoryTypes() const186*f6aab3d8Srobert std::vector<std::string> MachVMRegion::GetMemoryTypes() const {
187*f6aab3d8Srobert   std::vector<std::string> types;
188*f6aab3d8Srobert   if (m_data.user_tag == VM_MEMORY_STACK) {
189*f6aab3d8Srobert     if (m_data.protection == VM_PROT_NONE) {
190*f6aab3d8Srobert       types.push_back("stack-guard");
191*f6aab3d8Srobert     } else {
192*f6aab3d8Srobert       types.push_back("stack");
193*f6aab3d8Srobert     }
194*f6aab3d8Srobert   }
195*f6aab3d8Srobert   if (m_data.user_tag == VM_MEMORY_MALLOC) {
196*f6aab3d8Srobert     if (m_data.protection == VM_PROT_NONE)
197*f6aab3d8Srobert       types.push_back("malloc-guard");
198*f6aab3d8Srobert     else if (m_data.share_mode == SM_EMPTY)
199*f6aab3d8Srobert       types.push_back("malloc-reserved");
200*f6aab3d8Srobert     else
201*f6aab3d8Srobert       types.push_back("malloc-metadata");
202*f6aab3d8Srobert   }
203*f6aab3d8Srobert   if (m_data.user_tag == VM_MEMORY_MALLOC_NANO ||
204*f6aab3d8Srobert       m_data.user_tag == VM_MEMORY_MALLOC_TINY ||
205*f6aab3d8Srobert       m_data.user_tag == VM_MEMORY_MALLOC_SMALL ||
206*f6aab3d8Srobert       m_data.user_tag == VM_MEMORY_MALLOC_LARGE ||
207*f6aab3d8Srobert       m_data.user_tag == VM_MEMORY_MALLOC_LARGE_REUSED ||
208*f6aab3d8Srobert       m_data.user_tag == VM_MEMORY_MALLOC_LARGE_REUSABLE ||
209*f6aab3d8Srobert       m_data.user_tag == VM_MEMORY_MALLOC_HUGE ||
210*f6aab3d8Srobert       m_data.user_tag == VM_MEMORY_REALLOC ||
211*f6aab3d8Srobert       m_data.user_tag == VM_MEMORY_SBRK) {
212*f6aab3d8Srobert     types.push_back("heap");
213*f6aab3d8Srobert     if (m_data.user_tag == VM_MEMORY_MALLOC_TINY) {
214*f6aab3d8Srobert       types.push_back("malloc-tiny");
215*f6aab3d8Srobert     }
216*f6aab3d8Srobert     if (m_data.user_tag == VM_MEMORY_MALLOC_LARGE) {
217*f6aab3d8Srobert       types.push_back("malloc-large");
218*f6aab3d8Srobert     }
219*f6aab3d8Srobert     if (m_data.user_tag == VM_MEMORY_MALLOC_SMALL) {
220*f6aab3d8Srobert       types.push_back("malloc-small");
221*f6aab3d8Srobert     }
222*f6aab3d8Srobert   }
223*f6aab3d8Srobert   return types;
224*f6aab3d8Srobert }
225