github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/internal/util/mmap.go (about) 1 //go:build (darwin || linux) && (amd64 || arm64 || riscv64) && !(sqlite3_noshm || sqlite3_nosys) 2 3 package util 4 5 import ( 6 "context" 7 "os" 8 "unsafe" 9 10 "github.com/tetratelabs/wazero/api" 11 "github.com/tetratelabs/wazero/experimental" 12 "golang.org/x/sys/unix" 13 ) 14 15 func withAllocator(ctx context.Context) context.Context { 16 return experimental.WithMemoryAllocator(ctx, 17 experimental.MemoryAllocatorFunc(virtualAlloc)) 18 } 19 20 type mmapState struct { 21 regions []*MappedRegion 22 } 23 24 func (s *mmapState) new(ctx context.Context, mod api.Module, size int32) *MappedRegion { 25 // Find unused region. 26 for _, r := range s.regions { 27 if !r.used && r.size == size { 28 return r 29 } 30 } 31 32 // Allocate page aligned memmory. 33 alloc := mod.ExportedFunction("aligned_alloc") 34 stack := [2]uint64{ 35 uint64(unix.Getpagesize()), 36 uint64(size), 37 } 38 if err := alloc.CallWithStack(ctx, stack[:]); err != nil { 39 panic(err) 40 } 41 if stack[0] == 0 { 42 panic(OOMErr) 43 } 44 45 // Save the newly allocated region. 46 ptr := uint32(stack[0]) 47 buf := View(mod, ptr, uint64(size)) 48 addr := uintptr(unsafe.Pointer(&buf[0])) 49 s.regions = append(s.regions, &MappedRegion{ 50 Ptr: ptr, 51 addr: addr, 52 size: size, 53 }) 54 return s.regions[len(s.regions)-1] 55 } 56 57 type MappedRegion struct { 58 addr uintptr 59 Ptr uint32 60 size int32 61 used bool 62 } 63 64 func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32, prot int) (*MappedRegion, error) { 65 s := ctx.Value(moduleKey{}).(*moduleState) 66 r := s.new(ctx, mod, size) 67 err := r.mmap(f, offset, prot) 68 if err != nil { 69 return nil, err 70 } 71 return r, nil 72 } 73 74 func (r *MappedRegion) Unmap() error { 75 // We can't munmap the region, otherwise it could be remaped. 76 // Instead, convert it to a protected, private, anonymous mapping. 77 // If successful, it can be reused for a subsequent mmap. 78 _, err := mmap(r.addr, uintptr(r.size), 79 unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANON|unix.MAP_FIXED, 80 -1, 0) 81 r.used = err != nil 82 return err 83 } 84 85 func (r *MappedRegion) mmap(f *os.File, offset int64, prot int) error { 86 _, err := mmap(r.addr, uintptr(r.size), 87 prot, unix.MAP_SHARED|unix.MAP_FIXED, 88 int(f.Fd()), offset) 89 r.used = err == nil 90 return err 91 } 92 93 // We need the low level mmap for MAP_FIXED to work. 94 // Bind the syscall version hoping that it is more stable. 95 96 //go:linkname mmap syscall.mmap 97 func mmap(addr, length uintptr, prot, flag, fd int, pos int64) (*byte, error)