1-- Memory the system maps for the process: anonymous, zeroed, readable
2-- and writable pages, given back, grown where they are and, on Linux,
3-- moved; and the page size.
4
5import platform/libc
6import platform/linux
7
8--- Map `length` bytes of fresh, zeroed memory that this process alone may
9--- read and write, POSIX's anonymous private `mmap`: its first byte, which
10--- is aligned to a page, or the errno, `enomem` when the system has no
11--- room.
12public link(symbol: "_landin_posix_map") map:
13 (length: usize) -> (base: ptr mut u8) ! errno =
14 base = try system_map(length)
15end map
16
17--- Give back the `length` bytes mapped at `base`, POSIX's `munmap`.
18public link(symbol: "_landin_posix_unmap") unmap:
19 (base: ptr mut u8, length: usize) -> none ! errno =
20 try system_unmap(base, length)
21end unmap
22
23--- Make the `length` bytes `map` mapped at `base` `new_length` bytes long,
24--- at least `length`, where they are: the pages after them must be free,
25--- or it fails with `enomem` and the mapping is as it was. Linux's
26--- `mremap` without `MREMAP_MAYMOVE`. Darwin and FreeBSD have no `mremap`:
27--- there the pages after the mapping are asked for, and kept only when the
28--- system puts them there. A shorter `new_length` fails with `einval`;
29--- give a mapping's tail back with `unmap`.
30public link(symbol: "_landin_posix_extend") extend:
31 (base: ptr mut u8, length: usize, new_length: usize) -> none ! errno =
32 fail einval when new_length < length
33 return when new_length == length
34 try system_extend(base, length, new_length)
35end extend
36
37--- Make the `length` bytes `map` mapped at `base` `new_length` bytes long,
38--- at least `length`, and return where they now are: in place, as `extend`
39--- does, or else at an address with room, the pages moved rather than their
40--- bytes copied, which is Linux's `mremap` with `MREMAP_MAYMOVE`. Darwin
41--- and FreeBSD cannot move a mapping, so there it is `extend`. A failure
42--- leaves the mapping as it was; a shorter `new_length` fails with
43--- `einval`.
44public link(symbol: "_landin_posix_remap") remap:
45 (base: ptr mut u8, length: usize, new_length: usize)
46 -> (moved: ptr mut u8) ! errno =
47 fail einval when new_length < length
48 moved = ptr(usize(base))
49 return when new_length == length
50 moved = try system_remap(base, length, new_length)
51end remap
52
53--- The size of the system's pages in bytes, which `map` aligns to: 16 KiB
54--- on Darwin arm64 and 4 KiB on the others. On Linux it is what the
55--- kernel told a static executable's entry or, under `--link=libc`, what
56--- the auxiliary vector after C `main`'s `envp` says, and 4096 when no
57--- vector was found.
58public link(symbol: "_landin_posix_page_size") page_size:
59 () -> (bytes: usize) =
60 bytes = system_page_size()
61end page_size
62
63-- The system's own calls beneath the facility above: Linux's system
64-- calls through platform/linux, and the C library through
65-- platform/libc on Darwin and FreeBSD, whose system interface it is.
66
67fixed if compiler.os == linux then
68 system_map: (length: usize) -> (base: ptr mut u8) ! errno =
69 result: i64 = linux.mmap(0, length, linux.prot_read | linux.prot_write,
70 linux.map_private | linux.map_anonymous, -1,
71 0)
72 fail errno_of(i32(-result)) when result < 0
73 base = ptr(usize(result))
74 end system_map
75
76 system_unmap: (base: ptr mut u8, length: usize) -> none ! errno =
77 result: i32 = linux.munmap(usize(base), length)
78 fail errno_of(-result) when result < 0
79 end system_unmap
80
81 system_extend: (base: ptr mut u8, length: usize, new_length: usize)
82 -> none ! errno =
83 result: i64 = linux.mremap(usize(base), length, new_length, 0)
84 fail errno_of(i32(-result)) when result < 0
85 end system_extend
86
87 system_remap: (base: ptr mut u8, length: usize, new_length: usize)
88 -> (moved: ptr mut u8) ! errno =
89 result: i64 = linux.mremap(usize(base), length, new_length,
90 linux.mremap_maymove)
91 fail errno_of(i32(-result)) when result < 0
92 moved = ptr(usize(result))
93 end system_remap
94
95 system_page_size: () -> (bytes: usize) =
96 bytes = linux.page_size()
97 end system_page_size
98elsif compiler.os == darwin or compiler.os == freebsd then
99 system_map: (length: usize) -> (base: ptr mut u8) ! errno =
100 result: usize = libc.mmap(0, length, libc.prot_read | libc.prot_write,
101 libc.map_private | libc.map_anonymous, -1,
102 0)
103 fail failure() when result == libc.map_failed
104 base = ptr(result)
105 end system_map
106
107 system_unmap: (base: ptr mut u8, length: usize) -> none ! errno =
108 fail failure() when libc.munmap(usize(base), length) <> 0
109 end system_unmap
110
111 -- The pages after the mapping, asked for at its end without
112 -- `MAP_FIXED`, which would replace whatever is mapped there: a system
113 -- that puts them elsewhere has none free there, and they are given
114 -- back.
115 system_extend: (base: ptr mut u8, length: usize, new_length: usize)
116 -> none ! errno =
117 wanted: usize = usize(base) + length
118 extra: usize = new_length - length
119 result: usize = libc.mmap(wanted, extra,
120 libc.prot_read | libc.prot_write,
121 libc.map_private | libc.map_anonymous, -1,
122 0)
123 fail failure() when result == libc.map_failed
124 if result <> wanted then
125 _ = libc.munmap(result, extra)
126 fail enomem
127 end if
128 end system_extend
129
130 -- A mapping cannot move here, so it grows where it is or not at all.
131 system_remap: (base: ptr mut u8, length: usize, new_length: usize)
132 -> (moved: ptr mut u8) ! errno =
133 try system_extend(base, length, new_length)
134 moved = ptr(usize(base))
135 end system_remap
136
137 system_page_size: () -> (bytes: usize) =
138 bytes = usize(libc.getpagesize())
139 end system_page_size
140end if