1//go:build unix
2
3package util
4
5import (
6 "context"
7 "os"
8 "unsafe"
9
10 "github.com/tetratelabs/wazero/api"
11 "golang.org/x/sys/unix"
12)
13
14type mmapState struct {
15 regions []*MappedRegion
16}
17
18func (s *mmapState) new(ctx context.Context, mod api.Module, size int32) *MappedRegion {
19 // Find unused region.
20 for _, r := range s.regions {
21 if !r.used && r.size == size {
22 return r
23 }
24 }
25
26 // Allocate page aligned memmory.
27 alloc := mod.ExportedFunction("aligned_alloc")
28 stack := [...]Stk_t{
29 Stk_t(unix.Getpagesize()),
30 Stk_t(size),
31 }
32 if err := alloc.CallWithStack(ctx, stack[:]); err != nil {
33 panic(err)
34 }
35 if stack[0] == 0 {
36 panic(OOMErr)
37 }
38
39 // Save the newly allocated region.
40 ptr := Ptr_t(stack[0])
41 buf := View(mod, ptr, int64(size))
42 ret := &MappedRegion{
43 Ptr: ptr,
44 size: size,
45 addr: unsafe.Pointer(&buf[0]),
46 }
47 s.regions = append(s.regions, ret)
48 return ret
49}
50
51type MappedRegion struct {
52 addr unsafe.Pointer
53 Ptr Ptr_t
54 size int32
55 used bool
56}
57
58func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32, readOnly bool) (*MappedRegion, error) {
59 s := ctx.Value(moduleKey{}).(*moduleState)
60 r := s.new(ctx, mod, size)
61 err := r.mmap(f, offset, readOnly)
62 if err != nil {
63 return nil, err
64 }
65 return r, nil
66}
67
68func (r *MappedRegion) Unmap() error {
69 // We can't munmap the region, otherwise it could be remaped.
70 // Instead, convert it to a protected, private, anonymous mapping.
71 // If successful, it can be reused for a subsequent mmap.
72 _, err := unix.MmapPtr(-1, 0, r.addr, uintptr(r.size),
73 unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_FIXED|unix.MAP_ANON)
74 r.used = err != nil
75 return err
76}
77
78func (r *MappedRegion) mmap(f *os.File, offset int64, readOnly bool) error {
79 prot := unix.PROT_READ
80 if !readOnly {
81 prot |= unix.PROT_WRITE
82 }
83 _, err := unix.MmapPtr(int(f.Fd()), offset, r.addr, uintptr(r.size),
84 prot, unix.MAP_SHARED|unix.MAP_FIXED)
85 r.used = err == nil
86 return err
87}