wa-lang.org/wazero@v1.0.2/internal/platform/mmap_windows.go (about)

     1  //go:build windows
     2  
     3  package platform
     4  
     5  import (
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  	"syscall"
    10  	"unsafe"
    11  )
    12  
    13  var (
    14  	kernel32           = syscall.NewLazyDLL("kernel32.dll")
    15  	procVirtualAlloc   = kernel32.NewProc("VirtualAlloc")
    16  	procVirtualProtect = kernel32.NewProc("VirtualProtect")
    17  	procVirtualFree    = kernel32.NewProc("VirtualFree")
    18  )
    19  
    20  const (
    21  	windows_MEM_COMMIT             uintptr = 0x00001000
    22  	windows_MEM_RELEASE            uintptr = 0x00008000
    23  	windows_PAGE_READWRITE         uintptr = 0x00000004
    24  	windows_PAGE_EXECUTE_READ      uintptr = 0x00000020
    25  	windows_PAGE_EXECUTE_READWRITE uintptr = 0x00000040
    26  )
    27  
    28  func munmapCodeSegment(code []byte) error {
    29  	return freeMemory(code)
    30  }
    31  
    32  // allocateMemory commits the memory region via the "VirtualAlloc" function.
    33  // See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
    34  func allocateMemory(size uintptr, protect uintptr) (uintptr, error) {
    35  	address := uintptr(0) // TODO: document why zero
    36  	alloctype := windows_MEM_COMMIT
    37  	if r, _, err := procVirtualAlloc.Call(address, size, alloctype, protect); r == 0 {
    38  		return 0, fmt.Errorf("compiler: VirtualAlloc error: %w", ensureErr(err))
    39  	} else {
    40  		return r, nil
    41  	}
    42  }
    43  
    44  // freeMemory releases the memory region via the "VirtualFree" function.
    45  // See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfree
    46  func freeMemory(code []byte) error {
    47  	address := uintptr(unsafe.Pointer(&code[0]))
    48  	size := uintptr(0) // size must be 0 because we're using MEM_RELEASE.
    49  	freetype := windows_MEM_RELEASE
    50  	if r, _, err := procVirtualFree.Call(address, size, freetype); r == 0 {
    51  		return fmt.Errorf("compiler: VirtualFree error: %w", ensureErr(err))
    52  	}
    53  	return nil
    54  }
    55  
    56  func virtualProtect(address, size, newprotect uintptr, oldprotect *uint32) error {
    57  	if r, _, err := procVirtualProtect.Call(address, size, newprotect, uintptr(unsafe.Pointer(oldprotect))); r == 0 {
    58  		return fmt.Errorf("compiler: VirtualProtect error: %w", ensureErr(err))
    59  	}
    60  	return nil
    61  }
    62  
    63  func mmapCodeSegmentAMD64(code io.Reader, size int) ([]byte, error) {
    64  	p, err := allocateMemory(uintptr(size), windows_PAGE_EXECUTE_READWRITE)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	var mem []byte
    70  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&mem))
    71  	sh.Data = p
    72  	sh.Len = size
    73  	sh.Cap = size
    74  
    75  	w := &bufWriter{underlying: mem}
    76  	_, err = io.CopyN(w, code, int64(size))
    77  	return mem, err
    78  }
    79  
    80  func mmapCodeSegmentARM64(code io.Reader, size int) ([]byte, error) {
    81  	p, err := allocateMemory(uintptr(size), windows_PAGE_READWRITE)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	var mem []byte
    87  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&mem))
    88  	sh.Data = p
    89  	sh.Len = size
    90  	sh.Cap = size
    91  	w := &bufWriter{underlying: mem}
    92  	_, err = io.CopyN(w, code, int64(size))
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	old := uint32(windows_PAGE_READWRITE)
    98  	err = virtualProtect(p, uintptr(size), windows_PAGE_EXECUTE_READ, &old)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	return mem, nil
   103  }
   104  
   105  // ensureErr returns syscall.EINVAL when the input error is nil.
   106  //
   107  // We are supposed to use "GetLastError" which is more precise, but it is not safe to execute in goroutines. While
   108  // "GetLastError" is thread-local, goroutines are not pinned to threads.
   109  //
   110  // See https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
   111  func ensureErr(err error) error {
   112  	if err != nil {
   113  		return err
   114  	}
   115  	return syscall.EINVAL
   116  }