github.com/iDigitalFlame/xmt@v0.5.4/device/winapi/mem_helper.go (about) 1 //go:build windows && cgo && freemem 2 // +build windows,cgo,freemem 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 package winapi 21 22 /* 23 #include <windows.h> 24 25 // Free and release memory. This will remove the base memory allocations from Go. 26 // This has to be ran in CGo so we can prevent the runtime from panicing when 27 // we pull the rug (memory) from under it. 28 // 29 // This should be called from the last thread in "killRuntime". 30 // 31 // This function is passed the address of the "NtFreeVirtualMemory" function and 32 // a variable array of memory address regions collected from various sources. 33 void releaseMemory(void *func, int n, PVOID *regions) { 34 for (int i = 0; i < n; i++) { 35 #ifdef _AMD64_ 36 SIZE_T s = 0; 37 ((NTSTATUS (*)(HANDLE, PVOID*, PSIZE_T, ULONG))func)((HANDLE)-1, &(regions[i]), &s, 0x8000); 38 #else 39 // When we are in WOW64 or in 32bit mode, it seems that NtFreeVirtualMemory 40 // seems to cause an 0xC0000005 exception for some reason, but VirtualFree 41 // works perfectly fine, so we'll use that. 42 VirtualFree(regions[i], 0, 0x8000); 43 #endif 44 } 45 free(regions); 46 ExitThread(0); 47 } 48 */ 49 import "C" 50 import ( 51 "unsafe" 52 53 "github.com/iDigitalFlame/xmt/util/bugtrack" 54 ) 55 56 const memoryBasicInfoSize = unsafe.Sizeof(memoryBasicInfo{}) 57 58 //go:linkname heapMemory runtime.mheap_ 59 var heapMemory mheap 60 61 type memoryMap map[uintptr]struct{} 62 63 func freeRuntimeMemory() { 64 var ( 65 h = &heapMemory 66 x = *h 67 v = memoryBase(uintptr(unsafe.Pointer(&x))) 68 m = make(memoryMap, 64) 69 ) 70 enumRuntimeMemory(h, m) 71 if m.add(v); bugtrack.Enabled { 72 bugtrack.Track("winapi.freeRuntimeMemory(): Found %d runtime memory regions to free.", len(m)) 73 } 74 t := make([]uintptr, 0, len(m)) 75 for r := range m { 76 if bugtrack.Enabled { 77 bugtrack.Track("winapi.freeRuntimeMemory(): Free target %d: 0x%X", len(t), r) 78 } 79 t = append(t, r) 80 } 81 l := len(m) 82 if m = nil; l > 255 { 83 l = 255 84 } 85 var ( 86 b = C.malloc(C.size_t(l) * C.size_t(ptrSize)) 87 d = (*[255]uintptr)(b) 88 ) 89 for i := 0; i < l; i++ { 90 d[i] = t[i] 91 } 92 C.releaseMemory(unsafe.Pointer(funcNtFreeVirtualMemory.address()), C.int(l), (*C.PVOID)(b)) 93 } 94 func (m memoryMap) add(h uintptr) { 95 if h == 0 { 96 return 97 } 98 v := memoryBase(h) 99 if v == 0 { 100 return 101 } 102 if _, ok := m[v]; ok { 103 return 104 } 105 m[v] = caught 106 } 107 func memoryBase(h uintptr) uintptr { 108 if h == 0 { 109 return 0 110 } 111 var ( 112 m memoryBasicInfo 113 s uint32 114 r, _, _ = syscallN(funcNtQueryVirtualMemory.address(), CurrentProcess, h, 0, uintptr(unsafe.Pointer(&m)), memoryBasicInfoSize, uintptr(unsafe.Pointer(&s))) 115 ) 116 if r != 0 { 117 return h 118 } 119 // We can't free mapped or already free memory regions. 120 // 0x10000 - MEM_FREE 121 // 0x40000 - MEM_MAPPED 122 if m.State == 0x10000 || m.Type == 0x40000 { 123 return 0 124 } 125 return uintptr(m.AllocationBase) // return uintptr(m.BaseAddress) 126 }