mmap_windows.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. // Copyright 2011 Evan Shaw. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package mmap
  5. import (
  6. "errors"
  7. "os"
  8. "sync"
  9. "syscall"
  10. )
  11. // mmap on Windows is a two-step process.
  12. // First, we call CreateFileMapping to get a handle.
  13. // Then, we call MapviewToFile to get an actual pointer into memory.
  14. // Because we want to emulate a POSIX-style mmap, we don't want to expose
  15. // the handle -- only the pointer. We also want to return only a byte slice,
  16. // not a struct, so it's convenient to manipulate.
  17. // We keep this map so that we can get back the original handle from the memory address.
  18. var handleLock sync.Mutex
  19. var handleMap = map[uintptr]syscall.Handle{}
  20. func mmap(len int, prot, flags, hfile uintptr, off int64) ([]byte, error) {
  21. flProtect := uint32(syscall.PAGE_READONLY)
  22. dwDesiredAccess := uint32(syscall.FILE_MAP_READ)
  23. switch {
  24. case prot&COPY != 0:
  25. flProtect = syscall.PAGE_WRITECOPY
  26. dwDesiredAccess = syscall.FILE_MAP_COPY
  27. case prot&RDWR != 0:
  28. flProtect = syscall.PAGE_READWRITE
  29. dwDesiredAccess = syscall.FILE_MAP_WRITE
  30. }
  31. if prot&EXEC != 0 {
  32. flProtect <<= 4
  33. dwDesiredAccess |= syscall.FILE_MAP_EXECUTE
  34. }
  35. // The maximum size is the area of the file, starting from 0,
  36. // that we wish to allow to be mappable. It is the sum of
  37. // the length the user requested, plus the offset where that length
  38. // is starting from. This does not map the data into memory.
  39. maxSizeHigh := uint32((off + int64(len)) >> 32)
  40. maxSizeLow := uint32((off + int64(len)) & 0xFFFFFFFF)
  41. // TODO: Do we need to set some security attributes? It might help portability.
  42. h, errno := syscall.CreateFileMapping(syscall.Handle(hfile), nil, flProtect, maxSizeHigh, maxSizeLow, nil)
  43. if h == 0 {
  44. return nil, os.NewSyscallError("CreateFileMapping", errno)
  45. }
  46. // Actually map a view of the data into memory. The view's size
  47. // is the length the user requested.
  48. fileOffsetHigh := uint32(off >> 32)
  49. fileOffsetLow := uint32(off & 0xFFFFFFFF)
  50. addr, errno := syscall.MapViewOfFile(h, dwDesiredAccess, fileOffsetHigh, fileOffsetLow, uintptr(len))
  51. if addr == 0 {
  52. return nil, os.NewSyscallError("MapViewOfFile", errno)
  53. }
  54. handleLock.Lock()
  55. handleMap[addr] = h
  56. handleLock.Unlock()
  57. m := MMap{}
  58. dh := m.header()
  59. dh.Data = addr
  60. dh.Len = len
  61. dh.Cap = dh.Len
  62. return m, nil
  63. }
  64. func flush(addr, len uintptr) error {
  65. errno := syscall.FlushViewOfFile(addr, len)
  66. if errno != nil {
  67. return os.NewSyscallError("FlushViewOfFile", errno)
  68. }
  69. handleLock.Lock()
  70. defer handleLock.Unlock()
  71. handle, ok := handleMap[addr]
  72. if !ok {
  73. // should be impossible; we would've errored above
  74. return errors.New("unknown base address")
  75. }
  76. errno = syscall.FlushFileBuffers(handle)
  77. return os.NewSyscallError("FlushFileBuffers", errno)
  78. }
  79. func lock(addr, len uintptr) error {
  80. errno := syscall.VirtualLock(addr, len)
  81. return os.NewSyscallError("VirtualLock", errno)
  82. }
  83. func unlock(addr, len uintptr) error {
  84. errno := syscall.VirtualUnlock(addr, len)
  85. return os.NewSyscallError("VirtualUnlock", errno)
  86. }
  87. func unmap(addr, len uintptr) error {
  88. flush(addr, len)
  89. // Lock the UnmapViewOfFile along with the handleMap deletion.
  90. // As soon as we unmap the view, the OS is free to give the
  91. // same addr to another new map. We don't want another goroutine
  92. // to insert and remove the same addr into handleMap while
  93. // we're trying to remove our old addr/handle pair.
  94. handleLock.Lock()
  95. defer handleLock.Unlock()
  96. err := syscall.UnmapViewOfFile(addr)
  97. if err != nil {
  98. return err
  99. }
  100. handle, ok := handleMap[addr]
  101. if !ok {
  102. // should be impossible; we would've errored above
  103. return errors.New("unknown base address")
  104. }
  105. delete(handleMap, addr)
  106. e := syscall.CloseHandle(syscall.Handle(handle))
  107. return os.NewSyscallError("CloseHandle", e)
  108. }