Make improvements for Actually Portable Emacs

- Get SIGWINCH working again on the New Technology
- Correctly handle O_NOFOLLOW in open() on Windows
- Implement synthetic umask() functionality on Windows
- Do a better job managing file execute access on Windows
- Fill in `st_uid` and `st_gid` with username hash on Windows
- Munge UNICODE control pictures into control codes on Windows
- Do a better job ensuring Windows console settings are restored
- Introduce KPRINTF_LOG environment variable to log kprintf to a file
This commit is contained in:
Justine Tunney 2023-08-19 06:41:06 -07:00
parent 9c7b81ee0f
commit 965516e313
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
108 changed files with 1126 additions and 807 deletions

View file

@ -55,10 +55,10 @@ int rawmode(void) {
static bool once;
struct termios t;
if (!once) {
if (tcgetattr(1, &oldterm) != -1) {
if (tcgetattr(1, &oldterm)) {
atexit(restoretty);
} else {
return -1;
perror("tcgetattr");
}
once = true;
}
@ -82,7 +82,10 @@ int rawmode(void) {
t.c_cflag |= CS8;
t.c_iflag |= IUTF8;
tcsetattr(1, TCSANOW, &t);
if (tcsetattr(1, TCSANOW, &t)) {
perror("tcsetattr");
}
WRITE(1, ENABLE_SAFE_PASTE);
WRITE(1, ENABLE_MOUSE_TRACKING);
WRITE(1, PROBE_DISPLAY_SIZE);
@ -94,7 +97,7 @@ void getsize(void) {
printf("termios says terminal size is %hu×%hu\r\n", wsize.ws_col,
wsize.ws_row);
} else {
printf("%s\n", strerror(errno));
perror("tcgetwinsize");
}
}
@ -157,12 +160,13 @@ int main(int argc, char *argv[]) {
getsize();
resized = false;
}
bzero(code, sizeof(code));
if ((n = readansi(0, code, sizeof(code))) == -1) {
if (errno == EINTR) continue;
printf("ERROR: READ: %s\r\n", strerror(errno));
perror("read");
exit(1);
}
printf("%`'.*s ", n, code);
printf("%`'.*s (got %d) ", n, code, n);
if (iscntrl(code[0]) && !code[1]) {
printf("is CTRL-%c a.k.a. ^%c\r\n", CTRL(code[0]), CTRL(code[0]));
if (code[0] == CTRL('C') || code[0] == CTRL('D')) break;

View file

@ -26,6 +26,7 @@
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/sigset.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/internal.h"
@ -35,6 +36,7 @@
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
#ifdef __x86_64__
/**
@ -179,8 +181,16 @@ textwindows bool __sig_handle(int sigops, int sig, int si_code,
switch (__sighandrvas[sig]) {
case (intptr_t)SIG_DFL:
if (__sig_is_fatal(sig)) {
STRACE("terminating on %G", sig);
_restorewintty();
size_t len;
char name[16];
strsignal_r(sig, name);
len = strlen(name);
name[len++] = '\n';
WriteFile(GetStdHandle(kNtStdErrorHandle), name, len, 0, 0);
STRACE("terminating on %s", name);
if (_weaken(__restore_console_win32)) {
_weaken(__restore_console_win32)();
}
ExitProcess(sig);
}
// fallthrough

View file

@ -34,7 +34,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
for (;;) {
if (sys_clock_gettime_nt(clock, &now)) return -1;
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(0, g_fds.p)) return -1;
if (_check_interrupts(0)) return -1;
SleepEx(MIN(__SIG_POLLING_INTERVAL_MS,
timespec_tomillis(timespec_sub(abs, now))),
false);
@ -45,7 +45,7 @@ textwindows int sys_clock_nanosleep_nt(int clock, int flags,
for (;;) {
sys_clock_gettime_nt(clock, &now);
if (timespec_cmp(now, abs) >= 0) return 0;
if (_check_interrupts(0, g_fds.p)) {
if (_check_interrupts(0)) {
if (rem) *rem = timespec_sub(abs, now);
return -1;
}

View file

@ -23,11 +23,12 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sock/syscall_fd.internal.h"
#include "libc/sysv/errfuns.h"
#include "libc/runtime/zipos.internal.h"
/**
* Closes file descriptor.
@ -59,6 +60,8 @@ int close(int fd) {
if (fd < 0) {
rc = ebadf();
} else {
// helps guarantee stderr log gets duplicated before user closes
if (_weaken(kloghandle)) _weaken(kloghandle)();
// for performance reasons we want to avoid holding __fds_lock()
// while sys_close() is happening. this leaves the kernel / libc
// having a temporarily inconsistent state. routines that obtain

View file

@ -24,6 +24,7 @@
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filesharemode.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
// code size optimization
// <sync libc/sysv/consts.sh>
@ -34,69 +35,113 @@
#define _O_DIRECTORY 0x00010000 // kNtFileFlagBackupSemantics
#define _O_TMPFILE 0x00410000 // AttributeTemporary|FlagDeleteOnClose
#define _O_DIRECT 0x00004000 // kNtFileFlagNoBuffering
#define _O_NDELAY 0x00000800 // kNtFileFlagWriteThrough
#define _O_NONBLOCK 0x00000800 // kNtFileFlagWriteThrough
#define _O_RANDOM 0x80000000 // kNtFileFlagRandomAccess
#define _O_SEQUENTIAL 0x40000000 // kNtFileFlagSequentialScan
#define _O_COMPRESSED 0x20000000 // kNtFileAttributeCompressed
#define _O_INDEXED 0x10000000 // !kNtFileAttributeNotContentIndexed
#define _O_NONBLOCK 0x00000800
#define _O_CLOEXEC 0x00080000
// </sync libc/sysv/consts.sh>
textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
uint32_t *out_share, uint32_t *out_disp,
uint32_t *out_attr) {
bool is_creating_file;
uint32_t perm, share, disp, attr;
if (flags &
~(O_ACCMODE | _O_APPEND | _O_CREAT | _O_EXCL | _O_TRUNC | _O_DIRECTORY |
_O_TMPFILE | _O_NONBLOCK | _O_RANDOM | _O_SEQUENTIAL | _O_COMPRESSED |
_O_INDEXED | _O_CLOEXEC | _O_DIRECT)) {
return einval();
}
switch (flags & O_ACCMODE) {
case O_RDONLY:
perm = kNtFileGenericRead | kNtGenericExecute;
perm = kNtFileGenericRead;
break;
case O_WRONLY:
perm = kNtFileGenericWrite | kNtGenericExecute;
perm = kNtFileGenericWrite;
break;
case O_RDWR:
perm = kNtFileGenericRead | kNtFileGenericWrite | kNtGenericExecute;
perm = kNtFileGenericRead | kNtFileGenericWrite;
break;
default:
return -1;
return einval();
}
if (flags & _O_APPEND) {
perm = kNtFileAppendData;
perm = kNtFileAppendData; // todo: this is part of generic write above
}
attr = 0;
is_creating_file = (flags & _O_CREAT) || (flags & _O_TMPFILE) == _O_TMPFILE;
// POSIX O_EXEC doesn't mean the same thing as kNtGenericExecute. We
// request execute access when we can determine it from mode's bits.
// When opening existing files we greedily request execute access so
// mmap() won't fail. If it causes CreateFile() to fail, our wrapper
// will try calling CreateFile a second time without execute access.
if (is_creating_file) {
if (mode & 0111) {
perm |= kNtGenericExecute;
}
if (~mode & 0200) {
attr |= kNtFileAttributeReadonly; // not sure why anyone would
}
} else {
perm |= kNtGenericExecute;
}
// Be as generous as possible in sharing file access. Not doing this
// you'll quickly find yourself no longer able to administer Windows
if (flags & _O_EXCL) {
share = kNtFileShareExclusive;
} else {
share = kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete;
}
if ((flags & _O_CREAT) && (flags & _O_EXCL)) {
disp = kNtCreateNew;
} else if ((flags & _O_CREAT) && (flags & _O_TRUNC)) {
disp = kNtCreateAlways;
} else if (flags & _O_CREAT) {
disp = kNtOpenAlways;
// These POSIX to WIN32 mappings are relatively straightforward.
if (flags & _O_EXCL) {
if (is_creating_file) {
disp = kNtCreateNew;
} else {
return einval();
}
} else if (is_creating_file) {
if (flags & _O_TRUNC) {
disp = kNtCreateAlways;
} else {
disp = kNtOpenAlways;
}
} else if (flags & _O_TRUNC) {
disp = kNtTruncateExisting;
} else {
disp = kNtOpenExisting;
}
// Please use tmpfd() or tmpfile() instead of O_TMPFILE.
if ((flags & _O_TMPFILE) == _O_TMPFILE) {
attr = kNtFileAttributeTemporary | kNtFileFlagDeleteOnClose;
attr |= kNtFileAttributeTemporary | kNtFileFlagDeleteOnClose;
} else {
attr = kNtFileAttributeNormal;
attr |= kNtFileAttributeNormal;
if (flags & _O_DIRECTORY) {
attr |= kNtFileFlagBackupSemantics;
}
if (~mode & 0200) {
attr |= kNtFileAttributeReadonly;
}
}
if (~flags & _O_INDEXED) attr |= kNtFileAttributeNotContentIndexed;
if (flags & _O_COMPRESSED) attr |= kNtFileAttributeCompressed;
// The Windows content indexing service ravages performance similar to
// Windows Defender. Please pass O_INDEXED to openat() to enable this.
if (~flags & _O_INDEXED) {
attr |= kNtFileAttributeNotContentIndexed;
}
// Windows' transparent filesystem compression is really cool, as such
// we've introduced a nonstandard O_COMPRESSED flag to help you use it
if (flags & _O_COMPRESSED) {
attr |= kNtFileAttributeCompressed;
}
// Not certain yet what benefit these flags offer.
if (flags & _O_SEQUENTIAL) attr |= kNtFileFlagSequentialScan;
if (flags & _O_RANDOM) attr |= kNtFileFlagRandomAccess;
if (flags & _O_DIRECT) attr |= kNtFileFlagNoBuffering;

View file

@ -21,7 +21,9 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/errfuns.h"
/**
@ -60,6 +62,8 @@
*/
int dup2(int oldfd, int newfd) {
int rc;
// helps guarantee stderr log gets duplicated before user closes
if (_weaken(kloghandle)) _weaken(kloghandle)();
if (__isfdkind(oldfd, kFdZip)) {
rc = enotsup();
#ifdef __aarch64__

View file

@ -21,7 +21,9 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
@ -59,6 +61,8 @@
*/
int dup3(int oldfd, int newfd, int flags) {
int rc;
// helps guarantee stderr log gets duplicated before user closes
if (_weaken(kloghandle)) _weaken(kloghandle)();
if (oldfd == newfd || (flags & ~O_CLOEXEC)) {
rc = einval(); // NetBSD doesn't do this
} else if (oldfd < 0 || newfd < 0) {

View file

@ -45,6 +45,8 @@
#include "libc/sysv/consts/ok.h"
#include "libc/sysv/errfuns.h"
extern long __klog_handle;
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess;
@ -115,6 +117,12 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
startinfo.hStdOutput = g_fds.p[1].handle;
startinfo.hStdError = g_fds.p[2].handle;
if (_weaken(__klog_handle) && //
*_weaken(__klog_handle) != 0 && //
*_weaken(__klog_handle) != -1) {
__imp_CloseHandle(*_weaken(__klog_handle));
}
// spawn the process
rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &startinfo, &procinfo);
if (rc == -1) {

View file

@ -23,6 +23,6 @@
textwindows int sys_fdatasync_nt(int fd) {
// TODO(jart): what should we do with worker pipes?
if (!__isfdkind(fd, kFdFile)) return ebadf();
if (_check_interrupts(0, 0)) return -1;
if (_check_interrupts(0)) return -1;
return FlushFileBuffers(g_fds.p[fd].handle) ? 0 : -1;
}

View file

@ -17,9 +17,11 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bsr.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
@ -70,25 +72,27 @@ static textwindows uint32_t GetSizeOfReparsePoint(int64_t fh) {
textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
int filetype;
uint32_t umask;
uint64_t actualsize;
struct NtFileCompressionInfo fci;
struct NtByHandleFileInformation wst;
if (!st) return efault();
if ((filetype = GetFileType(handle))) {
bzero(st, sizeof(*st));
umask = atomic_load_explicit(&__umask, memory_order_acquire);
switch (filetype) {
case kNtFileTypeChar:
st->st_mode = S_IFCHR | 0644;
st->st_mode = S_IFCHR | (0666 & ~umask);
break;
case kNtFileTypePipe:
st->st_mode = S_IFIFO | 0644;
st->st_mode = S_IFIFO | (0666 & ~umask);
break;
case kNtFileTypeDisk:
if (GetFileInformationByHandle(handle, &wst)) {
st->st_mode = 0555;
st->st_mode = 0555 & ~umask;
st->st_flags = wst.dwFileAttributes;
if (!(wst.dwFileAttributes & kNtFileAttributeReadonly)) {
st->st_mode |= 0200;
st->st_mode |= 0222 & ~umask;
}
if (wst.dwFileAttributes & kNtFileAttributeDirectory) {
st->st_mode |= S_IFDIR;
@ -101,6 +105,7 @@ textwindows int sys_fstat_nt(int64_t handle, struct stat *st) {
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
st->st_birthtim = st->st_ctim;
st->st_gid = st->st_uid = __synthesize_uid();
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
st->st_blksize = 4096;
st->st_dev = wst.dwVolumeSerialNumber;

View file

@ -30,6 +30,10 @@
/**
* Returns information about file, via open()'d descriptor.
*
* On Windows, this implementation always sets `st_uid` and `st_gid` to
* `getuid()` and `getgid()`. The `st_mode` field is generated based on
* the current umask().
*
* @return 0 on success or -1 w/ errno
* @raise EBADF if `fd` isn't a valid file descriptor
* @raise EIO if an i/o error happens while reading from file system
@ -43,14 +47,14 @@ int fstat(int fd, struct stat *st) {
} else if (__isfdkind(fd, kFdZip)) {
rc = _weaken(__zipos_fstat)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st);
} else if (!IsWindows() && !IsMetal()) {
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
rc = sys_fstat(fd, st);
} else if (IsMetal()) {
rc = sys_fstat_metal(fd, st);
} else if (!__isfdkind(fd, kFdFile)) {
rc = ebadf();
} else {
} else if (IsWindows()) {
rc = sys_fstat_nt(__getfdhandleactual(fd), st);
} else {
rc = enosys();
}
STRACE("fstat(%d, [%s]) → %d% m", fd, DescribeStat(rc, st), rc);
return rc;

View file

@ -43,10 +43,26 @@ static inline const char *__strace_fstatat_flags(char buf[12], int flags) {
/**
* Returns information about thing.
*
* @param dirfd is normally AT_FDCWD but if it's an open directory and
* file is a relative path, then file becomes relative to dirfd
* On Windows, this implementation always sets `st_uid` and `st_gid` to
* `getuid()` and `getgid()`. Anyone who relies upon the information to
* secure a multi-tenant personal computer should consider improving it
* and further note that the `st_mode` group / other bits will be clear
*
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and
* file is a relative path, then `path` becomes relative to `dirfd`
* @param st is where the result is stored
* @param flags can have `AT_SYMLINK_NOFOLLOW`
* @param st is where result is stored
* @param flags can have AT_SYMLINK_NOFOLLOW
* @raise EACCES if denied access to component in path prefix
* @raise EIO if i/o error occurred while reading from filesystem
* @raise ELOOP if a symbolic link loop exists in `path`
* @raise ENAMETOOLONG if a component in `path` exceeds `NAME_MAX`
* @raise ENOENT on empty string or if component in path doesn't exist
* @raise ENOTDIR if a parent component existed that wasn't a directory
* @raise ENOTDIR if `path` is relative and `dirfd` isn't an open directory
* @raise EOVERFLOW shouldn't be possible on 64-bit systems
* @raise ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced
* @raise ENAMETOOLONG may happen if `path` exceeded `PATH_MAX`
* @return 0 on success, or -1 w/ errno
* @see S_ISDIR(st.st_mode), S_ISREG()
* @asyncsignalsafe
@ -70,10 +86,12 @@ int fstatat(int dirfd, const char *path, struct stat *st, int flags) {
} else {
rc = enotsup();
}
} else if (!IsWindows()) {
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd() || IsNetbsd()) {
rc = sys_fstatat(dirfd, path, st, flags);
} else {
} else if (IsWindows()) {
rc = sys_fstatat_nt(dirfd, path, st, flags);
} else {
rc = enosys();
}
STRACE("fstatat(%s, %#s, [%s], %s) → %d% m", DescribeDirfd(dirfd), path,
DescribeStat(rc, st), __strace_fstatat_flags(alloca(12), flags), rc);

View file

@ -17,9 +17,11 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
@ -27,7 +29,7 @@
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
static uint32_t KnuthMultiplicativeHash32(const void *buf, size_t size) {
static uint32_t __kmp32(const void *buf, size_t size) {
size_t i;
uint32_t h;
const uint32_t kPhiPrime = 0x9e3779b1;
@ -36,18 +38,24 @@ static uint32_t KnuthMultiplicativeHash32(const void *buf, size_t size) {
return h;
}
static textwindows dontinline uint32_t GetUserNameHash(void) {
textwindows uint32_t __synthesize_uid(void) {
char16_t buf[257];
uint32_t size = ARRAYLEN(buf);
GetUserName(&buf, &size);
return KnuthMultiplicativeHash32(buf, size >> 1) & INT_MAX;
static atomic_uint uid;
uint32_t tmp, size = ARRAYLEN(buf);
if (!(tmp = atomic_load_explicit(&uid, memory_order_acquire))) {
GetUserName(&buf, &size);
tmp = __kmp32(buf, size >> 1) & INT_MAX;
if (!tmp) ++tmp;
atomic_store_explicit(&uid, tmp, memory_order_release);
}
return tmp;
}
/**
* Returns real user id of process.
*
* This never fails. On Windows, which doesn't really have this concept,
* we return a deterministic value that's likely to work.
* we return a hash of the username.
*
* @return user id (always successful)
* @asyncsignalsafe
@ -61,7 +69,7 @@ uint32_t getuid(void) {
} else if (!IsWindows()) {
rc = sys_getuid();
} else {
rc = GetUserNameHash();
rc = __synthesize_uid();
}
npassert(rc >= 0);
STRACE("%s() → %d", "getuid", rc);
@ -72,7 +80,7 @@ uint32_t getuid(void) {
* Returns real group id of process.
*
* This never fails. On Windows, which doesn't really have this concept,
* we return a deterministic value that's likely to work.
* we return a hash of the username.
*
* @return group id (always successful)
* @asyncsignalsafe
@ -86,7 +94,7 @@ uint32_t getgid(void) {
} else if (!IsWindows()) {
rc = sys_getgid();
} else {
rc = GetUserNameHash();
rc = __synthesize_uid();
}
npassert(rc >= 0);
STRACE("%s() → %d", "getgid", rc);

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
#include "libc/atomic.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/sigval.h"
#include "libc/dce.h"
@ -16,6 +17,7 @@ COSMOPOLITAN_C_START_
#define kIoMotion ((const int8_t[3]){1, 0, 0})
extern struct Fds g_fds;
extern atomic_int __umask;
extern const struct Fd kEmptyFd;
int __reservefd(int);
@ -24,6 +26,7 @@ void __releasefd(int);
int __ensurefds(int);
int __ensurefds_unlocked(int);
void __printfds(void);
uint32_t __synthesize_uid(void);
forceinline int64_t __getfdhandleactual(int fd) {
return g_fds.p[fd].handle;
@ -37,8 +40,8 @@ forceinline bool __isfdkind(int fd, int kind) {
return 0 <= fd && fd < g_fds.n && g_fds.p[fd].kind == kind;
}
int _check_interrupts(int);
int sys_close_nt(struct Fd *, int);
int _check_interrupts(int, struct Fd *);
int sys_openat_metal(int, const char *, int, unsigned);
COSMOPOLITAN_C_END_

View file

@ -33,8 +33,8 @@
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
textwindows int _check_interrupts(int sigops, struct Fd *fd) {
int e, rc;
textwindows int _check_interrupts(int sigops) {
int e, rc, flags = 0;
e = errno;
if (_weaken(pthread_testcancel_np) &&
(rc = _weaken(pthread_testcancel_np)())) {
@ -42,21 +42,22 @@ textwindows int _check_interrupts(int sigops, struct Fd *fd) {
return -1;
}
if (__tls_enabled) {
flags = __get_tls()->tib_flags;
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
}
if (_weaken(_check_sigalrm)) {
_weaken(_check_sigalrm)();
}
if (__tls_enabled) {
__get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
__get_tls()->tib_flags = flags;
}
if (_weaken(_check_sigwinch)) {
_weaken(_check_sigwinch)();
}
if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
if (!(sigops & kSigOpNochld) && _weaken(_check_sigchld)) {
_weaken(_check_sigchld)();
}
if (fd && _weaken(_check_sigwinch)) {
_weaken(_check_sigwinch)(fd);
}
}
if (_weaken(__sig_check) && _weaken(__sig_check)(sigops)) {
return eintr();

View file

@ -672,9 +672,9 @@ int ioctl(int fd, unsigned long request, ...) {
if (request == FIONREAD) {
rc = ioctl_fionread(fd, arg);
} else if (request == TIOCGWINSZ) {
rc = tcgetwinsize(fd, arg);
return tcgetwinsize(fd, arg);
} else if (request == TIOCSWINSZ) {
rc = tcsetwinsize(fd, arg);
return tcsetwinsize(fd, arg);
} else if (request == SIOCGIFCONF) {
rc = ioctl_siocgifconf(fd, arg);
} else if (request == SIOCGIFADDR) {

View file

@ -78,7 +78,7 @@
int64_t lseek(int fd, int64_t offset, int whence) {
int64_t rc;
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = _weaken(__zipos_lseek)(
rc = _weaken(__zipos_seek)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, offset, whence);
} else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd()) {
rc = sys_lseek(fd, offset, whence, 0);

View file

@ -92,20 +92,24 @@ TryAgain:
if (result || flags == F_OK) {
rc = 0;
} else {
NTTRACE("ntaccesscheck finale failed %d %x", result, flags);
NTTRACE("ntaccesscheck finale failed: result=%d flags=%x", result,
flags);
rc = eacces();
}
} else {
rc = __winerr();
NTTRACE("%s(%#hs) failed: %m", "AccessCheck", pathname);
NTTRACE("%s(%#hs) failed: %s", "AccessCheck", pathname,
strerror(errno));
}
} else {
rc = __winerr();
NTTRACE("%s(%#hs) failed: %m", "DuplicateToken", pathname);
NTTRACE("%s(%#hs) failed: %s", "DuplicateToken", pathname,
strerror(errno));
}
} else {
rc = __winerr();
NTTRACE("%s(%#hs) failed: %m", "OpenProcessToken", pathname);
NTTRACE("%s(%#hs) failed: %s", "OpenProcessToken", pathname,
strerror(errno));
}
} else {
e = GetLastError();
@ -115,11 +119,13 @@ TryAgain:
goto TryAgain;
} else {
rc = enomem();
NTTRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname);
NTTRACE("%s(%#hs) failed: %s", "GetFileSecurity", pathname,
strerror(errno));
}
} else {
errno = e;
NTTRACE("%s(%#hs) failed: %m", "GetFileSecurity", pathname);
NTTRACE("%s(%#hs) failed: %s", "GetFileSecurity", pathname,
strerror(errno));
rc = -1;
}
}

View file

@ -40,6 +40,13 @@ static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
if (__mkntpathat(dirfd, path, flags, path16) == -1) {
return kNtInvalidHandleValue;
}
if (flags & O_NOFOLLOW) {
if ((attr = GetFileAttributes(path16)) != -1u && //
(attr & kNtFileAttributeReparsePoint)) {
return eloop();
}
flags &= ~O_NOFOLLOW;
}
if (GetNtOpenFlags(flags, mode, &perm, &share, &disp, &attr) == -1) {
return kNtInvalidHandleValue;
}
@ -57,7 +64,9 @@ static textwindows int sys_open_nt_console(int dirfd,
// this is an ugly hack that works for observed usage patterns
g_fds.p[fd].handle = g_fds.p[STDIN_FILENO].handle;
g_fds.p[fd].extra = g_fds.p[STDOUT_FILENO].handle;
g_fds.p[fd].dontclose = true; // don't call CloseHandle() upon close()
g_fds.p[STDOUT_FILENO].dontclose = true;
g_fds.p[STDIN_FILENO].dontclose = true;
g_fds.p[fd].dontclose = true;
} else if ((g_fds.p[fd].handle = sys_open_nt_impl(
dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode,
kNtFileFlagOverlapped)) != -1) {

View file

@ -61,6 +61,19 @@
* // run `zip program.com hi.txt` beforehand
* openat(AT_FDCWD, "/zip/hi.txt", O_RDONLY);
*
* Cosmopolitan's general approach on Windows to path translation is to
*
* - replace `/' with `\`
* - translate utf-8 into utf-16
* - turn `"\X\foo"` into `"\\?\X:\foo"`
* - turn `"\X"` into `"\\?\X:\"`
* - turn `"X:\foo"` into `"\\?\X:\foo"`
*
* On Windows, opening files in `/tmp` will open them in GetTempPath(),
* which is a secure per-user directory. Opening `/dev/tty` will open a
* special console file descriptor holding both `CONIN$` and `CONOUT$`,
* which can't be fully closed. Opening `/dev/null` will open up `NUL`.
*
* @param dirfd is normally `AT_FDCWD` but if it's an open directory and
* `file` names a relative path then it's opened relative to `dirfd`
* @param file is a UTF-8 string naming filesystem entity, e.g. `foo/bar.txt`,
@ -81,12 +94,12 @@
* - `O_CLOEXEC` automatic close() upon execve()
* - `O_EXCL` exclusive access (see below)
* - `O_APPEND` open file for appending only
* - `O_NOFOLLOW` fail with ELOOP if it's a symlink
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
* - `O_EXEC` open file for execution only; see fexecve()
* - `O_NOCTTY` prevents `file` possibly becoming controlling terminal
* - `O_NONBLOCK` asks read/write to fail with `EAGAIN` rather than block
* - `O_DIRECT` it's complicated (not supported on Apple and OpenBSD)
* - `O_DIRECTORY` useful for stat'ing (hint on UNIX but required on NT)
* - `O_NOFOLLOW` fail if it's a symlink (zero on Windows)
* - `O_DSYNC` it's complicated (zero on non-Linux/Apple)
* - `O_RSYNC` it's complicated (zero on non-Linux/Apple)
* - `O_VERIFY` it's complicated (zero on non-FreeBSD)

View file

@ -28,7 +28,7 @@
textwindows int sys_pause_nt(void) {
for (;;) {
if (_check_interrupts(0, g_fds.p)) {
if (_check_interrupts(0)) {
return -1;
}

View file

@ -69,7 +69,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
if (sigmask) {
__sig_mask(SIG_SETMASK, sigmask, &oldmask);
}
if ((rc = _check_interrupts(0, g_fds.p))) {
if ((rc = _check_interrupts(0))) {
goto ReturnPath;
}
@ -194,7 +194,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
}
// otherwise loop limitlessly for timeout to elapse while
// checking for signal delivery interrupts, along the way
if ((rc = _check_interrupts(0, g_fds.p))) {
if ((rc = _check_interrupts(0))) {
goto ReturnPath;
}
}

View file

@ -27,10 +27,6 @@
#include "libc/thread/tls.h"
#include "libc/thread/xnu.internal.h"
static textwindows inline bool HasWorkingConsole(void) {
return !!(__ntconsolemode[0] | __ntconsolemode[1] | __ntconsolemode[2]);
}
static dontubsan void RaiseSigFpe(void) {
volatile int x = 0;
x = 1 / x;

View file

@ -26,9 +26,12 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/calls/wincrash.internal.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/filetype.h"
#include "libc/nt/enum/wait.h"
#include "libc/nt/errors.h"
@ -48,83 +51,12 @@
#ifdef __x86_64__
enum Action {
DO_NOTHING,
DO_RESTART,
DO_EINTR,
};
static textwindows void sys_read_nt_abort(int64_t handle,
struct NtOverlapped *overlapped) {
unassert(CancelIoEx(handle, overlapped) ||
GetLastError() == kNtErrorNotFound);
}
static textwindows int MungeTerminalInput(char *p, uint32_t *n) {
size_t i, j;
if (!(__ttymagic & kFdTtyNoCr2Nl)) {
for (i = 0; i < *n; ++i) {
if (p[i] == '\r') {
p[i] = '\n';
}
}
}
if (!(__ttymagic & kFdTtyNoIsigs)) {
bool delivered = false;
bool got_vintr = false;
bool got_vquit = false;
for (j = i = 0; i < *n; ++i) {
if (__vintr != _POSIX_VDISABLE && p[i] == __vintr) {
got_vintr = true;
} else if (__vquit != _POSIX_VDISABLE && p[i] == __vquit) {
got_vquit = true;
} else {
p[j++] = p[i];
}
}
if (got_vintr) {
delivered |= __sig_handle(0, SIGINT, SI_KERNEL, 0);
}
if (got_vquit) {
delivered |= __sig_handle(0, SIGQUIT, SI_KERNEL, 0);
}
if (*n && !j) {
if (delivered) {
return DO_EINTR;
} else {
return DO_RESTART;
}
}
*n = j;
}
return DO_NOTHING;
}
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
static textwindows void EchoTerminalInput(struct Fd *fd, char *p, size_t n) {
int64_t hOutput;
if (fd->kind == kFdConsole) {
hOutput = fd->extra;
} else {
hOutput = g_fds.p[1].handle;
}
if (__ttymagic & kFdTtyEchoRaw) {
WriteFile(hOutput, p, n, 0, 0);
} else {
size_t i;
for (i = 0; i < n; ++i) {
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
char ctl[2];
ctl[0] = '^';
ctl[1] = p[i] ^ 0100;
WriteFile(hOutput, ctl, 2, 0, 0);
} else {
WriteFile(hOutput, p + i, 1, 0, 0);
}
}
}
}
static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
size_t size, int64_t offset) {
@ -133,11 +65,38 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
uint32_t got;
int filetype;
int64_t handle;
uint32_t remain;
uint32_t conmode;
char *targetdata;
uint32_t targetsize;
bool is_console_input;
int abort_errno = EAGAIN;
StartOver:
size = MIN(size, 0x7ffff000);
handle = __resolve_stdin_handle(fd->handle);
filetype = GetFileType(handle);
is_console_input = g_fds.stdin.handle ? fd->handle == g_fds.stdin.handle
: fd->handle == g_fds.p[0].handle;
// the caller might be reading a single byte at a time. but we need to
// be able to munge three bytes into just 1 e.g. "\342\220\200" → "\0"
if (size && fd->buflen) {
ReturnDataFromBuffer:
got = MIN(size, fd->buflen);
remain = fd->buflen - got;
if (got) memcpy(data, fd->buf, got);
if (remain) memmove(fd->buf, fd->buf + got, remain);
fd->buflen = remain;
return got;
}
if (is_console_input && size && size < 3 && (__ttymagic & kFdTtyMunging)) {
targetdata = fd->buf;
targetsize = sizeof(fd->buf);
} else {
targetdata = data;
targetsize = size;
}
if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) {
struct NtOverlapped overlap = {0};
if (offset != -1) {
@ -147,7 +106,7 @@ StartOver:
if ((overlap.hEvent = CreateEvent(0, 0, 0, 0))) {
// the win32 manual says it's important to *not* put &got here
// since for overlapped i/o, we always use GetOverlappedResult
ok = ReadFile(handle, data, size, 0, &overlap);
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) {
// the i/o operation is in flight; blocking is unavoidable
// if we're in a non-blocking mode, then immediately abort
@ -155,7 +114,7 @@ StartOver:
// otherwise wait for i/o periodically checking interrupts
if (fd->flags & O_NONBLOCK) {
sys_read_nt_abort(handle, &overlap);
} else if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
} else if (_check_interrupts(kSigOpRestartable)) {
Interrupted:
abort_errno = errno;
sys_read_nt_abort(handle, &overlap);
@ -164,7 +123,7 @@ StartOver:
uint32_t i;
i = WaitForSingleObject(overlap.hEvent, __SIG_POLLING_INTERVAL_MS);
if (i == kNtWaitTimeout) {
if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
if (_check_interrupts(kSigOpRestartable)) {
goto Interrupted;
}
} else {
@ -186,7 +145,7 @@ StartOver:
}
} else if (offset == -1) {
// perform simple blocking file read
ok = ReadFile(handle, data, size, &got, 0);
ok = ReadFile(handle, targetdata, targetsize, &got, 0);
} else {
// perform pread()-style file read at particular file offset
int64_t position;
@ -196,17 +155,17 @@ StartOver:
}
struct NtOverlapped overlap = {0};
overlap.Pointer = (void *)(uintptr_t)offset;
ok = ReadFile(handle, data, size, 0, &overlap);
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
if (!ok && GetLastError() == kNtErrorIoPending) ok = true;
if (ok) ok = GetOverlappedResult(handle, &overlap, &got, true);
// restore file pointer which windows clobbers, even on error
unassert(SetFilePointerEx(handle, position, 0, SEEK_SET));
}
if (ok) {
if (g_fds.stdin.handle ? fd->handle == g_fds.stdin.handle
: fd->handle == g_fds.p[0].handle) {
if (is_console_input) {
if (__ttymagic & kFdTtyMunging) {
switch (MungeTerminalInput(data, &got)) {
switch (_weaken(__munge_terminal_input)(targetdata, &got)) {
case DO_NOTHING:
break;
case DO_RESTART:
@ -218,9 +177,13 @@ StartOver:
}
}
if (__ttymagic & kFdTtyEchoing) {
EchoTerminalInput(fd, data, got);
_weaken(__echo_terminal_input)(fd, targetdata, got);
}
}
if (targetdata != data) {
fd->buflen = got;
goto ReturnDataFromBuffer;
}
return got;
}
@ -245,7 +208,7 @@ textwindows ssize_t sys_read_nt(struct Fd *fd, const struct iovec *iov,
uint32_t size;
size_t i, total;
if (opt_offset < -1) return einval();
if (_check_interrupts(kSigOpRestartable, fd)) return -1;
if (_check_interrupts(kSigOpRestartable)) return -1;
while (iovlen && !iov[0].iov_len) iov++, iovlen--;
if (iovlen) {
for (total = i = 0; i < iovlen; ++i) {

View file

@ -5,7 +5,7 @@
#include "libc/calls/ucontext.h"
#define __SIG_QUEUE_LENGTH 32
#define __SIG_POLLING_INTERVAL_MS 50
#define __SIG_POLLING_INTERVAL_MS 20
#define __SIG_LOGGING_INTERVAL_MS 1700
#if !(__ASSEMBLER__ + __LINKER__ + 0)

View file

@ -77,7 +77,7 @@ int sigsuspend(const sigset_t *ignore) {
long totoms = 0;
#endif
do {
if ((rc = _check_interrupts(0, g_fds.p))) {
if ((rc = _check_interrupts(0))) {
break;
}
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {

View file

@ -16,48 +16,57 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/atomic.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/struct/winsize.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/struct/consolescreenbufferinfoex.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
#ifdef __x86_64__
static struct winsize __ws;
static atomic_uint __win_winsize;
textwindows void _check_sigwinch(struct Fd *fd) {
int e;
siginfo_t si;
struct winsize ws, old;
struct NtConsoleScreenBufferInfoEx sbinfo;
if (__tls_enabled && __threaded != gettid()) return;
old = __ws;
e = errno;
if (old.ws_row != 0xffff) {
if (tcgetwinsize_nt(fd, &ws) != -1) {
if (old.ws_col != ws.ws_col || old.ws_row != ws.ws_row) {
__ws = ws;
if (old.ws_col | old.ws_row) {
__sig_add(0, SIGWINCH, SI_KERNEL);
}
}
static textwindows unsigned __get_console_size(void) {
for (int fd = 1; fd < 10; ++fd) {
intptr_t hConsoleOutput;
if (g_fds.p[fd].kind == kFdConsole) {
hConsoleOutput = g_fds.p[fd].extra;
} else {
if (!old.ws_row && !old.ws_col) {
__ws.ws_row = 0xffff;
}
hConsoleOutput = g_fds.p[fd].handle;
}
struct NtConsoleScreenBufferInfoEx sr = {.cbSize = sizeof(sr)};
if (GetConsoleScreenBufferInfoEx(hConsoleOutput, &sr)) {
unsigned short yn = sr.srWindow.Bottom - sr.srWindow.Top + 1;
unsigned short xn = sr.srWindow.Right - sr.srWindow.Left + 1;
return (unsigned)yn << 16 | xn;
}
}
errno = e;
return -1u;
}
textwindows void _check_sigwinch(void) {
unsigned old = atomic_load_explicit(&__win_winsize, memory_order_acquire);
if (old == -1u) return;
unsigned neu = __get_console_size();
old = atomic_exchange(&__win_winsize, neu);
if (neu != old) {
__sig_add(0, SIGWINCH, SI_KERNEL);
}
}
__attribute__((__constructor__)) static void sigwinch_init(void) {
if (!IsWindows()) return;
unsigned ws = __get_console_size();
atomic_store_explicit(&__win_winsize, ws, memory_order_release);
STRACE("sigwinch_init() → %08x", ws);
}
#endif /* __x86_64__ */

View file

@ -20,19 +20,14 @@
#include "libc/sysv/consts/at.h"
/**
* Returns information about thing.
* Returns information about file.
*
* @param st is where result is stored
* @see S_ISDIR(st.st_mode), S_ISREG(), etc.
* @raise EACCES if denied access to component in path prefix
* @raise EIO if i/o error occurred while reading from filesystem
* @raise ELOOP if a symbolic link loop exists in `path`
* @raise ENAMETOOLONG if a component in `path` exceeds `NAME_MAX`
* @raise ENOENT on empty string or if component in path doesn't exist
* @raise ENOTDIR if a parent component existed that wasn't a directory
* @raise EOVERFLOW shouldn't be possible on 64-bit systems
* @raise ELOOP may ahappen if `SYMLOOP_MAX` symlinks were dereferenced
* @raise ENAMETOOLONG may happen if `path` exceeded `PATH_MAX`
* This function is equivalent to:
*
* struct stat st;
* fstatat(AT_FDCWD, path, &st, 0);
*
* @see fstatat() for further documentation
* @asyncsignalsafe
*/
int stat(const char *path, struct stat *st) {

View file

@ -3,6 +3,10 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define DO_NOTHING 0
#define DO_RESTART 1
#define DO_EINTR 2
#define kFdEmpty 0
#define kFdFile 1
#define kFdSocket 2
@ -23,6 +27,8 @@ struct Fd {
char kind;
bool zombie;
bool dontclose;
char buflen;
char buf[4];
unsigned flags;
unsigned mode;
int64_t handle;
@ -44,8 +50,10 @@ struct Fds {
struct StdinRelay stdin;
};
int64_t __resolve_stdin_handle(int64_t);
void WinMainStdin(void);
int64_t __resolve_stdin_handle(int64_t);
int __munge_terminal_input(char *, uint32_t *);
void __echo_terminal_input(struct Fd *, char *, size_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -65,7 +65,7 @@ const char *DescribeSigaction(char[256], int, const struct sigaction *);
void _init_onntconsoleevent(void);
void _init_wincrash(void);
void _check_sigwinch();
void _check_sigwinch(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -6,7 +6,7 @@
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
int tcgetwinsize_nt(struct Fd *, struct winsize *);
int tcgetwinsize_nt(int, struct winsize *);
const char *DescribeWinsize(char[64], int, struct winsize *);
#define DescribeWinsize(rc, ws) DescribeWinsize(alloca(64), rc, ws)

View file

@ -33,8 +33,9 @@
static dontinline textwindows int sys_tcdrain_nt(int fd) {
if (!__isfdopen(fd)) return ebadf();
if (_check_interrupts(0, g_fds.p)) return -1;
if (!FlushFileBuffers(g_fds.p[fd].handle)) return __winerr();
if (_check_interrupts(0)) return -1;
// Tried FlushFileBuffers but it made Emacs hang when run in cmd.exe
// "Console output is not buffered." -Quoth MSDN on FlushFileBuffers
return 0;
}

View file

@ -17,67 +17,45 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/winsize.h"
#include "libc/calls/struct/winsize.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/macros.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/startupinfo.h"
#include "libc/nt/struct/consolescreenbufferinfoex.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/errfuns.h"
textwindows int tcgetwinsize_nt(struct Fd *fd, struct winsize *ws) {
int i, e, rc;
uint32_t mode;
struct Fd *fds[3];
struct NtStartupInfo startinfo;
struct NtConsoleScreenBufferInfoEx sbinfo;
rc = -1;
e = errno;
if (ws) {
__fds_lock();
fds[0] = fd, fds[1] = g_fds.p + 1, fds[2] = g_fds.p + 0;
GetStartupInfo(&startinfo);
for (i = 0; i < ARRAYLEN(fds); ++i) {
if (fds[i]->kind == kFdFile || fds[i]->kind == kFdConsole) {
if (GetConsoleMode(__getfdhandleactual(i), &mode)) {
bzero(&sbinfo, sizeof(sbinfo));
sbinfo.cbSize = sizeof(sbinfo);
if (GetConsoleScreenBufferInfoEx(__getfdhandleactual(i), &sbinfo)) {
ws->ws_col = sbinfo.srWindow.Right - sbinfo.srWindow.Left + 1;
ws->ws_row = sbinfo.srWindow.Bottom - sbinfo.srWindow.Top + 1;
ws->ws_xpixel = 0;
ws->ws_ypixel = 0;
errno = e;
rc = 0;
break;
} else if (startinfo.dwFlags & kNtStartfUsecountchars) {
ws->ws_col = startinfo.dwXCountChars;
ws->ws_row = startinfo.dwYCountChars;
ws->ws_xpixel = 0;
ws->ws_ypixel = 0;
errno = e;
rc = 0;
break;
} else {
__winerr();
}
} else {
enotty();
}
} else {
ebadf();
}
}
__fds_unlock();
} else {
efault();
textwindows int tcgetwinsize_nt(int fd, struct winsize *ws) {
// The Linux man page doesn't list EBADF as an errno for this.
if (!__isfdopen(fd)) {
return enotty();
}
// Unlike _check_sigwinch() this is an API so be stricter.
intptr_t hConsoleOutput;
if (fd == STDIN_FILENO) {
uint32_t dwMode;
// WIN32 doesn't allow GetConsoleScreenBufferInfoEx(stdin)
if (GetConsoleMode(g_fds.p[STDIN_FILENO].handle, &dwMode)) {
hConsoleOutput = g_fds.p[STDOUT_FILENO].handle;
if (GetConsoleMode(hConsoleOutput, &dwMode)) {
hConsoleOutput = g_fds.p[STDERR_FILENO].handle;
}
} else {
return enotty();
}
} else if (g_fds.p[fd].kind == kFdConsole) {
hConsoleOutput = g_fds.p[fd].extra;
} else {
hConsoleOutput = g_fds.p[fd].handle;
}
// Query the console.
struct NtConsoleScreenBufferInfoEx sr = {.cbSize = sizeof(sr)};
if (GetConsoleScreenBufferInfoEx(hConsoleOutput, &sr)) {
ws->ws_col = sr.srWindow.Right - sr.srWindow.Left + 1;
ws->ws_row = sr.srWindow.Bottom - sr.srWindow.Top + 1;
return 0;
} else {
return enotty();
}
return rc;
}

View file

@ -38,7 +38,7 @@ int tcgetwinsize(int fd, struct winsize *ws) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
rc = enotty();
} else if (IsWindows()) {
rc = tcgetwinsize_nt(g_fds.p + fd, ws);
rc = tcgetwinsize_nt(fd, ws);
} else {
rc = sys_ioctl(fd, TIOCGWINSZ, ws);
}

View file

@ -17,20 +17,151 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/metatermios.internal.h"
#include "libc/calls/termios.internal.h"
#include "libc/calls/ttydefaults.h"
#include "libc/dce.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/consolemodeflags.h"
#include "libc/nt/enum/version.h"
#include "libc/nt/runtime.h"
#include "libc/nt/version.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sicode.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
textwindows int __munge_terminal_input(char *p, uint32_t *n) {
size_t i, j;
if (!(__ttymagic & kFdTtyNoCr2Nl)) {
for (i = 0; i < *n; ++i) {
if (p[i] == '\r') {
p[i] = '\n';
}
}
}
bool delivered = false;
bool got_vintr = false;
bool got_vquit = false;
for (j = i = 0; i < *n; ++i) {
/*
It's not possible to type Ctrl+Space (aka ^@) into the CMD.EXE
console. Ctrl+Z is also problematic, since the Windows Console
processes that keystroke into an EOF event, even when we don't
enable input processing. These control codes are important for
Emacs users. One solution is to install the "Windows Terminal"
application from Microsoft Store, type Ctrl+Shift+, to get its
settings.json file opened, and add this code to its "actions":
"actions": [
{
"command": {
"action": "sendInput",
"input": ""
},
"keys": "ctrl+space"
},
{
"command": {
"action": "sendInput",
"input": ""
},
"keys": "ctrl+z"
},
{
"command": {
"action": "sendInput",
"input": "\u001f"
},
"keys": "ctrl+-"
}
],
Its not possible to configure Windows Terminal to output our
control codes. The workaround is to encode control sequences
using the "Control Pictures" UNICODE plane, which we'll then
translate back from UTF-8 glyphs, into actual control codes.
Another option Windows users can consider, particularly when
using CMD.EXE is installing Microsoft PowerTools whech makes
it possible to remap keys then configure ncurses to use them
https://github.com/microsoft/terminal/issues/2865
*/
int c;
if (i + 3 <= *n && // control pictures ascii nul ␀
(p[i + 0] & 255) == 0xe2 && // becomes ^@ a.k.a. ctrl-space
(p[i + 1] & 255) == 0x90 && // map the entire unicode plane
((p[i + 2] & 255) >= 0x80 && //
(p[i + 2] & 255) <= 0x9F)) {
c = (p[i + 2] & 255) - 0x80;
i += 2;
} else {
c = p[i] & 255;
}
if (!(__ttymagic & kFdTtyNoIsigs) && //
__vintr != _POSIX_VDISABLE && //
c == __vintr) {
got_vintr = true;
} else if (!(__ttymagic & kFdTtyNoIsigs) && //
__vquit != _POSIX_VDISABLE && //
c == __vquit) {
got_vquit = true;
} else {
p[j++] = c;
}
}
if (got_vintr) {
delivered |= __sig_handle(0, SIGINT, SI_KERNEL, 0);
}
if (got_vquit) {
delivered |= __sig_handle(0, SIGQUIT, SI_KERNEL, 0);
}
if (*n && !j) {
if (delivered) {
return DO_EINTR;
} else {
return DO_RESTART;
}
}
*n = j;
return DO_NOTHING;
}
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
textwindows void __echo_terminal_input(struct Fd *fd, char *p, size_t n) {
int64_t hOutput;
if (fd->kind == kFdConsole) {
hOutput = fd->extra;
} else {
hOutput = g_fds.p[1].handle;
}
if (__ttymagic & kFdTtyEchoRaw) {
WriteFile(hOutput, p, n, 0, 0);
} else {
size_t i;
for (i = 0; i < n; ++i) {
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
char ctl[2];
ctl[0] = '^';
ctl[1] = p[i] ^ 0100;
WriteFile(hOutput, ctl, 2, 0, 0);
} else {
WriteFile(hOutput, p + i, 1, 0, 0);
}
}
}
}
textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
bool32 ok;
int infd;
@ -122,3 +253,11 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
return enotty();
}
}
__attribute__((__constructor__)) static void tcsetattr_nt_init(void) {
if (!getenv("TERM")) {
setenv("TERM", "xterm-256color", true);
}
}
#endif /* __x86_64__ */

View file

@ -17,14 +17,22 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/nr.h"
/**
* Sets file mode creation mask.
*
* On Windows, the value of umask() determines how Cosmopolitan goes
* about creating synthetic `st_mode` bits. The default umask is 077
* which is based on the assumption that Windows is being used for a
* single user. Don't assume that Cosmopolitan Libc will help you to
* secure a multitenant Windows computer.
*
* @return previous mask
* @note always succeeds
*/
@ -33,8 +41,7 @@ unsigned umask(unsigned newmask) {
if (!IsWindows()) {
oldmask = sys_umask(newmask);
} else {
// TODO(jart): what should we do with this?
oldmask = newmask;
oldmask = atomic_exchange(&__umask, newmask);
}
STRACE("umask(%#o) → %#o", oldmask);
return oldmask;

View file

@ -155,7 +155,7 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options,
sigaddset(&mask, SIGCHLD);
__sig_mask(SIG_BLOCK, &mask, &oldmask);
do {
rc = _check_interrupts(kSigOpRestartable | kSigOpNochld, 0);
rc = _check_interrupts(kSigOpRestartable | kSigOpNochld);
if (rc == -1) break;
__fds_lock();
rc = sys_wait4_nt_impl(&pid, opt_out_wstatus, options, opt_out_rusage);

View file

@ -66,6 +66,7 @@ _start:
lea 8(%rsp),%rsi // argv
lea 16(%rsp,%rbx,8),%rdx // envp
mov %rsp,__oldstack(%rip)
mov %rdx,__envp(%rip)
// setup backtraces
xor %ebp,%ebp
@ -77,7 +78,8 @@ _start:
// make win32 imps noop
.weak ape_idata_iat
.weak ape_idata_iatend
ezlea __win32_oops,ax
.weak __oops_win32
ezlea __oops_win32,ax
ezlea ape_idata_iat,di
ezlea ape_idata_iatend,cx
sub %rdi,%rcx

View file

@ -22,7 +22,7 @@
#define ToUpper(c) \
(IsWindows() && (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
dontasan struct Env __getenv(char **p, const char *k) {
dontasan privileged struct Env __getenv(char **p, const char *k) {
char *t;
int i, j;
for (i = 0; (t = p[i]); ++i) {

View file

@ -1,33 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi
Copyright 2020 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/macros.internal.h"
#include "libc/notice.inc"
// __pid_exec is __pid that isn't updated on fork()
.initbss 301,_init__pid_exec
__pid_exec:
.quad 0
.endobj __pid_exec,globl
.previous
.init.start 301,_init__pid_exec
mov __pid(%rip),%rax
stosq
.init.end 301,_init__pid_exec

View file

@ -344,7 +344,7 @@ static void __asan_exit(void) {
kprintf("your asan runtime needs\n"
"\t__static_yoink(\"__die\");\n"
"in order to show you backtraces\n");
_Exitr(99);
_Exit(99);
}
dontdiscard static __asan_die_f *__asan_die(void) {
@ -1483,7 +1483,7 @@ void __asan_init(int argc, char **argv, char **envp, intptr_t *auxv) {
if (!_cmpxchg(&once, false, true)) return;
if (IsWindows() && NtGetVersion() < kNtVersionWindows10) {
__write_str("error: asan binaries require windows10\r\n");
_Exitr(0); /* So `make MODE=dbg test` passes w/ Windows7 */
_Exit(0); /* So `make MODE=dbg test` passes w/ Windows7 */
}
REQUIRE(_mmi);
REQUIRE(sys_mmap);

View file

@ -21,6 +21,8 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/creationdisposition.h"
#include "libc/nt/errors.h"
#include "libc/nt/runtime.h"
#include "libc/nt/synchronization.h"
@ -33,30 +35,50 @@ __msabi extern typeof(Sleep) *const __imp_Sleep;
/**
* Opens file on the New Technology.
*
* @return handle, or -1 on failure
* @return handle, or -1 on failure w/ `errno` set appropriately
* @note this wrapper takes care of ABI, STRACE(), and __winerr()
*/
textwindows int64_t CreateFile(
const char16_t *lpFileName, uint32_t dwDesiredAccess, uint32_t dwShareMode,
struct NtSecurityAttributes *opt_lpSecurityAttributes,
int dwCreationDisposition, uint32_t dwFlagsAndAttributes,
int64_t opt_hTemplateFile) {
textwindows int64_t CreateFile(const char16_t *lpFileName, //
uint32_t dwDesiredAccess, //
uint32_t dwShareMode, //
struct NtSecurityAttributes *opt_lpSecurity, //
int dwCreationDisposition, //
uint32_t dwFlagsAndAttributes, //
int64_t opt_hTemplateFile) {
int64_t hHandle;
uint32_t micros = 1;
TryAgain:
hHandle = __imp_CreateFileW(lpFileName, dwDesiredAccess, dwShareMode,
opt_lpSecurityAttributes, dwCreationDisposition,
opt_lpSecurity, dwCreationDisposition,
dwFlagsAndAttributes, opt_hTemplateFile);
if (hHandle == -1 && __imp_GetLastError() == kNtErrorPipeBusy) {
if (micros >= 1024) __imp_Sleep(micros / 1024);
if (micros / 1024 < __SIG_POLLING_INTERVAL_MS) micros <<= 1;
goto TryAgain;
if (hHandle == -1) {
switch (__imp_GetLastError()) {
case kNtErrorPipeBusy:
if (micros >= 1024) __imp_Sleep(micros / 1024);
if (micros / 1024 < __SIG_POLLING_INTERVAL_MS) micros <<= 1;
goto TryAgain;
case kNtErrorAccessDenied:
// GetNtOpenFlags() always greedily requests execute permissions
// because the POSIX flag O_EXEC doesn't mean the same thing. It
// seems however this causes the opening of certain files to not
// work, possibly due to Windows Defender or some security thing
// In that case, we'll cross our fingers the file isn't a binary
if ((dwDesiredAccess & kNtGenericExecute) &&
(dwCreationDisposition == kNtOpenExisting ||
dwCreationDisposition == kNtTruncateExisting)) {
dwDesiredAccess &= ~kNtGenericExecute;
goto TryAgain;
}
break;
default:
break;
}
__winerr();
}
if (hHandle == -1) __winerr();
NTTRACE("CreateFile(%#hs, %s, %s, %s, %s, %s, %ld) → %ld% m", lpFileName,
DescribeNtFileAccessFlags(dwDesiredAccess),
DescribeNtFileShareFlags(dwShareMode),
DescribeNtSecurityAttributes(opt_lpSecurityAttributes),
DescribeNtSecurityAttributes(opt_lpSecurity),
DescribeNtCreationDisposition(dwCreationDisposition),
DescribeNtFileFlagAttr(dwFlagsAndAttributes), opt_hTemplateFile,
hHandle);

View file

@ -43,10 +43,12 @@ textwindows int64_t CreateFileMapping(
flProtect, dwMaximumSizeHigh,
dwMaximumSizeLow, opt_lpName);
if (!hHandle) __winerr();
#if 1
NTTRACE("CreateFileMapping(%ld, %s, %s, %'zu, %#hs) → %ld% m", opt_hFile,
DescribeNtSecurityAttributes(opt_lpFileMappingAttributes),
DescribeNtPageFlags(flProtect),
(uint64_t)dwMaximumSizeHigh << 32 | dwMaximumSizeLow, opt_lpName,
hHandle);
#endif
return hHandle;
}

View file

@ -120,10 +120,6 @@ const char *(DescribeTermios)(char buf[N], ssize_t rc, struct termios *tio) {
append(", .c_lflag=%s",
DescribeFlags(b128, 128, kLocal, ARRAYLEN(kLocal), "", tio->c_lflag));
append(", c_cc[VINTR]=%#o", tio->c_cc[VINTR]);
append(", c_cc[VERASE]=%#o", tio->c_cc[VERASE]);
append(", c_cc[VWERASE]=%#o", tio->c_cc[VWERASE]);
append("}");
return buf;

View file

@ -19,31 +19,30 @@
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/errno.h"
#include "libc/intrin/directmap.internal.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/errors.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processmemorycounters.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
textwindows struct DirectMap sys_mmap_nt(void *addr, size_t size, int prot,
int flags, int fd, int64_t off) {
int iscow;
int64_t handle;
uint32_t oldprot;
struct DirectMap dm;
struct ProtectNt fl;
const struct NtSecurityAttributes *sec;
int64_t handle;
if (flags & MAP_ANONYMOUS) {
handle = kNtInvalidHandleValue;
} else {
handle = g_fds.p[fd].handle;
}
const struct NtSecurityAttributes *sec;
if ((flags & MAP_TYPE) != MAP_SHARED) {
sec = 0; // MAP_PRIVATE isn't inherited across fork()
} else {
@ -54,7 +53,8 @@ textwindows struct DirectMap sys_mmap_nt(void *addr, size_t size, int prot,
// later using mprotect(). the workaround is to always request execute
// and then virtualprotect() it away until we actually need it. please
// note that open-nt.c always requests an kNtGenericExecute accessmask
iscow = false;
int iscow = false;
struct ProtectNt fl;
if (handle != -1) {
if ((flags & MAP_TYPE) != MAP_SHARED) {
// windows has cow pages but they can't propagate across fork()
@ -77,16 +77,43 @@ textwindows struct DirectMap sys_mmap_nt(void *addr, size_t size, int prot,
kNtFileMapWrite | kNtFileMapExecute};
}
int e = errno;
struct DirectMap dm;
TryAgain:
if ((dm.maphandle = CreateFileMapping(handle, sec, fl.flags1,
(size + off) >> 32, (size + off), 0))) {
if ((dm.addr = MapViewOfFileEx(dm.maphandle, fl.flags2, off >> 32, off,
size, addr))) {
uint32_t oldprot;
if (VirtualProtect(addr, size, __prot2nt(prot, iscow), &oldprot)) {
return dm;
}
UnmapViewOfFile(dm.addr);
}
CloseHandle(dm.maphandle);
} else if (!(prot & PROT_EXEC) && //
(fl.flags2 & kNtFileMapExecute) && //
GetLastError() == kNtErrorAccessDenied) {
// your file needs to have been O_CREAT'd with exec `mode` bits in
// order to be mapped with executable permission. we always try to
// get execute permission if the kernel will give it to us because
// win32 would otherwise forbid mprotect() from elevating later on
fl.flags2 &= ~kNtFileMapExecute;
switch (fl.flags1) {
case kNtPageExecuteWritecopy:
fl.flags1 = kNtPageWritecopy;
break;
case kNtPageExecuteReadwrite:
fl.flags1 = kNtPageReadwrite;
break;
case kNtPageExecuteRead:
fl.flags1 = kNtPageReadonly;
break;
default:
__builtin_unreachable();
}
errno = e;
goto TryAgain;
}
dm.maphandle = kNtInvalidHandleValue;

View file

@ -19,29 +19,30 @@
#include "libc/dce.h"
#include "libc/intrin/promises.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/nexgen32e/vendor.internal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/nr.h"
/**
* Terminates process, ignoring destructors and atexit() handlers.
*
* When running on bare metal, this function will reboot your computer
* by hosing the interrupt descriptors and triple faulting the system.
*
* Exit codes are narrowed to an unsigned char on most platforms. The
* exceptions would be Windows, NetBSD, and OpenBSD, which should let
* you have larger exit codes.
*
* When running on bare metal, this function will reboot your computer
* by hosing the interrupt descriptors and triple faulting the system.
*
* @asyncsignalsafe
* @threadsafe
* @vforksafe
* @noreturn
*/
wontreturn void _Exit(int exitcode) {
int i;
STRACE("_Exit(%d)", exitcode);
if (!IsWindows() && !IsMetal()) {
// On Linux _Exit1 (exit) must be called in pledge("") mode. If we
@ -84,19 +85,24 @@ wontreturn void _Exit(int exitcode) {
#endif
} else if (IsWindows()) {
uint32_t waitstatus;
// Restoring the CMD.EXE program to its original state is critical.
if (_weaken(__restore_console_win32)) {
_weaken(__restore_console_win32)();
}
// What Microsoft calls an exit code, POSIX calls a status code. See
// also the WEXITSTATUS() and WIFEXITED() macros that POSIX defines.
waitstatus = exitcode;
waitstatus <<= 8;
// "The GetExitCodeProcess function returns a valid error code
// defined by the application only after the thread terminates.
// Therefore, an application should not use kNtStillActive (259) as
// an error code (kNtStillActive is a macro for kNtStatusPending).
// If a thread returns kNtStillActive (259) as an error code, then
// applications that test for that value could interpret it to mean
// that the thread is still running, and continue to test for the
// completion of the thread after the thread has terminated, which
// could put the application into an infinite loop." -Quoth MSDN
if (waitstatus == kNtStillActive) {
// The GetExitCodeProcess function returns a valid error code
// defined by the application only after the thread terminates.
// Therefore, an application should not use STILL_ACTIVE (259) as
// an error code (STILL_ACTIVE is a macro for STATUS_PENDING
// (minwinbase.h)). If a thread returns STILL_ACTIVE (259) as an
// error code, then applications that test for that value could
// interpret it to mean that the thread is still running, and
// continue to test for the completion of the thread after the
// thread has terminated, which could put the application into an
// infinite loop. -Quoth MSDN (see also libc/calls/wait4-nt.c)
waitstatus = 0xc9af3d51u;
}
ExitProcess(waitstatus);

View file

@ -1,27 +0,0 @@
/*-*- 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/dce.h"
#include "libc/runtime/runtime.h"
wontreturn void _Exitr(int exitcode) {
#if SupportsWindows()
_restorewintty();
#endif
_Exit(exitcode);
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#define ShouldUseMsabiAttribute() 1
#include "libc/intrin/kprintf.h"
#include "ape/sections.internal.h"
#include "libc/calls/calls.h"
@ -30,20 +29,29 @@
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/intrin/asmflag.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/getenv.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/nomultics.internal.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/log/internal.h"
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/uart.internal.h"
#include "libc/nt/createfile.h"
#include "libc/nt/enum/accessmask.h"
#include "libc/nt/enum/creationdisposition.h"
#include "libc/nt/enum/fileflagandattributes.h"
#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h"
@ -57,8 +65,13 @@
#include "libc/str/str.h"
#include "libc/str/tab.internal.h"
#include "libc/str/utf16.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/thread/tls.h"
#include "libc/thread/tls2.h"
@ -107,6 +120,17 @@
break; \
}
// clang-format off
__msabi extern typeof(CreateFile) *const __imp_CreateFileW;
__msabi extern typeof(DuplicateHandle) *const __imp_DuplicateHandle;
__msabi extern typeof(GetCurrentProcess) *const __imp_GetCurrentProcess;
__msabi extern typeof(GetEnvironmentVariable) *const __imp_GetEnvironmentVariableW;
__msabi extern typeof(GetLastError) *const __imp_GetLastError;
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
__msabi extern typeof(SetLastError) *const __imp_SetLastError;
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
// clang-format on
long __klog_handle;
extern struct SymbolTable *__symtab;
@ -188,24 +212,180 @@ privileged bool kisdangerous(const void *p) {
if (IsStackFrame(frame)) return false;
if (kismapped(frame)) return false;
}
if (GetStackAddr() + 16384 <= (uintptr_t)p &&
if (GetStackAddr() + GetGuardSize() <= (uintptr_t)p &&
(uintptr_t)p < GetStackAddr() + GetStackSize()) {
return false;
}
return true;
}
privileged static long kloghandle(void) {
if (__klog_handle) {
return __klog_handle;
} else if (!IsWindows()) {
return 2;
} else {
return __imp_GetStdHandle(kNtStdErrorHandle);
}
privileged static void klogclose(long fd) {
#ifdef __x86_64__
long ax = __NR_close;
asm volatile("syscall"
: "+a"(ax), "+D"(fd)
: /* inputs already specified */
: "rsi", "rdx", "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
#elif defined(__aarch64__)
register long x0 asm("x0") = fd;
register int x8 asm("x8") = __NR_close;
register int x16 asm("x16") = __NR_close;
asm volatile("svc\t0" : "+r"(x0) : "r"(x8), "r"(x16) : "x9", "memory");
#else
#error "unsupported architecture"
#endif
}
privileged void _klog(const char *b, size_t n) {
privileged static long klogfcntl(long fd, long cmd, long arg) {
#ifdef __x86_64__
char cf;
long ax = __NR_fcntl;
asm volatile("clc\n\tsyscall"
: CFLAG_CONSTRAINT(cf), "+a"(ax), "+D"(fd), "+S"(cmd), "+d"(arg)
: /* inputs already specified */
: "rcx", "r8", "r9", "r10", "r11", "memory");
if (cf) ax = -ax;
return ax;
#elif defined(__aarch64__)
register long x0 asm("x0") = fd;
register long x1 asm("x1") = cmd;
register long x2 asm("x2") = arg;
register int x8 asm("x8") = __NR_fcntl;
register int x16 asm("x16") = __NR_fcntl;
asm volatile("mov\tx9,0\n\t" // clear carry flag
"adds\tx9,x9,0\n\t" // clear carry flag
"svc\t0\n\t"
"bcs\t1f\n\t"
"b\t2f\n1:\t"
"neg\tx0,x0\n2:"
: "+r"(x0)
: "r"(x1), "r"(x2), "r"(x8), "r"(x16)
: "x9", "memory");
return x0;
#else
#error "unsupported architecture"
#endif
}
privileged static long klogopen(const char *path) {
long dirfd = AT_FDCWD;
long flags = O_WRONLY | O_CREAT | O_APPEND;
long mode = 0600;
#ifdef __x86_64__
char cf;
long ax = __NR_openat;
register long r10 asm("r10") = mode;
asm volatile(CFLAG_ASM("clc\n\tsyscall")
: CFLAG_CONSTRAINT(cf), "+a"(ax), "+D"(dirfd), "+S"(path),
"+d"(flags), "+r"(r10)
: /* inputs already specified */
: "rcx", "r8", "r9", "r11", "memory");
if (cf) ax = -ax;
return ax;
#elif defined(__aarch64__)
register long x0 asm("x0") = dirfd;
register long x1 asm("x1") = (long)path;
register long x2 asm("x2") = flags;
register long x3 asm("x3") = mode;
register int x8 asm("x8") = __NR_openat;
register int x16 asm("x16") = __NR_openat;
asm volatile("mov\tx9,0\n\t" // clear carry flag
"adds\tx9,x9,0\n\t" // clear carry flag
"svc\t0\n\t"
"bcs\t1f\n\t"
"b\t2f\n1:\t"
"neg\tx0,x0\n2:"
: "+r"(x0)
: "r"(x1), "r"(x2), "r"(x3), "r"(x8), "r"(x16)
: "x9", "memory");
return x0;
#else
#error "unsupported architecture"
#endif
}
// returns log handle or -1 if logging shouldn't happen
privileged long kloghandle(void) {
// kprintf() needs to own a file descriptor in case apps closes stderr
// our close() and dup() implementations will trigger this initializer
// to minimize a chance that the user accidentally closes their logger
// while at the same time, avoiding a mandatory initialization syscall
if (!__klog_handle) {
long hand;
// setting KPRINTF_LOG="/tmp/foo.log" will override stderr
// setting KPRINTF_LOG="INTEGER" logs to a file descriptor
// setting KPRINTF_LOG="" shall disable kprintf altogether
if (IsMetal()) {
hand = STDERR_FILENO;
} else if (IsWindows()) {
uint32_t e, n;
const char16_t path[512];
e = __imp_GetLastError();
n = __imp_GetEnvironmentVariableW(u"KPRINTF_LOG", path, 512);
if (!n && __imp_GetLastError() == kNtErrorEnvvarNotFound) {
long proc;
proc = __imp_GetCurrentProcess();
hand = __imp_GetStdHandle(kNtStdErrorHandle);
__imp_DuplicateHandle(proc, hand, proc, &hand, 0, true,
kNtDuplicateSameAccess);
} else if (n && n < 512) {
hand = __imp_CreateFileW(
path, kNtGenericWrite | kNtFileAppendData,
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
&kNtIsInheritable, kNtOpenAlways, kNtFileAttributeNormal, 0);
} else {
hand = -1; // KPRINTF_LOG was empty string or too long
}
__imp_SetLastError(e);
} else {
long fd, fd2;
bool closefd;
const char *path;
if (!__NR_write || !__envp) {
// it's too early in the initialization process for kprintf
return -1;
}
path = __getenv(__envp, "KPRINTF_LOG").s;
closefd = false;
if (!path) {
fd = STDERR_FILENO;
} else if (*path) {
const char *p;
for (fd = 0, p = path; *p; ++p) {
if ('0' <= *p && *p <= '9') {
fd *= 10;
fd += *p - '0';
} else {
fd = klogopen(path);
closefd = true;
break;
}
}
} else {
fd = -1;
}
if (fd >= 0) {
// avoid interfering with hard-coded assumptions about fds
if ((fd2 = klogfcntl(fd, F_DUPFD, 100)) >= 0) {
klogfcntl(fd2, F_SETFD, FD_CLOEXEC);
if (closefd) {
klogclose(fd);
}
} else {
// RLIMIT_NOFILE was probably too low for safe duplicate
fd2 = fd;
}
} else {
fd2 = -1;
}
hand = fd2;
}
__klog_handle = hand;
}
return __klog_handle;
}
privileged void klog(const char *b, size_t n) {
#ifdef __x86_64__
int e;
long h;
@ -215,7 +395,9 @@ privileged void _klog(const char *b, size_t n) {
uint32_t wrote;
unsigned char al;
long rax, rdi, rsi, rdx;
h = kloghandle();
if ((h = kloghandle()) == -1) {
return;
}
if (IsWindows()) {
e = __imp_GetLastError();
__imp_WriteFile(h, b, n, &wrote, 0);
@ -243,7 +425,7 @@ privileged void _klog(const char *b, size_t n) {
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
}
#elif defined(__aarch64__)
register long r0 asm("x0") = (long)kloghandle();
register long r0 asm("x0") = kloghandle();
register long r1 asm("x1") = (long)b;
register long r2 asm("x2") = (long)n;
register long r8 asm("x8") = (long)__NR_write;
@ -262,7 +444,6 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
va_list va) {
int si, y;
wint_t t, u;
char errnum[12];
const char *abet;
signed char type;
const char *s, *f;
@ -545,11 +726,16 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt,
break;
} else {
type = 0;
#if defined(SYSDEBUG) && _NTTRACE
strerror_r(e, z, sizeof(z));
s = z;
#else
s = _strerrno(e);
if (!s) {
FormatInt32(errnum, e);
s = errnum;
FormatInt32(z, e);
s = z;
}
#endif
goto FormatString;
}
}
@ -851,7 +1037,7 @@ privileged void kvprintf(const char *fmt, va_list v) {
size_t n;
char b[4000];
n = kformat(b, sizeof(b), fmt, v);
_klog(b, MIN(n, sizeof(b) - 1));
klog(b, MIN(n, sizeof(b) - 1));
}
/**
@ -926,9 +1112,9 @@ privileged void kvprintf(const char *fmt, va_list v) {
* @vforksafe
*/
privileged void kprintf(const char *fmt, ...) {
/* system call support runtime depends on this function */
/* function tracing runtime depends on this function */
/* asan runtime depends on this function */
// system call support runtime depends on this function
// function tracing runtime depends on this function
// asan runtime depends on this function
va_list v;
va_start(v, fmt);
kvprintf(fmt, v);

View file

@ -2,10 +2,12 @@
#define COSMOPOLITAN_LIBC_INTRIN_KPRINTF_H_
#ifdef _COSMO_SOURCE
#define klog __klog
#define kprintf __kprintf
#define ksnprintf __ksnprintf
#define kvprintf __kvprintf
#define kvsnprintf __kvsnprintf
#define kloghandle __kloghandle
#define kisdangerous __kisdangerous
#if !(__ASSEMBLER__ + __LINKER__ + 0)
@ -21,8 +23,10 @@ void kprintf(const char *, ...);
size_t ksnprintf(char *, size_t, const char *, ...);
void kvprintf(const char *, va_list);
size_t kvsnprintf(char *, size_t, const char *, va_list);
bool kisdangerous(const void *);
void _klog(const char *, size_t);
void klog(const char *, size_t);
long kloghandle(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -31,7 +31,6 @@
wontreturn void quick_exit(int exitcode) {
const uintptr_t *p;
STRACE("quick_exit(%d)", exitcode);
_restorewintty();
if (_weaken(fflush)) {
_weaken(fflush)(0);
}

View file

@ -1,47 +0,0 @@
/*-*- 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/dce.h"
#include "libc/nt/console.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/internal.h"
__msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId;
__msabi extern typeof(SetConsoleMode) *const __imp_SetConsoleMode;
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
extern uint32_t __pid_exec;
const signed char kNtConsoleHandles[3] = {
(signed char)kNtStdInputHandle,
(signed char)kNtStdOutputHandle,
(signed char)kNtStdErrorHandle,
};
// Puts cmd.exe gui back the way it was.
void _restorewintty(void) {
int i;
if (!IsWindows()) return;
if (__imp_GetCurrentProcessId() != __pid_exec) return;
for (i = 0; i < 3; ++i) {
__imp_SetConsoleMode(__imp_GetStdHandle(kNtConsoleHandles[i]),
__ntconsolemode[i]);
}
}

View file

@ -16,9 +16,17 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#define ShouldUseMsabiAttribute() 1
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/str/str.h"
// clang-format off
#if defined(SYSDEBUG) && _NTTRACE
dontasan dontubsan privileged
#endif
/**
* Converts errno value to string.
@ -27,6 +35,6 @@
* @return 0 on success, or error code
*/
int strerror_r(int err, char *buf, size_t size) {
int winerr = IsWindows() ? GetLastError() : 0;
int winerr = IsWindows() ? __imp_GetLastError() : 0;
return strerror_wr(err, winerr, buf, size);
}

View file

@ -21,11 +21,17 @@
#include "libc/fmt/fmt.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/enum/formatmessageflags.h"
#include "libc/nt/enum/lang.h"
#include "libc/nt/process.h"
#include "libc/str/str.h"
// clang-format off
#if defined(SYSDEBUG) && _NTTRACE
dontasan dontubsan privileged
#endif
/**
* Converts errno value to string with explicit windows errno too.
@ -53,7 +59,7 @@ int strerror_wr(int err, uint32_t winerr, char *buf, size_t size) {
for (; (c = *sym++); --size)
if (size > 1) *buf++ = c;
if (size) *buf = 0;
} else if (!IsWindows() || ((err == winerr || !winerr) && !wanting)) {
} else if (!IsWindows() /* || ((err == winerr || !winerr) && !wanting) */) {
ksnprintf(buf, size, "%s/%d/%s", sym, err, msg);
} else {
if ((n = __imp_FormatMessageW(

View file

@ -206,7 +206,7 @@ static void __ubsan_exit(void) {
kprintf("your ubsan runtime needs\n"
"\t__static_yoink(\"__die\");\n"
"in order to show you backtraces\n");
_Exitr(99);
_Exit(99);
}
static char *__ubsan_stpcpy(char *d, const char *s) {

View file

@ -1,7 +1,7 @@
/*-*- 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 2021 Justine Alexandra Roberts Tunney
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
@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/runtime/internal.h"
#include "libc/atomic.h"
#include "libc/calls/internal.h"
uint32_t __ntconsolemode[3];
atomic_int __umask;

View file

@ -148,11 +148,11 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
strlen(" (discriminator ") - 1)) &&
(p3 = memchr(p2, '\n', got - (p2 - p1)))) {
if (p3 > p2 && p3[-1] == '\r') --p3;
_klog(p1, p2 - p1);
klog(p1, p2 - p1);
got -= p3 - p1;
p1 += p3 - p1;
} else {
_klog(p1, got);
klog(p1, got);
break;
}
}

View file

@ -49,7 +49,7 @@ static relegated wontreturn void __check_fail_ndebug(uint64_t want, //
}
kprintf("\n");
if (_weaken(__die)) _weaken(__die)();
_Exitr(68);
_Exit(68);
}
void __check_fail_eq(uint64_t want, uint64_t got, const char *file, int line,

View file

@ -55,10 +55,10 @@ relegated wontreturn void __die(void) {
DebugBreak();
}
ShowBacktrace(2, __builtin_frame_address(0));
_Exitr(77);
_Exit(77);
} else if (owner == me) {
kprintf("die failed while dying\n");
_Exitr(79);
_Exit(79);
} else {
_Exit1(79);
}

View file

@ -115,6 +115,6 @@ dontasan void CheckForMemoryLeaks(void) {
/* __print_maps(); */
/* PrintSystemMappings(2); */
/* PrintGarbage(); */
_Exitr(78);
_Exit(78);
}
}

View file

@ -41,7 +41,6 @@ void _meminfo(int); /* shows malloc statistics &c. */
void _memsummary(int); /* light version of same thing */
bool IsTerminalInarticulate(void) nosideeffect;
const char *commandvenv(const char *, const char *);
int LogKprintfToFile(const char *);
const char *GetAddr2linePath(void);
const char *GetGdbPath(void);
bool32 IsDebuggerPresent(bool);

View file

@ -20,5 +20,5 @@
#include "libc/runtime/runtime.h"
wontreturn void _log_exit(int exitcode) {
_Exitr(exitcode);
_Exit(exitcode);
}

View file

@ -1,57 +0,0 @@
/*-*- 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/internal.h"
#include "libc/dce.h"
#include "libc/log/log.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/o.h"
static int __klog_fd;
extern long __klog_handle;
/**
* Redirects kprintf(), `--strace`, etc. output to file.
*
* @param path is filename to append to; if null is specified then
* this file logging facility will be disabled; when the empty
* string is specified, then the default path shall be used
* @return 0 on success, or -1 w/ errno
*/
int LogKprintfToFile(const char *path) {
int fd, dd;
if (!path) {
if (__klog_fd) {
__klog_handle = 0;
close(__klog_fd);
__klog_fd = 0;
}
return 0;
}
if (!*path) path = "/tmp/kprintf.log";
fd = open(path, O_WRONLY | O_APPEND | O_CREAT, 0644);
if (fd == -1) return -1;
dd = fcntl(fd, F_DUPFD_CLOEXEC, 100);
close(fd);
if (dd == -1) return -1;
if (__klog_fd) close(__klog_fd);
__klog_fd = dd;
__klog_handle = IsWindows() ? g_fds.p[dd].handle : dd;
return 0;
}

View file

@ -239,11 +239,11 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si,
p = ShowGeneralRegisters(p, ctx);
p = ShowSseRegisters(p, ctx);
*p++ = '\n';
_klog(buf, p - buf);
klog(buf, p - buf);
ShowFunctionCalls(ctx);
} else {
*p++ = '\n';
_klog(buf, p - buf);
klog(buf, p - buf);
}
kprintf("\n");
if (!IsWindows()) __print_maps();
@ -275,7 +275,7 @@ static wontreturn relegated dontinstrument void __minicrash(int sig,
kind, sig, __argv[0], ctx ? ctx->uc_mcontext.rip : 0,
ctx ? ctx->uc_mcontext.rsp : 0, ctx ? ctx->uc_mcontext.rbp : 0, __pid,
__tls_enabled ? __get_tls()->tib_tid : sys_gettid());
_Exitr(119);
_Exit(119);
}
/**
@ -334,7 +334,7 @@ relegated void __oncrash_amd64(int sig, struct siginfo *si, void *arg) {
if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) {
__restore_tty();
ShowCrashReport(err, sig, si, ctx);
_Exitr(128 + sig);
_Exit(128 + sig);
}
atomic_store_explicit(&once, 0, memory_order_relaxed);
} else {

View file

@ -1,31 +0,0 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi
Copyright 2020 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/macros.internal.h"
#include "libc/notice.inc"
.initbss 300,_init_envp
// Global variable holding _start(envp) parameter.
__envp: .quad 0
.endobj __envp,globl
.previous
.init.start 300,_init_envp
mov %r14,%rax
stosq
.init.end 300,_init_envp

View file

@ -17,8 +17,5 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/runtime/runtime.h"
#ifndef __x86_64__
char **__envp;
#endif /* __x86_64__ */

View file

@ -39,13 +39,13 @@ $(LIBC_NEXGEN32E_A).pkg: \
$(LIBC_NEXGEN32E_A_OBJS) \
$(foreach x,$(LIBC_NEXGEN32E_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/libc/nexgen32e/envp.o \
o/$(MODE)/libc/nexgen32e/argc2.o \
o/$(MODE)/libc/nexgen32e/argv2.o \
o/$(MODE)/libc/nexgen32e/auxv2.o \
o/$(MODE)/libc/nexgen32e/cescapec.o \
o/$(MODE)/libc/nexgen32e/crc32init.o \
o/$(MODE)/libc/nexgen32e/environ2.o \
o/$(MODE)/libc/nexgen32e/envp2.o \
o/$(MODE)/libc/nexgen32e/kbase36.o \
o/$(MODE)/libc/nexgen32e/ktens.o \
o/$(MODE)/libc/nexgen32e/ktolower.o \

View file

@ -77,8 +77,8 @@ bool32 SetNamedPipeHandleState(int64_t hNamedPipe, uint32_t *lpMode,
uint32_t *lpCollectDataTimeout);
bool32 PeekNamedPipe(int64_t hNamedPipe, void *lpBuffer, uint32_t nBufferSize,
uint32_t *lpBytesRead, uint32_t *lpTotalBytesAvail,
uint32_t *lpBytesLeftThisMessage);
uint32_t *opt_lpBytesRead, uint32_t *opt_lpTotalBytesAvail,
uint32_t *opt_lpBytesLeftThisMessage);
bool32 TransactNamedPipe(int64_t hNamedPipe, void *lpInBuffer,
uint32_t nInBufferSize, void *lpOutBuffer,

View file

@ -0,0 +1,2 @@
#include "libc/nt/codegen.h"
.imp kernel32,__imp_CreateFileA,CreateFileA

View file

@ -13,6 +13,7 @@ imp '' CloseHandle kernel32 1
imp '' CreateDirectoryW kernel32 2
imp '' CreateFileMappingNumaW kernel32 7
imp '' CreateFileMappingW kernel32 6
imp '' CreateFileA kernel32 7
imp '' CreateFileW kernel32 7
imp '' CreateNamedPipeW kernel32 8
imp '' CreatePipe kernel32 4

View file

@ -67,11 +67,6 @@ extern char ape_stack_prot[] __attribute__((__weak__));
extern pthread_mutex_t __mmi_lock_obj;
extern int hostos asm("__hostos");
void cosmo2(int, char **, char **, unsigned long *) wontreturn;
void __switch_stacks(int, char **, char **, unsigned long *,
void (*)(int, char **, char **, unsigned long *),
void *) wontreturn;
static const char *DecodeMagnum(const char *p, long *r) {
int k = 0;
unsigned long c, x = 0;
@ -96,6 +91,15 @@ wontreturn textstartup void cosmo(long *sp, struct Syslib *m1) {
unsigned long *auxv = (unsigned long *)(sp + 1 + argc + 1);
while (*auxv++) donothing;
// set helpful globals
__argc = argc;
__argv = argv;
__envp = envp;
__auxv = auxv;
environ = envp;
program_invocation_name = argv[0];
__oldstack = (intptr_t)sp;
// detect apple m1 environment
char *magnums;
if (SupportsXnu() && (__syslib = m1)) {
@ -134,16 +138,7 @@ wontreturn textstartup void cosmo(long *sp, struct Syslib *m1) {
sys_sigaction(SIGSYS, act, 0, 8, 0);
}
// set helpful globals
__argc = argc;
__argv = argv;
__envp = envp;
__auxv = auxv;
environ = envp;
program_invocation_name = argv[0];
// needed by kisdangerous()
__oldstack = (intptr_t)sp;
__pid = sys_getpid().ax;
// initialize memory manager

View file

@ -16,11 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
/**
* Exits process with grace.
@ -45,9 +43,5 @@ wontreturn void exit(int exitcode) {
for (p = __fini_array_end; p > __fini_array_start;) {
((void (*)(void))(*--p))();
}
#if SupportsWindows()
_Exitr(exitcode);
#else
_Exit(exitcode);
#endif
}

View file

@ -42,6 +42,7 @@
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/errors.h"
#include "libc/nt/ipc.h"
#include "libc/nt/memory.h"
#include "libc/nt/process.h"
@ -145,18 +146,38 @@ static textwindows dontinline void ReadOrDie(int64_t h, void *buf, size_t n) {
static textwindows int64_t MapOrDie(uint32_t prot, uint64_t size) {
int64_t h;
if ((h = CreateFileMapping(-1, 0, prot, size >> 32, size, 0))) {
return h;
} else {
for (;;) {
if ((h = CreateFileMapping(-1, 0, prot, size >> 32, size, 0))) {
return h;
}
if (GetLastError() == kNtErrorAccessDenied) {
switch (prot) {
case kNtPageExecuteWritecopy:
prot = kNtPageWritecopy;
continue;
case kNtPageExecuteReadwrite:
prot = kNtPageReadwrite;
continue;
case kNtPageExecuteRead:
prot = kNtPageReadonly;
continue;
default:
break;
}
}
AbortFork("MapOrDie");
}
}
static textwindows void ViewOrDie(int64_t h, uint32_t access, size_t pos,
size_t size, void *base) {
void *got;
got = MapViewOfFileEx(h, access, pos >> 32, pos, size, base);
if (!got || (base && got != base)) {
TryAgain:
if (!MapViewOfFileEx(h, access, pos >> 32, pos, size, base)) {
if ((access & kNtFileMapExecute) &&
GetLastError() == kNtErrorAccessDenied) {
access &= ~kNtFileMapExecute;
goto TryAgain;
}
AbortFork("ViewOrDie");
}
}

View file

@ -16,7 +16,6 @@ COSMOPOLITAN_C_START_
extern int __pid;
extern char __runlevel;
extern int ftrace_stackdigs;
extern uint32_t __ntconsolemode[3];
extern const char v_ntsubsystem[] __attribute__((__weak__));
extern const uintptr_t __fini_array_end[] __attribute__((__weak__));
extern const uintptr_t __fini_array_start[] __attribute__((__weak__));
@ -38,6 +37,7 @@ void __morph_tls(void);
void __enable_tls(void);
void __enable_threads(void);
void *__cxa_finalize(void *);
void __restore_console_win32(void);
void __stack_chk_fail(void) wontreturn relegated;
void __stack_chk_fail_local(void) wontreturn relegated;
void __asan_init(int, char **, char **, intptr_t *);

View file

@ -74,7 +74,7 @@ static inline pureconst unsigned long __rounddown2pow(unsigned long x) {
static wontreturn void __mmap_die(const char *s) {
if (_weaken(__die)) _weaken(__die)();
STRACE("%s %m", s);
_Exitr(199);
_Exit(199);
}
static dontasan inline bool __overlaps_existing_mapping(char *p, size_t n) {

View file

@ -480,13 +480,15 @@ dontasan textstartup void __printargs(const char *prologue) {
} else {
PRINT(" - stderr");
}
kprintf(prologue);
errno = 0;
kprintf(" isatty = %d% m\n", isatty(i));
PRINT(" isatty = %d% m", isatty(i));
if (!tcgetwinsize(i, &ws)) {
kprintf(" ws_row = %d\n", ws.ws_row);
kprintf(" ws_col = %d\n", ws.ws_col);
PRINT(" ws_row = %d", ws.ws_row);
PRINT(" ws_col = %d", ws.ws_col);
} else {
PRINT(" tcgetwinsize = %s", strerror(errno));
}
kprintf(prologue);
kprintf(" c_iflag =");
if (termios.c_iflag & IGNBRK) kprintf(" IGNBRK");
if (termios.c_iflag & BRKINT) kprintf(" BRKINT");

View file

@ -110,9 +110,7 @@ axdx_t setlongerjmp(jmp_buf)
libcesque returnstwice paramsnonnull();
void longerjmp(jmp_buf, intptr_t) libcesque wontreturn paramsnonnull();
void __warn_if_powersave(void);
void _Exitr(int) libcesque wontreturn;
void _Exit1(int) libcesque wontreturn;
void _restorewintty(void);
void __paginate(int, const char *);
/* memory management */
void _weakfree(void *);

View file

@ -22,6 +22,7 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/getenv.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/log/libfatal.internal.h"
#include "libc/macros.internal.h"
@ -59,6 +60,7 @@
// clang-format off
__msabi extern typeof(CreateFileMapping) *const __imp_CreateFileMappingW;
__msabi extern typeof(DuplicateHandle) *const __imp_DuplicateHandle;
__msabi extern typeof(ExitProcess) *const __imp_ExitProcess;
__msabi extern typeof(FreeEnvironmentStrings) *const __imp_FreeEnvironmentStringsW;
__msabi extern typeof(GetConsoleMode) *const __imp_GetConsoleMode;
__msabi extern typeof(GetCurrentProcess) *const __imp_GetCurrentProcess;
@ -71,11 +73,17 @@ __msabi extern typeof(SetConsoleMode) *const __imp_SetConsoleMode;
__msabi extern typeof(SetConsoleOutputCP) *const __imp_SetConsoleOutputCP;
__msabi extern typeof(SetStdHandle) *const __imp_SetStdHandle;
__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
// clang-format on
extern const signed char kNtConsoleHandles[3];
extern void cosmo(int, char **, char **, long (*)[2]) wontreturn;
static const signed char kNtStdio[3] = {
(signed char)kNtStdInputHandle,
(signed char)kNtStdOutputHandle,
(signed char)kNtStdErrorHandle,
};
static const short kConsoleModes[3] = {
kNtEnableProcessedInput | kNtEnableLineInput | kNtEnableEchoInput |
kNtEnableMouseInput | kNtEnableQuickEditMode | kNtEnableExtendedFlags |
@ -87,12 +95,25 @@ static const short kConsoleModes[3] = {
kNtEnableVirtualTerminalProcessing,
};
static uint32_t __init_pid;
static uint32_t __console_mode[3];
// implements all win32 apis on non-windows hosts
__msabi long __win32_oops(void) {
__msabi long __oops_win32(void) {
assert(!"win32 api called on non-windows host");
return 0;
}
// called by _exit to undo our config changes to cmd.exe
// it must never ever be called from forked subprocesses
void __restore_console_win32(void) {
if (__imp_GetCurrentProcessId() == __init_pid) {
for (int i = 0; i < 3; ++i) {
__imp_SetConsoleMode(__imp_GetStdHandle(kNtStdio[i]), __console_mode[i]);
}
}
}
// https://nullprogram.com/blog/2022/02/18/
__msabi static inline char16_t *MyCommandLine(void) {
void *cmd;
@ -106,14 +127,14 @@ __msabi static inline char16_t *MyCommandLine(void) {
// this ensures close(1) won't accidentally close(2) for example
__msabi static textwindows void DeduplicateStdioHandles(void) {
for (long i = 0; i < 3; ++i) {
int64_t h1 = __imp_GetStdHandle(kNtConsoleHandles[i]);
int64_t h1 = __imp_GetStdHandle(kNtStdio[i]);
for (long j = i + 1; j < 3; ++j) {
int64_t h2 = __imp_GetStdHandle(kNtConsoleHandles[j]);
int64_t h2 = __imp_GetStdHandle(kNtStdio[j]);
if (h1 == h2) {
int64_t h3, proc = __imp_GetCurrentProcess();
__imp_DuplicateHandle(proc, h2, proc, &h3, 0, true,
kNtDuplicateSameAccess);
__imp_SetStdHandle(kNtConsoleHandles[j], h3);
__imp_SetStdHandle(kNtStdio[j], h3);
}
}
}
@ -123,14 +144,15 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) {
size_t stacksize;
struct WinArgs *wa;
uintptr_t stackaddr;
__init_pid = __pid;
__oldstack = (intptr_t)__builtin_frame_address(0);
if (NtGetPeb()->OSMajorVersion >= 10 &&
(intptr_t)v_ntsubsystem == kNtImageSubsystemWindowsCui) {
__imp_SetConsoleCP(kNtCpUtf8);
__imp_SetConsoleOutputCP(kNtCpUtf8);
for (int i = 0; i < 3; ++i) {
int64_t hand = __imp_GetStdHandle(kNtConsoleHandles[i]);
__imp_GetConsoleMode(hand, __ntconsolemode + i);
int64_t hand = __imp_GetStdHandle(kNtStdio[i]);
__imp_GetConsoleMode(hand, __console_mode + i);
__imp_SetConsoleMode(hand, kConsoleModes[i]);
}
}
@ -167,6 +189,7 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) {
GetDosEnviron(env16, wa->envblock, ARRAYLEN(wa->envblock) - 8, wa->envp,
ARRAYLEN(wa->envp) - 1);
__imp_FreeEnvironmentStringsW(env16);
__envp = &wa->envp[0];
_jmpstack((char *)(stackaddr + (stacksize - sizeof(struct WinArgs))), cosmo,
count, wa->argv, wa->envp, wa->auxv);
}
@ -175,8 +198,9 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
const char *lpCmdLine, int64_t nCmdShow) {
const char16_t *cmdline;
extern char os asm("__hostos");
os = _HOSTWINDOWS; /* madness https://news.ycombinator.com/item?id=21019722 */
os = _HOSTWINDOWS; // madness https://news.ycombinator.com/item?id=21019722
kStartTsc = rdtsc();
__umask = 077;
__pid = __imp_GetCurrentProcessId();
DeduplicateStdioHandles();
if (_weaken(WinMainStdin)) {
@ -184,7 +208,7 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
}
cmdline = MyCommandLine();
#ifdef SYSDEBUG
/* sloppy flag-only check for early initialization */
// sloppy flag-only check for early initialization
if (__strstr16(cmdline, u"--strace")) ++__strace;
#endif
if (_weaken(WinSockInit)) {

View file

@ -16,9 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/metalfile.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/cosmo.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kmalloc.h"
@ -28,9 +30,11 @@
#include "libc/mem/alg.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/posix.h"
#include "libc/sysv/consts/prot.h"
#include "libc/thread/thread.h"
#include "libc/zip.internal.h"
@ -39,23 +43,33 @@
__static_yoink(APE_COM_NAME);
#endif
static uint64_t __zipos_get_min_offset(const uint8_t *map,
const uint8_t *cdir) {
uint64_t i, n, c, r, o;
static struct Zipos __zipos;
static atomic_uint __zipos_once;
static void __zipos_dismiss(const uint8_t *map, const uint8_t *cdir, long pg) {
uint64_t i, n, c, ef, lf, mo, lo, hi;
// determine the byte range of zip file content (excluding central dir)
c = GetZipCdirOffset(cdir);
n = GetZipCdirRecords(cdir);
for (r = c, i = 0; i < n; ++i, c += ZIP_CFILE_HDRSIZE(map + c)) {
o = GetZipCfileOffset(map + c);
if (o < r) r = o;
for (lo = c, hi = i = 0; i < n; ++i, c += ZIP_CFILE_HDRSIZE(map + c)) {
lf = GetZipCfileOffset(map + c);
if (lf < lo) lo = lf;
ef = lf + ZIP_LFILE_HDRSIZE(map + lf) + GetZipLfileCompressedSize(map + lf);
if (ef > hi) hi = ef;
}
return r;
}
static void __zipos_munmap_unneeded(const uint8_t *map, const uint8_t *cdir) {
uint64_t n;
n = __zipos_get_min_offset(map, cdir);
n = ROUNDDOWN(n, FRAMESIZE);
if (n) munmap(map, n);
// unmap the executable portion beneath the local files
mo = ROUNDDOWN(lo, FRAMESIZE);
if (mo) munmap(map, mo);
// this is supposed to reduce our rss usage but does it
pg = getauxval(AT_PAGESZ);
lo = ROUNDDOWN(lo, pg);
hi = MIN(ROUNDUP(hi, pg), ROUNDDOWN(c, pg));
if (hi > lo) {
posix_madvise(map + lo, hi - lo, POSIX_MADV_DONTNEED);
}
}
static int __zipos_compare_names(const void *a, const void *b, void *c) {
@ -88,75 +102,67 @@ static void __zipos_generate_index(struct Zipos *zipos) {
__zipos_compare_names, zipos);
}
static void __zipos_init(void) {
char *endptr;
const char *s;
struct stat st;
int x, fd, err, msg;
uint8_t *map, *cdir;
const char *progpath;
if (!(s = getenv("COSMOPOLITAN_DISABLE_ZIPOS"))) {
// this environment variable may be a filename or file descriptor
if ((progpath = getenv("COSMOPOLITAN_INIT_ZIPOS")) &&
(x = strtol(progpath, &endptr, 10)) >= 0 && !*endptr) {
fd = x;
} else {
fd = -1;
}
if (fd != -1 || PLEDGED(RPATH)) {
if (fd == -1) {
if (!progpath) {
progpath = GetProgramExecutableName();
}
fd = open(progpath, O_RDONLY);
}
if (fd != -1) {
if (!fstat(fd, &st) && (map = mmap(0, st.st_size, PROT_READ, MAP_SHARED,
fd, 0)) != MAP_FAILED) {
if ((cdir = GetZipEocd(map, st.st_size, &err))) {
long pagesz = getauxval(AT_PAGESZ);
__zipos_dismiss(map, cdir, pagesz);
__zipos.map = map;
__zipos.cdir = cdir;
__zipos.dev = st.st_ino;
__zipos.pagesz = pagesz;
__zipos_generate_index(&__zipos);
msg = kZipOk;
} else {
munmap(map, st.st_size);
msg = !cdir ? err : kZipErrorRaceCondition;
}
} else {
msg = kZipErrorMapFailed;
}
close(fd);
} else {
msg = kZipErrorOpenFailed;
}
} else {
msg = -666;
}
} else {
progpath = 0;
msg = -777;
}
STRACE("__zipos_get(%#s) → %d% m", progpath, msg);
}
/**
* Returns pointer to zip central directory of current executable.
* @asyncsignalsafe
* @threadsafe
*/
struct Zipos *__zipos_get(void) {
char *endptr;
const char *s;
struct stat st;
static bool once;
struct Zipos *res;
int x, fd, err, msg;
uint8_t *map, *cdir;
const char *progpath;
static struct Zipos zipos;
__zipos_lock();
if (!once) {
if (!(s = getenv("COSMOPOLITAN_DISABLE_ZIPOS"))) {
// this environment variable may be a filename or file descriptor
if ((progpath = getenv("COSMOPOLITAN_INIT_ZIPOS")) &&
(x = strtol(progpath, &endptr, 10)) >= 0 && !*endptr) {
fd = x;
} else {
fd = -1;
}
if (fd != -1 || PLEDGED(RPATH)) {
if (fd == -1) {
if (!progpath) {
progpath = GetProgramExecutableName();
}
fd = open(progpath, O_RDONLY);
}
if (fd != -1) {
if (!fstat(fd, &st) &&
(map = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) !=
MAP_FAILED) {
if ((cdir = GetZipEocd(map, st.st_size, &err))) {
__zipos_munmap_unneeded(map, cdir);
zipos.map = map;
zipos.cdir = cdir;
zipos.dev = st.st_ino;
__zipos_generate_index(&zipos);
msg = kZipOk;
} else {
munmap(map, st.st_size);
msg = !cdir ? err : kZipErrorRaceCondition;
}
} else {
msg = kZipErrorMapFailed;
}
close(fd);
} else {
msg = kZipErrorOpenFailed;
}
} else {
msg = -666;
}
} else {
progpath = 0;
msg = -777;
}
STRACE("__zipos_get(%#s) → %d% m", progpath, msg);
once = true;
}
__zipos_unlock();
if (zipos.cdir) {
res = &zipos;
} else {
res = 0;
}
return res;
cosmo_once(&__zipos_once, __zipos_init);
return __zipos.cdir ? &__zipos : 0;
}

View file

@ -1,39 +0,0 @@
/*-*- 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/thread/thread.h"
#include "libc/runtime/zipos.internal.h"
static pthread_mutex_t __zipos_lock_obj;
void(__zipos_lock)(void) {
pthread_mutex_lock(&__zipos_lock_obj);
}
void(__zipos_unlock)(void) {
pthread_mutex_unlock(&__zipos_lock_obj);
}
void __zipos_funlock(void) {
pthread_mutex_init(&__zipos_lock_obj, 0);
}
__attribute__((__constructor__)) static void __zipos_init(void) {
__zipos_funlock();
pthread_atfork(__zipos_lock, __zipos_unlock, __zipos_funlock);
}

View file

@ -45,21 +45,39 @@
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/zip.internal.h"
static char *mapend;
static size_t maptotal;
static char *__zipos_mapend;
static size_t __zipos_maptotal;
static pthread_mutex_t __zipos_lock_obj;
static void __zipos_lock(void) {
if (__threaded) {
pthread_mutex_lock(&__zipos_lock_obj);
}
}
static void __zipos_unlock(void) {
if (__threaded) {
pthread_mutex_unlock(&__zipos_lock_obj);
}
}
static void __zipos_funlock(void) {
pthread_mutex_init(&__zipos_lock_obj, 0);
}
static void *__zipos_mmap_space(size_t mapsize) {
char *start;
size_t offset;
unassert(mapsize);
offset = maptotal;
maptotal += mapsize;
offset = __zipos_maptotal;
__zipos_maptotal += mapsize;
start = (char *)kMemtrackZiposStart;
if (!mapend) mapend = start;
mapend = _extend(start, maptotal, mapend, MAP_PRIVATE,
kMemtrackZiposStart + kMemtrackZiposSize);
if (!__zipos_mapend) __zipos_mapend = start;
__zipos_mapend = _extend(start, __zipos_maptotal, __zipos_mapend, MAP_PRIVATE,
kMemtrackZiposStart + kMemtrackZiposSize);
return start + offset;
}
@ -68,7 +86,6 @@ void __zipos_free(struct ZiposHandle *h) {
__asan_poison((char *)h + sizeof(struct ZiposHandle),
h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree);
}
pthread_mutex_destroy(&h->lock);
__zipos_lock();
do h->next = h->zipos->freelist;
while (!_cmpxchg(&h->zipos->freelist, h->next, h));
@ -105,7 +122,6 @@ StartOver:
h->size = size;
h->zipos = zipos;
h->mapsize = mapsize;
pthread_mutex_init(&h->lock, 0);
}
return h;
}
@ -197,16 +213,40 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, int flags,
return -1;
}
static int __zipos_open_impl(struct ZiposUri *name, int flags) {
struct Zipos *zipos;
/**
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
*
* @param uri is obtained via __zipos_parseuri()
* @asyncsignalsafe
* @threadsafe
*/
int __zipos_open(struct ZiposUri *name, int flags) {
// check if this thread is cancelled
int rc;
if (_weaken(pthread_testcancel_np) &&
(rc = _weaken(pthread_testcancel_np)())) {
errno = rc;
return -1;
}
// validate api usage
if ((flags & O_CREAT) || //
(flags & O_TRUNC) || //
(flags & O_ACCMODE) != O_RDONLY) {
return erofs();
}
// get the zipos global singleton
struct Zipos *zipos;
if (!(zipos = __zipos_get())) {
return enoexec();
}
// most open() calls are due to languages path searching assets. the
// majority of these calls will return ENOENT or ENOTDIR. we need to
// perform two extremely costly sigprocmask() calls below. thanks to
// zipos being a read-only filesystem, we can avoid it in many cases
ssize_t cf;
if ((cf = __zipos_find(zipos, name)) == -1) {
return -1;
@ -223,25 +263,14 @@ static int __zipos_open_impl(struct ZiposUri *name, int flags) {
return eacces();
}
}
return __zipos_load(zipos, cf, flags, name);
}
/**
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
*
* @param uri is obtained via __zipos_parseuri()
* @asyncsignalsafe
* @threadsafe
*/
int __zipos_open(struct ZiposUri *name, int flags) {
int rc;
if (_weaken(pthread_testcancel_np) &&
(rc = _weaken(pthread_testcancel_np)())) {
errno = rc;
return -1;
}
// now do the heavy lifting
BLOCK_SIGNALS;
rc = __zipos_open_impl(name, flags);
rc = __zipos_load(zipos, cf, flags, name);
ALLOW_SIGNALS;
return rc;
}
__attribute__((__constructor__)) static void __zipos_ctor(void) {
pthread_atfork(__zipos_lock, __zipos_unlock, __zipos_funlock);
}

View file

@ -21,14 +21,9 @@
#include "libc/runtime/zipos.internal.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/tls.h"
#include "libc/zip.internal.h"
static size_t GetIovSize(const struct iovec *iov, size_t iovlen) {
size_t i, r;
for (r = i = 0; i < iovlen; ++i) r += iov[i].iov_len;
return r;
}
static ssize_t __zipos_read_impl(struct ZiposHandle *h, const struct iovec *iov,
size_t iovlen, ssize_t opt_offset) {
int i;
@ -61,10 +56,6 @@ static ssize_t __zipos_read_impl(struct ZiposHandle *h, const struct iovec *iov,
*/
ssize_t __zipos_read(struct ZiposHandle *h, const struct iovec *iov,
size_t iovlen, ssize_t opt_offset) {
ssize_t rc;
unassert(opt_offset >= 0 || opt_offset == -1);
pthread_mutex_lock(&h->lock);
rc = __zipos_read_impl(h, iov, iovlen, opt_offset);
pthread_mutex_unlock(&h->lock);
return rc;
return __zipos_read_impl(h, iov, iovlen, opt_offset);
}

View file

@ -22,10 +22,11 @@
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/zip.internal.h"
static int64_t __zipos_lseek_impl(struct ZiposHandle *h, int64_t offset,
unsigned whence) {
static int64_t __zipos_seek_impl(struct ZiposHandle *h, int64_t offset,
unsigned whence) {
int64_t pos;
if (h->cfile == ZIPOS_SYNTHETIC_DIRECTORY ||
S_ISDIR(GetZipCfileMode(h->zipos->map + h->cfile))) {
@ -71,12 +72,10 @@ static int64_t __zipos_lseek_impl(struct ZiposHandle *h, int64_t offset,
* @return new position relative to beginning, or -1 on error
* @asyncsignalsafe
*/
int64_t __zipos_lseek(struct ZiposHandle *h, int64_t offset, unsigned whence) {
int64_t __zipos_seek(struct ZiposHandle *h, int64_t offset, unsigned whence) {
int64_t pos;
pthread_mutex_lock(&h->lock);
if ((pos = __zipos_lseek_impl(h, offset, whence)) != -1) {
if ((pos = __zipos_seek_impl(h, offset, whence)) != -1) {
h->pos = pos;
}
pthread_mutex_unlock(&h->lock);
return pos;
}

View file

@ -16,7 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
@ -30,7 +32,8 @@ int __zipos_stat_impl(struct Zipos *zipos, size_t cf, struct stat *st) {
st->st_dev = zipos->dev;
st->st_blksize = FRAMESIZE;
if (cf == ZIPOS_SYNTHETIC_DIRECTORY) {
st->st_mode = S_IFDIR | 0555;
st->st_mode = S_IFDIR | (0555 & ~atomic_load_explicit(
&__umask, memory_order_acquire));
} else {
lf = GetZipCfileOffset(zipos->map + cf);
st->st_mode = GetZipCfileMode(zipos->map + cf);

View file

@ -26,7 +26,7 @@
.yoink __zipos_fcntl
.yoink __zipos_fstat
.yoink __zipos_access
.yoink __zipos_lseek
.yoink __zipos_seek
.yoink __zipos_open
.yoink __zipos_parseuri
.yoink __zipos_read

View file

@ -1,8 +1,5 @@
#ifndef COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
#define COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
#include "libc/intrin/nopl.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -21,7 +18,6 @@ struct ZiposUri {
struct ZiposHandle {
struct ZiposHandle *next;
pthread_mutex_t lock;
struct Zipos *zipos;
size_t size;
size_t mapsize;
@ -32,6 +28,7 @@ struct ZiposHandle {
};
struct Zipos {
long pagesz;
uint8_t *map;
uint8_t *cdir;
uint64_t dev;
@ -41,8 +38,6 @@ struct Zipos {
};
int __zipos_close(int);
void __zipos_lock(void);
void __zipos_unlock(void);
void __zipos_free(struct ZiposHandle *);
struct Zipos *__zipos_get(void) pureconst;
size_t __zipos_normpath(char *, const char *, size_t);
@ -57,20 +52,12 @@ int __zipos_fstat(struct ZiposHandle *, struct stat *);
int __zipos_stat_impl(struct Zipos *, size_t, struct stat *);
ssize_t __zipos_read(struct ZiposHandle *, const struct iovec *, size_t,
ssize_t);
int64_t __zipos_lseek(struct ZiposHandle *, int64_t, unsigned);
int64_t __zipos_seek(struct ZiposHandle *, int64_t, unsigned);
int __zipos_fcntl(int, int, uintptr_t);
int __zipos_notat(int, const char *);
void *__zipos_mmap(void *, uint64_t, int32_t, int32_t, struct ZiposHandle *,
int64_t) dontasan;
#ifdef _NOPL0
#define __zipos_lock() _NOPL0("__threadcalls", __zipos_lock)
#define __zipos_unlock() _NOPL0("__threadcalls", __zipos_unlock)
#else
#define __zipos_lock() (__threaded ? __zipos_lock() : 0)
#define __zipos_unlock() (__threaded ? __zipos_unlock() : 0)
#endif
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_ */

View file

@ -57,7 +57,7 @@ static textwindows int SendfileBlock(int64_t handle,
NTTRACE("WSAWaitForMultipleEvents failed %lm");
return __winsockerr();
} else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) {
if (_check_interrupts(kSigOpRestartable, g_fds.p)) return -1;
if (_check_interrupts(kSigOpRestartable)) return -1;
#if _NTTRACE
POLLTRACE("WSAWaitForMultipleEvents...");
#endif

View file

@ -53,7 +53,7 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
abort_errno = EAGAIN;
if (fd->flags & O_NONBLOCK) {
__wsablock_abort(fd->handle, overlapped);
} else if (_check_interrupts(sigops, g_fds.p)) {
} else if (_check_interrupts(sigops)) {
Interrupted:
abort_errno = errno; // EINTR or ECANCELED
__wsablock_abort(fd->handle, overlapped);
@ -62,7 +62,7 @@ textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
i = WSAWaitForMultipleEvents(1, &overlapped->hEvent, true,
__SIG_POLLING_INTERVAL_MS, true);
if (i == kNtWaitFailed || i == kNtWaitTimeout) {
if (_check_interrupts(sigops, g_fds.p)) {
if (_check_interrupts(sigops)) {
goto Interrupted;
}
if (i == kNtWaitFailed) {

View file

@ -46,6 +46,7 @@
#include "libc/sysv/consts/s.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include "libc/zip.internal.h"
/**

View file

@ -176,7 +176,7 @@ static ssize_t GetDevUrandom(char *p, size_t n) {
ssize_t __getrandom(void *p, size_t n, unsigned f) {
ssize_t rc;
if (IsWindows()) {
if (_check_interrupts(kSigOpRestartable, 0)) {
if (_check_interrupts(kSigOpRestartable)) {
return -1;
}
rc = RtlGenRandom(p, n) ? n : __winerr();

View file

@ -245,20 +245,20 @@ syscon compat MAP_32BIT 0x00000040 0x00000040 0 0x00008000 0x00080000 0
#
# group name GNU/Systemd GNU/Systemd (Aarch64) XNU's Not UNIX! MacOS (Arm64) FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon madv MADV_NORMAL 0 0 0 0 0 0 0 0 # consensus
syscon compat POSIX_FADV_NORMAL 0 0 0 0 0 0 0 0 # consensus
syscon compat POSIX_MADV_NORMAL 0 0 0 0 0 0 0 0 # consensus
syscon madv POSIX_FADV_NORMAL 0 0 0 0 0 0 0 0 # consensus
syscon madv POSIX_MADV_NORMAL 0 0 0 0 0 0 0 0 # consensus
syscon madv MADV_DONTNEED 4 4 4 4 4 4 4 127 # TODO(jart): weird nt decommit thing?
syscon compat POSIX_MADV_DONTNEED 4 4 4 4 4 4 4 127 # unix consensus
syscon compat POSIX_FADV_DONTNEED 4 4 127 127 4 4 4 127 # unix consensus
syscon madv POSIX_MADV_DONTNEED 4 4 4 4 4 4 4 127 # unix consensus
syscon madv POSIX_FADV_DONTNEED 4 4 127 127 4 4 4 127 # unix consensus
syscon madv MADV_RANDOM 1 1 1 1 1 1 1 1 # unix consensus
syscon compat POSIX_MADV_RANDOM 1 1 1 1 1 1 1 1 # unix consensus
syscon compat POSIX_FADV_RANDOM 1 1 127 127 1 1 1 1 # unix consensus
syscon madv POSIX_MADV_RANDOM 1 1 1 1 1 1 1 1 # unix consensus
syscon madv POSIX_FADV_RANDOM 1 1 127 127 1 1 1 1 # unix consensus
syscon madv MADV_SEQUENTIAL 2 2 2 2 2 2 2 2 # unix consensus
syscon compat POSIX_MADV_SEQUENTIAL 2 2 2 2 2 2 2 2 # unix consensus
syscon compat POSIX_FADV_SEQUENTIAL 2 2 127 127 2 2 2 2 # TODO(jart): double check xnu
syscon madv POSIX_MADV_SEQUENTIAL 2 2 2 2 2 2 2 2 # unix consensus
syscon madv POSIX_FADV_SEQUENTIAL 2 2 127 127 2 2 2 2 # TODO(jart): double check xnu
syscon madv MADV_WILLNEED 3 3 3 3 3 3 3 3 # unix consensus (faked on NT)
syscon compat POSIX_MADV_WILLNEED 3 3 3 3 3 3 3 3 # unix consensus
syscon compat POSIX_FADV_WILLNEED 3 3 127 127 3 3 3 3 # TODO(jart): double check xnu
syscon madv POSIX_MADV_WILLNEED 3 3 3 3 3 3 3 3 # unix consensus
syscon madv POSIX_FADV_WILLNEED 3 3 127 127 3 3 3 3 # TODO(jart): double check xnu
syscon madv MADV_MERGEABLE 12 12 127 127 127 127 127 127 # turns on (private anon range) page scanning and merging service (linux only)
syscon madv MADV_UNMERGEABLE 13 13 127 127 127 127 127 127 # turns off mergeable (linux only)
syscon madv MADV_FREE 8 8 5 5 5 6 6 8 # Linux 4.5+ (c. 2016) / NT Faked → VMOfferPriorityNormal (Win8+)

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon access,F_OK,0,0,0,0,0,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,POSIX_FADV_DONTNEED,4,4,127,127,4,4,4,127
.syscon madv,POSIX_FADV_DONTNEED,4,4,127,127,4,4,4,127

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,POSIX_FADV_NORMAL,0,0,0,0,0,0,0,0
.syscon madv,POSIX_FADV_NORMAL,0,0,0,0,0,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,POSIX_FADV_RANDOM,1,1,127,127,1,1,1,1
.syscon madv,POSIX_FADV_RANDOM,1,1,127,127,1,1,1,1

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,POSIX_FADV_SEQUENTIAL,2,2,127,127,2,2,2,2
.syscon madv,POSIX_FADV_SEQUENTIAL,2,2,127,127,2,2,2,2

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,POSIX_FADV_WILLNEED,3,3,127,127,3,3,3,3
.syscon madv,POSIX_FADV_WILLNEED,3,3,127,127,3,3,3,3

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,POSIX_MADV_DONTNEED,4,4,4,4,4,4,4,127
.syscon madv,POSIX_MADV_DONTNEED,4,4,4,4,4,4,4,127

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,POSIX_MADV_NORMAL,0,0,0,0,0,0,0,0
.syscon madv,POSIX_MADV_NORMAL,0,0,0,0,0,0,0,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,POSIX_MADV_RANDOM,1,1,1,1,1,1,1,1
.syscon madv,POSIX_MADV_RANDOM,1,1,1,1,1,1,1,1

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,POSIX_MADV_SEQUENTIAL,2,2,2,2,2,2,2,2
.syscon madv,POSIX_MADV_SEQUENTIAL,2,2,2,2,2,2,2,2

Some files were not shown because too many files have changed in this diff Show more