Make mmap() work better

- Mapping file offsets now works on Windows
- Mapping stack memory now works on OpenBSD
This commit is contained in:
Justine Tunney 2021-02-03 00:10:12 -08:00
parent 23a14b537c
commit 27c899af56
10 changed files with 71 additions and 56 deletions

View file

@ -42,16 +42,7 @@ static textwindows int64_t open$nt$impl(int dirfd, const char *path,
char16_t path16[PATH_MAX];
if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1;
if ((handle = CreateFile(
path16,
(flags & 0xf000000f) |
(/* this is needed if we mmap(rwx+cow)
nt is choosy about open() access */
(flags & O_APPEND)
? kNtFileAppendData
: (flags & O_ACCMODE) == O_RDONLY
? kNtGenericExecute | kNtFileGenericRead
: kNtGenericExecute | kNtFileGenericRead |
kNtFileGenericWrite),
path16, flags & 0xf000000f, /* see consts.sh */
(flags & O_EXCL)
? kNtFileShareExclusive
: kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
@ -75,14 +66,6 @@ static textwindows int64_t open$nt$impl(int dirfd, const char *path,
kNtFileFlagBackupSemantics | kNtFileFlagPosixSemantics |
kNtFileAttributeTemporary)))),
0)) != -1) {
if (flags & O_SPARSE) {
/*
* TODO(jart): Can all files be sparse files? That seems to be the
* way Linux behaves out of the box.
* TODO(jart): Wow why does sparse wreak havoc?
*/
DeviceIoControl(handle, kNtFsctlSetSparse, NULL, 0, NULL, 0, &br, NULL);
}
return handle;
} else if (GetLastError() == kNtErrorFileExists &&
((flags & O_CREAT) &&

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
@ -30,7 +31,8 @@ textwindows struct DirectMap __mmap$nt(void *addr, size_t size, unsigned prot,
if ((dm.maphandle = CreateFileMappingNuma(
handle, &kNtIsInheritable,
(prot & PROT_WRITE) ? kNtPageExecuteReadwrite : kNtPageExecuteRead,
size >> 32, size, NULL, kNtNumaNoPreferredNode))) {
handle != -1 ? 0 : size >> 32, handle != -1 ? 0 : size, NULL,
kNtNumaNoPreferredNode))) {
if (!(dm.addr = MapViewOfFileExNuma(
dm.maphandle,
(prot & PROT_WRITE)

View file

@ -52,13 +52,14 @@
* @param flags can have MAP_ANONYMOUS, MAP_SHARED, MAP_PRIVATE, etc.
* @param fd is an open()'d file descriptor whose contents shall be
* mapped, and is ignored if MAP_ANONYMOUS is specified
* @param offset specifies absolute byte index of fd's file for mapping,
* and should be zero if MAP_ANONYMOUS is specified
* @param off specifies absolute byte index of fd's file for mapping,
* should be zero if MAP_ANONYMOUS is specified, and sadly needs
* to be 64kb aligned too
* @return virtual base address of new mapping, or MAP_FAILED w/ errno
*/
void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) {
int i, x, n, a, b;
struct DirectMap dm;
int i, x, n, a, b, f;
if (!size) return VIP(einval());
if (!ALIGNED(off)) return VIP(einval());
if (!ALIGNED(addr)) return VIP(einval());
@ -84,7 +85,12 @@ void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) {
}
addr = (void *)(intptr_t)((int64_t)x << 16);
}
dm = __mmap(addr, size, prot, flags | MAP_FIXED, fd, off);
f = flags | MAP_FIXED;
if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */
dm = __mmap(addr, size, prot, f & ~MAP_GROWSDOWN, fd, off);
if (dm.addr == MAP_FAILED) return MAP_FAILED;
}
dm = __mmap(addr, size, prot, f, fd, off);
if (dm.addr == MAP_FAILED || dm.addr != addr) {
return MAP_FAILED;
}

View file

@ -205,10 +205,10 @@ syscon sig SIGRTMIN 0 0 65 0 0
# │││ │ ┌┴───dwDesiredAccess
# N │││ │ │
# group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD T │││┌─┴┐│ Commentary
syscon open O_RDONLY 0 0 0 0 0x80000000 # unix consensus & kNtGenericRead
syscon open O_RDONLY 0 0 0 0 0xA0000000 # unix consensus & kNtGenericRead|kNtGenericExecute
syscon open O_WRONLY 1 1 1 1 0x40000000 # unix consensus & kNtGenericWrite
syscon open O_RDWR 2 2 2 2 0xc0000000 # unix consensus & kNtGenericRead|kNtGenericWrite
syscon open O_ACCMODE 3 3 3 3 0xc0000000 # O_RDONLY|O_WRONLY|O_RDWR
syscon open O_RDWR 2 2 2 2 0xE0000000 # unix consensus & kNtGenericRead|kNtGenericWrite|kNtGenericExecute
syscon open O_ACCMODE 3 3 3 3 0xE0000000 # O_RDONLY|O_WRONLY|O_RDWR
syscon open O_APPEND 0x0400 8 8 8 0x00000004 # bsd consensus & kNtFileAppendData; won't pose issues w/ mknod(S_IFIFO)
syscon open O_CREAT 0x40 0x0200 0x0200 0x0200 0x00000040 # bsd consensus & NT faked as Linux
syscon open O_EXCL 0x80 0x0800 0x0800 0x0800 0x00000080 # bsd consensus & NT faked as Linux

View file

@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
.syscon open O_ACCMODE 3 3 3 3 0xc0000000
.syscon open O_ACCMODE 3 3 3 3 0xE0000000

View file

@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
.syscon open O_RDONLY 0 0 0 0 0x80000000
.syscon open O_RDONLY 0 0 0 0 0xA0000000

View file

@ -1,2 +1,2 @@
.include "libc/sysv/consts/syscon.inc"
.syscon open O_RDWR 2 2 2 2 0xc0000000
.syscon open O_RDWR 2 2 2 2 0xE0000000

View file

@ -88,6 +88,42 @@ TEST(mmap, testMapFixed_destroysEverythingInItsPath) {
EXPECT_NE(-1, munmap((void *)kFixedmapStart, FRAMESIZE * 3));
}
TEST(mmap, customStackMemory_isAuthorized) {
char *stack;
uintptr_t w, r;
ASSERT_NE(MAP_FAILED,
(stack = mmap(NULL, STACKSIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0)));
asm("mov\t%%rsp,%0\n\t"
"mov\t%2,%%rsp\n\t"
"push\t%3\n\t"
"pop\t%1\n\t"
"mov\t%0,%%rsp"
: "=&r"(w), "=&r"(r)
: "rm"(stack + STACKSIZE - 8), "i"(123));
ASSERT_EQ(123, r);
}
TEST(mmap, fileOffset) {
int fd;
char *map;
char testdir[PATH_MAX];
sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid());
ASSERT_NE(-1, makedirs(testdir, 0755));
ASSERT_NE(-1, chdir(testdir));
ASSERT_NE(-1, (fd = open("foo", O_CREAT | O_RDWR, 0644)));
EXPECT_NE(-1, ftruncate(fd, FRAMESIZE * 2));
EXPECT_NE(-1, pwrite(fd, "hello", 5, FRAMESIZE * 0));
EXPECT_NE(-1, pwrite(fd, "there", 5, FRAMESIZE * 1));
ASSERT_NE(MAP_FAILED, (map = mmap(NULL, FRAMESIZE, PROT_READ, MAP_PRIVATE, fd,
FRAMESIZE)));
EXPECT_EQ(0, memcmp(map, "there", 5), "%#.*s", 5, map);
EXPECT_NE(-1, munmap(map, FRAMESIZE));
EXPECT_NE(-1, close(fd));
ASSERT_NE(-1, chdir("../../.."));
ASSERT_NE(-1, rmrf(testdir));
}
TEST(isheap, nullPtr) {
ASSERT_FALSE(isheap(NULL));
}

View file

@ -21,13 +21,8 @@ THIRD_PARTY_CHIBICC_TEST_HDRS = $(filter %.h,$(THIRD_PARTY_CHIBICC_TEST_FILES))
THIRD_PARTY_CHIBICC_TEST_TESTS = $(THIRD_PARTY_CHIBICC_TEST_COMS:%=%.ok)
THIRD_PARTY_CHIBICC_TEST_COMS = \
$(THIRD_PARTY_CHIBICC_TEST_SRCS_TEST:%.c=o/$(MODE)/%.com)
# TODO(jart): make chibicc compiled chibicc work with asan runtime
ifneq ($(MODE),dbg)
THIRD_PARTY_CHIBICC_TEST_COMS += \
$(THIRD_PARTY_CHIBICC_TEST_SRCS_TEST:%.c=o/$(MODE)/%2.com)
endif
$(THIRD_PARTY_CHIBICC_TEST_SRCS_TEST:%_test.c=o/$(MODE)/%_test.com) \
$(THIRD_PARTY_CHIBICC_TEST_SRCS_TEST:%_test.c=o/$(MODE)/%_test2.com)
THIRD_PARTY_CHIBICC_TEST_OBJS = \
$(THIRD_PARTY_CHIBICC_TEST_SRCS:%.c=o/$(MODE)/%.chibicc.o)
@ -62,21 +57,19 @@ THIRD_PARTY_CHIBICC_TEST_DEPS := \
$(call uniq,$(foreach x,$(THIRD_PARTY_CHIBICC_TEST_DIRECTDEPS),$($(x))))
$(THIRD_PARTY_CHIBICC_TEST_A): \
third_party/chibicc/test/ \
$(THIRD_PARTY_CHIBICC_TEST_A).pkg \
$(THIRD_PARTY_CHIBICC_TEST_OBJS)
o/$(MODE)/third_party/chibicc/test/common.chibicc.o
$(THIRD_PARTY_CHIBICC_TEST2_A): \
third_party/chibicc/test/ \
$(THIRD_PARTY_CHIBICC_TEST2_A).pkg \
$(THIRD_PARTY_CHIBICC_TEST2_OBJS)
o/$(MODE)/third_party/chibicc/test/common.chibicc2.o
$(THIRD_PARTY_CHIBICC_TEST_A).pkg: \
$(THIRD_PARTY_CHIBICC_TEST_OBJS) \
o/$(MODE)/third_party/chibicc/test/common.chibicc.o \
$(foreach x,$(THIRD_PARTY_CHIBICC_TEST_DIRECTDEPS),$($(x)_A).pkg)
$(THIRD_PARTY_CHIBICC_TEST2_A).pkg: \
$(THIRD_PARTY_CHIBICC_TEST2_OBJS) \
o/$(MODE)/third_party/chibicc/test/common.chibicc2.o \
$(foreach x,$(THIRD_PARTY_CHIBICC_TEST_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/third_party/chibicc/test/%.com.dbg: \

View file

@ -136,21 +136,16 @@ static int read_ident(char *start) {
}
}
static int from_hex(char c) {
if ('0' <= c && c <= '9') return c - '0';
if ('a' <= c && c <= 'f') return c - 'a' + 10;
return c - 'A' + 10;
}
// Read a punctuator token from p and returns its length.
int read_punct(char *p) {
static char kw[][4] = {"<<=", ">>=", "...", "==", "!=", "<=", ">=", "->",
"+=", "-=", "*=", "/=", "++", "--", "%=", "&=",
"|=", "^=", "&&", "||", "<<", ">>", "##"};
for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++) {
static const char kPunct[][4] = {
"<<=", ">>=", "...", "==", "!=", "<=", ">=", "->", "+=", "-=", "*=", "/=",
"++", "--", "%=", "&=", "|=", "^=", "&&", "||", "<<", ">>", "##",
};
for (int i = 0; i < sizeof(kPunct) / sizeof(*kPunct); i++) {
for (int j = 0;;) {
if (p[j] != kw[i][j]) break;
if (!kw[i][++j]) return j;
if (p[j] != kPunct[i][j]) break;
if (!kPunct[i][++j]) return j;
}
}
return ispunct(*p) ? 1 : 0;
@ -200,7 +195,7 @@ int read_escaped_char(char **new_pos, char *p) {
if (!isxdigit(*p)) error_at(p, "invalid hex escape sequence");
unsigned c = 0;
for (; isxdigit(*p); p++) {
c = (c << 4) + from_hex(*p); /* TODO(jart): overflow here unicode_test */
c = (c << 4) + hextoint(*p); /* TODO(jart): overflow here unicode_test */
}
*new_pos = p;
return c;
@ -628,7 +623,7 @@ static uint32_t read_universal_char(char *p, int len) {
uint32_t c = 0;
for (int i = 0; i < len; i++) {
if (!isxdigit(p[i])) return 0;
c = (c << 4) | from_hex(p[i]);
c = (c << 4) | hextoint(p[i]);
}
return c;
}