Make improvements

- Expand redbean UNIX module
- Expand redbean documentation
- Ensure Lua copyright is embedded in binary
- Increase the PATH_MAX limit especially on NT
- Use column major sorting for linenoise completions
- Fix some suboptimalities in redbean's new UNIX API
- Figured out right flags for Multics newline in raw mode
This commit is contained in:
Justine Tunney 2022-04-24 09:59:22 -07:00
parent cf3174dc74
commit 2046c0d2ae
305 changed files with 6602 additions and 4221 deletions

View file

@ -24,7 +24,7 @@
struct FindComBinary {
bool once;
const char *res;
char buf[PATH_MAX];
char buf[PATH_MAX + 1];
};
static struct FindComBinary g_findcombinary;

View file

@ -30,6 +30,7 @@ int GetDosArgv(const char16_t *, char *, size_t, char **, size_t);
Elf64_Ehdr *MapElfRead(const char *, struct MappedFile *) hidden;
int GetDosEnviron(const char16_t *, char *, size_t, char **, size_t);
bool __intercept_flag(int *, char *[], const char *);
int sys_mprotect_nt(void *, size_t, int) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -18,7 +18,7 @@
*/
#include "libc/runtime/memtrack.internal.h"
bool IsMemtracked(int x, int y) {
static inline bool IsMemtrackedImpl(int x, int y) {
unsigned i;
i = FindMemoryInterval(&_mmi, x);
if (i == _mmi.i) return false;
@ -29,3 +29,9 @@ bool IsMemtracked(int x, int y) {
if (_mmi.p[i].x != _mmi.p[i - 1].y + 1) return false;
}
}
bool IsMemtracked(int x, int y) {
bool res;
res = IsMemtrackedImpl(x, y);
return res;
}

View file

@ -46,6 +46,7 @@ struct MemoryIntervals {
size_t i, n;
struct MemoryInterval *p;
struct MemoryInterval s[OPEN_MAX];
_Alignas(64) char lock;
};
extern hidden struct MemoryIntervals _mmi;

View file

@ -27,6 +27,7 @@
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/spinlock.h"
#include "libc/limits.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/libfatal.internal.h"
@ -198,6 +199,133 @@ static textwindows dontinline noasan void *MapMemories(char *addr, size_t size,
return addr;
}
static noasan inline void *Mmap(void *addr, size_t size, int prot, int flags,
int fd, int64_t off) {
#if defined(SYSDEBUG) && (_KERNTRACE || _NTTRACE)
if (IsWindows()) {
STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → ...", addr, size,
DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off);
}
#endif
char *p = addr;
struct DirectMap dm;
size_t virtualused, virtualneed;
int a, b, i, f, m, n, x;
if (UNLIKELY(!size)) {
STRACE("size=0");
return VIP(einval());
}
if (UNLIKELY(!IsLegalSize(size))) {
STRACE("size isn't 48-bit");
return VIP(einval());
}
if (UNLIKELY(!IsLegalPointer(p))) {
STRACE("p isn't 48-bit");
return VIP(einval());
}
if (UNLIKELY(!ALIGNED(p))) {
STRACE("p isn't 64kb aligned");
return VIP(einval());
}
if (UNLIKELY(fd < -1)) {
STRACE("mmap(%.12p, %'zu, fd=%d) EBADF", p, size, fd);
return VIP(ebadf());
}
if (UNLIKELY(!((fd != -1) ^ !!(flags & MAP_ANONYMOUS)))) {
STRACE("fd anonymous mismatch");
return VIP(einval());
}
if (UNLIKELY(!(!!(flags & MAP_PRIVATE) ^ !!(flags & MAP_SHARED)))) {
STRACE("MAP_SHARED ^ MAP_PRIVATE");
return VIP(einval());
}
if (UNLIKELY(off < 0)) {
STRACE("neg off");
return VIP(einval());
}
if (UNLIKELY(INT64_MAX - size < off)) {
STRACE("too large");
return VIP(einval());
}
if (UNLIKELY(!ALIGNED(off))) {
STRACE("p isn't 64kb aligned");
return VIP(einval());
}
if ((flags & MAP_FIXED_NOREPLACE) && IsMapped(p, size)) {
#ifdef SYSDEBUG
if (OverlapsImageSpace(p, size)) {
STRACE("overlaps image");
} else {
STRACE("overlaps existing");
}
#endif
return VIP(efault());
}
if (__isfdkind(fd, kFdZip)) {
STRACE("fd is zipos handle");
return VIP(einval());
}
if (__virtualmax < LONG_MAX &&
(__builtin_add_overflow((virtualused = GetMemtrackSize(&_mmi)), size,
&virtualneed) ||
virtualneed > __virtualmax)) {
STRACE("%'zu size + %'zu inuse exceeds virtual memory limit %'zu", size,
virtualused, __virtualmax);
return VIP(enomem());
}
if (fd == -1) {
size = ROUNDUP(size, FRAMESIZE);
if (IsWindows()) {
prot |= PROT_WRITE; /* kludge */
}
}
n = (int)(size >> 16) + !!(size & (FRAMESIZE - 1));
assert(n > 0);
f = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED;
if (flags & MAP_FIXED) {
x = FRAME(p);
if (IsWindows()) {
if (UntrackMemoryIntervals(p, size)) {
OnUnrecoverableMmapError("FIXED UNTRACK FAILED");
}
}
} else if (!NeedAutomap(p, size)) {
x = FRAME(p);
} else if (!Automap(n, &x)) {
STRACE("AUTOMAP OUT OF MEMORY D:");
return VIP(enomem());
}
p = (char *)ADDR(x);
if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */
dm = sys_mmap(p, size, prot, f & ~MAP_GROWSDOWN, fd, off);
if (dm.addr == MAP_FAILED) {
return MAP_FAILED;
}
}
if (!IsWindows()) {
return MapMemory(p, size, prot, flags, fd, off, f, x, n);
} else {
return MapMemories(p, size, prot, flags, fd, off, f, x, n);
}
}
/**
* Beseeches system for page-table entries, e.g.
*
@ -229,100 +357,10 @@ static textwindows dontinline noasan void *MapMemories(char *addr, size_t size,
*/
noasan void *mmap(void *addr, size_t size, int prot, int flags, int fd,
int64_t off) {
#if defined(SYSDEBUG) && (_KERNTRACE || _NTTRACE)
if (IsWindows()) {
STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → ...", addr, size,
DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off);
}
#endif
void *res;
char *p = addr;
struct DirectMap dm;
size_t virtualused, virtualneed;
int a, b, i, f, m, n, x;
if (UNLIKELY(!size)) {
STRACE("size=0");
res = VIP(einval());
} else if (UNLIKELY(!IsLegalSize(size))) {
STRACE("size isn't 48-bit");
res = VIP(einval());
} else if (UNLIKELY(!IsLegalPointer(p))) {
STRACE("p isn't 48-bit");
res = VIP(einval());
} else if (UNLIKELY(!ALIGNED(p))) {
STRACE("p isn't 64kb aligned");
res = VIP(einval());
} else if (UNLIKELY(fd < -1)) {
STRACE("mmap(%.12p, %'zu, fd=%d) EBADF", p, size, fd);
res = VIP(ebadf());
} else if (UNLIKELY(!((fd != -1) ^ !!(flags & MAP_ANONYMOUS)))) {
STRACE("fd anonymous mismatch");
res = VIP(einval());
} else if (UNLIKELY(!(!!(flags & MAP_PRIVATE) ^ !!(flags & MAP_SHARED)))) {
STRACE("MAP_SHARED ^ MAP_PRIVATE");
res = VIP(einval());
} else if (UNLIKELY(off < 0)) {
STRACE("neg off");
res = VIP(einval());
} else if (UNLIKELY(INT64_MAX - size < off)) {
STRACE("too large");
res = VIP(einval());
} else if (UNLIKELY(!ALIGNED(off))) {
STRACE("p isn't 64kb aligned");
res = VIP(einval());
} else if ((flags & MAP_FIXED_NOREPLACE) && IsMapped(p, size)) {
#ifdef SYSDEBUG
if (OverlapsImageSpace(p, size)) {
STRACE("overlaps image");
} else {
STRACE("overlaps existing");
}
#endif
res = VIP(efault());
} else if (__isfdkind(fd, kFdZip)) {
STRACE("fd is zipos handle");
res = VIP(einval());
} else if (__virtualmax < LONG_MAX &&
(__builtin_add_overflow((virtualused = GetMemtrackSize(&_mmi)),
size, &virtualneed) ||
virtualneed > __virtualmax)) {
STRACE("%'zu size + %'zu inuse exceeds virtual memory limit %'zu", size,
virtualused, __virtualmax);
res = VIP(enomem());
} else {
if (fd == -1) {
size = ROUNDUP(size, FRAMESIZE);
if (IsWindows()) {
prot |= PROT_WRITE; /* kludge */
}
}
n = (int)(size >> 16) + !!(size & (FRAMESIZE - 1));
assert(n > 0);
f = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED;
if (flags & MAP_FIXED) {
x = FRAME(p);
if (IsWindows()) {
if (UntrackMemoryIntervals(p, size)) {
OnUnrecoverableMmapError("FIXED UNTRACK FAILED");
}
}
} else if (!NeedAutomap(p, size)) {
x = FRAME(p);
} else if (!Automap(n, &x)) {
STRACE("AUTOMAP OUT OF MEMORY D:");
return VIP(enomem());
}
p = (char *)ADDR(x);
if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */
dm = sys_mmap(p, size, prot, f & ~MAP_GROWSDOWN, fd, off);
if (dm.addr == MAP_FAILED) res = MAP_FAILED;
}
if (!IsWindows()) {
res = MapMemory(p, size, prot, flags, fd, off, f, x, n);
} else {
res = MapMemories(p, size, prot, flags, fd, off, f, x, n);
}
}
_spinlock(&_mmi.lock);
res = Mmap(addr, size, prot, flags, fd, off);
_spunlock(&_mmi.lock);
STRACE("mmap(%p, %'zu, %s, %s, %d, %'ld) → %p% m", addr, size,
DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off, res);
return res;

View file

@ -0,0 +1,63 @@
/*-*- 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 2022 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/intrin/spinlock.h"
#include "libc/nt/memory.h"
#include "libc/runtime/directmap.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h"
#define ADDR(x) ((char *)((int64_t)((uint64_t)(x) << 32) >> 16))
privileged int sys_mprotect_nt(void *addr, size_t size, int prot) {
int rc = 0;
unsigned i;
uint32_t op;
char *a, *b, *x, *y, *p;
_spinlock(&_mmi.lock);
p = addr;
i = FindMemoryInterval(&_mmi, (intptr_t)p >> 16);
if (i == _mmi.i || (!i && p + size <= ADDR(_mmi.p[0].x))) {
// memory isn't in memtrack
// let's just trust the user then
// it's probably part of the executable
if (!VirtualProtect(addr, size, __prot2nt(prot, false), &op)) {
rc = -1;
}
} else {
// memory is in memtrack, so use memtrack, to do dimensioning
// we unfortunately must do something similar to this for cow
for (; i < _mmi.i; ++i) {
x = ADDR(_mmi.p[i].x);
y = x + _mmi.p[i].size;
if ((x <= p && p < y) || (x < p + size && p + size <= y) ||
(p < x && y < p + size)) {
a = MIN(MAX(p, x), y);
b = MAX(MIN(p + size, y), x);
if (!VirtualProtect(a, b - a, __prot2nt(prot, _mmi.p[i].iscow), &op)) {
rc = -1;
break;
}
} else {
break;
}
}
}
_spunlock(&_mmi.lock);
return rc;
}

View file

@ -16,26 +16,16 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/asmflag.h"
#include "libc/bits/likely.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/memory.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/directmap.internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/sysv/consts/nr.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/errfuns.h"
__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
#define ADDR(x) ((char *)((int64_t)((uint64_t)(x) << 32) >> 16))
/**
* Modifies restrictions on virtual memory address range.
*
@ -44,12 +34,8 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
* @return 0 on success, or -1 w/ errno
* @see mmap()
*/
noasan noubsan privileged int mprotect(void *addr, size_t size, int prot) {
bool cf;
privileged int mprotect(void *addr, size_t size, int prot) {
int64_t rc;
unsigned i;
uint32_t op;
char *a, *b, *x, *y, *p;
if (SupportsWindows() && (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC |
PROT_GROWSDOWN | PROT_GROWSUP))) {
rc = einval(); // unix checks prot before checking size
@ -58,48 +44,9 @@ noasan noubsan privileged int mprotect(void *addr, size_t size, int prot) {
} else if (UNLIKELY((intptr_t)addr & 4095)) {
rc = einval();
} else if (!IsWindows()) {
asm volatile(CFLAG_ASM("clc\n\tsyscall")
: CFLAG_CONSTRAINT(cf), "=a"(rc)
: "1"(__NR_mprotect), "D"(addr), "S"(size), "d"(prot)
: "rcx", "r11", "memory", "cc");
if (cf) {
errno = rc;
rc = -1;
} else if (rc > -4096ul) {
errno = -rc;
rc = -1;
}
rc = sys_mprotect(addr, size, prot);
} else {
rc = 0;
p = addr;
i = FindMemoryInterval(&_mmi, (intptr_t)p >> 16);
if (i == _mmi.i || (!i && p + size <= ADDR(_mmi.p[0].x))) {
// memory isn't in memtrack
// let's just trust the user then
// it's probably part of the executable
if (!VirtualProtect(addr, size, __prot2nt(prot, false), &op)) {
rc = -1;
}
} else {
// memory is in memtrack, so use memtrack, to do dimensioning
// we unfortunately must do something similar to this for cow
for (; i < _mmi.i; ++i) {
x = ADDR(_mmi.p[i].x);
y = x + _mmi.p[i].size;
if ((x <= p && p < y) || (x < p + size && p + size <= y) ||
(p < x && y < p + size)) {
a = MIN(MAX(p, x), y);
b = MAX(MIN(p + size, y), x);
if (!VirtualProtect(a, b - a, __prot2nt(prot, _mmi.p[i].iscow),
&op)) {
rc = -1;
break;
}
} else {
break;
}
}
}
rc = sys_mprotect_nt(addr, size, prot);
}
STRACE("mprotect(%p, %'zu, %s) → %d% m", addr, size, DescribeProtFlags(prot),
rc);

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/intrin/spinlock.h"
#include "libc/macros.internal.h"
#include "libc/nt/files.h"
#include "libc/nt/memory.h"
@ -31,6 +32,7 @@
noasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) {
int i, rc = 0;
char *a, *b, *x, *y;
_spinlock(&_mmi.lock);
for (i = FindMemoryInterval(&_mmi, (intptr_t)addr >> 16); i < _mmi.i; ++i) {
x = ADDR(_mmi.p[i].x);
y = x + _mmi.p[i].size;
@ -47,30 +49,6 @@ noasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) {
break;
}
}
_spunlock(&_mmi.lock);
return rc;
}
#if 0
noasan textwindows int sys_msync_nt(char *addr, size_t size, int flags) {
char *a, *b;
int rc, x, y, l, r, i;
rc = 0;
for (i = FindMemoryInterval(&_mmi, (intptr_t)addr >> 16); i < _mmi.i; ++i) {
if ((ADDR(_mmi.p[i].x) <= addr && addr < ADDR(_mmi.p[i].y + 1)) ||
(ADDR(_mmi.p[i].x) < addr + size &&
addr + size <= ADDR(_mmi.p[i].y + 1)) ||
(addr < ADDR(_mmi.p[i].x) && ADDR(_mmi.p[i].y + 1) < addr + size)) {
a = MIN(MAX(addr, ADDR(_mmi.p[i].x)), ADDR(_mmi.p[i].y + 1));
b = MAX(MIN(addr + size, ADDR(_mmi.p[i].y + 1)), ADDR(_mmi.p[i].x));
if (!FlushViewOfFile(a, b - a)) {
rc = -1;
break;
}
// TODO(jart): FlushFileBuffers too on g_fds handle if MS_SYNC?
} else {
break;
}
}
return rc;
}
#endif

View file

@ -22,6 +22,7 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/spinlock.h"
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/directmap.internal.h"
@ -35,6 +36,76 @@
#define ADDR(x) ((int64_t)((uint64_t)(x) << 32) >> 16)
#define FRAME(x) ((int)((intptr_t)(x) >> 16))
static noasan int Munmap(void *v, size_t n) {
char poison, *p = v;
intptr_t a, b, x, y;
assert(!__vforked);
if (UNLIKELY(!n)) {
STRACE("munmap(%.12p, %'zu) %s (n=0)", p, n);
return einval();
}
if (UNLIKELY(!IsLegalSize(n))) {
STRACE("munmap(%.12p, %'zu) EINVAL (n isn't 48-bit)", p, n);
return einval();
}
if (UNLIKELY(!IsLegalPointer(p))) {
STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 48-bit)", p, n);
return einval();
}
if (UNLIKELY(!IsLegalPointer(p + (n - 1)))) {
STRACE("munmap(%.12p, %'zu) EINVAL (p+(n-1) isn't 48-bit)", p, n);
return einval();
}
if (UNLIKELY(!ALIGNED(p))) {
STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 64kb aligned)", p, n);
return einval();
}
if (!IsMemtracked(FRAME(p), FRAME(p + (n - 1)))) {
STRACE("munmap(%.12p, %'zu) EFAULT (interval not tracked)", p, n);
return efault();
}
if (UntrackMemoryIntervals(p, n) == -1) {
return -1;
}
if (IsWindows()) {
return 0; // UntrackMemoryIntervals does it for NT
}
if (sys_munmap(p, n) == -1) {
return -1; // ouch
}
if (IsAsan() && !OverlapsShadowSpace(p, n)) {
a = ((intptr_t)p >> 3) + 0x7fff8000;
b = a + (n >> 3);
if (IsMemtracked(FRAME(a), FRAME(b - 1))) {
x = ROUNDUP(a, FRAMESIZE);
y = ROUNDDOWN(b, FRAMESIZE);
if (x < y) {
// delete shadowspace if unmapping ≥512kb
__repstosb((void *)a, kAsanUnmapped, x - a);
Munmap((void *)x, y - x);
__repstosb((void *)y, kAsanUnmapped, b - y);
} else {
// otherwise just poison and assume reuse
__repstosb((void *)a, kAsanUnmapped, b - a);
}
} else {
STRACE("unshadow(%.12p, %p) EFAULT", a, b - a);
}
}
return 0;
}
/**
* Releases memory pages.
*
@ -49,66 +120,11 @@
* and for files size needs to be perfect to the byte bc openbsd
* @return 0 on success, or -1 w/ errno
*/
noasan int munmap(void *v, size_t n) {
/* asan runtime depends on this function */
noasan int munmap(void *p, size_t n) {
int rc;
char poison, *p = v;
intptr_t a, b, x, y;
assert(!__vforked);
if (UNLIKELY(!n)) {
STRACE("munmap(%.12p, %'zu) %s (n=0)", p, n);
return einval();
}
if (UNLIKELY(!IsLegalSize(n))) {
STRACE("munmap(%.12p, %'zu) EINVAL (n isn't 48-bit)", p, n);
return einval();
}
if (UNLIKELY(!IsLegalPointer(p))) {
STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 48-bit)", p, n);
return einval();
}
if (UNLIKELY(!IsLegalPointer(p + (n - 1)))) {
STRACE("munmap(%.12p, %'zu) EINVAL (p+(n-1) isn't 48-bit)", p, n);
return einval();
}
if (UNLIKELY(!ALIGNED(p))) {
STRACE("munmap(%.12p, %'zu) EINVAL (p isn't 64kb aligned)", p, n);
return einval();
}
if (!IsMemtracked(FRAME(p), FRAME(p + (n - 1)))) {
STRACE("munmap(%.12p, %'zu) EFAULT (interval not tracked)", p, n);
return efault();
}
if (UntrackMemoryIntervals(p, n) != -1) {
if (!IsWindows()) {
rc = sys_munmap(p, n);
if (rc != -1) {
if (IsAsan() && !OverlapsShadowSpace(p, n)) {
a = ((intptr_t)p >> 3) + 0x7fff8000;
b = a + (n >> 3);
if (IsMemtracked(FRAME(a), FRAME(b - 1))) {
x = ROUNDUP(a, FRAMESIZE);
y = ROUNDDOWN(b, FRAMESIZE);
if (x < y) {
/* delete shadowspace if unmapping ≥512kb */
__repstosb((void *)a, kAsanUnmapped, x - a);
munmap((void *)x, y - x);
__repstosb((void *)y, kAsanUnmapped, b - y);
} else {
/* otherwise just poison and assume reuse */
__repstosb((void *)a, kAsanUnmapped, b - a);
}
} else {
STRACE("unshadow(%.12p, %p) EFAULT", a, b - a);
}
}
}
} else {
rc = 0; /* UntrackMemoryIntervals does it for NT */
}
} else {
rc = -1;
}
_spinlock(&_mmi.lock);
rc = Munmap(p, n);
_spunlock(&_mmi.lock);
STRACE("munmap(%.12p, %'zu) → %d% m", p, n, rc);
return rc;
}

57
libc/runtime/paginate.c Normal file
View file

@ -0,0 +1,57 @@
/*-*- 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 2022 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/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/fmt/fmt.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/x/x.h"
/**
* Displays wall of text in terminal with pagination.
*/
void __paginate(int fd, const char *s) {
int tfd, pid;
char *args[3] = {0};
char tmppath[PATH_MAX + 1];
char progpath[PATH_MAX + 1];
if (strcmp(nulltoempty(getenv("TERM")), "dumb") && isatty(0) && isatty(1) &&
((args[0] = commandv("less", progpath, sizeof(progpath))) ||
(args[0] = commandv("more", progpath, sizeof(progpath))))) {
snprintf(tmppath, sizeof(tmppath), "%s%s-%s-%d.txt", kTmpPath,
program_invocation_short_name, "paginate", getpid());
if ((tfd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 0644)) != -1) {
write(tfd, s, strlen(s));
close(tfd);
args[1] = tmppath;
if ((pid = vfork()) != -1) {
if (!pid) {
execv(args[0], args);
_Exit(127);
}
waitpid(pid, 0, 0);
unlink(tmppath);
return;
}
unlink(tmppath);
}
}
write(fd, s, strlen(s));
}

View file

@ -44,6 +44,7 @@
#include "libc/runtime/stack.h"
#include "libc/sock/internal.h"
#include "libc/sock/sock.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/poll.h"
@ -52,12 +53,13 @@
#include "tool/decode/lib/idname.h"
#include "tool/decode/lib/x86idnames.h"
STATIC_YOINK("strerror"); // for kprintf()
STATIC_YOINK("strsignal"); // for kprintf()
#define PRINT(FMT, ...) \
do { \
kprintf(prologue); \
kprintf(FMT "%n", ##__VA_ARGS__); \
kprintf(FMT "\n", ##__VA_ARGS__); \
} while (0)
static const struct AuxiliaryValue {
@ -137,8 +139,8 @@ textstartup void __printargs(const char *prologue) {
unsigned i, n;
uintptr_t *auxp;
struct utsname uts;
char path[PATH_MAX];
struct termios termios;
char path[PATH_MAX + 1];
int e, x, st, ft, flags;
struct pollfd pfds[128];
struct AuxiliaryValue *auxinfo;
@ -157,7 +159,7 @@ textstartup void __printargs(const char *prologue) {
kprintf(" %s", uts.release);
}
}
kprintf("%n");
kprintf("\n");
} else {
PRINT(" uname() failed %m");
}
@ -177,7 +179,7 @@ textstartup void __printargs(const char *prologue) {
FindNameById(kX86GradeNames,
getx86processormodel(kX86ProcessorModelKey)->grade));
}
kprintf("%n");
kprintf("\n");
if ((x = KCPUIDS(16H, EAX) & 0x7fff)) {
kprintf(prologue);
kprintf(" %dmhz %s", x, "freq");
@ -187,7 +189,7 @@ textstartup void __printargs(const char *prologue) {
if ((x = KCPUIDS(16H, ECX) & 0x7fff)) {
kprintf(" / %dmhz %s", x, "bus");
}
kprintf("%n");
kprintf("\n");
}
if (X86_HAVE(HYPERVISOR)) {
unsigned eax, ebx, ecx, edx;
@ -203,8 +205,9 @@ textstartup void __printargs(const char *prologue) {
PRINT(" L%d%s%s %u-way %,u byte cache w/%s "
"%,u sets of %,u byte lines shared across %u threads%s",
CPUID4_CACHE_LEVEL,
CPUID4_CACHE_TYPE == 1 ? " data"
: CPUID4_CACHE_TYPE == 2 ? " code" : "",
CPUID4_CACHE_TYPE == 1 ? " data"
: CPUID4_CACHE_TYPE == 2 ? " code"
: "",
CPUID4_IS_FULLY_ASSOCIATIVE ? " fully-associative" : "",
CPUID4_WAYS_OF_ASSOCIATIVITY, CPUID4_CACHE_SIZE_IN_BYTES,
CPUID4_PHYSICAL_LINE_PARTITIONS > 1 ? " physically partitioned" : "",
@ -233,7 +236,7 @@ textstartup void __printargs(const char *prologue) {
if (X86_HAVE(RDPID)) kprintf(" RDPID");
if (X86_HAVE(LA57)) kprintf(" LA57");
if (X86_HAVE(FSGSBASE)) kprintf(" FSGSBASE");
kprintf("%n");
kprintf("\n");
PRINT("");
PRINT("FILE DESCRIPTORS");
@ -355,7 +358,7 @@ textstartup void __printargs(const char *prologue) {
if (termios.c_iflag & IMAXBEL) kprintf(" IMAXBEL");
if (termios.c_iflag & IUTF8) kprintf(" IUTF8");
if (termios.c_iflag & IUCLC) kprintf(" IUCLC");
kprintf("%n");
kprintf("\n");
kprintf(prologue);
kprintf(" c_oflag =");
if (termios.c_oflag & OPOST) kprintf(" OPOST");
@ -408,15 +411,15 @@ textstartup void __printargs(const char *prologue) {
} else if ((termios.c_oflag & FFDLY) == FF1) {
kprintf(" FF1");
}
kprintf("%n");
kprintf("\n");
kprintf(prologue);
kprintf(" c_cflag =");
if (termios.c_cflag & ISIG) kprintf(" ISIG");
if (termios.c_cflag & CSTOPB) kprintf(" CSTOPB");
if (termios.c_cflag & CREAD) kprintf(" CREAD");
if (termios.c_cflag & PARENB) kprintf(" PARENB");
if (termios.c_cflag & PARODD) kprintf(" PARODD");
if (termios.c_cflag & CSTOPB) kprintf(" CSTOPB");
if (termios.c_cflag & PARODD) kprintf(" PARODD");
if (termios.c_cflag & HUPCL) kprintf(" HUPCL");
if (termios.c_cflag & CREAD) kprintf(" CREAD");
if (termios.c_cflag & CLOCAL) kprintf(" CLOCAL");
if ((termios.c_cflag & CSIZE) == CS5) {
kprintf(" CS5");
@ -427,7 +430,7 @@ textstartup void __printargs(const char *prologue) {
} else if ((termios.c_cflag & CSIZE) == CS8) {
kprintf(" CS8");
}
kprintf("%n");
kprintf("\n");
kprintf(prologue);
kprintf(" c_lflag =");
if (termios.c_lflag & ISIG) kprintf(" ISIG");
@ -445,7 +448,7 @@ textstartup void __printargs(const char *prologue) {
if (termios.c_lflag & FLUSHO) kprintf(" FLUSHO");
if (termios.c_lflag & PENDIN) kprintf(" PENDIN");
if (termios.c_lflag & XCASE) kprintf(" XCASE");
kprintf("%n");
kprintf("\n");
PRINT(" c_ispeed = %u", termios.c_ispeed);
PRINT(" c_ospeed = %u", termios.c_ospeed);
PRINT(" c_cc[VINTR] = CTRL-%c", CTRL(termios.c_cc[VINTR]));

View file

@ -54,7 +54,7 @@ void PrintMemoryIntervals(int fd, const struct MemoryIntervals *mm) {
if (mm->p[i].h != -1) {
kprintf(" h=%ld", mm->p[i].h);
}
kprintf("%n");
kprintf("\n");
}
kprintf("# %ld frames mapped w/ %'ld frames gapped%n", maptally, gaptally);
kprintf("# %ld frames mapped w/ %'ld frames gapped\n", maptally, gaptally);
}

View file

@ -104,6 +104,7 @@ long GetMaxFd(void);
char *GetProgramExecutableName(void);
char *GetInterpreterExecutableName(char *, size_t);
void __printargs(const char *);
void __paginate(int, const char *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -69,10 +69,12 @@ o/$(MODE)/libc/runtime/ezmap.o \
o/$(MODE)/libc/runtime/getdosargv.o \
o/$(MODE)/libc/runtime/getdosenviron.o \
o/$(MODE)/libc/runtime/hook.greg.o \
o/$(MODE)/libc/runtime/mprotect.greg.o \
o/$(MODE)/libc/runtime/mprotect-nt.greg.o \
o/$(MODE)/libc/runtime/ismemtracked.greg.o \
o/$(MODE)/libc/runtime/isheap.o \
o/$(MODE)/libc/runtime/memtracknt.o \
o/$(MODE)/libc/runtime/memtrack.greg.o \
o/$(MODE)/libc/runtime/ismemtracked.greg.o \
o/$(MODE)/libc/runtime/metalprintf.greg.o \
o/$(MODE)/libc/runtime/printargs.greg.o \
o/$(MODE)/libc/runtime/mman.greg.o \

View file

@ -94,7 +94,7 @@ vfork.bsd:
#ifdef SYSDEBUG
.rodata.str1.1
.Llog: .ascii STRACE_PROLOGUE
.asciz "vfork()%n"
.asciz "vfork()\n"
.previous
#endif /* DEBUGSYS */

View file

@ -55,6 +55,7 @@
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/internal.h"
#include "libc/str/str.h"
#include "libc/str/tpenc.h"
#include "libc/str/utf16.h"
@ -120,6 +121,15 @@ forceinline void MakeLongDoubleLongAgain(void) {
asm volatile("fldcw\t%0" : /* no outputs */ : "m"(x87cw));
}
static inline size_t StrLen16(const char16_t *s) {
size_t n;
for (n = 0;; ++n) {
if (!s[n]) {
return n;
}
}
}
__msabi static textwindows int OnEarlyWinCrash(struct NtExceptionPointers *ep) {
uint32_t wrote;
char buf[64], *p = buf;
@ -209,6 +219,9 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) {
}
}
env16 = GetEnvironmentStrings();
for (char16_t *e = env16; *e; e += StrLen16(e) + 1) {
NTTRACE("GetEnvironmentStrings() → %!#hs", e);
}
NTTRACE("WinMainNew() loading environment");
GetDosEnviron(env16, wa->envblock, ARRAYLEN(wa->envblock) - 8, wa->envp,
ARRAYLEN(wa->envp) - 1);
@ -257,7 +270,6 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
extern uint64_t ts asm("kStartTsc");
os = WINDOWS; /* madness https://news.ycombinator.com/item?id=21019722 */
ts = rdtsc();
__nomultics = true;
__pid = GetCurrentProcessId();
__wincrashearly = AddVectoredExceptionHandler(1, (void *)OnEarlyWinCrash);
cmdline = GetCommandLine();