mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Add some documentation
This commit is contained in:
parent
567d8fe32d
commit
1ff037df3c
5 changed files with 177 additions and 22 deletions
|
@ -544,8 +544,13 @@ static void *__mmap(char *addr, size_t size, int prot, int flags, int fd,
|
|||
int gransz = __gransize;
|
||||
|
||||
// validate arguments
|
||||
if (((uintptr_t)addr & (gransz - 1)) || //
|
||||
!size || (uintptr_t)addr + size < size)
|
||||
if ((uintptr_t)addr & (gransz - 1))
|
||||
addr = NULL;
|
||||
if (!addr && (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE)))
|
||||
return (void *)eperm();
|
||||
if ((intptr_t)addr < 0)
|
||||
return (void *)enomem();
|
||||
if (!size || (uintptr_t)addr + size < size)
|
||||
return (void *)einval();
|
||||
if (size > WINMAXX)
|
||||
return (void *)enomem();
|
||||
|
@ -732,13 +737,100 @@ static void *__mremap(char *old_addr, size_t old_size, size_t new_size,
|
|||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates memory mapping.
|
||||
*
|
||||
* The mmap() function is used by Cosmopolitan's malloc() implementation
|
||||
* to obtain new memory from the operating system. This function is also
|
||||
* useful for establishing a mapping between a file on disk and a memory
|
||||
* address, which avoids most need to call read() and write(). It is how
|
||||
* executables are loaded into memory, for instance, in which case pages
|
||||
* are loaded lazily from disk by the operating system.
|
||||
*
|
||||
* The `addr` parameter may be zero. This lets the implementation choose
|
||||
* an available address in memory. OSes normally pick something randomly
|
||||
* assigned, for security. Most OSes try to make sure subsequent mapping
|
||||
* requests will be adjacent to one another. More paranoid OSes may have
|
||||
* different mappings be sparse, with unmapped content between them. You
|
||||
* may not use the `MAP_FIXED` parameter to create a memory map at NULL.
|
||||
*
|
||||
* The `addr` parameter may be non-zero, in which case Cosmopolitan will
|
||||
* give you a mapping at that specific address if it's available. When a
|
||||
* mapping already exists at the requested address then another one will
|
||||
* be chosen automatically. On most OSes the newly selected address will
|
||||
* be as close-by as possible, but that's not guaranteed. If `MAP_FIXED`
|
||||
* is also supplied in `flags` then this hint is taken as mandatory, and
|
||||
* existing mappings at the requested interval shall be auto-unmapped.
|
||||
*
|
||||
* The `size` parameter is implicitly rounded up to the system page size
|
||||
* reported by getpagesize() and sysconf(_SC_PAGESIZE). Your extra bytes
|
||||
* will be zero-initialized.
|
||||
*
|
||||
* The returned address will always be aligned, on the system allocation
|
||||
* granularity. This value may be obtained from getgransize() or calling
|
||||
* sysconf(_SC_GRANSIZE). Granularity is always greater than or equal to
|
||||
* the page size. On some platforms, i.e. Windows, it may be larger than
|
||||
* the page size.
|
||||
*
|
||||
* The `prot` value specifies the memory protection of the mapping. This
|
||||
* may be `PROT_NONE` to disallow all access otherwise it's a bitwise or
|
||||
* of the following constants:
|
||||
*
|
||||
* - `PROT_READ` allows read access
|
||||
* - `PROT_WRITE` allows write access
|
||||
* - `PROT_EXEC` allows execute access
|
||||
*
|
||||
* Some OSes (i.e. OpenBSD) will raise an error if both `PROT_WRITE` and
|
||||
* `PROT_EXEC` are requested. You may still modify executable memory but
|
||||
* you must use mprotect() to transition between the two states. On some
|
||||
* OSes like MacOS ARM64, you need to pass the `MAP_JIT` flag to get RWX
|
||||
* memory, which is considered zero on other OSes.
|
||||
*
|
||||
* The lower bits of the `flags` parameter specify the `MAP_TYPE`, which
|
||||
* may be:
|
||||
*
|
||||
* - `MAP_PRIVATE` for non-shared and copy-on-write mappings
|
||||
* - `MAP_SHARED` for memory that may be shared between processes
|
||||
*
|
||||
* Your `fd` argument specifies the file descriptor of the open file you
|
||||
* want to map. This parameter is ignored when `MAP_ANONYMOUS` is passed
|
||||
* via `flags`.
|
||||
*
|
||||
* Your `off` argument specifies the offset into a, file at which mapped
|
||||
* memory shall begin. It must be aligned to the allocation granularity,
|
||||
* which may be obtained from getgransize() or sysconf(_SC_GRANSIZE).
|
||||
*
|
||||
* The `MAP_FIXED_NOREPLACE` flag may be passed in `flags` which has the
|
||||
* same behavior as `MAP_FIXED` except it raises `EEXIST` when a mapping
|
||||
* already exists on the requested interval.
|
||||
*
|
||||
* The `MAP_CONCEAL` flag may be passed to prevent a memory mapping from
|
||||
* appearing in core dumps. This is currently supported on BSD OSes, and
|
||||
* is ignored on everything else.
|
||||
*/
|
||||
void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) {
|
||||
void *res = __mmap(addr, size, prot, flags, fd, off);
|
||||
STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → %p% m", addr, size,
|
||||
DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off, res);
|
||||
STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → %p% m (%'zu bytes total)", addr,
|
||||
size, DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off, res,
|
||||
__maps.pages * __pagesize);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes memory mapping.
|
||||
*
|
||||
* This system call lets you move memory without copying it. It can also
|
||||
* be used to shrink memory mappings.
|
||||
*
|
||||
* This system call is supported on Linux and NetBSD. It's used by Cosmo
|
||||
* Libc's realloc() implementation under the hood.
|
||||
*
|
||||
* The `flags` parameter may have:
|
||||
*
|
||||
* - `MREMAP_MAYMOVE` to allow relocation
|
||||
* - `MREMAP_FIXED` in which case an additional parameter is taken
|
||||
*
|
||||
*/
|
||||
void *mremap(void *old_addr, size_t old_size, size_t new_size, int flags, ...) {
|
||||
va_list ap;
|
||||
void *new_addr = 0;
|
||||
|
@ -748,11 +840,19 @@ void *mremap(void *old_addr, size_t old_size, size_t new_size, int flags, ...) {
|
|||
va_end(ap);
|
||||
}
|
||||
void *res = __mremap(old_addr, old_size, new_size, flags, new_addr);
|
||||
STRACE("mremap(%p, %'zu, %'zu, %s, %p) → %p% m", old_addr, old_size, new_size,
|
||||
DescribeMremapFlags(flags), new_addr, res);
|
||||
STRACE("mremap(%p, %'zu, %'zu, %s, %p) → %p% m (%'zu bytes total)", old_addr,
|
||||
old_size, new_size, DescribeMremapFlags(flags), new_addr, res,
|
||||
__maps.pages * __pagesize);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes memory mapping.
|
||||
*
|
||||
* The `size` parameter is implicitly rounded up to the page size.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno.
|
||||
*/
|
||||
int munmap(void *addr, size_t size) {
|
||||
int rc = __munmap(addr, size);
|
||||
STRACE("munmap(%p, %'zu) → %d% m", addr, size, rc);
|
||||
|
|
|
@ -223,7 +223,6 @@ syscon mmap MAP_TYPE 15 15 15 15 15 15 15 15 # mask for type
|
|||
syscon mmap MAP_FIXED 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 0x00000010 # unix consensus; openbsd appears to forbid; faked nt
|
||||
syscon mmap MAP_FIXED_NOREPLACE 0x08000000 0x08000000 0x00004000 0x00004000 0x08000000 0x08000000 0x08000000 0x08000000 # handled and defined by cosmo runtime; 0x100000 on linux 4.7+; MAP_FIXED|MAP_EXCL on FreeBSD
|
||||
syscon mmap MAP_ANONYMOUS 0x00000020 0x00000020 0x00001000 0x00001000 0x00001000 0x00001000 0x00001000 0x00000020 # bsd consensus; faked nt
|
||||
syscon mmap MAP_GROWSDOWN 0x00000100 0x00000100 0 0 0 0 0 0 # use MAP_STACK; abstracted by MAP_STACK; may be passed to __sys_mmap() for low-level Linux fiddling
|
||||
syscon mmap MAP_LOCKED 0x00002000 0x00002000 0 0 0 0 0 0
|
||||
syscon mmap MAP_NORESERVE 0x00004000 0x00004000 0x00000040 0x00000040 0 0 0x00000040 0 # Linux calls it "reserve"; NT calls it "commit"? which is default?
|
||||
syscon mmap MAP_POPULATE 0x00008000 0x00008000 0 0 0x00040000 0 0 0 # MAP_PREFAULT_READ on FreeBSD; can avoid madvise(MADV_WILLNEED) on private file mapping
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon mmap,MAP_GROWSDOWN,0x00000100,0x00000100,0,0,0,0,0,0
|
|
@ -22,6 +22,7 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/intrin/maps.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/literal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
|
@ -146,7 +147,6 @@ TEST(mmap, hint) {
|
|||
TEST(mprotect, punchHoleAndFillHole) {
|
||||
char *p;
|
||||
int count = __maps.count;
|
||||
int gransz = getgransize();
|
||||
|
||||
// obtain memory
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
|
@ -223,16 +223,17 @@ TEST(mmap, testMapFile_fdGetsClosed_makesNoDifference) {
|
|||
TEST(mmap, fileOffset) {
|
||||
int fd;
|
||||
char *map;
|
||||
int offset_align = IsWindows() ? gransz : getpagesize();
|
||||
ASSERT_NE(-1, (fd = open("foo", O_CREAT | O_RDWR, 0644)));
|
||||
EXPECT_NE(-1, ftruncate(fd, offset_align * 2));
|
||||
EXPECT_NE(-1, pwrite(fd, "hello", 5, offset_align * 0));
|
||||
EXPECT_NE(-1, pwrite(fd, "there", 5, offset_align * 1));
|
||||
EXPECT_NE(-1, ftruncate(fd, gransz * 2));
|
||||
EXPECT_NE(-1, pwrite(fd, "hello", 5, gransz * 0));
|
||||
EXPECT_NE(-1, pwrite(fd, "there", 5, gransz * 1));
|
||||
EXPECT_NE(-1, fdatasync(fd));
|
||||
ASSERT_NE(MAP_FAILED, (map = mmap(NULL, offset_align, PROT_READ, MAP_PRIVATE,
|
||||
fd, offset_align)));
|
||||
ASSERT_SYS(EINVAL, MAP_FAILED,
|
||||
mmap(NULL, gransz, PROT_READ, MAP_PRIVATE, fd, gransz - 1));
|
||||
ASSERT_NE(MAP_FAILED,
|
||||
(map = mmap(NULL, gransz, PROT_READ, MAP_PRIVATE, fd, gransz)));
|
||||
EXPECT_EQ(0, memcmp(map, "there", 5), "%#.*s", 5, map);
|
||||
EXPECT_NE(-1, munmap(map, offset_align));
|
||||
EXPECT_NE(-1, munmap(map, gransz));
|
||||
EXPECT_NE(-1, close(fd));
|
||||
}
|
||||
|
||||
|
@ -263,6 +264,67 @@ TEST(mmap, ziposCannotBeShared) {
|
|||
close(fd);
|
||||
}
|
||||
|
||||
TEST(mmap, misalignedAddr_justIgnoresIt) {
|
||||
char *p;
|
||||
ASSERT_NE(MAP_FAILED, (p = mmap((void *)1, 1, PROT_READ,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
|
||||
munmap(p, 1);
|
||||
}
|
||||
|
||||
TEST(mmap, nullFixed_isNotAllowed) {
|
||||
ASSERT_SYS(
|
||||
EPERM, MAP_FAILED,
|
||||
mmap(0, 1, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0));
|
||||
ASSERT_SYS(EPERM, MAP_FAILED,
|
||||
mmap(0, 1, PROT_NONE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0));
|
||||
}
|
||||
|
||||
static int GetBitsInAddressSpace(void) {
|
||||
int i;
|
||||
void *ptr;
|
||||
uint64_t want;
|
||||
for (i = 0; i < 40; ++i) {
|
||||
want = UINT64_C(0x8123000000000000) >> i;
|
||||
if (want > UINTPTR_MAX)
|
||||
continue;
|
||||
if (msync((void *)(uintptr_t)want, 1, MS_ASYNC) == 0 || errno == EBUSY)
|
||||
return 64 - i;
|
||||
ptr = mmap((void *)(uintptr_t)want, 1, PROT_READ,
|
||||
MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
|
||||
if (ptr != MAP_FAILED) {
|
||||
munmap(ptr, 1);
|
||||
return 64 - i;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
TEST(mmap, negative_isNotAllowed) {
|
||||
ASSERT_SYS(ENOMEM, MAP_FAILED,
|
||||
mmap((void *)-(70 * 1024 * 1024), 1, PROT_NONE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0));
|
||||
}
|
||||
|
||||
TEST(mmap, pml5t) {
|
||||
switch (GetBitsInAddressSpace()) {
|
||||
case 56:
|
||||
ASSERT_EQ((void *)0x00fff2749119c000,
|
||||
mmap((void *)0x00fff2749119c000, 1, PROT_NONE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0));
|
||||
munmap((void *)0x00fff2749119c000, 1);
|
||||
break;
|
||||
case 48:
|
||||
ASSERT_EQ((void *)0x0000f2749119c000,
|
||||
mmap((void *)0x0000f2749119c000, 1, PROT_NONE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0));
|
||||
munmap((void *)0x0000f2749119c000, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// zipos NON-SHARED READ-ONLY FILE MEMORY
|
||||
|
||||
|
|
6
third_party/dlmalloc/platform.inc
vendored
6
third_party/dlmalloc/platform.inc
vendored
|
@ -505,11 +505,7 @@ FORCEINLINE int win32munmap(void* ptr, size_t size) {
|
|||
* Define CALL_MREMAP
|
||||
*/
|
||||
#if HAVE_MMAP && HAVE_MREMAP
|
||||
#ifdef MREMAP
|
||||
#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv))
|
||||
#else /* MREMAP */
|
||||
#define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv))
|
||||
#endif /* MREMAP */
|
||||
#define CALL_MREMAP(addr, osz, nsz, mv) ({ int olderr = errno; void *res = mremap((addr), (osz), (nsz), (mv)); if (res == MAP_FAILED) errno = olderr; res; })
|
||||
#else /* HAVE_MMAP && HAVE_MREMAP */
|
||||
#define CALL_MREMAP(addr, osz, nsz, mv) MAP_FAILED
|
||||
#endif /* HAVE_MMAP && HAVE_MREMAP */
|
||||
|
|
Loading…
Reference in a new issue