mmap_unix.go

 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}