• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Memory management declarations.
2 
3 use crate::Result;
4 #[cfg(not(target_os = "android"))]
5 use crate::NixPath;
6 use crate::errno::Errno;
7 #[cfg(not(target_os = "android"))]
8 use crate::fcntl::OFlag;
9 use libc::{self, c_int, c_void, size_t, off_t};
10 #[cfg(not(target_os = "android"))]
11 use crate::sys::stat::Mode;
12 use std::os::unix::io::RawFd;
13 
14 libc_bitflags!{
15     /// Desired memory protection of a memory mapping.
16     pub struct ProtFlags: c_int {
17         /// Pages cannot be accessed.
18         PROT_NONE;
19         /// Pages can be read.
20         PROT_READ;
21         /// Pages can be written.
22         PROT_WRITE;
23         /// Pages can be executed
24         PROT_EXEC;
25         /// Apply protection up to the end of a mapping that grows upwards.
26         #[cfg(any(target_os = "android", target_os = "linux"))]
27         PROT_GROWSDOWN;
28         /// Apply protection down to the beginning of a mapping that grows downwards.
29         #[cfg(any(target_os = "android", target_os = "linux"))]
30         PROT_GROWSUP;
31     }
32 }
33 
34 libc_bitflags!{
35     /// Additional parameters for [`mmap`].
36     pub struct MapFlags: c_int {
37         /// Compatibility flag. Ignored.
38         MAP_FILE;
39         /// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
40         MAP_SHARED;
41         /// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.
42         MAP_PRIVATE;
43         /// Place the mapping at exactly the address specified in `addr`.
44         MAP_FIXED;
45         /// To be used with `MAP_FIXED`, to forbid the system
46         /// to select a different address than the one specified.
47         #[cfg(target_os = "freebsd")]
48         MAP_EXCL;
49         /// Synonym for `MAP_ANONYMOUS`.
50         MAP_ANON;
51         /// The mapping is not backed by any file.
52         MAP_ANONYMOUS;
53         /// Put the mapping into the first 2GB of the process address space.
54         #[cfg(any(all(any(target_os = "android", target_os = "linux"),
55                       any(target_arch = "x86", target_arch = "x86_64")),
56                   all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")),
57                   all(target_os = "freebsd", target_pointer_width = "64")))]
58         MAP_32BIT;
59         /// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
60         #[cfg(any(target_os = "android", target_os = "linux"))]
61         MAP_GROWSDOWN;
62         /// Compatibility flag. Ignored.
63         #[cfg(any(target_os = "android", target_os = "linux"))]
64         MAP_DENYWRITE;
65         /// Compatibility flag. Ignored.
66         #[cfg(any(target_os = "android", target_os = "linux"))]
67         MAP_EXECUTABLE;
68         /// Mark the mmaped region to be locked in the same way as `mlock(2)`.
69         #[cfg(any(target_os = "android", target_os = "linux"))]
70         MAP_LOCKED;
71         /// Do not reserve swap space for this mapping.
72         ///
73         /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
74         #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))]
75         MAP_NORESERVE;
76         /// Populate page tables for a mapping.
77         #[cfg(any(target_os = "android", target_os = "linux"))]
78         MAP_POPULATE;
79         /// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
80         #[cfg(any(target_os = "android", target_os = "linux"))]
81         MAP_NONBLOCK;
82         /// Allocate the mapping using "huge pages."
83         #[cfg(any(target_os = "android", target_os = "linux"))]
84         MAP_HUGETLB;
85         /// Make use of 64KB huge page (must be supported by the system)
86         #[cfg(target_os = "linux")]
87         MAP_HUGE_64KB;
88         /// Make use of 512KB huge page (must be supported by the system)
89         #[cfg(target_os = "linux")]
90         MAP_HUGE_512KB;
91         /// Make use of 1MB huge page (must be supported by the system)
92         #[cfg(target_os = "linux")]
93         MAP_HUGE_1MB;
94         /// Make use of 2MB huge page (must be supported by the system)
95         #[cfg(target_os = "linux")]
96         MAP_HUGE_2MB;
97         /// Make use of 8MB huge page (must be supported by the system)
98         #[cfg(target_os = "linux")]
99         MAP_HUGE_8MB;
100         /// Make use of 16MB huge page (must be supported by the system)
101         #[cfg(target_os = "linux")]
102         MAP_HUGE_16MB;
103         /// Make use of 32MB huge page (must be supported by the system)
104         #[cfg(target_os = "linux")]
105         MAP_HUGE_32MB;
106         /// Make use of 256MB huge page (must be supported by the system)
107         #[cfg(target_os = "linux")]
108         MAP_HUGE_256MB;
109         /// Make use of 512MB huge page (must be supported by the system)
110         #[cfg(target_os = "linux")]
111         MAP_HUGE_512MB;
112         /// Make use of 1GB huge page (must be supported by the system)
113         #[cfg(target_os = "linux")]
114         MAP_HUGE_1GB;
115         /// Make use of 2GB huge page (must be supported by the system)
116         #[cfg(target_os = "linux")]
117         MAP_HUGE_2GB;
118         /// Make use of 16GB huge page (must be supported by the system)
119         #[cfg(target_os = "linux")]
120         MAP_HUGE_16GB;
121 
122         /// Lock the mapped region into memory as with `mlock(2)`.
123         #[cfg(target_os = "netbsd")]
124         MAP_WIRED;
125         /// Causes dirtied data in the specified range to be flushed to disk only when necessary.
126         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
127         MAP_NOSYNC;
128         /// Rename private pages to a file.
129         ///
130         /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
131         #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
132         MAP_RENAME;
133         /// Region may contain semaphores.
134         #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
135         MAP_HASSEMAPHORE;
136         /// Region grows down, like a stack.
137         #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
138         MAP_STACK;
139         /// Pages in this mapping are not retained in the kernel's memory cache.
140         #[cfg(any(target_os = "ios", target_os = "macos"))]
141         MAP_NOCACHE;
142         /// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
143         #[cfg(any(target_os = "ios", target_os = "macos"))]
144         MAP_JIT;
145         /// Allows to use large pages, underlying alignment based on size.
146         #[cfg(target_os = "freebsd")]
147         MAP_ALIGNED_SUPER;
148         /// Pages will be discarded in the core dumps.
149         #[cfg(target_os = "openbsd")]
150         MAP_CONCEAL;
151     }
152 }
153 
154 #[cfg(any(target_os = "linux", target_os = "netbsd"))]
155 libc_bitflags!{
156     /// Options for [`mremap`].
157     pub struct MRemapFlags: c_int {
158         /// Permit the kernel to relocate the mapping to a new virtual address, if necessary.
159         #[cfg(target_os = "linux")]
160         MREMAP_MAYMOVE;
161         /// Place the mapping at exactly the address specified in `new_address`.
162         #[cfg(target_os = "linux")]
163         MREMAP_FIXED;
164         /// Permits to use the old and new address as hints to relocate the mapping.
165         #[cfg(target_os = "netbsd")]
166         MAP_FIXED;
167         /// Allows to duplicate the mapping to be able to apply different flags on the copy.
168         #[cfg(target_os = "netbsd")]
169         MAP_REMAPDUP;
170     }
171 }
172 
173 libc_enum!{
174     /// Usage information for a range of memory to allow for performance optimizations by the kernel.
175     ///
176     /// Used by [`madvise`].
177     #[repr(i32)]
178     #[non_exhaustive]
179     pub enum MmapAdvise {
180         /// No further special treatment. This is the default.
181         MADV_NORMAL,
182         /// Expect random page references.
183         MADV_RANDOM,
184         /// Expect sequential page references.
185         MADV_SEQUENTIAL,
186         /// Expect access in the near future.
187         MADV_WILLNEED,
188         /// Do not expect access in the near future.
189         MADV_DONTNEED,
190         /// Free up a given range of pages and its associated backing store.
191         #[cfg(any(target_os = "android", target_os = "linux"))]
192         MADV_REMOVE,
193         /// Do not make pages in this range available to the child after a `fork(2)`.
194         #[cfg(any(target_os = "android", target_os = "linux"))]
195         MADV_DONTFORK,
196         /// Undo the effect of `MADV_DONTFORK`.
197         #[cfg(any(target_os = "android", target_os = "linux"))]
198         MADV_DOFORK,
199         /// Poison the given pages.
200         ///
201         /// Subsequent references to those pages are treated like hardware memory corruption.
202         #[cfg(any(target_os = "android", target_os = "linux"))]
203         MADV_HWPOISON,
204         /// Enable Kernel Samepage Merging (KSM) for the given pages.
205         #[cfg(any(target_os = "android", target_os = "linux"))]
206         MADV_MERGEABLE,
207         /// Undo the effect of `MADV_MERGEABLE`
208         #[cfg(any(target_os = "android", target_os = "linux"))]
209         MADV_UNMERGEABLE,
210         /// Preserve the memory of each page but offline the original page.
211         #[cfg(any(target_os = "android",
212             all(target_os = "linux", any(
213                 target_arch = "aarch64",
214                 target_arch = "arm",
215                 target_arch = "powerpc",
216                 target_arch = "powerpc64",
217                 target_arch = "s390x",
218                 target_arch = "x86",
219                 target_arch = "x86_64",
220                 target_arch = "sparc64"))))]
221         MADV_SOFT_OFFLINE,
222         /// Enable Transparent Huge Pages (THP) for pages in the given range.
223         #[cfg(any(target_os = "android", target_os = "linux"))]
224         MADV_HUGEPAGE,
225         /// Undo the effect of `MADV_HUGEPAGE`.
226         #[cfg(any(target_os = "android", target_os = "linux"))]
227         MADV_NOHUGEPAGE,
228         /// Exclude the given range from a core dump.
229         #[cfg(any(target_os = "android", target_os = "linux"))]
230         MADV_DONTDUMP,
231         /// Undo the effect of an earlier `MADV_DONTDUMP`.
232         #[cfg(any(target_os = "android", target_os = "linux"))]
233         MADV_DODUMP,
234         /// Specify that the application no longer needs the pages in the given range.
235         MADV_FREE,
236         /// Request that the system not flush the current range to disk unless it needs to.
237         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
238         MADV_NOSYNC,
239         /// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
240         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
241         MADV_AUTOSYNC,
242         /// Region is not included in a core file.
243         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
244         MADV_NOCORE,
245         /// Include region in a core file
246         #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
247         MADV_CORE,
248         #[cfg(any(target_os = "freebsd"))]
249         MADV_PROTECT,
250         /// Invalidate the hardware page table for the given region.
251         #[cfg(target_os = "dragonfly")]
252         MADV_INVAL,
253         /// Set the offset of the page directory page to `value` for the virtual page table.
254         #[cfg(target_os = "dragonfly")]
255         MADV_SETMAP,
256         /// Indicates that the application will not need the data in the given range.
257         #[cfg(any(target_os = "ios", target_os = "macos"))]
258         MADV_ZERO_WIRED_PAGES,
259         #[cfg(any(target_os = "ios", target_os = "macos"))]
260         MADV_FREE_REUSABLE,
261         #[cfg(any(target_os = "ios", target_os = "macos"))]
262         MADV_FREE_REUSE,
263         #[cfg(any(target_os = "ios", target_os = "macos"))]
264         MADV_CAN_REUSE,
265     }
266 }
267 
268 libc_bitflags!{
269     /// Configuration flags for [`msync`].
270     pub struct MsFlags: c_int {
271         /// Schedule an update but return immediately.
272         MS_ASYNC;
273         /// Invalidate all cached data.
274         MS_INVALIDATE;
275         /// Invalidate pages, but leave them mapped.
276         #[cfg(any(target_os = "ios", target_os = "macos"))]
277         MS_KILLPAGES;
278         /// Deactivate pages, but leave them mapped.
279         #[cfg(any(target_os = "ios", target_os = "macos"))]
280         MS_DEACTIVATE;
281         /// Perform an update and wait for it to complete.
282         MS_SYNC;
283     }
284 }
285 
286 libc_bitflags!{
287     /// Flags for [`mlockall`].
288     pub struct MlockAllFlags: c_int {
289         /// Lock pages that are currently mapped into the address space of the process.
290         MCL_CURRENT;
291         /// Lock pages which will become mapped into the address space of the process in the future.
292         MCL_FUTURE;
293     }
294 }
295 
296 /// Locks all memory pages that contain part of the address range with `length`
297 /// bytes starting at `addr`.
298 ///
299 /// Locked pages never move to the swap area.
300 ///
301 /// # Safety
302 ///
303 /// `addr` must meet all the requirements described in the [`mlock(2)`] man page.
304 ///
305 /// [`mlock(2)`]: https://man7.org/linux/man-pages/man2/mlock.2.html
mlock(addr: *const c_void, length: size_t) -> Result<()>306 pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
307     Errno::result(libc::mlock(addr, length)).map(drop)
308 }
309 
310 /// Unlocks all memory pages that contain part of the address range with
311 /// `length` bytes starting at `addr`.
312 ///
313 /// # Safety
314 ///
315 /// `addr` must meet all the requirements described in the [`munlock(2)`] man
316 /// page.
317 ///
318 /// [`munlock(2)`]: https://man7.org/linux/man-pages/man2/munlock.2.html
munlock(addr: *const c_void, length: size_t) -> Result<()>319 pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
320     Errno::result(libc::munlock(addr, length)).map(drop)
321 }
322 
323 /// Locks all memory pages mapped into this process' address space.
324 ///
325 /// Locked pages never move to the swap area. For more information, see [`mlockall(2)`].
326 ///
327 /// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
mlockall(flags: MlockAllFlags) -> Result<()>328 pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
329     unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
330 }
331 
332 /// Unlocks all memory pages mapped into this process' address space.
333 ///
334 /// For more information, see [`munlockall(2)`].
335 ///
336 /// [`munlockall(2)`]: https://man7.org/linux/man-pages/man2/munlockall.2.html
munlockall() -> Result<()>337 pub fn munlockall() -> Result<()> {
338     unsafe { Errno::result(libc::munlockall()) }.map(drop)
339 }
340 
341 /// allocate memory, or map files or devices into memory
342 ///
343 /// # Safety
344 ///
345 /// See the [`mmap(2)`] man page for detailed requirements.
346 ///
347 /// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void>348 pub unsafe fn mmap(addr: *mut c_void, length: size_t, prot: ProtFlags, flags: MapFlags, fd: RawFd, offset: off_t) -> Result<*mut c_void> {
349     let ret = libc::mmap(addr, length, prot.bits(), flags.bits(), fd, offset);
350 
351     if ret == libc::MAP_FAILED {
352         Err(Errno::last())
353     } else {
354         Ok(ret)
355     }
356 }
357 
358 /// Expands (or shrinks) an existing memory mapping, potentially moving it at
359 /// the same time.
360 ///
361 /// # Safety
362 ///
363 /// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for
364 /// detailed requirements.
365 #[cfg(any(target_os = "linux", target_os = "netbsd"))]
mremap( addr: *mut c_void, old_size: size_t, new_size: size_t, flags: MRemapFlags, new_address: Option<* mut c_void>, ) -> Result<*mut c_void>366 pub unsafe fn mremap(
367     addr: *mut c_void,
368     old_size: size_t,
369     new_size: size_t,
370     flags: MRemapFlags,
371     new_address: Option<* mut c_void>,
372 ) -> Result<*mut c_void> {
373     #[cfg(target_os = "linux")]
374     let ret = libc::mremap(addr, old_size, new_size, flags.bits(), new_address.unwrap_or(std::ptr::null_mut()));
375     #[cfg(target_os = "netbsd")]
376     let ret = libc::mremap(
377         addr,
378         old_size,
379         new_address.unwrap_or(std::ptr::null_mut()),
380         new_size,
381         flags.bits(),
382         );
383 
384     if ret == libc::MAP_FAILED {
385         Err(Errno::last())
386     } else {
387         Ok(ret)
388     }
389 }
390 
391 /// remove a mapping
392 ///
393 /// # Safety
394 ///
395 /// `addr` must meet all the requirements described in the [`munmap(2)`] man
396 /// page.
397 ///
398 /// [`munmap(2)`]: https://man7.org/linux/man-pages/man2/munmap.2.html
munmap(addr: *mut c_void, len: size_t) -> Result<()>399 pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
400     Errno::result(libc::munmap(addr, len)).map(drop)
401 }
402 
403 /// give advice about use of memory
404 ///
405 /// # Safety
406 ///
407 /// See the [`madvise(2)`] man page.  Take special care when using
408 /// [`MmapAdvise::MADV_FREE`].
409 ///
410 /// [`madvise(2)`]: https://man7.org/linux/man-pages/man2/madvise.2.html
madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()>411 pub unsafe fn madvise(addr: *mut c_void, length: size_t, advise: MmapAdvise) -> Result<()> {
412     Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
413 }
414 
415 /// Set protection of memory mapping.
416 ///
417 /// See [`mprotect(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html) for
418 /// details.
419 ///
420 /// # Safety
421 ///
422 /// Calls to `mprotect` are inherently unsafe, as changes to memory protections can lead to
423 /// SIGSEGVs.
424 ///
425 /// ```
426 /// # use nix::libc::size_t;
427 /// # use nix::sys::mman::{mmap, mprotect, MapFlags, ProtFlags};
428 /// # use std::ptr;
429 /// const ONE_K: size_t = 1024;
430 /// let mut slice: &mut [u8] = unsafe {
431 ///     let mem = mmap(ptr::null_mut(), ONE_K, ProtFlags::PROT_NONE,
432 ///                    MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, -1, 0).unwrap();
433 ///     mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap();
434 ///     std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
435 /// };
436 /// assert_eq!(slice[0], 0x00);
437 /// slice[0] = 0xFF;
438 /// assert_eq!(slice[0], 0xFF);
439 /// ```
mprotect(addr: *mut c_void, length: size_t, prot: ProtFlags) -> Result<()>440 pub unsafe fn mprotect(addr: *mut c_void, length: size_t, prot: ProtFlags) -> Result<()> {
441     Errno::result(libc::mprotect(addr, length, prot.bits())).map(drop)
442 }
443 
444 /// synchronize a mapped region
445 ///
446 /// # Safety
447 ///
448 /// `addr` must meet all the requirements described in the [`msync(2)`] man
449 /// page.
450 ///
451 /// [`msync(2)`]: https://man7.org/linux/man-pages/man2/msync.2.html
msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()>452 pub unsafe fn msync(addr: *mut c_void, length: size_t, flags: MsFlags) -> Result<()> {
453     Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
454 }
455 
456 /// Creates and opens a new, or opens an existing, POSIX shared memory object.
457 ///
458 /// For more information, see [`shm_open(3)`].
459 ///
460 /// [`shm_open(3)`]: https://man7.org/linux/man-pages/man3/shm_open.3.html
461 #[cfg(not(target_os = "android"))]
shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd>462 pub fn shm_open<P: ?Sized + NixPath>(name: &P, flag: OFlag, mode: Mode) -> Result<RawFd> {
463     let ret = name.with_nix_path(|cstr| {
464         #[cfg(any(target_os = "macos", target_os = "ios"))]
465         unsafe {
466             libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
467         }
468         #[cfg(not(any(target_os = "macos", target_os = "ios")))]
469         unsafe {
470             libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
471         }
472     })?;
473 
474     Errno::result(ret)
475 }
476 
477 /// Performs the converse of [`shm_open`], removing an object previously created.
478 ///
479 /// For more information, see [`shm_unlink(3)`].
480 ///
481 /// [`shm_unlink(3)`]: https://man7.org/linux/man-pages/man3/shm_unlink.3.html
482 #[cfg(not(target_os = "android"))]
shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()>483 pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
484     let ret = name.with_nix_path(|cstr| {
485         unsafe { libc::shm_unlink(cstr.as_ptr()) }
486     })?;
487 
488     Errno::result(ret).map(drop)
489 }
490