mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-23 13:52:28 +00:00
Implement zipos mmap (#725)
This commit is contained in:
parent
6d36584ff2
commit
fd0da9c0df
7 changed files with 175 additions and 4 deletions
|
@ -50,6 +50,7 @@
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/prot.h"
|
||||||
#include "libc/sysv/errfuns.h"
|
#include "libc/sysv/errfuns.h"
|
||||||
#include "libc/thread/thread.h"
|
#include "libc/thread/thread.h"
|
||||||
|
#include "libc/zipos/zipos.internal.h"
|
||||||
|
|
||||||
#define MAP_ANONYMOUS_linux 0x00000020
|
#define MAP_ANONYMOUS_linux 0x00000020
|
||||||
#define MAP_ANONYMOUS_openbsd 0x00001000
|
#define MAP_ANONYMOUS_openbsd 0x00001000
|
||||||
|
@ -485,6 +486,11 @@ static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags,
|
||||||
void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) {
|
void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) {
|
||||||
void *res;
|
void *res;
|
||||||
size_t toto;
|
size_t toto;
|
||||||
|
if (__isfdkind(fd, kFdZip)) {
|
||||||
|
return _weaken(__zipos_mmap)(
|
||||||
|
addr, size, prot, flags,
|
||||||
|
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, off);
|
||||||
|
}
|
||||||
#if defined(SYSDEBUG) && (_KERNTRACE || _NTTRACE)
|
#if defined(SYSDEBUG) && (_KERNTRACE || _NTTRACE)
|
||||||
if (IsWindows()) {
|
if (IsWindows()) {
|
||||||
STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → ...", addr, size,
|
STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → ...", addr, size,
|
||||||
|
|
76
libc/zipos/mmap.c
Normal file
76
libc/zipos/mmap.c
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/calls.h"
|
||||||
|
#include "libc/calls/struct/iovec.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/errno.h"
|
||||||
|
#include "libc/intrin/likely.h"
|
||||||
|
#include "libc/intrin/strace.internal.h"
|
||||||
|
#include "libc/sysv/consts/map.h"
|
||||||
|
#include "libc/sysv/consts/prot.h"
|
||||||
|
#include "libc/sysv/errfuns.h"
|
||||||
|
#include "libc/zipos/zipos.internal.h"
|
||||||
|
|
||||||
|
#define IP(X) (intptr_t)(X)
|
||||||
|
#define VIP(X) (void *)IP(X)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map zipos file into memory. See mmap.
|
||||||
|
*
|
||||||
|
* @param addr should be 0 or a compatible address
|
||||||
|
* @param size must be >0 and will be rounded up to FRAMESIZE
|
||||||
|
* automatically.
|
||||||
|
* @param prot can have PROT_READ/PROT_WRITE/PROT_EXEC/PROT_NONE/etc.
|
||||||
|
* @param flags cannot have `MAP_SHARED` or `MAP_ANONYMOUS`, there is
|
||||||
|
* no actual file backing for zipos files. `MAP_SHARED` could be
|
||||||
|
* simulated for non-writable mappings, but that would require
|
||||||
|
* tracking zipos mappings to prevent making it PROT_WRITE.
|
||||||
|
* @param h is a zip store object
|
||||||
|
* @param off specifies absolute byte index of h's file for mapping,
|
||||||
|
* it does not need to be 64kb aligned.
|
||||||
|
* @return virtual base address of new mapping, or MAP_FAILED w/ errno
|
||||||
|
*/
|
||||||
|
void *__zipos_mmap(void *addr, size_t size, int prot, int flags,
|
||||||
|
struct ZiposHandle *h, int64_t off) {
|
||||||
|
if (!(flags & MAP_PRIVATE) ||
|
||||||
|
(flags & ~(MAP_PRIVATE | MAP_FILE | MAP_FIXED | MAP_FIXED_NOREPLACE)) ||
|
||||||
|
(!!(flags & MAP_FIXED) ^ !!(flags & MAP_FIXED_NOREPLACE))) {
|
||||||
|
STRACE(
|
||||||
|
"zipos mappings currently only support MAP_PRIVATE with select flags");
|
||||||
|
return VIP(einval());
|
||||||
|
}
|
||||||
|
|
||||||
|
const int tempProt = !IsXnu() ? prot | PROT_WRITE : PROT_WRITE;
|
||||||
|
void *outAddr =
|
||||||
|
mmap(addr, size, tempProt, (flags & (~MAP_FILE)) | MAP_ANONYMOUS, -1, 0);
|
||||||
|
if (outAddr == MAP_FAILED) {
|
||||||
|
return MAP_FAILED;
|
||||||
|
}
|
||||||
|
const int64_t beforeOffset = __zipos_lseek(h, 0, SEEK_CUR);
|
||||||
|
if ((beforeOffset == -1) ||
|
||||||
|
(__zipos_read(h, &(struct iovec){outAddr, size}, 1, off) == -1) ||
|
||||||
|
(__zipos_lseek(h, beforeOffset, SEEK_SET) == -1) ||
|
||||||
|
((prot != tempProt) && (mprotect(outAddr, size, prot) == -1))) {
|
||||||
|
const int e = errno;
|
||||||
|
munmap(outAddr, size);
|
||||||
|
errno = e;
|
||||||
|
return MAP_FAILED;
|
||||||
|
}
|
||||||
|
return outAddr;
|
||||||
|
}
|
|
@ -48,7 +48,7 @@
|
||||||
static char *mapend;
|
static char *mapend;
|
||||||
static size_t maptotal;
|
static size_t maptotal;
|
||||||
|
|
||||||
static void *__zipos_mmap(size_t mapsize) {
|
static void *__zipos_mmap_space(size_t mapsize) {
|
||||||
char *start;
|
char *start;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
_unassert(mapsize);
|
_unassert(mapsize);
|
||||||
|
@ -78,7 +78,7 @@ StartOver:
|
||||||
ph = &h->next;
|
ph = &h->next;
|
||||||
}
|
}
|
||||||
if (!h) {
|
if (!h) {
|
||||||
h = __zipos_mmap(mapsize);
|
h = __zipos_mmap_space(mapsize);
|
||||||
}
|
}
|
||||||
__zipos_unlock();
|
__zipos_unlock();
|
||||||
if (IsAsan()) {
|
if (IsAsan()) {
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
.yoink __zipos_read
|
.yoink __zipos_read
|
||||||
.yoink __zipos_stat
|
.yoink __zipos_stat
|
||||||
.yoink __zipos_notat
|
.yoink __zipos_notat
|
||||||
|
.yoink __zipos_mmap
|
||||||
|
|
||||||
// TODO(jart): why does corruption happen when zip has no assets?
|
// TODO(jart): why does corruption happen when zip has no assets?
|
||||||
.yoink .cosmo
|
.yoink .cosmo
|
||||||
|
|
|
@ -48,6 +48,8 @@ ssize_t __zipos_write(struct ZiposHandle *, const struct iovec *, size_t,
|
||||||
int64_t __zipos_lseek(struct ZiposHandle *, int64_t, unsigned) _Hide;
|
int64_t __zipos_lseek(struct ZiposHandle *, int64_t, unsigned) _Hide;
|
||||||
int __zipos_fcntl(int, int, uintptr_t) _Hide;
|
int __zipos_fcntl(int, int, uintptr_t) _Hide;
|
||||||
int __zipos_notat(int, const char *) _Hide;
|
int __zipos_notat(int, const char *) _Hide;
|
||||||
|
void *__zipos_mmap(void *, uint64_t, int32_t, int32_t, struct ZiposHandle *,
|
||||||
|
int64_t) _Hide;
|
||||||
|
|
||||||
#ifdef _NOPL0
|
#ifdef _NOPL0
|
||||||
#define __zipos_lock() _NOPL0("__threadcalls", __zipos_lock)
|
#define __zipos_lock() _NOPL0("__threadcalls", __zipos_lock)
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
#include "libc/x/xspawn.h"
|
#include "libc/x/xspawn.h"
|
||||||
#include "third_party/xed/x86.h"
|
#include "third_party/xed/x86.h"
|
||||||
|
|
||||||
|
STATIC_YOINK("zip_uri_support");
|
||||||
|
|
||||||
char testlib_enable_tmp_setup_teardown;
|
char testlib_enable_tmp_setup_teardown;
|
||||||
|
|
||||||
void SetUpOnce(void) {
|
void SetUpOnce(void) {
|
||||||
|
@ -219,6 +221,86 @@ TEST(isheap, mallocOffset) {
|
||||||
ASSERT_TRUE(_isheap(p + 100000));
|
ASSERT_TRUE(_isheap(p + 100000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *ziposLifePath = "/zip/life.elf";
|
||||||
|
TEST(mmap, ziposCannotBeAnonymous) {
|
||||||
|
int fd;
|
||||||
|
void *p;
|
||||||
|
ASSERT_NE(-1, (fd = open(ziposLifePath, O_RDONLY), "%s", ziposLifePath));
|
||||||
|
EXPECT_SYS(EINVAL, MAP_FAILED,
|
||||||
|
(p = mmap(NULL, 0x00010000, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS,
|
||||||
|
fd, 0)));
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(mmap, ziposCannotBeShared) {
|
||||||
|
int fd;
|
||||||
|
void *p;
|
||||||
|
ASSERT_NE(-1, (fd = open(ziposLifePath, O_RDONLY), "%s", ziposLifePath));
|
||||||
|
EXPECT_SYS(EINVAL, MAP_FAILED,
|
||||||
|
(p = mmap(NULL, 0x00010000, PROT_READ, MAP_SHARED, fd, 0)));
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// zipos NON-SHARED READ-ONLY FILE MEMORY
|
||||||
|
|
||||||
|
TEST(mmap, ziposCow) {
|
||||||
|
int fd;
|
||||||
|
void *p;
|
||||||
|
ASSERT_NE(-1, (fd = open(ziposLifePath, O_RDONLY), "%s", ziposLifePath));
|
||||||
|
EXPECT_NE(MAP_FAILED,
|
||||||
|
(p = mmap(NULL, 0x00010000, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||||
|
EXPECT_STREQN("ELF", ((const char *)p) + 1, 3);
|
||||||
|
EXPECT_NE(-1, munmap(p, 0x00010000));
|
||||||
|
EXPECT_NE(-1, close(fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// zipos NON-SHARED READ-ONLY FILE MEMORY BETWEEN PROCESSES
|
||||||
|
|
||||||
|
TEST(mmap, ziposCowFileMapReadonlyFork) {
|
||||||
|
int fd, ws;
|
||||||
|
void *p;
|
||||||
|
ASSERT_NE(-1, (fd = open(ziposLifePath, O_RDONLY), "%s", ziposLifePath));
|
||||||
|
EXPECT_NE(MAP_FAILED, (p = mmap(NULL, 4, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||||
|
EXPECT_STREQN("ELF", ((const char *)p) + 1, 3);
|
||||||
|
ASSERT_NE(-1, (ws = xspawn(0)));
|
||||||
|
if (ws == -2) {
|
||||||
|
ASSERT_STREQN("ELF", ((const char *)p) + 1, 3);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(0, ws);
|
||||||
|
EXPECT_STREQN("ELF", ((const char *)p) + 1, 3);
|
||||||
|
EXPECT_NE(-1, munmap(p, 6));
|
||||||
|
EXPECT_NE(-1, close(fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// zipos NON-SHARED READ/WRITE FILE MEMORY BETWEEN PROCESSES
|
||||||
|
|
||||||
|
TEST(mmap, ziposCowFileMapFork) {
|
||||||
|
int fd, ws;
|
||||||
|
void *p;
|
||||||
|
char lol[4];
|
||||||
|
ASSERT_NE(-1, (fd = open(ziposLifePath, O_RDONLY), "%s", ziposLifePath));
|
||||||
|
EXPECT_NE(MAP_FAILED,
|
||||||
|
(p = mmap(NULL, 6, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)));
|
||||||
|
memcpy(p, "parnt", 6);
|
||||||
|
ASSERT_NE(-1, (ws = xspawn(0)));
|
||||||
|
if (ws == -2) {
|
||||||
|
ASSERT_STREQN("parnt", p, 5);
|
||||||
|
strcpy(p, "child");
|
||||||
|
ASSERT_STREQN("child", p, 5);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(0, ws);
|
||||||
|
EXPECT_STREQN("parnt", p, 5); // child changing memory did not change parent
|
||||||
|
EXPECT_EQ(4, pread(fd, lol, 4, 0));
|
||||||
|
EXPECT_STREQN("ELF", &lol[1], 3); // changing memory did not change file
|
||||||
|
EXPECT_NE(-1, munmap(p, 6));
|
||||||
|
EXPECT_NE(-1, close(fd));
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// NON-SHARED READ-ONLY FILE MEMORY
|
// NON-SHARED READ-ONLY FILE MEMORY
|
||||||
|
|
||||||
|
@ -231,8 +313,7 @@ TEST(mmap, cow) {
|
||||||
path);
|
path);
|
||||||
EXPECT_EQ(5, write(fd, "hello", 5));
|
EXPECT_EQ(5, write(fd, "hello", 5));
|
||||||
EXPECT_NE(-1, fdatasync(fd));
|
EXPECT_NE(-1, fdatasync(fd));
|
||||||
EXPECT_NE(MAP_FAILED,
|
EXPECT_NE(MAP_FAILED, (p = mmap(NULL, 5, PROT_READ, MAP_PRIVATE, fd, 0)));
|
||||||
(p = mmap(NULL, 5, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)));
|
|
||||||
EXPECT_STREQN("hello", p, 5);
|
EXPECT_STREQN("hello", p, 5);
|
||||||
EXPECT_NE(-1, munmap(p, 5));
|
EXPECT_NE(-1, munmap(p, 5));
|
||||||
EXPECT_NE(-1, close(fd));
|
EXPECT_NE(-1, close(fd));
|
||||||
|
@ -258,6 +339,7 @@ TEST(mmap, cowFileMapReadonlyFork) {
|
||||||
ASSERT_STREQN("hello", p, 5);
|
ASSERT_STREQN("hello", p, 5);
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
EXPECT_EQ(0, ws);
|
||||||
EXPECT_STREQN("hello", p, 5);
|
EXPECT_STREQN("hello", p, 5);
|
||||||
EXPECT_NE(-1, munmap(p, 6));
|
EXPECT_NE(-1, munmap(p, 6));
|
||||||
EXPECT_NE(-1, close(fd));
|
EXPECT_NE(-1, close(fd));
|
||||||
|
@ -285,6 +367,7 @@ TEST(mmap, cowFileMapFork) {
|
||||||
ASSERT_STREQN("child", p, 5);
|
ASSERT_STREQN("child", p, 5);
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
EXPECT_EQ(0, ws);
|
||||||
EXPECT_STREQN("parnt", p, 5); // child changing memory did not change parent
|
EXPECT_STREQN("parnt", p, 5); // child changing memory did not change parent
|
||||||
EXPECT_EQ(6, pread(fd, lol, 6, 0));
|
EXPECT_EQ(6, pread(fd, lol, 6, 0));
|
||||||
EXPECT_STREQN("parnt", lol, 5); // changing memory did not change file
|
EXPECT_STREQN("parnt", lol, 5); // changing memory did not change file
|
||||||
|
@ -310,6 +393,7 @@ TEST(mmap, sharedAnonMapFork) {
|
||||||
ASSERT_STREQN("child", p, 5);
|
ASSERT_STREQN("child", p, 5);
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
EXPECT_EQ(0, ws);
|
||||||
EXPECT_STREQN("child", p, 5); // boom
|
EXPECT_STREQN("child", p, 5); // boom
|
||||||
EXPECT_NE(-1, munmap(p, 5));
|
EXPECT_NE(-1, munmap(p, 5));
|
||||||
}
|
}
|
||||||
|
@ -336,6 +420,7 @@ TEST(mmap, sharedFileMapFork) {
|
||||||
ASSERT_NE(-1, msync(p, 6, MS_SYNC | MS_INVALIDATE));
|
ASSERT_NE(-1, msync(p, 6, MS_SYNC | MS_INVALIDATE));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}
|
}
|
||||||
|
EXPECT_EQ(0, ws);
|
||||||
EXPECT_STREQN("child", p, 5); // child changing memory changed parent memory
|
EXPECT_STREQN("child", p, 5); // child changing memory changed parent memory
|
||||||
// XXX: RHEL5 has a weird issue where if we read the file into its own
|
// XXX: RHEL5 has a weird issue where if we read the file into its own
|
||||||
// shared memory then corruption occurs!
|
// shared memory then corruption occurs!
|
||||||
|
|
|
@ -51,6 +51,7 @@ o/$(MODE)/test/libc/runtime/runtime.pkg: \
|
||||||
|
|
||||||
o/$(MODE)/test/libc/runtime/%.com.dbg: \
|
o/$(MODE)/test/libc/runtime/%.com.dbg: \
|
||||||
$(TEST_LIBC_RUNTIME_DEPS) \
|
$(TEST_LIBC_RUNTIME_DEPS) \
|
||||||
|
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \
|
||||||
o/$(MODE)/test/libc/runtime/%.o \
|
o/$(MODE)/test/libc/runtime/%.o \
|
||||||
o/$(MODE)/test/libc/runtime/runtime.pkg \
|
o/$(MODE)/test/libc/runtime/runtime.pkg \
|
||||||
$(LIBC_TESTMAIN) \
|
$(LIBC_TESTMAIN) \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue