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  }