mirror of
https://github.com/jart/cosmopolitan.git
synced 2026-11-09 02:39:44 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55d5d6bbc0 | ||
|
|
d259e573b6 | ||
|
|
7b284f6bda | ||
|
|
b0e3d89942 | ||
|
|
0b1acce680 | ||
|
|
fadb64a2bf | ||
|
|
a1e1e821cb | ||
|
|
2af7c802b6 | ||
|
|
3b791d2f44 | ||
|
|
b4084dd6c6 | ||
|
|
ee82f90bba | ||
|
|
a699cda5c8 | ||
|
|
f86ffa1a25 | ||
|
|
a09f62141d | ||
|
|
ed17d3008b | ||
|
|
062b2d776e |
No files matched your search
@@ -85,7 +85,7 @@ endif
|
||||
ifeq ($(TOOLCHAIN),) # if TOOLCHAIN isn't defined
|
||||
ifeq ("$(wildcard o/third_party/gcc/bin/x86_64-linux-cosmo-*)","") # if our gcc isn't unbundled
|
||||
ifneq ($(UNAME_M)-$(UNAME_S), x86_64-Linux) # if this is not amd64 linux
|
||||
$(error you need to download https://justine.lol/cosmocc-0.0.12.zip and unzip it inside the cosmo directory)
|
||||
$(error you need to download https://justine.lol/cosmocc-0.0.14.zip and unzip it inside the cosmo directory)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -170,11 +170,13 @@ include third_party/puff/puff.mk # │
|
||||
include libc/elf/elf.mk # │
|
||||
include ape/ape.mk # │
|
||||
include libc/fmt/fmt.mk # │
|
||||
include libc/vga/vga.mk #─┘
|
||||
include libc/vga/vga.mk # │
|
||||
include libc/irq/irq.mk #─┘
|
||||
include libc/calls/calls.mk #─┐
|
||||
include third_party/nsync/nsync.mk # │
|
||||
include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME
|
||||
include third_party/double-conversion/dc.mk # │ You can issue system calls
|
||||
include libc/irq/irq.mk # ├──SYSTEMS RUNTIME
|
||||
include third_party/nsync/nsync.mk # │ You can issue system calls
|
||||
include libc/runtime/runtime.mk # │
|
||||
include third_party/double-conversion/dc.mk # │
|
||||
include libc/crt/crt.mk # │
|
||||
include third_party/dlmalloc/dlmalloc.mk #─┘
|
||||
include libc/mem/mem.mk #─┐
|
||||
@@ -374,6 +376,7 @@ COSMOPOLITAN_OBJECTS = \
|
||||
LIBC_RUNTIME \
|
||||
THIRD_PARTY_NSYNC \
|
||||
LIBC_ELF \
|
||||
LIBC_IRQ \
|
||||
LIBC_CALLS \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_VGA \
|
||||
|
||||
@@ -293,6 +293,8 @@ for x; do
|
||||
elif [ x"$x" = x"-dumpversion" ]; then
|
||||
echo $GCC_VERSION
|
||||
exit 0
|
||||
elif [ x"$x" = x"-Wl,--version" ]; then
|
||||
GOT_SOME=1
|
||||
fi
|
||||
set -- "$@" "$x"
|
||||
done
|
||||
|
||||
@@ -46,6 +46,7 @@ EXAMPLES_DIRECTDEPS = \
|
||||
LIBC_DNS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_IRQ \
|
||||
LIBC_LOG \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
__static_yoink("vga_console");
|
||||
__static_yoink("_idt");
|
||||
__static_yoink("_AcpiMadtFlags");
|
||||
__static_yoink("_AcpiBootFlags");
|
||||
__static_yoink("EfiMain");
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
+8
-5
@@ -158,6 +158,8 @@ int setregid(unsigned, unsigned);
|
||||
int setreuid(unsigned, unsigned);
|
||||
int setsid(void);
|
||||
int setuid(unsigned);
|
||||
int shm_open(const char *, int, unsigned);
|
||||
int shm_unlink(const char *);
|
||||
int sigignore(int);
|
||||
int siginterrupt(int, int);
|
||||
int symlink(const char *, const char *);
|
||||
@@ -211,36 +213,37 @@ int madvise(void *, uint64_t, int);
|
||||
#ifdef _COSMO_SOURCE
|
||||
bool32 fdexists(int);
|
||||
bool32 fileexists(const char *);
|
||||
bool32 ischardev(int);
|
||||
bool32 isdirectory(const char *);
|
||||
bool32 isexecutable(const char *);
|
||||
bool32 isregularfile(const char *);
|
||||
bool32 issymlink(const char *);
|
||||
bool32 ischardev(int);
|
||||
char *commandv(const char *, char *, size_t);
|
||||
int clone(void *, void *, size_t, int, void *, void *, void *, void *);
|
||||
int fadvise(int, uint64_t, uint64_t, int);
|
||||
int makedirs(const char *, unsigned);
|
||||
int pivot_root(const char *, const char *);
|
||||
int pledge(const char *, const char *);
|
||||
int seccomp(unsigned, unsigned, void *);
|
||||
int sys_iopl(int);
|
||||
int sys_ioprio_get(int, int);
|
||||
int sys_ioprio_set(int, int, int);
|
||||
int sys_mlock(const void *, size_t);
|
||||
int sys_mlock2(const void *, size_t, int);
|
||||
int sys_mlockall(int);
|
||||
int sys_personality(uint64_t);
|
||||
int sys_munlock(const void *, size_t);
|
||||
int sys_munlockall(void);
|
||||
int sys_personality(uint64_t);
|
||||
int sys_ptrace(int, ...);
|
||||
int sys_sysctl(const int *, unsigned, void *, size_t *, void *, size_t);
|
||||
int sys_ioprio_get(int, int);
|
||||
int sys_ioprio_set(int, int, int);
|
||||
int tmpfd(void);
|
||||
int touch(const char *, unsigned);
|
||||
int unveil(const char *, const char *);
|
||||
long ptrace(int, ...);
|
||||
int fadvise(int, uint64_t, uint64_t, int);
|
||||
ssize_t copyfd(int, int, size_t);
|
||||
ssize_t readansi(int, char *, size_t);
|
||||
ssize_t tinyprint(int, const char *, ...) nullterminated();
|
||||
void shm_path_np(const char *, char[hasatleast 78]);
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
||||
int __wifstopped(int) pureconst;
|
||||
|
||||
@@ -145,6 +145,8 @@ o/$(MODE)/libc/calls/tailcontext.o: libc/calls/tailcontext.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/calls/stackjump.o: libc/calls/stackjump.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/calls/sched_yield.o: libc/calls/sched_yield.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
||||
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))
|
||||
LIBC_CALLS_SRCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_SRCS))
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
@@ -37,13 +38,12 @@
|
||||
* @raise EBADF on OpenBSD if `first` is greater than highest fd
|
||||
* @raise EINVAL if flags are bad or first is greater than last
|
||||
* @raise EMFILE if a weird race condition happens on Linux
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise EINTR possibly on OpenBSD
|
||||
* @raise ENOMEM on Linux maybe
|
||||
*/
|
||||
int closefrom(int first) {
|
||||
int rc, err;
|
||||
(void)err;
|
||||
int rc;
|
||||
BLOCK_CANCELATION;
|
||||
if (first < 0) {
|
||||
// consistent with openbsd
|
||||
// freebsd allows this but it's dangerous
|
||||
@@ -58,6 +58,7 @@ int closefrom(int first) {
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
ALLOW_CANCELATION;
|
||||
STRACE("closefrom(%d) → %d% m", first, rc);
|
||||
return rc;
|
||||
}
|
||||
+1
-1
@@ -44,7 +44,7 @@ ssize_t copyfd(int in, int out, size_t n) {
|
||||
if (dw != dr) {
|
||||
// POSIX requires atomic IO up to PIPE_BUF
|
||||
// The minimum permissible PIPE_BUF is 512
|
||||
abort();
|
||||
notpossible;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
|
||||
@@ -91,11 +91,9 @@ textwindows int GetNtOpenFlags(int flags, int mode, uint32_t *out_perm,
|
||||
|
||||
// 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;
|
||||
}
|
||||
// and we need this to be the case even in O_EXCL mode otherwise the
|
||||
// shm_open_test.c won't behave consistently on Windows like on UNIX
|
||||
share = kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete;
|
||||
|
||||
// These POSIX to WIN32 mappings are relatively straightforward.
|
||||
if (flags & _O_EXCL) {
|
||||
|
||||
@@ -25,5 +25,6 @@
|
||||
bool IsApeLoadable(char buf[8]) {
|
||||
return READ32LE(buf) == READ32LE("\177ELF") ||
|
||||
READ64LE(buf) == READ64LE("MZqFpD='") ||
|
||||
READ64LE(buf) == READ64LE("JTqFpD='");
|
||||
READ64LE(buf) == READ64LE("jartsr='") ||
|
||||
READ64LE(buf) == READ64LE("APEDBG='");
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
#include "libc/intrin/directmap.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
@@ -74,6 +75,7 @@ textstartup void InitializeMetalFile(void) {
|
||||
memcpy(copied_base, (void *)(BANE + IMAGE_BASE_PHYSICAL), size);
|
||||
__ape_com_base = copied_base;
|
||||
__ape_com_size = size;
|
||||
KINFOF("%s @ %p,+%#zx", APE_COM_NAME, copied_base, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
|
||||
.init.start 101,_init_metalfile
|
||||
.init.start 102,_init_metalfile
|
||||
push %rdi
|
||||
push %rsi
|
||||
call InitializeMetalFile
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
.init.end 101,_init_metalfile
|
||||
.init.end 102,_init_metalfile
|
||||
APE_COM_NAME:
|
||||
.endobj APE_COM_NAME,globl,hidden
|
||||
@@ -33,6 +33,9 @@ static int FindPromise(const char *name) {
|
||||
/**
|
||||
* Parses the arguments to pledge() into a bitmask.
|
||||
*
|
||||
* @param out receives the integral promises mask, which zero is defined
|
||||
* as the set of all promises, and -1 is defined as the empty set of
|
||||
* promises, which is equivalent to `promises` being an empty string
|
||||
* @return 0 on success, or -1 if invalid
|
||||
*/
|
||||
int ParsePromises(const char *promises, unsigned long *out,
|
||||
|
||||
@@ -535,7 +535,10 @@ static const struct thatispacked SyscallName {
|
||||
};
|
||||
|
||||
static const uint16_t kPledgeDefault[] = {
|
||||
__NR_linux_exit, // thread return / exit()
|
||||
__NR_linux_exit, // thread return / exit()
|
||||
#ifdef __NR_linux_arch_prctl //
|
||||
__NR_linux_arch_prctl, // or else launching musl process crashes (tls)
|
||||
#endif //
|
||||
};
|
||||
|
||||
// stdio contains all the benign system calls. openbsd makes the
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,45 @@
|
||||
/*-*- 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/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Opens POSIX named memory object.
|
||||
*
|
||||
* @param name should begin with a `/` and shouldn't contain subsequent
|
||||
* slash characters, and furthermore shouldn't exceed NAME_MAX, for
|
||||
* maximum portability; POSIX defines it as opening a multi-process
|
||||
* object and leaves all other names implementation defined; and we
|
||||
* choose (in this implementation) to define those names as meaning
|
||||
* the same thing while not imposing any length limit; cosmo always
|
||||
* feeds your name through BLAKE2B to create a `.sem` file existing
|
||||
* under `/dev/shm` if available, otherwise it will go under `/tmp`
|
||||
* @return open file descriptor, or -1 w/ errno
|
||||
*/
|
||||
int shm_open(const char *name, int oflag, unsigned mode) {
|
||||
int fd;
|
||||
char path[78];
|
||||
shm_path_np(name, path);
|
||||
BLOCK_CANCELATION;
|
||||
fd = openat(AT_FDCWD, path, oflag, mode);
|
||||
ALLOW_CANCELATION;
|
||||
return fd;
|
||||
}
|
||||
@@ -20,36 +20,26 @@
|
||||
#include "libc/dce.h"
|
||||
#include "libc/str/blake2.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
/**
|
||||
* Returns filesystem pathname of named semaphore.
|
||||
*
|
||||
* @param name is `name` of semaphore which should begin with slash
|
||||
* @param buf is temporary storage with at least `size` bytes
|
||||
* @param size is size of `buf` in bytes
|
||||
* @param buf is temporary storage with at least 78 bytes
|
||||
* @return pointer to file system path
|
||||
* @raise ENAMETOOLONG if constructed path would exceed `size`
|
||||
*/
|
||||
const char *sem_path_np(const char *name, char *buf, size_t size) {
|
||||
void shm_path_np(const char *name, char buf[hasatleast 78]) {
|
||||
char *p;
|
||||
unsigned n;
|
||||
const char *path, *a;
|
||||
const char *a;
|
||||
uint8_t digest[BLAKE2B256_DIGEST_LENGTH];
|
||||
a = "/tmp/", n = 5;
|
||||
if (IsLinux() && isdirectory("/dev/shm")) {
|
||||
a = "/dev/shm/", n = 9;
|
||||
}
|
||||
if (n + BLAKE2B256_DIGEST_LENGTH * 2 + 4 < size) {
|
||||
BLAKE2B256(name, strlen(name), digest);
|
||||
p = mempcpy(buf, a, n);
|
||||
p = hexpcpy(p, digest, BLAKE2B256_DIGEST_LENGTH);
|
||||
p = mempcpy(p, ".sem", 5);
|
||||
path = buf;
|
||||
} else {
|
||||
enametoolong();
|
||||
path = 0;
|
||||
}
|
||||
return path;
|
||||
BLAKE2B256(name, strlen(name), digest);
|
||||
p = mempcpy(buf, a, n);
|
||||
p = hexpcpy(p, digest, BLAKE2B256_DIGEST_LENGTH);
|
||||
mempcpy(p, ".sem", 5);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*-*- 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"
|
||||
|
||||
/**
|
||||
* Deletes POSIX named memory object.
|
||||
*/
|
||||
int shm_unlink(const char *name) {
|
||||
char path[78];
|
||||
shm_path_np(name, path);
|
||||
return unlink(path);
|
||||
}
|
||||
@@ -29,6 +29,7 @@ void __stat2cosmo(struct stat *restrict st, const union metastat *ms) {
|
||||
st->st_uid = ms->linux.st_uid;
|
||||
st->st_gid = ms->linux.st_gid;
|
||||
st->st_flags = 0;
|
||||
st->st_gen = 0;
|
||||
st->st_rdev = ms->linux.st_rdev;
|
||||
st->st_size = ms->linux.st_size;
|
||||
st->st_blksize = ms->linux.st_blksize;
|
||||
|
||||
@@ -12,7 +12,7 @@ struct sysinfo {
|
||||
uint64_t bufferram; /* lingering disk pages; see fadvise */
|
||||
uint64_t totalswap; /* size of emergency memory */
|
||||
uint64_t freeswap; /* hopefully equal to totalswap */
|
||||
int16_t procs; /* number of processes */
|
||||
uint16_t procs; /* number of processes */
|
||||
int16_t __ignore1; /* padding */
|
||||
int32_t __ignore2; /* padding */
|
||||
uint64_t totalhigh; /* wut */
|
||||
|
||||
@@ -47,6 +47,29 @@ static inline bool timeval_isvalid(struct timeval __tv) {
|
||||
}
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
||||
#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
|
||||
#define timerisset(t) ((t)->tv_sec || (t)->tv_usec)
|
||||
#define timerclear(t) ((t)->tv_sec = (t)->tv_usec = 0)
|
||||
#define timercmp(s, t, op) \
|
||||
((s)->tv_sec == (t)->tv_sec ? (s)->tv_usec op(t)->tv_usec \
|
||||
: (s)->tv_sec op(t)->tv_sec)
|
||||
#define timeradd(s, t, a) \
|
||||
(void)((a)->tv_sec = (s)->tv_sec + (t)->tv_sec, \
|
||||
((a)->tv_usec = (s)->tv_usec + (t)->tv_usec) >= 1000000 && \
|
||||
((a)->tv_usec -= 1000000, (a)->tv_sec++))
|
||||
#define timersub(s, t, a) \
|
||||
(void)((a)->tv_sec = (s)->tv_sec - (t)->tv_sec, \
|
||||
((a)->tv_usec = (s)->tv_usec - (t)->tv_usec) < 0 && \
|
||||
((a)->tv_usec += 1000000, (a)->tv_sec--))
|
||||
#endif
|
||||
|
||||
#ifdef _GNU_SOURCE
|
||||
#define TIMEVAL_TO_TIMESPEC(tv, ts) \
|
||||
((ts)->tv_sec = (tv)->tv_sec, (ts)->tv_nsec = (tv)->tv_usec * 1000, (void)0)
|
||||
#define TIMESPEC_TO_TIMEVAL(tv, ts) \
|
||||
((tv)->tv_sec = (ts)->tv_sec, (tv)->tv_usec = (ts)->tv_nsec / 1000, (void)0)
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_TIMEVAL_H_ */
|
||||
+39
-11
@@ -16,27 +16,55 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/sysinfo.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/struct/sysinfo.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/struct/memorystatusex.h"
|
||||
#include "libc/nt/struct/systeminfo.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/systeminfo.h"
|
||||
|
||||
static textwindows uint16_t GetProcessCount(void) {
|
||||
uint16_t res;
|
||||
uint32_t have, got, *pids;
|
||||
uint32_t stack_memory[1000];
|
||||
have = 0xFFFF * 4;
|
||||
if (!_weaken(malloc) || !(pids = _weaken(malloc)(have))) {
|
||||
pids = stack_memory;
|
||||
have = sizeof(stack_memory);
|
||||
}
|
||||
if (EnumProcesses(pids, have, &got)) {
|
||||
res = got / 4;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
if (pids != stack_memory && _weaken(free)) {
|
||||
_weaken(free)(pids);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
textwindows int sys_sysinfo_nt(struct sysinfo *info) {
|
||||
int i;
|
||||
double load[3];
|
||||
struct NtMemoryStatusEx memstat;
|
||||
struct NtSystemInfo sysinfo;
|
||||
GetSystemInfo(&sysinfo);
|
||||
BLOCK_SIGNALS;
|
||||
if (sys_getloadavg_nt(load, 3) != -1) {
|
||||
for (i = 0; i < 3; ++i) {
|
||||
info->loads[i] = load[i] * 65536;
|
||||
}
|
||||
}
|
||||
memstat.dwLength = sizeof(struct NtMemoryStatusEx);
|
||||
if (GlobalMemoryStatusEx(&memstat)) {
|
||||
info->mem_unit = 1;
|
||||
info->totalram = memstat.ullTotalPhys;
|
||||
info->freeram = memstat.ullAvailPhys;
|
||||
info->procs = sysinfo.dwNumberOfProcessors;
|
||||
info->uptime = GetTickCount64() / 1000;
|
||||
return 0;
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
info->uptime = GetTickCount64() / 1000;
|
||||
info->procs = GetProcessCount();
|
||||
info->mem_unit = 1;
|
||||
ALLOW_SIGNALS;
|
||||
return 0;
|
||||
}
|
||||
+1
-51
@@ -188,56 +188,6 @@ static int unveil_init(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins paths, e.g.
|
||||
*
|
||||
* 0 + 0 → 0
|
||||
* "" + "" → ""
|
||||
* "a" + 0 → "a"
|
||||
* "a" + "" → "a/"
|
||||
* 0 + "b" → "b"
|
||||
* "" + "b" → "b"
|
||||
* "." + "b" → "./b"
|
||||
* "b" + "." → "b/."
|
||||
* "a" + "b" → "a/b"
|
||||
* "a/" + "b" → "a/b"
|
||||
* "a" + "b/" → "a/b/"
|
||||
* "a" + "/b" → "/b"
|
||||
*
|
||||
* @return joined path, which may be `buf`, `path`, or `other`, or null
|
||||
* if (1) `buf` didn't have enough space, or (2) both `path` and
|
||||
* `other` were null
|
||||
*/
|
||||
static char *JoinPaths(char *buf, size_t size, const char *path,
|
||||
const char *other) {
|
||||
size_t pathlen, otherlen;
|
||||
if (!other) return (char *)path;
|
||||
if (!path) return (char *)other;
|
||||
pathlen = strlen(path);
|
||||
if (!pathlen || *other == '/') {
|
||||
return (/*unconst*/ char *)other;
|
||||
}
|
||||
otherlen = strlen(other);
|
||||
if (path[pathlen - 1] == '/') {
|
||||
if (pathlen + otherlen + 1 <= size) {
|
||||
memmove(buf, path, pathlen);
|
||||
memmove(buf + pathlen, other, otherlen + 1);
|
||||
return buf;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (pathlen + 1 + otherlen + 1 <= size) {
|
||||
memmove(buf, path, pathlen);
|
||||
buf[pathlen] = '/';
|
||||
memmove(buf + pathlen + 1, other, otherlen + 1);
|
||||
return buf;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sys_unveil_linux(const char *path, const char *permissions) {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Wframe-larger-than="
|
||||
@@ -302,7 +252,7 @@ int sys_unveil_linux(const char *path, const char *permissions) {
|
||||
// next = join(dirname(next), link)
|
||||
strcpy(b.buf2, next);
|
||||
dir = dirname(b.buf2);
|
||||
if ((next = JoinPaths(b.buf3, PATH_MAX, dir, b.lbuf))) {
|
||||
if ((next = __join_paths(b.buf3, PATH_MAX, dir, b.lbuf))) {
|
||||
// next now points to either: buf3, buf2, lbuf, rodata
|
||||
strcpy(b.buf4, next);
|
||||
next = b.buf4;
|
||||
|
||||
@@ -728,6 +728,8 @@ void abort(void) wontreturn;
|
||||
#endif
|
||||
|
||||
#define __weak_reference(sym, alias) \
|
||||
__weak_reference_impl(sym, alias)
|
||||
#define __weak_reference_impl(sym, alias) \
|
||||
__asm__(".weak\t" #alias "\n\t" \
|
||||
".equ\t" #alias ", " #sym "\n\t" \
|
||||
".type\t" #alias ",@notype")
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#ifdef __COSMOPOLITAN__
|
||||
#undef __COSMOPOLITAN__
|
||||
#endif
|
||||
|
||||
#define __COSMOPOLITAN_MAJOR__ 3
|
||||
#define __COSMOPOLITAN_MINOR__ 0
|
||||
#define __COSMOPOLITAN_PATCH__ 0
|
||||
#define __COSMOPOLITAN_PATCH__ 1
|
||||
#define __COSMOPOLITAN__ \
|
||||
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
|
||||
__COSMOPOLITAN_PATCH__)
|
||||
|
||||
@@ -21,7 +21,10 @@ extern struct CxaAtexitBlocks __cxa_blocks;
|
||||
|
||||
void __cxa_lock(void);
|
||||
void __cxa_unlock(void);
|
||||
void __cxa_thread_finalize(void);
|
||||
void __cxa_printexits(FILE *, void *);
|
||||
int __cxa_thread_atexit(void *, void *, void *);
|
||||
int __cxa_thread_atexit_impl(void *, void *, void *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
|
||||
// Code and data structures for bare metal interrupt handling.
|
||||
|
||||
@@ -119,8 +119,6 @@ o/$(MODE)/libc/intrin/ksockoptnames.o: libc/intrin/ksockoptnames.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/intrin/ktcpoptnames.o: libc/intrin/ktcpoptnames.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/intrin/sched_yield.o: libc/intrin/sched_yield.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
o/$(MODE)/libc/intrin/stackcall.o: libc/intrin/stackcall.S
|
||||
@$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(OUTPUT_OPTION) -c $<
|
||||
|
||||
|
||||
+30
-15
@@ -385,14 +385,31 @@ privileged long kloghandle(void) {
|
||||
return __klog_handle;
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
privileged void _klog_serial(const char *b, size_t n) {
|
||||
size_t i;
|
||||
uint16_t dx;
|
||||
unsigned char al;
|
||||
for (i = 0; i < n; ++i) {
|
||||
for (;;) {
|
||||
dx = 0x3F8 + UART_LSR;
|
||||
asm("inb\t%1,%0" : "=a"(al) : "dN"(dx));
|
||||
if (al & UART_TTYTXR) break;
|
||||
asm("pause");
|
||||
}
|
||||
dx = 0x3F8;
|
||||
asm volatile("outb\t%0,%1"
|
||||
: /* no inputs */
|
||||
: "a"(b[i]), "dN"(dx));
|
||||
}
|
||||
}
|
||||
#endif /* __x86_64__ */
|
||||
|
||||
privileged void klog(const char *b, size_t n) {
|
||||
#ifdef __x86_64__
|
||||
int e;
|
||||
long h;
|
||||
size_t i;
|
||||
uint16_t dx;
|
||||
uint32_t wrote;
|
||||
unsigned char al;
|
||||
long rax, rdi, rsi, rdx;
|
||||
if ((h = kloghandle()) == -1) {
|
||||
return;
|
||||
@@ -407,23 +424,15 @@ privileged void klog(const char *b, size_t n) {
|
||||
if (_weaken(_klog_vga)) {
|
||||
_weaken(_klog_vga)(b, n);
|
||||
}
|
||||
for (i = 0; i < n; ++i) {
|
||||
for (;;) {
|
||||
dx = 0x3F8 + UART_LSR;
|
||||
asm("inb\t%1,%0" : "=a"(al) : "dN"(dx));
|
||||
if (al & UART_TTYTXR) break;
|
||||
asm("pause");
|
||||
}
|
||||
dx = 0x3F8;
|
||||
asm volatile("outb\t%0,%1"
|
||||
: /* no inputs */
|
||||
: "a"(b[i]), "dN"(dx));
|
||||
}
|
||||
_klog_serial(b, n);
|
||||
} else {
|
||||
asm volatile("syscall"
|
||||
: "=a"(rax), "=D"(rdi), "=S"(rsi), "=d"(rdx)
|
||||
: "0"(__NR_write), "1"(h), "2"(b), "3"(n)
|
||||
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
|
||||
if (rax < 0) {
|
||||
__klog_handle = 0;
|
||||
}
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
// this isn't a cancelation point because we don't acknowledge eintr
|
||||
@@ -438,6 +447,9 @@ privileged void klog(const char *b, size_t n) {
|
||||
: "=r"(res_x0)
|
||||
: "r"(r0), "r"(r1), "r"(r2), "r"(r8), "r"(r16)
|
||||
: "memory");
|
||||
if (res_x0 < 0) {
|
||||
__klog_handle = 0;
|
||||
}
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
@@ -1152,3 +1164,6 @@ privileged void kprintf(const char *fmt, ...) {
|
||||
kvprintf(fmt, v);
|
||||
va_end(v);
|
||||
}
|
||||
|
||||
__weak_reference(kprintf, uprintf);
|
||||
__weak_reference(kvprintf, uvprintf);
|
||||
+30
-6
@@ -9,25 +9,49 @@
|
||||
#define kvsnprintf __kvsnprintf
|
||||
#define kloghandle __kloghandle
|
||||
#define kisdangerous __kisdangerous
|
||||
#define uprintf __uprintf
|
||||
#define uvprintf __uvprintf
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
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 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_serial(const char *, size_t);
|
||||
long kloghandle(void);
|
||||
|
||||
void uprintf(const char *, ...);
|
||||
void uvprintf(const char *, va_list);
|
||||
|
||||
#ifndef TINY
|
||||
#define KINFOF(FMT, ...) \
|
||||
do { \
|
||||
uprintf("\r\e[35m%s:%d: " FMT "\e[0m\n", \
|
||||
__FILE__, __LINE__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define KWARNF(FMT, ...) \
|
||||
do { \
|
||||
uprintf("\r\e[94;49mwarn: %s:%d: " FMT "\e[0m\n", \
|
||||
__FILE__, __LINE__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
#else
|
||||
#define KINFOF(FMT, ...) ((void)0)
|
||||
#define KWARNF(FMT, ...) ((void)0)
|
||||
#endif
|
||||
#define KDIEF(FMT, ...) \
|
||||
do { \
|
||||
kprintf("\r\e[30;101mfatal: %s:%d: " FMT "\e[0m\n", \
|
||||
__FILE__, __LINE__, ## __VA_ARGS__); \
|
||||
for (;;) asm volatile("cli\n\thlt"); \
|
||||
} while (0)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/nexgen32e/yield.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
@@ -64,7 +65,7 @@
|
||||
* @see pthread_spin_lock()
|
||||
* @vforksafe
|
||||
*/
|
||||
int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
errno_t pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
int t;
|
||||
|
||||
LOCKTRACE("pthread_mutex_lock(%t)", mutex);
|
||||
@@ -82,7 +83,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
|
||||
if (mutex->_type == PTHREAD_MUTEX_NORMAL) {
|
||||
while (atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
pthread_yield();
|
||||
spin_yield();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -102,7 +103,7 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
|
||||
}
|
||||
|
||||
while (atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
pthread_yield();
|
||||
spin_yield();
|
||||
}
|
||||
|
||||
mutex->_depth = 0;
|
||||
|
||||
@@ -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 2022 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 │
|
||||
@@ -19,27 +19,29 @@
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
||||
/**
|
||||
* Locks mutex if it isn't locked already.
|
||||
* Attempts acquiring lock.
|
||||
*
|
||||
* Unlike pthread_mutex_lock() this function won't block and instead
|
||||
* returns an error immediately if the lock couldn't be acquired.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EBUSY if lock is already held
|
||||
* @raise ENOTRECOVERABLE if `mutex` is corrupted
|
||||
* @return 0 if lock was acquired, otherwise an errno
|
||||
* @raise EAGAIN if maximum number of recursive locks is held
|
||||
* @raise EBUSY if lock is currently held in read or write mode
|
||||
* @raise EINVAL if `mutex` doesn't refer to an initialized lock
|
||||
* @raise EDEADLK if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the
|
||||
* current thread already holds this mutex
|
||||
*/
|
||||
errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||
int t;
|
||||
|
||||
if (__tls_enabled && //
|
||||
mutex->_type == PTHREAD_MUTEX_NORMAL && //
|
||||
// delegate to *NSYNC if possible
|
||||
if (mutex->_type == PTHREAD_MUTEX_NORMAL &&
|
||||
mutex->_pshared == PTHREAD_PROCESS_PRIVATE && //
|
||||
_weaken(nsync_mu_trylock)) {
|
||||
if (_weaken(nsync_mu_trylock)((nsync_mu *)mutex)) {
|
||||
@@ -49,6 +51,7 @@ errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||
}
|
||||
}
|
||||
|
||||
// handle normal mutexes
|
||||
if (mutex->_type == PTHREAD_MUTEX_NORMAL) {
|
||||
if (!atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
return 0;
|
||||
@@ -57,6 +60,7 @@ errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||
}
|
||||
}
|
||||
|
||||
// handle recursive and error check mutexes
|
||||
t = gettid();
|
||||
if (mutex->_owner == t) {
|
||||
if (mutex->_type != PTHREAD_MUTEX_ERRORCHECK) {
|
||||
@@ -67,15 +71,17 @@ errno_t pthread_mutex_trylock(pthread_mutex_t *mutex) {
|
||||
return EAGAIN;
|
||||
}
|
||||
} else {
|
||||
return EBUSY;
|
||||
return EDEADLK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
mutex->_depth = 0;
|
||||
mutex->_owner = t;
|
||||
return 0;
|
||||
} else {
|
||||
if (atomic_exchange_explicit(&mutex->_lock, 1, memory_order_acquire)) {
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
mutex->_depth = 0;
|
||||
mutex->_owner = t;
|
||||
mutex->_pid = __pid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
* @raises EPERM if in error check mode and not owned by caller
|
||||
* @vforksafe
|
||||
*/
|
||||
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||
errno_t pthread_mutex_unlock(pthread_mutex_t *mutex) {
|
||||
int t;
|
||||
|
||||
LOCKTRACE("pthread_mutex_unlock(%t)", mutex);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "libc/assert.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nexgen32e/yield.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
intptr_t _pthread_syshand(struct PosixThread *pt) {
|
||||
@@ -27,6 +28,6 @@ intptr_t _pthread_syshand(struct PosixThread *pt) {
|
||||
for (;;) {
|
||||
syshand = atomic_load_explicit(&pt->tib->tib_syshand, memory_order_acquire);
|
||||
if (syshand) return syshand;
|
||||
pthread_yield();
|
||||
spin_yield();
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,13 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nexgen32e/yield.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
int _pthread_tid(struct PosixThread *pt) {
|
||||
int tid = 0;
|
||||
while (pt && !(tid = atomic_load_explicit(&pt->ptid, memory_order_acquire))) {
|
||||
pthread_yield();
|
||||
spin_yield();
|
||||
}
|
||||
return tid;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
|
||||
.init.start 312,_init_acpi_fadt
|
||||
push %rdi
|
||||
push %rsi
|
||||
call _AcpiFadtInit
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
.init.end 312,_init_acpi_fadt
|
||||
.data
|
||||
_AcpiBootFlags:
|
||||
.short kAcpiFadtLegacyDevices | kAcpiFadt8042
|
||||
.endobj _AcpiBootFlags,globl
|
||||
.previous
|
||||
@@ -0,0 +1,80 @@
|
||||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
textstartup static void _AcpiDsdtInit(uintptr_t dsdt_phy) {
|
||||
const AcpiTableDsdt *dsdt;
|
||||
size_t length;
|
||||
if (!dsdt_phy) {
|
||||
KWARNF("FADT: no DSDT");
|
||||
return;
|
||||
}
|
||||
dsdt = _AcpiMapTable(dsdt_phy);
|
||||
KINFOF("FADT: DSDT @ %p", dsdt);
|
||||
length = dsdt->Header.Length;
|
||||
if (length <= offsetof(AcpiTableDsdt, Aml)) {
|
||||
KWARNF("DSDT: no AML?");
|
||||
return;
|
||||
}
|
||||
/* TODO: parse AML to discover hardware configuration */
|
||||
}
|
||||
|
||||
textstartup void _AcpiFadtInit(void) {
|
||||
if (IsMetal()) {
|
||||
const AcpiTableFadt *fadt;
|
||||
size_t length;
|
||||
uint16_t flags;
|
||||
uintptr_t dsdt_phy = 0;
|
||||
if (!_AcpiSuccess(_AcpiGetTable("FACP", 0, (void **)&fadt))) {
|
||||
KINFOF("no FADT found");
|
||||
return;
|
||||
}
|
||||
length = fadt->Header.Length;
|
||||
KINFOF("FADT @ %p,+%#zx", fadt, length);
|
||||
_Static_assert(offsetof(AcpiTableFadt, Dsdt) == 40);
|
||||
_Static_assert(offsetof(AcpiTableFadt, BootFlags) == 109);
|
||||
_Static_assert(offsetof(AcpiTableFadt, XDsdt) == 140);
|
||||
if (length >= offsetof(AcpiTableFadt, BootFlags) + sizeof(fadt->BootFlags))
|
||||
{
|
||||
_AcpiBootFlags = flags = fadt->BootFlags;
|
||||
KINFOF("FADT: boot flags %#x", (unsigned)flags);
|
||||
}
|
||||
if (length >= offsetof(AcpiTableFadt, XDsdt) + sizeof(fadt->XDsdt) &&
|
||||
fadt->XDsdt) {
|
||||
dsdt_phy = fadt->XDsdt;
|
||||
} else if (length >= offsetof(AcpiTableFadt, Dsdt) + sizeof(fadt->Dsdt)) {
|
||||
dsdt_phy = fadt->Dsdt;
|
||||
}
|
||||
_AcpiDsdtInit(dsdt_phy);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -0,0 +1,51 @@
|
||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
|
||||
.init.start 311,_init_acpi_madt
|
||||
push %rdi
|
||||
push %rsi
|
||||
call _AcpiMadtInit
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
.init.end 311,_init_acpi_madt
|
||||
.data
|
||||
_AcpiMadtFlags:
|
||||
.long kAcpiMadtPcAtCompat
|
||||
.endobj _AcpiMadtFlags,globl
|
||||
.previous
|
||||
.bss
|
||||
_AcpiNumIoApics:
|
||||
.skip 8
|
||||
.endobj _AcpiNumIoApics,globl
|
||||
_AcpiIoApics:
|
||||
.skip 8
|
||||
.endobj _AcpiIoApics,globl
|
||||
.previous
|
||||
@@ -0,0 +1,81 @@
|
||||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/inttypes.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
textstartup void _AcpiMadtInit(void) {
|
||||
if (IsMetal()) {
|
||||
const AcpiTableMadt *madt;
|
||||
size_t length, num_io_apics = 0;
|
||||
const char *madt_end, *p;
|
||||
const AcpiSubtableHeader *h;
|
||||
const AcpiMadtIoApic **icp;
|
||||
uint32_t flags;
|
||||
if (!_AcpiSuccess(_AcpiGetTable("APIC", 0, (void **)&madt))) {
|
||||
KINFOF("no MADT found");
|
||||
return;
|
||||
}
|
||||
length = madt->Header.Length;
|
||||
if (length < sizeof(*madt)) {
|
||||
KDIEF("MADT has short length %#zx", length);
|
||||
}
|
||||
_AcpiMadtFlags = flags = madt->Flags;
|
||||
KINFOF("MADT @ %p, flags %#" PRIx32, madt, flags);
|
||||
madt_end = (char *)madt + length;
|
||||
p = madt->Subtable;
|
||||
while (p != madt_end) {
|
||||
h = (const AcpiSubtableHeader *)p;
|
||||
switch (h->Type) {
|
||||
case kAcpiMadtIoApic:
|
||||
++num_io_apics;
|
||||
}
|
||||
p += h->Length;
|
||||
}
|
||||
KINFOF("MADT: %zu I/O APIC(s)", num_io_apics);
|
||||
icp = _AcpiOsAllocate(num_io_apics * sizeof(const AcpiMadtIoApic *));
|
||||
if (icp) {
|
||||
_AcpiIoApics = icp;
|
||||
p = madt->Subtable;
|
||||
while (p != madt_end) {
|
||||
h = (const AcpiSubtableHeader *)p;
|
||||
switch (h->Type) {
|
||||
case kAcpiMadtIoApic:
|
||||
*icp++ = (const AcpiMadtIoApic *)p;
|
||||
}
|
||||
p += h->Length;
|
||||
}
|
||||
_AcpiNumIoApics = num_io_apics;
|
||||
} else {
|
||||
KWARNF("MADT: no memory for I/O APICs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -0,0 +1,46 @@
|
||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
|
||||
.init.start 310,_init_acpi_xsdt
|
||||
push %rdi
|
||||
push %rsi
|
||||
call _AcpiXsdtInit
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
.init.end 310,_init_acpi_xsdt
|
||||
.bss
|
||||
_AcpiXsdtNumEntries:
|
||||
.skip 8
|
||||
.endobj _AcpiXsdtNumEntries,globl
|
||||
_AcpiXsdtEntries:
|
||||
.skip 8
|
||||
.endobj _AcpiXsdtEntries,globl
|
||||
.previous
|
||||
@@ -0,0 +1,243 @@
|
||||
/*-*- 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│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ This is free and unencumbered software released into the public domain. │
|
||||
│ │
|
||||
│ Anyone is free to copy, modify, publish, use, compile, sell, or │
|
||||
│ distribute this software, either in source code form or as a compiled │
|
||||
│ binary, for any purpose, commercial or non-commercial, and by any │
|
||||
│ means. │
|
||||
│ │
|
||||
│ In jurisdictions that recognize copyright laws, the author or authors │
|
||||
│ of this software dedicate any and all copyright interest in the │
|
||||
│ software to the public domain. We make this dedication for the benefit │
|
||||
│ of the public at large and to the detriment of our heirs and │
|
||||
│ successors. We intend this dedication to be an overt act of │
|
||||
│ relinquishment in perpetuity of all present and future rights to this │
|
||||
│ software under copyright law. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │
|
||||
│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
||||
│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/directmap.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/irq/acpi.internal.h"
|
||||
#include "libc/log/color.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/efi.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
textstartup static void *_AcpiOsMapRoMemory(uintptr_t phy, size_t n) {
|
||||
__invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n,
|
||||
PAGE_XD);
|
||||
return (void *)(BANE + phy);
|
||||
}
|
||||
|
||||
textstartup static void *_AcpiOsMapMemory(uintptr_t phy, size_t n) {
|
||||
__invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n,
|
||||
PAGE_XD | PAGE_RW);
|
||||
return (void *)(BANE + phy);
|
||||
}
|
||||
|
||||
textstartup void *_AcpiOsMapUncachedMemory(uintptr_t phy, size_t n) {
|
||||
__invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n,
|
||||
PAGE_XD | PAGE_PCD | PAGE_RW);
|
||||
return (void *)(BANE + phy);
|
||||
}
|
||||
|
||||
textstartup static void *_AcpiOsAllocatePages(size_t n) {
|
||||
struct DirectMap dm = sys_mmap_metal(NULL, n, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
void *addr = dm.addr;
|
||||
if (addr == (void *)-1) addr = NULL;
|
||||
return addr;
|
||||
}
|
||||
|
||||
textstartup void *_AcpiOsAllocate(size_t n) {
|
||||
static _Atomic(char *) slack = NULL;
|
||||
char *addr = NULL;
|
||||
size_t align = __BIGGEST_ALIGNMENT__, use;
|
||||
if (n >= 4096) return _AcpiOsAllocatePages(n);
|
||||
n = ROUNDUP(n, align);
|
||||
for (;;) {
|
||||
addr = atomic_exchange(&slack, NULL);
|
||||
if (!addr) {
|
||||
addr = _AcpiOsAllocatePages(4096);
|
||||
if (!addr) return NULL;
|
||||
}
|
||||
use = (uintptr_t)addr % 4096 + n;
|
||||
if (use <= 4096) {
|
||||
if (use < 4096) atomic_store(&slack, addr + n);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textstartup static uint8_t _AcpiTbChecksum(const uint8_t *p, size_t n) {
|
||||
uint8_t c = 0;
|
||||
while (n-- != 0) c += *p++;
|
||||
return c;
|
||||
}
|
||||
|
||||
textstartup static AcpiStatus _AcpiTbVerifyChecksum(const uint8_t *p,
|
||||
size_t n) {
|
||||
uint8_t sum = _AcpiTbChecksum(p, n);
|
||||
if (!sum) return kAcpiOk;
|
||||
KWARNF("bad ACPI table cksum %#x != 0 @ %p,+%#zx", (unsigned)sum, p, n);
|
||||
return kAcpiExBadChecksum;
|
||||
}
|
||||
|
||||
textstartup static AcpiStatus _AcpiRsdpVerifyChecksums(const uint8_t *p) {
|
||||
const AcpiTableRsdp *q = (const AcpiTableRsdp *)p;
|
||||
size_t length = offsetof(AcpiTableRsdp, Length);
|
||||
AcpiStatus sta = _AcpiTbVerifyChecksum(p, length);
|
||||
if (!_AcpiSuccess(sta)) return sta;
|
||||
if (q->Revision <= 1) return kAcpiOk;
|
||||
length = q->Length;
|
||||
if (length < offsetof(AcpiTableRsdp, Reserved)) {
|
||||
KWARNF("malformed ACPI 2+ RSDP, length %#zx < %#zx",
|
||||
length, offsetof(AcpiTableRsdp, Reserved));
|
||||
if (length < offsetof(AcpiTableRsdp, RsdtPhysicalAddress)
|
||||
+ sizeof(q->RsdtPhysicalAddress)) {
|
||||
return kAcpiExBadHeader;
|
||||
}
|
||||
return kAcpiOk;
|
||||
}
|
||||
return _AcpiTbVerifyChecksum(p, length);
|
||||
}
|
||||
|
||||
textstartup static bool _AcpiTbIsValidRsdp(const uint8_t *p) {
|
||||
const AcpiTableRsdp *q = (const AcpiTableRsdp *)p;
|
||||
if (READ64LE(q->Signature) != READ64LE("RSD PTR ")) return false;
|
||||
KINFOF("\"RSD PTR \" @ %p, ACPI rev %u", q, (unsigned)q->Revision);
|
||||
if (!_AcpiSuccess(_AcpiRsdpVerifyChecksums(p))) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
textstartup static const AcpiTableRsdp *_AcpiFindRsdp(void) {
|
||||
const size_t PARA_SZ = 0x10;
|
||||
size_t off;
|
||||
struct mman *mm = __get_mm();
|
||||
uint64_t rsdp_phy = mm->pc_acpi_rsdp;
|
||||
uint16_t ebda_para;
|
||||
const uint8_t *area;
|
||||
if (rsdp_phy) return _AcpiOsMapRoMemory(rsdp_phy, sizeof(AcpiTableRsdp));
|
||||
/*
|
||||
* "OSPM finds the Root System Description Pointer (RSDP) structure by
|
||||
* searching physical memory ranges on 16-byte boundaries for a valid
|
||||
* Root System Description Pointer structure signature and checksum match
|
||||
* as follows:
|
||||
* * The first 1 KB of the Extended BIOS Data Area (EBDA). For EISA or
|
||||
* MCA systems, the EBDA can be found in the two-byte location 40:0Eh
|
||||
* on the BIOS data area.
|
||||
* * The BIOS read-only memory space between 0E0000h and 0FFFFFh."
|
||||
* — Advanced Configuration and Power Interface (ACPI) Specification,
|
||||
* Release 6.5, §5.2.5.1
|
||||
*/
|
||||
ebda_para = *(const uint16_t *)(BANE + PC_BIOS_DATA_AREA + 0xE);
|
||||
if (ebda_para) {
|
||||
area = _AcpiOsMapMemory((uint32_t)ebda_para * PARA_SZ, 1024);
|
||||
KINFOF("search EBDA @ %p for RSDP", area);
|
||||
off = 1024;
|
||||
while (off != 0) {
|
||||
off -= PARA_SZ;
|
||||
if (_AcpiTbIsValidRsdp(area + off)) {
|
||||
return (const AcpiTableRsdp *)(area + off);
|
||||
}
|
||||
}
|
||||
}
|
||||
area = _AcpiOsMapMemory(0xE0000, 0xFFFFF + 1 - 0xE0000);
|
||||
KINFOF("search ROM BIOS for RSDP");
|
||||
off = ROUNDUP(0xFFFFF + 1 - 0xE0000, PARA_SZ);
|
||||
while (off != 0) {
|
||||
off -= PARA_SZ;
|
||||
if (_AcpiTbIsValidRsdp(area + off)) {
|
||||
return (const AcpiTableRsdp *)(area + off);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
textstartup void *_AcpiMapTable(uintptr_t phy) {
|
||||
void *p = _AcpiOsMapRoMemory(phy, sizeof(AcpiTableHeader));
|
||||
AcpiTableHeader *hdr = p;
|
||||
size_t length = hdr->Length;
|
||||
if (length < sizeof(*hdr)) {
|
||||
KDIEF("ACPI table @ %p has short length %#zx", p, length);
|
||||
} else {
|
||||
p = _AcpiOsMapRoMemory(phy, length);
|
||||
}
|
||||
_AcpiTbVerifyChecksum((const uint8_t *)p, length);
|
||||
return p;
|
||||
}
|
||||
|
||||
textstartup void _AcpiXsdtInit(void) {
|
||||
if (IsMetal()) {
|
||||
size_t nents, i;
|
||||
void **ents = NULL;
|
||||
const AcpiTableRsdp *rsdp = _AcpiFindRsdp();
|
||||
if (!rsdp) {
|
||||
KINFOF("no RSDP found");
|
||||
return;
|
||||
}
|
||||
KINFOF("RSDP @ %p", rsdp);
|
||||
if (rsdp->Revision <= 1 ||
|
||||
rsdp->Length < offsetof(AcpiTableRsdp, Reserved) ||
|
||||
!rsdp->XsdtPhysicalAddress) {
|
||||
const AcpiTableRsdt *rsdt = _AcpiMapTable(rsdp->RsdtPhysicalAddress);
|
||||
nents = (rsdt->Header.Length - sizeof(rsdt->Header)) / sizeof(uint32_t);
|
||||
KINFOF("RSDT @ %p, %#zx entries", rsdt, nents);
|
||||
ents = _AcpiOsAllocate(nents * sizeof(AcpiTableHeader *));
|
||||
if (ents) {
|
||||
for (i = 0; i < nents; ++i) {
|
||||
ents[i] = _AcpiMapTable(rsdt->TableOffsetEntry[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const AcpiTableXsdt *xsdt = _AcpiMapTable(rsdp->XsdtPhysicalAddress);
|
||||
nents = (xsdt->Header.Length - sizeof(xsdt->Header)) / sizeof(uint64_t);
|
||||
KINFOF("XSDT @ %p, %#zx entries", xsdt, nents);
|
||||
ents = _AcpiOsAllocate(nents * sizeof(AcpiTableHeader *));
|
||||
if (ents) {
|
||||
for (i = 0; i < nents; ++i) {
|
||||
ents[i] = _AcpiMapTable(xsdt->TableOffsetEntry[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ents) {
|
||||
KWARNF("no memory for mapped RSDT/XSDT entries");
|
||||
nents = 0;
|
||||
}
|
||||
_AcpiXsdtEntries = ents;
|
||||
_AcpiXsdtNumEntries = nents;
|
||||
}
|
||||
}
|
||||
|
||||
textstartup AcpiStatus _AcpiGetTableImpl(uint32_t sig, uint32_t inst,
|
||||
void **phdr) {
|
||||
void **p = _AcpiXsdtEntries;
|
||||
size_t n = _AcpiXsdtNumEntries;
|
||||
while (n-- != 0) {
|
||||
AcpiTableHeader *h = *p++;
|
||||
if (READ32LE(h->Signature) != sig) continue;
|
||||
if (inst-- != 0) continue;
|
||||
*phdr = h;
|
||||
return kAcpiOk;
|
||||
}
|
||||
return kAcpiExNotFound;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -0,0 +1,232 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/color.internal.h"
|
||||
|
||||
/**
|
||||
* AcpiStatus values.
|
||||
*/
|
||||
#define kAcpiOk 0x0000
|
||||
#define kAcpiExNotFound 0x0005
|
||||
#define kAcpiExBadHeader 0x2002
|
||||
#define kAcpiExBadChecksum 0x2003
|
||||
|
||||
/**
|
||||
* Flags for AcpiTableMadt::Flags.
|
||||
*/
|
||||
#define kAcpiMadtPcAtCompat 0x0001
|
||||
|
||||
/**
|
||||
* Flags for AcpiTableFadt::BootFlags.
|
||||
*/
|
||||
#define kAcpiFadtLegacyDevices 0x0001
|
||||
#define kAcpiFadt8042 0x0002
|
||||
#define kAcpiFadtNoVga 0x0004
|
||||
#define kAcpiFadtNoMsi 0x0008
|
||||
#define kAcpiFadtNoAspm 0x0010
|
||||
#define kAcpiFadtNoCmosRtc 0x0020
|
||||
|
||||
/**
|
||||
* Values for AcpiSubtableHeader::Type under an AcpiTableMadt.
|
||||
*/
|
||||
#define kAcpiMadtLocalApic 0
|
||||
#define kAcpiMadtIoApic 1
|
||||
#define kAcpiMadtIntOverride 2
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
/**
|
||||
* @fileoverview Declarations for bare metal code to interact with ACPI
|
||||
*
|
||||
* @see UEFI Forum, Inc. Advanced Configuration and Power Interface (ACPI)
|
||||
* Specification, Version 6.5, 2022. https://uefi.org/specifications
|
||||
* @see Intel Corporation. ACPI Component Architecture: User Guide and
|
||||
* Programmer Reference, Revision 6.2, 2017. https://acpica.org
|
||||
*/
|
||||
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Structure of an ACPI Root System Description Pointer (RSDP) table. This
|
||||
* points to either an RSDT, or an XSDT, or both.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t Signature[8];
|
||||
uint8_t Checksum;
|
||||
uint8_t OemId[6];
|
||||
uint8_t Revision;
|
||||
uint32_t RsdtPhysicalAddress;
|
||||
uint32_t Length;
|
||||
uint64_t XsdtPhysicalAddress;
|
||||
uint8_t ExtendedChecksum;
|
||||
uint8_t Reserved[3];
|
||||
} AcpiTableRsdp;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Structure of a System Description Table Header, which appears at the
|
||||
* beginning of each ACPI system description table.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
uint8_t Signature[4];
|
||||
uint32_t Length;
|
||||
uint8_t Revision;
|
||||
uint8_t Checksum;
|
||||
uint8_t OemId[6];
|
||||
uint8_t OemTableId[8];
|
||||
uint32_t OemRevision;
|
||||
uint8_t AslCompilerId[4];
|
||||
uint32_t AslCompilerRevision;
|
||||
} AcpiTableHeader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* ACPI Root System Description Table (RSDT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint32_t TableOffsetEntry[];
|
||||
} AcpiTableRsdt;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* ACPI Extended System Description Table (XSDT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint64_t TableOffsetEntry[];
|
||||
} AcpiTableXsdt;
|
||||
|
||||
typedef struct thatispacked {
|
||||
uint8_t SpaceId;
|
||||
uint8_t BitWidth;
|
||||
uint8_t BitOffset;
|
||||
uint8_t AccessWidth;
|
||||
uint64_t Address;
|
||||
} AcpiGenericAddress;
|
||||
|
||||
typedef struct thatispacked {
|
||||
uint8_t Type;
|
||||
uint8_t Length;
|
||||
} AcpiSubtableHeader;
|
||||
|
||||
typedef struct thatispacked {
|
||||
AcpiSubtableHeader Header;
|
||||
uint8_t Id;
|
||||
uint8_t Reserved;
|
||||
uint32_t Address;
|
||||
uint32_t GlobalIrqBase;
|
||||
} AcpiMadtIoApic;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* ACPI Multiple APIC Description Table (MADT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint32_t Address; /* local APIC address */
|
||||
uint32_t Flags; /* multiple APIC flags */
|
||||
char Subtable[]; /* ...interrupt controller structures... */
|
||||
} AcpiTableMadt;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Fixed ACPI Description Table (FADT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint32_t Facs;
|
||||
uint32_t Dsdt;
|
||||
uint8_t Model;
|
||||
uint8_t PreferredProfile;
|
||||
uint16_t SciInterrupt;
|
||||
uint32_t SmiCommand;
|
||||
uint8_t AcpiEnable;
|
||||
uint8_t AcpiDisable;
|
||||
uint8_t S4BiosRequest;
|
||||
uint8_t PstateControl;
|
||||
uint32_t Pm1aEventBlock;
|
||||
uint32_t Pm1bEventBlock;
|
||||
uint32_t Pm1aControlBlock;
|
||||
uint32_t Pm1bControlBlock;
|
||||
uint32_t Pm2ControlBlock;
|
||||
uint32_t PmTimerBlock;
|
||||
uint32_t Gpe0Block;
|
||||
uint32_t Gpe1Block;
|
||||
uint8_t Pm1EventLength;
|
||||
uint8_t Pm1ControlLength;
|
||||
uint8_t Pm2ControlLength;
|
||||
uint8_t PmTimerLength;
|
||||
uint8_t Gpe0BlockLength;
|
||||
uint8_t Gpe1BlockLength;
|
||||
uint8_t Gpe1Base;
|
||||
uint8_t CstControl;
|
||||
uint16_t C2Latency;
|
||||
uint16_t C3Latency;
|
||||
uint16_t FlushSize;
|
||||
uint16_t FlushStride;
|
||||
uint8_t DutyOffset;
|
||||
uint8_t DutyWidth;
|
||||
uint8_t DayAlarm;
|
||||
uint8_t MonthAlarm;
|
||||
uint8_t Century;
|
||||
uint16_t BootFlags;
|
||||
uint8_t Reserved;
|
||||
uint32_t Flags;
|
||||
AcpiGenericAddress ResetRegister;
|
||||
uint8_t ResetValue;
|
||||
uint16_t ArmBootFlags;
|
||||
uint8_t MinorRevision;
|
||||
uint64_t XFacs;
|
||||
uint64_t XDsdt;
|
||||
AcpiGenericAddress XPm1aEventBlock;
|
||||
AcpiGenericAddress XPm1bEventBlock;
|
||||
AcpiGenericAddress XPm1aControlBlock;
|
||||
AcpiGenericAddress XPm1bControlBlock;
|
||||
AcpiGenericAddress XPm2ControlBlock;
|
||||
AcpiGenericAddress XPmTimerBlock;
|
||||
AcpiGenericAddress XGpe0Block;
|
||||
AcpiGenericAddress XGpe1Block;
|
||||
AcpiGenericAddress SleepControl;
|
||||
AcpiGenericAddress SleepStatus;
|
||||
uint64_t HypervisorId;
|
||||
} AcpiTableFadt;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* ACPI Differentiated System Description Table (DSDT) structure.
|
||||
*/
|
||||
typedef struct thatispacked {
|
||||
AcpiTableHeader Header;
|
||||
uint8_t Aml[];
|
||||
} AcpiTableDsdt;
|
||||
|
||||
typedef uint32_t AcpiStatus;
|
||||
|
||||
extern size_t _AcpiXsdtNumEntries, _AcpiNumIoApics;
|
||||
extern void **_AcpiXsdtEntries;
|
||||
extern uint16_t _AcpiBootFlags;
|
||||
extern uint32_t _AcpiMadtFlags;
|
||||
extern const AcpiMadtIoApic **_AcpiIoApics;
|
||||
|
||||
extern void *_AcpiOsMapUncachedMemory(uintptr_t, size_t);
|
||||
extern void *_AcpiOsAllocate(size_t);
|
||||
extern void *_AcpiMapTable(uintptr_t);
|
||||
|
||||
extern AcpiStatus _AcpiGetTableImpl(uint32_t, uint32_t, void **);
|
||||
|
||||
forceinline bool _AcpiSuccess(AcpiStatus __sta) {
|
||||
return __sta == kAcpiOk;
|
||||
}
|
||||
|
||||
forceinline AcpiStatus _AcpiGetTable(const char __sig[4], uint32_t __inst,
|
||||
void **__phdr) {
|
||||
uint8_t __sig_copy[4] = { __sig[0], __sig[1], __sig[2], __sig[3] };
|
||||
return _AcpiGetTableImpl(READ32LE(__sig_copy), __inst, __phdr);
|
||||
}
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_ */
|
||||
@@ -0,0 +1,57 @@
|
||||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
|
||||
|
||||
PKGS += LIBC_IRQ
|
||||
|
||||
LIBC_IRQ_ARTIFACTS += LIBC_IRQ_A
|
||||
LIBC_IRQ_A = o/$(MODE)/libc/irq/irq.a
|
||||
LIBC_IRQ_A_FILES := $(wildcard libc/irq/*)
|
||||
LIBC_IRQ_A_HDRS = $(filter %.h,$(LIBC_IRQ_A_FILES))
|
||||
LIBC_IRQ_A_INCS = $(filter %.inc,$(LIBC_IRQ_A_FILES))
|
||||
LIBC_IRQ_A_SRCS_S = $(filter %.S,$(LIBC_IRQ_A_FILES))
|
||||
LIBC_IRQ_A_SRCS_C = $(filter %.c,$(LIBC_IRQ_A_FILES))
|
||||
|
||||
LIBC_IRQ = \
|
||||
$(LIBC_IRQ_A_DEPS) \
|
||||
$(LIBC_IRQ_A)
|
||||
|
||||
LIBC_IRQ_A_SRCS = \
|
||||
$(LIBC_IRQ_A_SRCS_S) \
|
||||
$(LIBC_IRQ_A_SRCS_C)
|
||||
|
||||
LIBC_IRQ_A_OBJS = \
|
||||
$(LIBC_IRQ_A_SRCS_S:%.S=o/$(MODE)/%.o) \
|
||||
$(LIBC_IRQ_A_SRCS_C:%.c=o/$(MODE)/%.o)
|
||||
|
||||
LIBC_IRQ_A_CHECKS = \
|
||||
$(LIBC_IRQ_A).pkg \
|
||||
$(LIBC_IRQ_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
LIBC_IRQ_A_DIRECTDEPS = \
|
||||
LIBC_INTRIN \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV
|
||||
|
||||
LIBC_IRQ_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(LIBC_IRQ_A_DIRECTDEPS),$($(x))))
|
||||
|
||||
$(LIBC_IRQ_A):libc/irq/ \
|
||||
$(LIBC_IRQ_A).pkg \
|
||||
$(LIBC_IRQ_A_OBJS)
|
||||
|
||||
$(LIBC_IRQ_A).pkg: \
|
||||
$(LIBC_IRQ_A_OBJS) \
|
||||
$(foreach x,$(LIBC_IRQ_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
LIBC_IRQ_LIBS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)))
|
||||
LIBC_IRQ_SRCS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_IRQ_HDRS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_HDRS))
|
||||
LIBC_IRQ_INCS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_INCS))
|
||||
LIBC_IRQ_BINS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_BINS))
|
||||
LIBC_IRQ_CHECKS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_CHECKS))
|
||||
LIBC_IRQ_OBJS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_OBJS))
|
||||
LIBC_IRQ_TESTS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_TESTS))
|
||||
$(LIBC_IRQ_OBJS): $(BUILD_FILES) libc/irq/irq.mk
|
||||
|
||||
.PHONY: o/$(MODE)/libc/irq
|
||||
o/$(MODE)/libc/irq: $(LIBC_IRQ_CHECKS)
|
||||
@@ -0,0 +1,4 @@
|
||||
#ifndef COSMOPOLITAN_LIBC_ISYSTEM_MUTEX_
|
||||
#define COSMOPOLITAN_LIBC_ISYSTEM_MUTEX_
|
||||
#include "third_party/libcxx/shared_mutex"
|
||||
#endif /* COSMOPOLITAN_LIBC_ISYSTEM_MUTEX_ */
|
||||
@@ -20,6 +20,7 @@ o/$(MODE)/libc: o/$(MODE)/libc/calls \
|
||||
o/$(MODE)/libc/elf \
|
||||
o/$(MODE)/libc/fmt \
|
||||
o/$(MODE)/libc/intrin \
|
||||
o/$(MODE)/libc/irq \
|
||||
o/$(MODE)/libc/log \
|
||||
o/$(MODE)/libc/mem \
|
||||
o/$(MODE)/libc/nexgen32e \
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/cxaatexit.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
@@ -94,6 +95,7 @@ dontasan void CheckForMemoryLeaks(void) {
|
||||
exit(0);
|
||||
}
|
||||
_pthread_unwind(_pthread_self());
|
||||
__cxa_thread_finalize();
|
||||
_pthread_unkey(__get_tls());
|
||||
_pthread_ungarbage();
|
||||
__cxa_finalize(0);
|
||||
|
||||
@@ -103,6 +103,18 @@
|
||||
0x96, 0xFB, 0x7A, 0xDE, 0xD0, 0x80, 0x51, 0x6A \
|
||||
} \
|
||||
}
|
||||
#define ACPI_20_TABLE_GUID \
|
||||
{ \
|
||||
0x8868E871, 0xE4F1, 0x11D3, { \
|
||||
0xBC, 0x22, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 \
|
||||
} \
|
||||
}
|
||||
#define ACPI_10_TABLE_GUID \
|
||||
{ \
|
||||
0xEB9D2D30, 0x2D88, 0x11D3, { \
|
||||
0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D \
|
||||
} \
|
||||
}
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
@@ -43,6 +43,8 @@ struct EfiArgs {
|
||||
|
||||
static EFI_GUID kEfiLoadedImageProtocol = LOADED_IMAGE_PROTOCOL;
|
||||
static EFI_GUID kEfiGraphicsOutputProtocol = GRAPHICS_OUTPUT_PROTOCOL;
|
||||
static const EFI_GUID kEfiAcpi20TableGuid = ACPI_20_TABLE_GUID;
|
||||
static const EFI_GUID kEfiAcpi10TableGuid = ACPI_10_TABLE_GUID;
|
||||
|
||||
extern const char vga_console[];
|
||||
extern void _EfiPostboot(struct mman *, uint64_t *, uintptr_t, char **);
|
||||
@@ -117,6 +119,25 @@ static void EfiInitVga(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) {
|
||||
GraphMode->FrameBufferSize, 0);
|
||||
}
|
||||
|
||||
static void EfiInitAcpi(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) {
|
||||
void *rsdp1 = NULL, *rsdp2 = NULL;
|
||||
uintptr_t n = SystemTable->NumberOfTableEntries, i;
|
||||
EFI_CONFIGURATION_TABLE *tab;
|
||||
for (i = 0, tab = SystemTable->ConfigurationTable; i < n; ++i, ++tab) {
|
||||
EFI_GUID *guid = &tab->VendorGuid;
|
||||
if (memcmp(guid, &kEfiAcpi20TableGuid, sizeof(EFI_GUID)) == 0) {
|
||||
rsdp2 = tab->VendorTable;
|
||||
} else if (memcmp(guid, &kEfiAcpi20TableGuid, sizeof(EFI_GUID)) == 0) {
|
||||
rsdp1 = tab->VendorTable;
|
||||
}
|
||||
}
|
||||
if (rsdp2) {
|
||||
mm->pc_acpi_rsdp = (uintptr_t)rsdp2;
|
||||
} else {
|
||||
mm->pc_acpi_rsdp = (uintptr_t)rsdp1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* EFI Application Entrypoint.
|
||||
*
|
||||
@@ -203,6 +224,11 @@ __msabi EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
|
||||
*/
|
||||
if (_weaken(vga_console)) EfiInitVga(mm, SystemTable);
|
||||
|
||||
/*
|
||||
* Gets a pointer to the ACPI RSDP.
|
||||
*/
|
||||
EfiInitAcpi(mm, SystemTable);
|
||||
|
||||
/*
|
||||
* Asks UEFI which parts of our RAM we're allowed to use.
|
||||
*/
|
||||
|
||||
@@ -53,9 +53,9 @@ int login_tty(int fd) {
|
||||
errno = e;
|
||||
// take control of teletypewriter (requires being leader)
|
||||
if ((rc = sys_ioctl(fd, TIOCSCTTY, 0)) != -1) {
|
||||
unassert(!sys_dup2(fd, 0, 0));
|
||||
unassert(!sys_dup2(fd, 1, 0));
|
||||
unassert(!sys_dup2(fd, 2, 0));
|
||||
unassert(sys_dup2(fd, 0, 0) == 0);
|
||||
unassert(sys_dup2(fd, 1, 0) == 1);
|
||||
unassert(sys_dup2(fd, 2, 0) == 2);
|
||||
if (fd > 2) {
|
||||
unassert(!sys_close(fd));
|
||||
}
|
||||
|
||||
@@ -39,9 +39,12 @@ struct mman {
|
||||
struct { /* 0x1d48 — starting cursor pos. */
|
||||
unsigned short y, x;
|
||||
} pc_video_curs_info;
|
||||
unsigned short pc_video_char_height; /* 0x1d4c — character height (useful
|
||||
for setting cursor shape
|
||||
in text mode) */
|
||||
unsigned short pc_video_char_height; /* 0x1d4c — character height (useful
|
||||
for setting cursor shape
|
||||
in text mode) */
|
||||
uint64_t pc_acpi_rsdp; /* 0x1d50 — pointer to ACPI RSDP;
|
||||
NULL means to search for
|
||||
it in legacy BIOS areas */
|
||||
};
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
||||
+9
-24
@@ -20,22 +20,9 @@
|
||||
#include "libc/errno.h"
|
||||
#include "libc/runtime/utmpx.h"
|
||||
|
||||
void setutent(void) {
|
||||
}
|
||||
|
||||
void endutent(void) {
|
||||
}
|
||||
|
||||
void endutxent(void) {
|
||||
}
|
||||
|
||||
struct utmp *getutent(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void updwtmp(const char *x, const struct utmp *y) {
|
||||
}
|
||||
|
||||
void updwtmpx(const char *x, const struct utmpx *y) {
|
||||
}
|
||||
|
||||
@@ -46,10 +33,6 @@ struct utmpx *pututxline(const struct utmpx *p) {
|
||||
void setutxent(void) {
|
||||
}
|
||||
|
||||
struct utmp *getutid(const struct utmp *x) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct utmpx *getutxent(void) {
|
||||
return 0;
|
||||
}
|
||||
@@ -67,10 +50,12 @@ int __utmpxname() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int utmpname(const char *x) {
|
||||
return __utmpxname();
|
||||
}
|
||||
|
||||
int utmpxname(const char *x) {
|
||||
return __utmpxname();
|
||||
}
|
||||
__weak_reference(endutxent, endutent);
|
||||
__weak_reference(setutxent, setutent);
|
||||
__weak_reference(getutxent, getutent);
|
||||
__weak_reference(getutxid, getutid);
|
||||
__weak_reference(getutxline, getutline);
|
||||
__weak_reference(pututxline, pututline);
|
||||
__weak_reference(updwtmpx, updwtmp);
|
||||
__weak_reference(__utmpxname, utmpname);
|
||||
__weak_reference(__utmpxname, utmpxname);
|
||||
@@ -0,0 +1,69 @@
|
||||
/*-*- 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/str/str.h"
|
||||
|
||||
/**
|
||||
* Joins paths, e.g.
|
||||
*
|
||||
* 0 + 0 → 0
|
||||
* "" + "" → ""
|
||||
* "a" + 0 → "a"
|
||||
* "a" + "" → "a/"
|
||||
* 0 + "b" → "b"
|
||||
* "" + "b" → "b"
|
||||
* "." + "b" → "./b"
|
||||
* "b" + "." → "b/."
|
||||
* "a" + "b" → "a/b"
|
||||
* "a/" + "b" → "a/b"
|
||||
* "a" + "b/" → "a/b/"
|
||||
* "a" + "/b" → "/b"
|
||||
*
|
||||
* @return joined path, which may be `buf`, `path`, or `other`, or null
|
||||
* if (1) `buf` didn't have enough space, or (2) both `path` and
|
||||
* `other` were null
|
||||
*/
|
||||
char *__join_paths(char *buf, size_t size, const char *path,
|
||||
const char *other) {
|
||||
size_t pathlen, otherlen;
|
||||
if (!other) return (char *)path;
|
||||
if (!path) return (char *)other;
|
||||
pathlen = strlen(path);
|
||||
if (!pathlen || *other == '/') {
|
||||
return (/*unconst*/ char *)other;
|
||||
}
|
||||
otherlen = strlen(other);
|
||||
if (path[pathlen - 1] == '/') {
|
||||
if (pathlen + otherlen + 1 <= size) {
|
||||
memmove(buf, path, pathlen);
|
||||
memmove(buf + pathlen, other, otherlen + 1);
|
||||
return buf;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (pathlen + 1 + otherlen + 1 <= size) {
|
||||
memmove(buf, path, pathlen);
|
||||
buf[pathlen] = '/';
|
||||
memmove(buf + pathlen + 1, other, otherlen + 1);
|
||||
return buf;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,6 +217,7 @@ axdx_t tprecode8to16(char16_t *, size_t, const char *);
|
||||
axdx_t tprecode16to8(char *, size_t, const char16_t *);
|
||||
bool wcsstartswith(const wchar_t *, const wchar_t *) strlenesque;
|
||||
bool wcsendswith(const wchar_t *, const wchar_t *) strlenesque;
|
||||
char *__join_paths(char *, size_t, const char *, const char *) __wur;
|
||||
#endif /* _COSMO_SOURCE */
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*-*- 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/intrin/cxaatexit.internal.h"
|
||||
|
||||
int __cxa_thread_atexit(void *fun, void *arg, void *dso_symbol) {
|
||||
return __cxa_thread_atexit_impl(fun, arg, dso_symbol);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*-*- 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/intrin/cxaatexit.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
||||
struct Dtor {
|
||||
void *fun;
|
||||
void *arg;
|
||||
struct Dtor *next;
|
||||
};
|
||||
|
||||
static _Thread_local struct Dtor *__cxa_thread_atexit_list;
|
||||
|
||||
void __cxa_thread_finalize(void) {
|
||||
struct Dtor *dtor;
|
||||
while ((dtor = __cxa_thread_atexit_list)) {
|
||||
__cxa_thread_atexit_list = dtor->next;
|
||||
((void (*)(void *))dtor->fun)(dtor->arg);
|
||||
free(dtor);
|
||||
}
|
||||
}
|
||||
|
||||
int __cxa_thread_atexit_impl(void *fun, void *arg, void *dso_symbol) {
|
||||
struct Dtor *dtor;
|
||||
if (!(dtor = malloc(sizeof(struct Dtor)))) return -1;
|
||||
dtor->fun = fun;
|
||||
dtor->arg = arg;
|
||||
dtor->next = __cxa_thread_atexit_list;
|
||||
__cxa_thread_atexit_list = dtor;
|
||||
return 0;
|
||||
}
|
||||
@@ -57,6 +57,7 @@
|
||||
|
||||
__static_yoink("nsync_mu_lock");
|
||||
__static_yoink("nsync_mu_unlock");
|
||||
__static_yoink("nsync_mu_trylock");
|
||||
__static_yoink("nsync_mu_rlock");
|
||||
__static_yoink("nsync_mu_runlock");
|
||||
__static_yoink("_pthread_atfork");
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/cxaatexit.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
@@ -110,6 +111,9 @@ wontreturn void pthread_exit(void *rc) {
|
||||
|
||||
// free resources
|
||||
_pthread_unwind(pt);
|
||||
if (_weaken(__cxa_thread_finalize)) {
|
||||
_weaken(__cxa_thread_finalize)();
|
||||
}
|
||||
_pthread_unkey(tib);
|
||||
_pthread_ungarbage();
|
||||
_pthread_decimate();
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*-*- 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/errno.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
||||
/**
|
||||
* Attempts acquiring read lock on read-write lock.
|
||||
*
|
||||
* @return 0 if lock was acquired, otherwise an errno
|
||||
* @raise EBUSY if lock is currently held in write mode
|
||||
* @raise EAGAIN if maximum number of read locks are held
|
||||
* @raise EINVAL if `rwlock` doesn't refer to an initialized r/w lock
|
||||
*/
|
||||
errno_t pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) {
|
||||
if (nsync_mu_rtrylock((nsync_mu *)rwlock)) {
|
||||
return 0;
|
||||
} else {
|
||||
return EBUSY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*-*- 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/errno.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "third_party/nsync/mu.h"
|
||||
|
||||
/**
|
||||
* Attempts acquiring write lock on read-write lock.
|
||||
*
|
||||
* @return 0 if lock was acquired, otherwise an errno
|
||||
* @raise EBUSY if lock is currently held in read or write mode
|
||||
* @raise EINVAL if `rwlock` doesn't refer to an initialized r/w lock
|
||||
*/
|
||||
errno_t pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) {
|
||||
if (nsync_mu_trylock((nsync_mu *)rwlock)) {
|
||||
rwlock->_iswrite = 1;
|
||||
return 0;
|
||||
} else {
|
||||
return EBUSY;
|
||||
}
|
||||
}
|
||||
@@ -171,9 +171,8 @@ static struct Semaphore *sem_open_get(const sem_t *sem,
|
||||
sem_t *sem_open(const char *name, int oflag, ...) {
|
||||
sem_t *sem;
|
||||
va_list va;
|
||||
const char *path;
|
||||
char path[78];
|
||||
struct Semaphore *s;
|
||||
char pathbuf[PATH_MAX];
|
||||
unsigned mode = 0, value = 0;
|
||||
|
||||
va_start(va, oflag);
|
||||
@@ -206,9 +205,7 @@ sem_t *sem_open(const char *name, int oflag, ...) {
|
||||
return SEM_FAILED;
|
||||
}
|
||||
}
|
||||
if (!(path = sem_path_np(name, pathbuf, sizeof(pathbuf)))) {
|
||||
return SEM_FAILED;
|
||||
}
|
||||
shm_path_np(name, path);
|
||||
BLOCK_CANCELATION;
|
||||
sem_open_init();
|
||||
sem_open_lock();
|
||||
@@ -321,10 +318,9 @@ int sem_close(sem_t *sem) {
|
||||
* @raise ENAMETOOLONG if too long
|
||||
*/
|
||||
int sem_unlink(const char *name) {
|
||||
const char *path;
|
||||
char path[78];
|
||||
int rc, e = errno;
|
||||
struct Semaphore *s;
|
||||
char pathbuf[PATH_MAX];
|
||||
|
||||
#if 0
|
||||
if (IsXnuSilicon()) {
|
||||
@@ -332,7 +328,7 @@ int sem_unlink(const char *name) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(path = sem_path_np(name, pathbuf, sizeof(pathbuf)))) return -1;
|
||||
shm_path_np(name, path);
|
||||
if ((rc = unlink(path)) == -1 && IsWindows() && errno == EACCES) {
|
||||
sem_open_init();
|
||||
sem_open_lock();
|
||||
|
||||
@@ -38,7 +38,6 @@ int sem_getvalue(sem_t *, int *);
|
||||
sem_t *sem_open(const char *, int, ...);
|
||||
int sem_close(sem_t *);
|
||||
int sem_unlink(const char *);
|
||||
const char *sem_path_np(const char *, char *, size_t);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
||||
@@ -334,3 +334,8 @@ double erfc(double x)
|
||||
}
|
||||
return sign ? 2 - 0x1p-1022 : 0x1p-1022*0x1p-1022;
|
||||
}
|
||||
|
||||
#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
|
||||
__weak_reference(erf, erfl);
|
||||
__weak_reference(erfc, erfcl);
|
||||
#endif
|
||||
@@ -0,0 +1,381 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╚──────────────────────────────────────────────────────────────────────────────╝
|
||||
│ │
|
||||
│ Musl Libc │
|
||||
│ Copyright © 2005-2014 Rich Felker, et al. │
|
||||
│ │
|
||||
│ Permission is hereby granted, free of charge, to any person obtaining │
|
||||
│ a copy of this software and associated documentation files (the │
|
||||
│ "Software"), to deal in the Software without restriction, including │
|
||||
│ without limitation the rights to use, copy, modify, merge, publish, │
|
||||
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
||||
│ permit persons to whom the Software is furnished to do so, subject to │
|
||||
│ the following conditions: │
|
||||
│ │
|
||||
│ The above copyright notice and this permission notice shall be │
|
||||
│ included in all copies or substantial portions of the Software. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
||||
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
||||
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
||||
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
||||
│ │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/math.h"
|
||||
#include "libc/tinymath/ldshape.internal.h"
|
||||
#if LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
OpenBSD libm (ISC License)\\n\
|
||||
Copyright (c) 2008 Stephen L. Moshier <[email protected]>\"");
|
||||
asm(".ident\t\"\\n\\n\
|
||||
Musl libc (MIT License)\\n\
|
||||
Copyright 2005-2014 Rich Felker, et. al.\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
// clang-format off
|
||||
|
||||
/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_erfl.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2008 Stephen L. Moshier <[email protected]>
|
||||
*
|
||||
* Permission to use, copy, modify, and 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.
|
||||
*/
|
||||
/* double erf(double x)
|
||||
* double erfc(double x)
|
||||
* x
|
||||
* 2 |\
|
||||
* erf(x) = --------- | exp(-t*t)dt
|
||||
* sqrt(pi) \|
|
||||
* 0
|
||||
*
|
||||
* erfc(x) = 1-erf(x)
|
||||
* Note that
|
||||
* erf(-x) = -erf(x)
|
||||
* erfc(-x) = 2 - erfc(x)
|
||||
*
|
||||
* Method:
|
||||
* 1. For |x| in [0, 0.84375]
|
||||
* erf(x) = x + x*R(x^2)
|
||||
* erfc(x) = 1 - erf(x) if x in [-.84375,0.25]
|
||||
* = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375]
|
||||
* Remark. The formula is derived by noting
|
||||
* erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....)
|
||||
* and that
|
||||
* 2/sqrt(pi) = 1.128379167095512573896158903121545171688
|
||||
* is close to one. The interval is chosen because the fix
|
||||
* point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is
|
||||
* near 0.6174), and by some experiment, 0.84375 is chosen to
|
||||
* guarantee the error is less than one ulp for erf.
|
||||
*
|
||||
* 2. For |x| in [0.84375,1.25], let s = |x| - 1, and
|
||||
* c = 0.84506291151 rounded to single (24 bits)
|
||||
* erf(x) = sign(x) * (c + P1(s)/Q1(s))
|
||||
* erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0
|
||||
* 1+(c+P1(s)/Q1(s)) if x < 0
|
||||
* Remark: here we use the taylor series expansion at x=1.
|
||||
* erf(1+s) = erf(1) + s*Poly(s)
|
||||
* = 0.845.. + P1(s)/Q1(s)
|
||||
* Note that |P1/Q1|< 0.078 for x in [0.84375,1.25]
|
||||
*
|
||||
* 3. For x in [1.25,1/0.35(~2.857143)],
|
||||
* erfc(x) = (1/x)*exp(-x*x-0.5625+R1(z)/S1(z))
|
||||
* z=1/x^2
|
||||
* erf(x) = 1 - erfc(x)
|
||||
*
|
||||
* 4. For x in [1/0.35,107]
|
||||
* erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
|
||||
* = 2.0 - (1/x)*exp(-x*x-0.5625+R2(z)/S2(z))
|
||||
* if -6.666<x<0
|
||||
* = 2.0 - tiny (if x <= -6.666)
|
||||
* z=1/x^2
|
||||
* erf(x) = sign(x)*(1.0 - erfc(x)) if x < 6.666, else
|
||||
* erf(x) = sign(x)*(1.0 - tiny)
|
||||
* Note1:
|
||||
* To compute exp(-x*x-0.5625+R/S), let s be a single
|
||||
* precision number and s := x; then
|
||||
* -x*x = -s*s + (s-x)*(s+x)
|
||||
* exp(-x*x-0.5626+R/S) =
|
||||
* exp(-s*s-0.5625)*exp((s-x)*(s+x)+R/S);
|
||||
* Note2:
|
||||
* Here 4 and 5 make use of the asymptotic series
|
||||
* exp(-x*x)
|
||||
* erfc(x) ~ ---------- * ( 1 + Poly(1/x^2) )
|
||||
* x*sqrt(pi)
|
||||
*
|
||||
* 5. For inf > x >= 107
|
||||
* erf(x) = sign(x) *(1 - tiny) (raise inexact)
|
||||
* erfc(x) = tiny*tiny (raise underflow) if x > 0
|
||||
* = 2 - tiny if x<0
|
||||
*
|
||||
* 7. Special case:
|
||||
* erf(0) = 0, erf(inf) = 1, erf(-inf) = -1,
|
||||
* erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2,
|
||||
* erfc/erf(NaN) is NaN
|
||||
*/
|
||||
|
||||
static const long double
|
||||
erx = 0.845062911510467529296875L,
|
||||
|
||||
/*
|
||||
* Coefficients for approximation to erf on [0,0.84375]
|
||||
*/
|
||||
/* 8 * (2/sqrt(pi) - 1) */
|
||||
efx8 = 1.0270333367641005911692712249723613735048E0L,
|
||||
pp[6] = {
|
||||
1.122751350964552113068262337278335028553E6L,
|
||||
-2.808533301997696164408397079650699163276E6L,
|
||||
-3.314325479115357458197119660818768924100E5L,
|
||||
-6.848684465326256109712135497895525446398E4L,
|
||||
-2.657817695110739185591505062971929859314E3L,
|
||||
-1.655310302737837556654146291646499062882E2L,
|
||||
},
|
||||
qq[6] = {
|
||||
8.745588372054466262548908189000448124232E6L,
|
||||
3.746038264792471129367533128637019611485E6L,
|
||||
7.066358783162407559861156173539693900031E5L,
|
||||
7.448928604824620999413120955705448117056E4L,
|
||||
4.511583986730994111992253980546131408924E3L,
|
||||
1.368902937933296323345610240009071254014E2L,
|
||||
/* 1.000000000000000000000000000000000000000E0 */
|
||||
},
|
||||
|
||||
/*
|
||||
* Coefficients for approximation to erf in [0.84375,1.25]
|
||||
*/
|
||||
/* erf(x+1) = 0.845062911510467529296875 + pa(x)/qa(x)
|
||||
-0.15625 <= x <= +.25
|
||||
Peak relative error 8.5e-22 */
|
||||
pa[8] = {
|
||||
-1.076952146179812072156734957705102256059E0L,
|
||||
1.884814957770385593365179835059971587220E2L,
|
||||
-5.339153975012804282890066622962070115606E1L,
|
||||
4.435910679869176625928504532109635632618E1L,
|
||||
1.683219516032328828278557309642929135179E1L,
|
||||
-2.360236618396952560064259585299045804293E0L,
|
||||
1.852230047861891953244413872297940938041E0L,
|
||||
9.394994446747752308256773044667843200719E-2L,
|
||||
},
|
||||
qa[7] = {
|
||||
4.559263722294508998149925774781887811255E2L,
|
||||
3.289248982200800575749795055149780689738E2L,
|
||||
2.846070965875643009598627918383314457912E2L,
|
||||
1.398715859064535039433275722017479994465E2L,
|
||||
6.060190733759793706299079050985358190726E1L,
|
||||
2.078695677795422351040502569964299664233E1L,
|
||||
4.641271134150895940966798357442234498546E0L,
|
||||
/* 1.000000000000000000000000000000000000000E0 */
|
||||
},
|
||||
|
||||
/*
|
||||
* Coefficients for approximation to erfc in [1.25,1/0.35]
|
||||
*/
|
||||
/* erfc(1/x) = x exp (-1/x^2 - 0.5625 + ra(x^2)/sa(x^2))
|
||||
1/2.85711669921875 < 1/x < 1/1.25
|
||||
Peak relative error 3.1e-21 */
|
||||
ra[] = {
|
||||
1.363566591833846324191000679620738857234E-1L,
|
||||
1.018203167219873573808450274314658434507E1L,
|
||||
1.862359362334248675526472871224778045594E2L,
|
||||
1.411622588180721285284945138667933330348E3L,
|
||||
5.088538459741511988784440103218342840478E3L,
|
||||
8.928251553922176506858267311750789273656E3L,
|
||||
7.264436000148052545243018622742770549982E3L,
|
||||
2.387492459664548651671894725748959751119E3L,
|
||||
2.220916652813908085449221282808458466556E2L,
|
||||
},
|
||||
sa[] = {
|
||||
-1.382234625202480685182526402169222331847E1L,
|
||||
-3.315638835627950255832519203687435946482E2L,
|
||||
-2.949124863912936259747237164260785326692E3L,
|
||||
-1.246622099070875940506391433635999693661E4L,
|
||||
-2.673079795851665428695842853070996219632E4L,
|
||||
-2.880269786660559337358397106518918220991E4L,
|
||||
-1.450600228493968044773354186390390823713E4L,
|
||||
-2.874539731125893533960680525192064277816E3L,
|
||||
-1.402241261419067750237395034116942296027E2L,
|
||||
/* 1.000000000000000000000000000000000000000E0 */
|
||||
},
|
||||
|
||||
/*
|
||||
* Coefficients for approximation to erfc in [1/.35,107]
|
||||
*/
|
||||
/* erfc(1/x) = x exp (-1/x^2 - 0.5625 + rb(x^2)/sb(x^2))
|
||||
1/6.6666259765625 < 1/x < 1/2.85711669921875
|
||||
Peak relative error 4.2e-22 */
|
||||
rb[] = {
|
||||
-4.869587348270494309550558460786501252369E-5L,
|
||||
-4.030199390527997378549161722412466959403E-3L,
|
||||
-9.434425866377037610206443566288917589122E-2L,
|
||||
-9.319032754357658601200655161585539404155E-1L,
|
||||
-4.273788174307459947350256581445442062291E0L,
|
||||
-8.842289940696150508373541814064198259278E0L,
|
||||
-7.069215249419887403187988144752613025255E0L,
|
||||
-1.401228723639514787920274427443330704764E0L,
|
||||
},
|
||||
sb[] = {
|
||||
4.936254964107175160157544545879293019085E-3L,
|
||||
1.583457624037795744377163924895349412015E-1L,
|
||||
1.850647991850328356622940552450636420484E0L,
|
||||
9.927611557279019463768050710008450625415E0L,
|
||||
2.531667257649436709617165336779212114570E1L,
|
||||
2.869752886406743386458304052862814690045E1L,
|
||||
1.182059497870819562441683560749192539345E1L,
|
||||
/* 1.000000000000000000000000000000000000000E0 */
|
||||
},
|
||||
/* erfc(1/x) = x exp (-1/x^2 - 0.5625 + rc(x^2)/sc(x^2))
|
||||
1/107 <= 1/x <= 1/6.6666259765625
|
||||
Peak relative error 1.1e-21 */
|
||||
rc[] = {
|
||||
-8.299617545269701963973537248996670806850E-5L,
|
||||
-6.243845685115818513578933902532056244108E-3L,
|
||||
-1.141667210620380223113693474478394397230E-1L,
|
||||
-7.521343797212024245375240432734425789409E-1L,
|
||||
-1.765321928311155824664963633786967602934E0L,
|
||||
-1.029403473103215800456761180695263439188E0L,
|
||||
},
|
||||
sc[] = {
|
||||
8.413244363014929493035952542677768808601E-3L,
|
||||
2.065114333816877479753334599639158060979E-1L,
|
||||
1.639064941530797583766364412782135680148E0L,
|
||||
4.936788463787115555582319302981666347450E0L,
|
||||
5.005177727208955487404729933261347679090E0L,
|
||||
/* 1.000000000000000000000000000000000000000E0 */
|
||||
};
|
||||
|
||||
static long double erfc1(long double x)
|
||||
{
|
||||
long double s,P,Q;
|
||||
|
||||
s = fabsl(x) - 1;
|
||||
P = pa[0] + s * (pa[1] + s * (pa[2] +
|
||||
s * (pa[3] + s * (pa[4] + s * (pa[5] + s * (pa[6] + s * pa[7]))))));
|
||||
Q = qa[0] + s * (qa[1] + s * (qa[2] +
|
||||
s * (qa[3] + s * (qa[4] + s * (qa[5] + s * (qa[6] + s))))));
|
||||
return 1 - erx - P / Q;
|
||||
}
|
||||
|
||||
static long double erfc2(uint32_t ix, long double x)
|
||||
{
|
||||
union ldshape u;
|
||||
long double s,z,R,S;
|
||||
|
||||
if (ix < 0x3fffa000) /* 0.84375 <= |x| < 1.25 */
|
||||
return erfc1(x);
|
||||
|
||||
x = fabsl(x);
|
||||
s = 1 / (x * x);
|
||||
if (ix < 0x4000b6db) { /* 1.25 <= |x| < 2.857 ~ 1/.35 */
|
||||
R = ra[0] + s * (ra[1] + s * (ra[2] + s * (ra[3] + s * (ra[4] +
|
||||
s * (ra[5] + s * (ra[6] + s * (ra[7] + s * ra[8])))))));
|
||||
S = sa[0] + s * (sa[1] + s * (sa[2] + s * (sa[3] + s * (sa[4] +
|
||||
s * (sa[5] + s * (sa[6] + s * (sa[7] + s * (sa[8] + s))))))));
|
||||
} else if (ix < 0x4001d555) { /* 2.857 <= |x| < 6.6666259765625 */
|
||||
R = rb[0] + s * (rb[1] + s * (rb[2] + s * (rb[3] + s * (rb[4] +
|
||||
s * (rb[5] + s * (rb[6] + s * rb[7]))))));
|
||||
S = sb[0] + s * (sb[1] + s * (sb[2] + s * (sb[3] + s * (sb[4] +
|
||||
s * (sb[5] + s * (sb[6] + s))))));
|
||||
} else { /* 6.666 <= |x| < 107 (erfc only) */
|
||||
R = rc[0] + s * (rc[1] + s * (rc[2] + s * (rc[3] +
|
||||
s * (rc[4] + s * rc[5]))));
|
||||
S = sc[0] + s * (sc[1] + s * (sc[2] + s * (sc[3] +
|
||||
s * (sc[4] + s))));
|
||||
}
|
||||
u.f = x;
|
||||
u.i.m &= -1ULL << 40;
|
||||
z = u.f;
|
||||
return expl(-z*z - 0.5625) * expl((z - x) * (z + x) + R / S) / x;
|
||||
}
|
||||
|
||||
long double erfl(long double x)
|
||||
{
|
||||
long double r, s, z, y;
|
||||
union ldshape u = {x};
|
||||
uint32_t ix = (u.i.se & 0x7fffU)<<16 | u.i.m>>48;
|
||||
int sign = u.i.se >> 15;
|
||||
|
||||
if (ix >= 0x7fff0000)
|
||||
/* erf(nan)=nan, erf(+-inf)=+-1 */
|
||||
return 1 - 2*sign + 1/x;
|
||||
if (ix < 0x3ffed800) { /* |x| < 0.84375 */
|
||||
if (ix < 0x3fde8000) { /* |x| < 2**-33 */
|
||||
return 0.125 * (8 * x + efx8 * x); /* avoid underflow */
|
||||
}
|
||||
z = x * x;
|
||||
r = pp[0] + z * (pp[1] +
|
||||
z * (pp[2] + z * (pp[3] + z * (pp[4] + z * pp[5]))));
|
||||
s = qq[0] + z * (qq[1] +
|
||||
z * (qq[2] + z * (qq[3] + z * (qq[4] + z * (qq[5] + z)))));
|
||||
y = r / s;
|
||||
return x + x * y;
|
||||
}
|
||||
if (ix < 0x4001d555) /* |x| < 6.6666259765625 */
|
||||
y = 1 - erfc2(ix,x);
|
||||
else
|
||||
y = 1 - 0x1p-16382L;
|
||||
return sign ? -y : y;
|
||||
}
|
||||
|
||||
long double erfcl(long double x)
|
||||
{
|
||||
long double r, s, z, y;
|
||||
union ldshape u = {x};
|
||||
uint32_t ix = (u.i.se & 0x7fffU)<<16 | u.i.m>>48;
|
||||
int sign = u.i.se >> 15;
|
||||
|
||||
if (ix >= 0x7fff0000)
|
||||
/* erfc(nan) = nan, erfc(+-inf) = 0,2 */
|
||||
return 2*sign + 1/x;
|
||||
if (ix < 0x3ffed800) { /* |x| < 0.84375 */
|
||||
if (ix < 0x3fbe0000) /* |x| < 2**-65 */
|
||||
return 1.0 - x;
|
||||
z = x * x;
|
||||
r = pp[0] + z * (pp[1] +
|
||||
z * (pp[2] + z * (pp[3] + z * (pp[4] + z * pp[5]))));
|
||||
s = qq[0] + z * (qq[1] +
|
||||
z * (qq[2] + z * (qq[3] + z * (qq[4] + z * (qq[5] + z)))));
|
||||
y = r / s;
|
||||
if (ix < 0x3ffd8000) /* x < 1/4 */
|
||||
return 1.0 - (x + x * y);
|
||||
return 0.5 - (x - 0.5 + x * y);
|
||||
}
|
||||
if (ix < 0x4005d600) /* |x| < 107 */
|
||||
return sign ? 2 - erfc2(ix,x) : erfc2(ix,x);
|
||||
y = 0x1p-16382L;
|
||||
return sign ? 2 - y : y*y;
|
||||
}
|
||||
|
||||
#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
|
||||
// TODO: broken implementation to make things compile
|
||||
long double erfl(long double x)
|
||||
{
|
||||
return erf(x);
|
||||
}
|
||||
long double erfcl(long double x)
|
||||
{
|
||||
return erfc(x);
|
||||
}
|
||||
#endif /* long double is long */
|
||||
@@ -1,318 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╚──────────────────────────────────────────────────────────────────────────────╝
|
||||
│ │
|
||||
│ Musl Libc │
|
||||
│ Copyright © 2005-2014 Rich Felker, et al. │
|
||||
│ │
|
||||
│ Permission is hereby granted, free of charge, to any person obtaining │
|
||||
│ a copy of this software and associated documentation files (the │
|
||||
│ "Software"), to deal in the Software without restriction, including │
|
||||
│ without limitation the rights to use, copy, modify, merge, publish, │
|
||||
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
||||
│ permit persons to whom the Software is furnished to do so, subject to │
|
||||
│ the following conditions: │
|
||||
│ │
|
||||
│ The above copyright notice and this permission notice shall be │
|
||||
│ included in all copies or substantial portions of the Software. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
||||
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
||||
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
||||
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
||||
│ │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/math.h"
|
||||
#include "libc/tinymath/kernel.internal.h"
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
fdlibm (fdlibm license)\\n\
|
||||
Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.\"");
|
||||
asm(".ident\t\"\\n\\n\
|
||||
Musl libc (MIT License)\\n\
|
||||
Copyright 2005-2014 Rich Felker, et. al.\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
|
||||
/* clang-format off */
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_lgamma_r.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*
|
||||
*/
|
||||
/* lgamma_r(x, signgamp)
|
||||
* Reentrant version of the logarithm of the Gamma function
|
||||
* with user provide pointer for the sign of Gamma(x).
|
||||
*
|
||||
* Method:
|
||||
* 1. Argument Reduction for 0 < x <= 8
|
||||
* Since gamma(1+s)=s*gamma(s), for x in [0,8], we may
|
||||
* reduce x to a number in [1.5,2.5] by
|
||||
* lgamma(1+s) = log(s) + lgamma(s)
|
||||
* for example,
|
||||
* lgamma(7.3) = log(6.3) + lgamma(6.3)
|
||||
* = log(6.3*5.3) + lgamma(5.3)
|
||||
* = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
|
||||
* 2. Polynomial approximation of lgamma around its
|
||||
* minimum ymin=1.461632144968362245 to maintain monotonicity.
|
||||
* On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
|
||||
* Let z = x-ymin;
|
||||
* lgamma(x) = -1.214862905358496078218 + z^2*poly(z)
|
||||
* where
|
||||
* poly(z) is a 14 degree polynomial.
|
||||
* 2. Rational approximation in the primary interval [2,3]
|
||||
* We use the following approximation:
|
||||
* s = x-2.0;
|
||||
* lgamma(x) = 0.5*s + s*P(s)/Q(s)
|
||||
* with accuracy
|
||||
* |P/Q - (lgamma(x)-0.5s)| < 2**-61.71
|
||||
* Our algorithms are based on the following observation
|
||||
*
|
||||
* zeta(2)-1 2 zeta(3)-1 3
|
||||
* lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ...
|
||||
* 2 3
|
||||
*
|
||||
* where Euler = 0.5771... is the Euler constant, which is very
|
||||
* close to 0.5.
|
||||
*
|
||||
* 3. For x>=8, we have
|
||||
* lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
|
||||
* (better formula:
|
||||
* lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
|
||||
* Let z = 1/x, then we approximation
|
||||
* f(z) = lgamma(x) - (x-0.5)(log(x)-1)
|
||||
* by
|
||||
* 3 5 11
|
||||
* w = w0 + w1*z + w2*z + w3*z + ... + w6*z
|
||||
* where
|
||||
* |w - f(z)| < 2**-58.74
|
||||
*
|
||||
* 4. For negative x, since (G is gamma function)
|
||||
* -x*G(-x)*G(x) = pi/sin(pi*x),
|
||||
* we have
|
||||
* G(x) = pi/(sin(pi*x)*(-x)*G(-x))
|
||||
* since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
|
||||
* Hence, for x<0, signgam = sign(sin(pi*x)) and
|
||||
* lgamma(x) = log(|Gamma(x)|)
|
||||
* = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
|
||||
* Note: one should avoid compute pi*(-x) directly in the
|
||||
* computation of sin(pi*(-x)).
|
||||
*
|
||||
* 5. Special Cases
|
||||
* lgamma(2+s) ~ s*(1-Euler) for tiny s
|
||||
* lgamma(1) = lgamma(2) = 0
|
||||
* lgamma(x) ~ -log(|x|) for tiny x
|
||||
* lgamma(0) = lgamma(neg.integer) = inf and raise divide-by-zero
|
||||
* lgamma(inf) = inf
|
||||
* lgamma(-inf) = inf (bug for bug compatible with C99!?)
|
||||
*
|
||||
*/
|
||||
|
||||
static const double
|
||||
pi = 3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */
|
||||
a0 = 7.72156649015328655494e-02, /* 0x3FB3C467, 0xE37DB0C8 */
|
||||
a1 = 3.22467033424113591611e-01, /* 0x3FD4A34C, 0xC4A60FAD */
|
||||
a2 = 6.73523010531292681824e-02, /* 0x3FB13E00, 0x1A5562A7 */
|
||||
a3 = 2.05808084325167332806e-02, /* 0x3F951322, 0xAC92547B */
|
||||
a4 = 7.38555086081402883957e-03, /* 0x3F7E404F, 0xB68FEFE8 */
|
||||
a5 = 2.89051383673415629091e-03, /* 0x3F67ADD8, 0xCCB7926B */
|
||||
a6 = 1.19270763183362067845e-03, /* 0x3F538A94, 0x116F3F5D */
|
||||
a7 = 5.10069792153511336608e-04, /* 0x3F40B6C6, 0x89B99C00 */
|
||||
a8 = 2.20862790713908385557e-04, /* 0x3F2CF2EC, 0xED10E54D */
|
||||
a9 = 1.08011567247583939954e-04, /* 0x3F1C5088, 0x987DFB07 */
|
||||
a10 = 2.52144565451257326939e-05, /* 0x3EFA7074, 0x428CFA52 */
|
||||
a11 = 4.48640949618915160150e-05, /* 0x3F07858E, 0x90A45837 */
|
||||
tc = 1.46163214496836224576e+00, /* 0x3FF762D8, 0x6356BE3F */
|
||||
tf = -1.21486290535849611461e-01, /* 0xBFBF19B9, 0xBCC38A42 */
|
||||
/* tt = -(tail of tf) */
|
||||
tt = -3.63867699703950536541e-18, /* 0xBC50C7CA, 0xA48A971F */
|
||||
t0 = 4.83836122723810047042e-01, /* 0x3FDEF72B, 0xC8EE38A2 */
|
||||
t1 = -1.47587722994593911752e-01, /* 0xBFC2E427, 0x8DC6C509 */
|
||||
t2 = 6.46249402391333854778e-02, /* 0x3FB08B42, 0x94D5419B */
|
||||
t3 = -3.27885410759859649565e-02, /* 0xBFA0C9A8, 0xDF35B713 */
|
||||
t4 = 1.79706750811820387126e-02, /* 0x3F9266E7, 0x970AF9EC */
|
||||
t5 = -1.03142241298341437450e-02, /* 0xBF851F9F, 0xBA91EC6A */
|
||||
t6 = 6.10053870246291332635e-03, /* 0x3F78FCE0, 0xE370E344 */
|
||||
t7 = -3.68452016781138256760e-03, /* 0xBF6E2EFF, 0xB3E914D7 */
|
||||
t8 = 2.25964780900612472250e-03, /* 0x3F6282D3, 0x2E15C915 */
|
||||
t9 = -1.40346469989232843813e-03, /* 0xBF56FE8E, 0xBF2D1AF1 */
|
||||
t10 = 8.81081882437654011382e-04, /* 0x3F4CDF0C, 0xEF61A8E9 */
|
||||
t11 = -5.38595305356740546715e-04, /* 0xBF41A610, 0x9C73E0EC */
|
||||
t12 = 3.15632070903625950361e-04, /* 0x3F34AF6D, 0x6C0EBBF7 */
|
||||
t13 = -3.12754168375120860518e-04, /* 0xBF347F24, 0xECC38C38 */
|
||||
t14 = 3.35529192635519073543e-04, /* 0x3F35FD3E, 0xE8C2D3F4 */
|
||||
u0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */
|
||||
u1 = 6.32827064025093366517e-01, /* 0x3FE4401E, 0x8B005DFF */
|
||||
u2 = 1.45492250137234768737e+00, /* 0x3FF7475C, 0xD119BD6F */
|
||||
u3 = 9.77717527963372745603e-01, /* 0x3FEF4976, 0x44EA8450 */
|
||||
u4 = 2.28963728064692451092e-01, /* 0x3FCD4EAE, 0xF6010924 */
|
||||
u5 = 1.33810918536787660377e-02, /* 0x3F8B678B, 0xBF2BAB09 */
|
||||
v1 = 2.45597793713041134822e+00, /* 0x4003A5D7, 0xC2BD619C */
|
||||
v2 = 2.12848976379893395361e+00, /* 0x40010725, 0xA42B18F5 */
|
||||
v3 = 7.69285150456672783825e-01, /* 0x3FE89DFB, 0xE45050AF */
|
||||
v4 = 1.04222645593369134254e-01, /* 0x3FBAAE55, 0xD6537C88 */
|
||||
v5 = 3.21709242282423911810e-03, /* 0x3F6A5ABB, 0x57D0CF61 */
|
||||
s0 = -7.72156649015328655494e-02, /* 0xBFB3C467, 0xE37DB0C8 */
|
||||
s1 = 2.14982415960608852501e-01, /* 0x3FCB848B, 0x36E20878 */
|
||||
s2 = 3.25778796408930981787e-01, /* 0x3FD4D98F, 0x4F139F59 */
|
||||
s3 = 1.46350472652464452805e-01, /* 0x3FC2BB9C, 0xBEE5F2F7 */
|
||||
s4 = 2.66422703033638609560e-02, /* 0x3F9B481C, 0x7E939961 */
|
||||
s5 = 1.84028451407337715652e-03, /* 0x3F5E26B6, 0x7368F239 */
|
||||
s6 = 3.19475326584100867617e-05, /* 0x3F00BFEC, 0xDD17E945 */
|
||||
r1 = 1.39200533467621045958e+00, /* 0x3FF645A7, 0x62C4AB74 */
|
||||
r2 = 7.21935547567138069525e-01, /* 0x3FE71A18, 0x93D3DCDC */
|
||||
r3 = 1.71933865632803078993e-01, /* 0x3FC601ED, 0xCCFBDF27 */
|
||||
r4 = 1.86459191715652901344e-02, /* 0x3F9317EA, 0x742ED475 */
|
||||
r5 = 7.77942496381893596434e-04, /* 0x3F497DDA, 0xCA41A95B */
|
||||
r6 = 7.32668430744625636189e-06, /* 0x3EDEBAF7, 0xA5B38140 */
|
||||
w0 = 4.18938533204672725052e-01, /* 0x3FDACFE3, 0x90C97D69 */
|
||||
w1 = 8.33333333333329678849e-02, /* 0x3FB55555, 0x5555553B */
|
||||
w2 = -2.77777777728775536470e-03, /* 0xBF66C16C, 0x16B02E5C */
|
||||
w3 = 7.93650558643019558500e-04, /* 0x3F4A019F, 0x98CF38B6 */
|
||||
w4 = -5.95187557450339963135e-04, /* 0xBF4380CB, 0x8C0FE741 */
|
||||
w5 = 8.36339918996282139126e-04, /* 0x3F4B67BA, 0x4CDAD5D1 */
|
||||
w6 = -1.63092934096575273989e-03; /* 0xBF5AB89D, 0x0B9E43E4 */
|
||||
|
||||
/* sin(pi*x) assuming x > 2^-100, if sin(pi*x)==0 the sign is arbitrary */
|
||||
static double sin_pi(double x)
|
||||
{
|
||||
int n;
|
||||
|
||||
/* spurious inexact if odd int */
|
||||
x = 2.0*(x*0.5 - floor(x*0.5)); /* x mod 2.0 */
|
||||
|
||||
n = (int)(x*4.0);
|
||||
n = (n+1)/2;
|
||||
x -= n*0.5f;
|
||||
x *= pi;
|
||||
|
||||
switch (n) {
|
||||
default: /* case 4: */
|
||||
case 0: return __sin(x, 0.0, 0);
|
||||
case 1: return __cos(x, 0.0);
|
||||
case 2: return __sin(-x, 0.0, 0);
|
||||
case 3: return -__cos(x, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
double lgamma_r(double x, int *signgamp)
|
||||
{
|
||||
union {double f; uint64_t i;} u = {x};
|
||||
double_t t,y,z,nadj,p,p1,p2,p3,q,r,w;
|
||||
uint32_t ix;
|
||||
int sign,i;
|
||||
|
||||
/* purge off +-inf, NaN, +-0, tiny and negative arguments */
|
||||
*signgamp = 1;
|
||||
sign = u.i>>63;
|
||||
ix = u.i>>32 & 0x7fffffff;
|
||||
if (ix >= 0x7ff00000)
|
||||
return x*x;
|
||||
if (ix < (0x3ff-70)<<20) { /* |x|<2**-70, return -log(|x|) */
|
||||
if(sign) {
|
||||
x = -x;
|
||||
*signgamp = -1;
|
||||
}
|
||||
return -log(x);
|
||||
}
|
||||
if (sign) {
|
||||
x = -x;
|
||||
t = sin_pi(x);
|
||||
if (t == 0.0) /* -integer */
|
||||
return 1.0/(x-x);
|
||||
if (t > 0.0)
|
||||
*signgamp = -1;
|
||||
else
|
||||
t = -t;
|
||||
nadj = log(pi/(t*x));
|
||||
}
|
||||
|
||||
/* purge off 1 and 2 */
|
||||
if ((ix == 0x3ff00000 || ix == 0x40000000) && (uint32_t)u.i == 0)
|
||||
r = 0;
|
||||
/* for x < 2.0 */
|
||||
else if (ix < 0x40000000) {
|
||||
if (ix <= 0x3feccccc) { /* lgamma(x) = lgamma(x+1)-log(x) */
|
||||
r = -log(x);
|
||||
if (ix >= 0x3FE76944) {
|
||||
y = 1.0 - x;
|
||||
i = 0;
|
||||
} else if (ix >= 0x3FCDA661) {
|
||||
y = x - (tc-1.0);
|
||||
i = 1;
|
||||
} else {
|
||||
y = x;
|
||||
i = 2;
|
||||
}
|
||||
} else {
|
||||
r = 0.0;
|
||||
if (ix >= 0x3FFBB4C3) { /* [1.7316,2] */
|
||||
y = 2.0 - x;
|
||||
i = 0;
|
||||
} else if(ix >= 0x3FF3B4C4) { /* [1.23,1.73] */
|
||||
y = x - tc;
|
||||
i = 1;
|
||||
} else {
|
||||
y = x - 1.0;
|
||||
i = 2;
|
||||
}
|
||||
}
|
||||
switch (i) {
|
||||
case 0:
|
||||
z = y*y;
|
||||
p1 = a0+z*(a2+z*(a4+z*(a6+z*(a8+z*a10))));
|
||||
p2 = z*(a1+z*(a3+z*(a5+z*(a7+z*(a9+z*a11)))));
|
||||
p = y*p1+p2;
|
||||
r += (p-0.5*y);
|
||||
break;
|
||||
case 1:
|
||||
z = y*y;
|
||||
w = z*y;
|
||||
p1 = t0+w*(t3+w*(t6+w*(t9 +w*t12))); /* parallel comp */
|
||||
p2 = t1+w*(t4+w*(t7+w*(t10+w*t13)));
|
||||
p3 = t2+w*(t5+w*(t8+w*(t11+w*t14)));
|
||||
p = z*p1-(tt-w*(p2+y*p3));
|
||||
r += tf + p;
|
||||
break;
|
||||
case 2:
|
||||
p1 = y*(u0+y*(u1+y*(u2+y*(u3+y*(u4+y*u5)))));
|
||||
p2 = 1.0+y*(v1+y*(v2+y*(v3+y*(v4+y*v5))));
|
||||
r += -0.5*y + p1/p2;
|
||||
}
|
||||
} else if (ix < 0x40200000) { /* x < 8.0 */
|
||||
i = (int)x;
|
||||
y = x - (double)i;
|
||||
p = y*(s0+y*(s1+y*(s2+y*(s3+y*(s4+y*(s5+y*s6))))));
|
||||
q = 1.0+y*(r1+y*(r2+y*(r3+y*(r4+y*(r5+y*r6)))));
|
||||
r = 0.5*y+p/q;
|
||||
z = 1.0; /* lgamma(1+s) = log(s) + lgamma(s) */
|
||||
switch (i) {
|
||||
case 7: z *= y + 6.0; /* FALLTHRU */
|
||||
case 6: z *= y + 5.0; /* FALLTHRU */
|
||||
case 5: z *= y + 4.0; /* FALLTHRU */
|
||||
case 4: z *= y + 3.0; /* FALLTHRU */
|
||||
case 3: z *= y + 2.0; /* FALLTHRU */
|
||||
r += log(z);
|
||||
break;
|
||||
}
|
||||
} else if (ix < 0x43900000) { /* 8.0 <= x < 2**58 */
|
||||
t = log(x);
|
||||
z = 1.0/x;
|
||||
y = z*z;
|
||||
w = w0+z*(w1+y*(w2+y*(w3+y*(w4+y*(w5+y*w6)))));
|
||||
r = (x-0.5)*(t-1.0)+w;
|
||||
} else /* 2**58 <= x <= inf */
|
||||
r = x*(log(x)-1.0);
|
||||
if (sign)
|
||||
r = nadj - r;
|
||||
return r;
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/math.h"
|
||||
#include "libc/tinymath/kernel.internal.h"
|
||||
|
||||
/**
|
||||
* Returns natural logarithm of absolute value of gamma function.
|
||||
@@ -25,3 +24,7 @@
|
||||
double lgamma(double x) {
|
||||
return lgamma_r(x, &signgam);
|
||||
}
|
||||
|
||||
#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
|
||||
__weak_reference(lgamma, lgammal);
|
||||
#endif
|
||||
@@ -319,3 +319,7 @@ double lgamma_r(double x, int *signgamp)
|
||||
r = nadj - r;
|
||||
return r;
|
||||
}
|
||||
|
||||
#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
|
||||
__weak_reference(lgamma_r, lgammal_r);
|
||||
#endif
|
||||
@@ -0,0 +1,388 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╚──────────────────────────────────────────────────────────────────────────────╝
|
||||
│ │
|
||||
│ Musl Libc │
|
||||
│ Copyright © 2005-2014 Rich Felker, et al. │
|
||||
│ │
|
||||
│ Permission is hereby granted, free of charge, to any person obtaining │
|
||||
│ a copy of this software and associated documentation files (the │
|
||||
│ "Software"), to deal in the Software without restriction, including │
|
||||
│ without limitation the rights to use, copy, modify, merge, publish, │
|
||||
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
||||
│ permit persons to whom the Software is furnished to do so, subject to │
|
||||
│ the following conditions: │
|
||||
│ │
|
||||
│ The above copyright notice and this permission notice shall be │
|
||||
│ included in all copies or substantial portions of the Software. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
||||
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
||||
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
||||
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
||||
│ │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/math.h"
|
||||
#include "libc/tinymath/kernel.internal.h"
|
||||
#include "libc/tinymath/ldshape.internal.h"
|
||||
#if LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
OpenBSD libm (ISC License)\\n\
|
||||
Copyright (c) 2008 Stephen L. Moshier <[email protected]>\"");
|
||||
asm(".ident\t\"\\n\\n\
|
||||
Musl libc (MIT License)\\n\
|
||||
Copyright 2005-2014 Rich Felker, et. al.\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
// clang-format off
|
||||
|
||||
/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_lgammal.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2008 Stephen L. Moshier <[email protected]>
|
||||
*
|
||||
* Permission to use, copy, modify, and 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.
|
||||
*/
|
||||
/* lgammal(x)
|
||||
* Reentrant version of the logarithm of the Gamma function
|
||||
* with user provide pointer for the sign of Gamma(x).
|
||||
*
|
||||
* Method:
|
||||
* 1. Argument Reduction for 0 < x <= 8
|
||||
* Since gamma(1+s)=s*gamma(s), for x in [0,8], we may
|
||||
* reduce x to a number in [1.5,2.5] by
|
||||
* lgamma(1+s) = log(s) + lgamma(s)
|
||||
* for example,
|
||||
* lgamma(7.3) = log(6.3) + lgamma(6.3)
|
||||
* = log(6.3*5.3) + lgamma(5.3)
|
||||
* = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
|
||||
* 2. Polynomial approximation of lgamma around its
|
||||
* minimun ymin=1.461632144968362245 to maintain monotonicity.
|
||||
* On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
|
||||
* Let z = x-ymin;
|
||||
* lgamma(x) = -1.214862905358496078218 + z^2*poly(z)
|
||||
* 2. Rational approximation in the primary interval [2,3]
|
||||
* We use the following approximation:
|
||||
* s = x-2.0;
|
||||
* lgamma(x) = 0.5*s + s*P(s)/Q(s)
|
||||
* Our algorithms are based on the following observation
|
||||
*
|
||||
* zeta(2)-1 2 zeta(3)-1 3
|
||||
* lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ...
|
||||
* 2 3
|
||||
*
|
||||
* where Euler = 0.5771... is the Euler constant, which is very
|
||||
* close to 0.5.
|
||||
*
|
||||
* 3. For x>=8, we have
|
||||
* lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
|
||||
* (better formula:
|
||||
* lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
|
||||
* Let z = 1/x, then we approximation
|
||||
* f(z) = lgamma(x) - (x-0.5)(log(x)-1)
|
||||
* by
|
||||
* 3 5 11
|
||||
* w = w0 + w1*z + w2*z + w3*z + ... + w6*z
|
||||
*
|
||||
* 4. For negative x, since (G is gamma function)
|
||||
* -x*G(-x)*G(x) = pi/sin(pi*x),
|
||||
* we have
|
||||
* G(x) = pi/(sin(pi*x)*(-x)*G(-x))
|
||||
* since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
|
||||
* Hence, for x<0, signgam = sign(sin(pi*x)) and
|
||||
* lgamma(x) = log(|Gamma(x)|)
|
||||
* = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
|
||||
* Note: one should avoid compute pi*(-x) directly in the
|
||||
* computation of sin(pi*(-x)).
|
||||
*
|
||||
* 5. Special Cases
|
||||
* lgamma(2+s) ~ s*(1-Euler) for tiny s
|
||||
* lgamma(1)=lgamma(2)=0
|
||||
* lgamma(x) ~ -log(x) for tiny x
|
||||
* lgamma(0) = lgamma(inf) = inf
|
||||
* lgamma(-integer) = +-inf
|
||||
*
|
||||
*/
|
||||
|
||||
static const long double
|
||||
pi = 3.14159265358979323846264L,
|
||||
|
||||
/* lgam(1+x) = 0.5 x + x a(x)/b(x)
|
||||
-0.268402099609375 <= x <= 0
|
||||
peak relative error 6.6e-22 */
|
||||
a0 = -6.343246574721079391729402781192128239938E2L,
|
||||
a1 = 1.856560238672465796768677717168371401378E3L,
|
||||
a2 = 2.404733102163746263689288466865843408429E3L,
|
||||
a3 = 8.804188795790383497379532868917517596322E2L,
|
||||
a4 = 1.135361354097447729740103745999661157426E2L,
|
||||
a5 = 3.766956539107615557608581581190400021285E0L,
|
||||
|
||||
b0 = 8.214973713960928795704317259806842490498E3L,
|
||||
b1 = 1.026343508841367384879065363925870888012E4L,
|
||||
b2 = 4.553337477045763320522762343132210919277E3L,
|
||||
b3 = 8.506975785032585797446253359230031874803E2L,
|
||||
b4 = 6.042447899703295436820744186992189445813E1L,
|
||||
/* b5 = 1.000000000000000000000000000000000000000E0 */
|
||||
|
||||
|
||||
tc = 1.4616321449683623412626595423257213284682E0L,
|
||||
tf = -1.2148629053584961146050602565082954242826E-1, /* double precision */
|
||||
/* tt = (tail of tf), i.e. tf + tt has extended precision. */
|
||||
tt = 3.3649914684731379602768989080467587736363E-18L,
|
||||
/* lgam ( 1.4616321449683623412626595423257213284682E0 ) =
|
||||
-1.2148629053584960809551455717769158215135617312999903886372437313313530E-1 */
|
||||
|
||||
/* lgam (x + tc) = tf + tt + x g(x)/h(x)
|
||||
-0.230003726999612341262659542325721328468 <= x
|
||||
<= 0.2699962730003876587373404576742786715318
|
||||
peak relative error 2.1e-21 */
|
||||
g0 = 3.645529916721223331888305293534095553827E-18L,
|
||||
g1 = 5.126654642791082497002594216163574795690E3L,
|
||||
g2 = 8.828603575854624811911631336122070070327E3L,
|
||||
g3 = 5.464186426932117031234820886525701595203E3L,
|
||||
g4 = 1.455427403530884193180776558102868592293E3L,
|
||||
g5 = 1.541735456969245924860307497029155838446E2L,
|
||||
g6 = 4.335498275274822298341872707453445815118E0L,
|
||||
|
||||
h0 = 1.059584930106085509696730443974495979641E4L,
|
||||
h1 = 2.147921653490043010629481226937850618860E4L,
|
||||
h2 = 1.643014770044524804175197151958100656728E4L,
|
||||
h3 = 5.869021995186925517228323497501767586078E3L,
|
||||
h4 = 9.764244777714344488787381271643502742293E2L,
|
||||
h5 = 6.442485441570592541741092969581997002349E1L,
|
||||
/* h6 = 1.000000000000000000000000000000000000000E0 */
|
||||
|
||||
|
||||
/* lgam (x+1) = -0.5 x + x u(x)/v(x)
|
||||
-0.100006103515625 <= x <= 0.231639862060546875
|
||||
peak relative error 1.3e-21 */
|
||||
u0 = -8.886217500092090678492242071879342025627E1L,
|
||||
u1 = 6.840109978129177639438792958320783599310E2L,
|
||||
u2 = 2.042626104514127267855588786511809932433E3L,
|
||||
u3 = 1.911723903442667422201651063009856064275E3L,
|
||||
u4 = 7.447065275665887457628865263491667767695E2L,
|
||||
u5 = 1.132256494121790736268471016493103952637E2L,
|
||||
u6 = 4.484398885516614191003094714505960972894E0L,
|
||||
|
||||
v0 = 1.150830924194461522996462401210374632929E3L,
|
||||
v1 = 3.399692260848747447377972081399737098610E3L,
|
||||
v2 = 3.786631705644460255229513563657226008015E3L,
|
||||
v3 = 1.966450123004478374557778781564114347876E3L,
|
||||
v4 = 4.741359068914069299837355438370682773122E2L,
|
||||
v5 = 4.508989649747184050907206782117647852364E1L,
|
||||
/* v6 = 1.000000000000000000000000000000000000000E0 */
|
||||
|
||||
|
||||
/* lgam (x+2) = .5 x + x s(x)/r(x)
|
||||
0 <= x <= 1
|
||||
peak relative error 7.2e-22 */
|
||||
s0 = 1.454726263410661942989109455292824853344E6L,
|
||||
s1 = -3.901428390086348447890408306153378922752E6L,
|
||||
s2 = -6.573568698209374121847873064292963089438E6L,
|
||||
s3 = -3.319055881485044417245964508099095984643E6L,
|
||||
s4 = -7.094891568758439227560184618114707107977E5L,
|
||||
s5 = -6.263426646464505837422314539808112478303E4L,
|
||||
s6 = -1.684926520999477529949915657519454051529E3L,
|
||||
|
||||
r0 = -1.883978160734303518163008696712983134698E7L,
|
||||
r1 = -2.815206082812062064902202753264922306830E7L,
|
||||
r2 = -1.600245495251915899081846093343626358398E7L,
|
||||
r3 = -4.310526301881305003489257052083370058799E6L,
|
||||
r4 = -5.563807682263923279438235987186184968542E5L,
|
||||
r5 = -3.027734654434169996032905158145259713083E4L,
|
||||
r6 = -4.501995652861105629217250715790764371267E2L,
|
||||
/* r6 = 1.000000000000000000000000000000000000000E0 */
|
||||
|
||||
|
||||
/* lgam(x) = ( x - 0.5 ) * log(x) - x + LS2PI + 1/x w(1/x^2)
|
||||
x >= 8
|
||||
Peak relative error 1.51e-21
|
||||
w0 = LS2PI - 0.5 */
|
||||
w0 = 4.189385332046727417803e-1L,
|
||||
w1 = 8.333333333333331447505E-2L,
|
||||
w2 = -2.777777777750349603440E-3L,
|
||||
w3 = 7.936507795855070755671E-4L,
|
||||
w4 = -5.952345851765688514613E-4L,
|
||||
w5 = 8.412723297322498080632E-4L,
|
||||
w6 = -1.880801938119376907179E-3L,
|
||||
w7 = 4.885026142432270781165E-3L;
|
||||
|
||||
/* sin(pi*x) assuming x > 2^-1000, if sin(pi*x)==0 the sign is arbitrary */
|
||||
static long double sin_pi(long double x)
|
||||
{
|
||||
int n;
|
||||
|
||||
/* spurious inexact if odd int */
|
||||
x *= 0.5;
|
||||
x = 2.0*(x - floorl(x)); /* x mod 2.0 */
|
||||
|
||||
n = (int)(x*4.0);
|
||||
n = (n+1)/2;
|
||||
x -= n*0.5f;
|
||||
x *= pi;
|
||||
|
||||
switch (n) {
|
||||
default: /* case 4: */
|
||||
case 0: return __sinl(x, 0.0, 0);
|
||||
case 1: return __cosl(x, 0.0);
|
||||
case 2: return __sinl(-x, 0.0, 0);
|
||||
case 3: return -__cosl(x, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
long double lgammal_r(long double x, int *sg) {
|
||||
long double t, y, z, nadj, p, p1, p2, q, r, w;
|
||||
union ldshape u = {x};
|
||||
uint32_t ix = (u.i.se & 0x7fffU)<<16 | u.i.m>>48;
|
||||
int sign = u.i.se >> 15;
|
||||
int i;
|
||||
|
||||
*sg = 1;
|
||||
|
||||
/* purge off +-inf, NaN, +-0, tiny and negative arguments */
|
||||
if (ix >= 0x7fff0000)
|
||||
return x * x;
|
||||
if (ix < 0x3fc08000) { /* |x|<2**-63, return -log(|x|) */
|
||||
if (sign) {
|
||||
*sg = -1;
|
||||
x = -x;
|
||||
}
|
||||
return -logl(x);
|
||||
}
|
||||
if (sign) {
|
||||
x = -x;
|
||||
t = sin_pi(x);
|
||||
if (t == 0.0)
|
||||
return 1.0 / (x-x); /* -integer */
|
||||
if (t > 0.0)
|
||||
*sg = -1;
|
||||
else
|
||||
t = -t;
|
||||
nadj = logl(pi / (t * x));
|
||||
}
|
||||
|
||||
/* purge off 1 and 2 (so the sign is ok with downward rounding) */
|
||||
if ((ix == 0x3fff8000 || ix == 0x40008000) && u.i.m == 0) {
|
||||
r = 0;
|
||||
} else if (ix < 0x40008000) { /* x < 2.0 */
|
||||
if (ix <= 0x3ffee666) { /* 8.99993896484375e-1 */
|
||||
/* lgamma(x) = lgamma(x+1) - log(x) */
|
||||
r = -logl(x);
|
||||
if (ix >= 0x3ffebb4a) { /* 7.31597900390625e-1 */
|
||||
y = x - 1.0;
|
||||
i = 0;
|
||||
} else if (ix >= 0x3ffced33) { /* 2.31639862060546875e-1 */
|
||||
y = x - (tc - 1.0);
|
||||
i = 1;
|
||||
} else { /* x < 0.23 */
|
||||
y = x;
|
||||
i = 2;
|
||||
}
|
||||
} else {
|
||||
r = 0.0;
|
||||
if (ix >= 0x3fffdda6) { /* 1.73162841796875 */
|
||||
/* [1.7316,2] */
|
||||
y = x - 2.0;
|
||||
i = 0;
|
||||
} else if (ix >= 0x3fff9da6) { /* 1.23162841796875 */
|
||||
/* [1.23,1.73] */
|
||||
y = x - tc;
|
||||
i = 1;
|
||||
} else {
|
||||
/* [0.9, 1.23] */
|
||||
y = x - 1.0;
|
||||
i = 2;
|
||||
}
|
||||
}
|
||||
switch (i) {
|
||||
case 0:
|
||||
p1 = a0 + y * (a1 + y * (a2 + y * (a3 + y * (a4 + y * a5))));
|
||||
p2 = b0 + y * (b1 + y * (b2 + y * (b3 + y * (b4 + y))));
|
||||
r += 0.5 * y + y * p1/p2;
|
||||
break;
|
||||
case 1:
|
||||
p1 = g0 + y * (g1 + y * (g2 + y * (g3 + y * (g4 + y * (g5 + y * g6)))));
|
||||
p2 = h0 + y * (h1 + y * (h2 + y * (h3 + y * (h4 + y * (h5 + y)))));
|
||||
p = tt + y * p1/p2;
|
||||
r += (tf + p);
|
||||
break;
|
||||
case 2:
|
||||
p1 = y * (u0 + y * (u1 + y * (u2 + y * (u3 + y * (u4 + y * (u5 + y * u6))))));
|
||||
p2 = v0 + y * (v1 + y * (v2 + y * (v3 + y * (v4 + y * (v5 + y)))));
|
||||
r += (-0.5 * y + p1 / p2);
|
||||
}
|
||||
} else if (ix < 0x40028000) { /* 8.0 */
|
||||
/* x < 8.0 */
|
||||
i = (int)x;
|
||||
y = x - (double)i;
|
||||
p = y * (s0 + y * (s1 + y * (s2 + y * (s3 + y * (s4 + y * (s5 + y * s6))))));
|
||||
q = r0 + y * (r1 + y * (r2 + y * (r3 + y * (r4 + y * (r5 + y * (r6 + y))))));
|
||||
r = 0.5 * y + p / q;
|
||||
z = 1.0;
|
||||
/* lgamma(1+s) = log(s) + lgamma(s) */
|
||||
switch (i) {
|
||||
case 7:
|
||||
z *= (y + 6.0); /* FALLTHRU */
|
||||
case 6:
|
||||
z *= (y + 5.0); /* FALLTHRU */
|
||||
case 5:
|
||||
z *= (y + 4.0); /* FALLTHRU */
|
||||
case 4:
|
||||
z *= (y + 3.0); /* FALLTHRU */
|
||||
case 3:
|
||||
z *= (y + 2.0); /* FALLTHRU */
|
||||
r += logl(z);
|
||||
break;
|
||||
}
|
||||
} else if (ix < 0x40418000) { /* 2^66 */
|
||||
/* 8.0 <= x < 2**66 */
|
||||
t = logl(x);
|
||||
z = 1.0 / x;
|
||||
y = z * z;
|
||||
w = w0 + z * (w1 + y * (w2 + y * (w3 + y * (w4 + y * (w5 + y * (w6 + y * w7))))));
|
||||
r = (x - 0.5) * (t - 1.0) + w;
|
||||
} else /* 2**66 <= x <= inf */
|
||||
r = x * (logl(x) - 1.0);
|
||||
if (sign)
|
||||
r = nadj - r;
|
||||
return r;
|
||||
}
|
||||
|
||||
long double lgammal(long double x)
|
||||
{
|
||||
return lgammal_r(x, &signgam);
|
||||
}
|
||||
|
||||
#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
|
||||
// TODO: broken implementation to make things compile
|
||||
long double lgammal_r(long double x, int *sg)
|
||||
{
|
||||
return lgamma_r(x, sg);
|
||||
}
|
||||
long double lgammal(long double x)
|
||||
{
|
||||
return lgamma_r(x, &signgam);
|
||||
}
|
||||
#endif /* long double is long */
|
||||
@@ -208,3 +208,7 @@ double tgamma(double x)
|
||||
y = r * z * z;
|
||||
return y;
|
||||
}
|
||||
|
||||
#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
|
||||
__weak_reference(tgamma, tgammal);
|
||||
#endif
|
||||
@@ -0,0 +1,314 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╚──────────────────────────────────────────────────────────────────────────────╝
|
||||
│ │
|
||||
│ Musl Libc │
|
||||
│ Copyright © 2005-2014 Rich Felker, et al. │
|
||||
│ │
|
||||
│ Permission is hereby granted, free of charge, to any person obtaining │
|
||||
│ a copy of this software and associated documentation files (the │
|
||||
│ "Software"), to deal in the Software without restriction, including │
|
||||
│ without limitation the rights to use, copy, modify, merge, publish, │
|
||||
│ distribute, sublicense, and/or sell copies of the Software, and to │
|
||||
│ permit persons to whom the Software is furnished to do so, subject to │
|
||||
│ the following conditions: │
|
||||
│ │
|
||||
│ The above copyright notice and this permission notice shall be │
|
||||
│ included in all copies or substantial portions of the Software. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │
|
||||
│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │
|
||||
│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │
|
||||
│ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY │
|
||||
│ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, │
|
||||
│ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE │
|
||||
│ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
||||
│ │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/math.h"
|
||||
#include "libc/tinymath/internal.h"
|
||||
#if LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
OpenBSD libm (ISC License)\\n\
|
||||
Copyright (c) 2008 Stephen L. Moshier <[email protected]>\"");
|
||||
asm(".ident\t\"\\n\\n\
|
||||
Musl libc (MIT License)\\n\
|
||||
Copyright 2005-2014 Rich Felker, et. al.\"");
|
||||
asm(".include \"libc/disclaimer.inc\"");
|
||||
// clang-format off
|
||||
|
||||
/* origin: OpenBSD /usr/src/lib/libm/src/ld80/e_tgammal.c */
|
||||
/*
|
||||
* Copyright (c) 2008 Stephen L. Moshier <[email protected]>
|
||||
*
|
||||
* Permission to use, copy, modify, and 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.
|
||||
*/
|
||||
/*
|
||||
* Gamma function
|
||||
*
|
||||
*
|
||||
* SYNOPSIS:
|
||||
*
|
||||
* long double x, y, tgammal();
|
||||
*
|
||||
* y = tgammal( x );
|
||||
*
|
||||
*
|
||||
* DESCRIPTION:
|
||||
*
|
||||
* Returns gamma function of the argument. The result is
|
||||
* correctly signed.
|
||||
*
|
||||
* Arguments |x| <= 13 are reduced by recurrence and the function
|
||||
* approximated by a rational function of degree 7/8 in the
|
||||
* interval (2,3). Large arguments are handled by Stirling's
|
||||
* formula. Large negative arguments are made positive using
|
||||
* a reflection formula.
|
||||
*
|
||||
*
|
||||
* ACCURACY:
|
||||
*
|
||||
* Relative error:
|
||||
* arithmetic domain # trials peak rms
|
||||
* IEEE -40,+40 10000 3.6e-19 7.9e-20
|
||||
* IEEE -1755,+1755 10000 4.8e-18 6.5e-19
|
||||
*
|
||||
* Accuracy for large arguments is dominated by error in powl().
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
tgamma(x+2) = tgamma(x+2) P(x)/Q(x)
|
||||
0 <= x <= 1
|
||||
Relative error
|
||||
n=7, d=8
|
||||
Peak error = 1.83e-20
|
||||
Relative error spread = 8.4e-23
|
||||
*/
|
||||
static const long double P[8] = {
|
||||
4.212760487471622013093E-5L,
|
||||
4.542931960608009155600E-4L,
|
||||
4.092666828394035500949E-3L,
|
||||
2.385363243461108252554E-2L,
|
||||
1.113062816019361559013E-1L,
|
||||
3.629515436640239168939E-1L,
|
||||
8.378004301573126728826E-1L,
|
||||
1.000000000000000000009E0L,
|
||||
};
|
||||
static const long double Q[9] = {
|
||||
-1.397148517476170440917E-5L,
|
||||
2.346584059160635244282E-4L,
|
||||
-1.237799246653152231188E-3L,
|
||||
-7.955933682494738320586E-4L,
|
||||
2.773706565840072979165E-2L,
|
||||
-4.633887671244534213831E-2L,
|
||||
-2.243510905670329164562E-1L,
|
||||
4.150160950588455434583E-1L,
|
||||
9.999999999999999999908E-1L,
|
||||
};
|
||||
|
||||
/*
|
||||
static const long double P[] = {
|
||||
-3.01525602666895735709e0L,
|
||||
-3.25157411956062339893e1L,
|
||||
-2.92929976820724030353e2L,
|
||||
-1.70730828800510297666e3L,
|
||||
-7.96667499622741999770e3L,
|
||||
-2.59780216007146401957e4L,
|
||||
-5.99650230220855581642e4L,
|
||||
-7.15743521530849602425e4L
|
||||
};
|
||||
static const long double Q[] = {
|
||||
1.00000000000000000000e0L,
|
||||
-1.67955233807178858919e1L,
|
||||
8.85946791747759881659e1L,
|
||||
5.69440799097468430177e1L,
|
||||
-1.98526250512761318471e3L,
|
||||
3.31667508019495079814e3L,
|
||||
1.60577839621734713377e4L,
|
||||
-2.97045081369399940529e4L,
|
||||
-7.15743521530849602412e4L
|
||||
};
|
||||
*/
|
||||
#define MAXGAML 1755.455L
|
||||
/*static const long double LOGPI = 1.14472988584940017414L;*/
|
||||
|
||||
/* Stirling's formula for the gamma function
|
||||
tgamma(x) = sqrt(2 pi) x^(x-.5) exp(-x) (1 + 1/x P(1/x))
|
||||
z(x) = x
|
||||
13 <= x <= 1024
|
||||
Relative error
|
||||
n=8, d=0
|
||||
Peak error = 9.44e-21
|
||||
Relative error spread = 8.8e-4
|
||||
*/
|
||||
static const long double STIR[9] = {
|
||||
7.147391378143610789273E-4L,
|
||||
-2.363848809501759061727E-5L,
|
||||
-5.950237554056330156018E-4L,
|
||||
6.989332260623193171870E-5L,
|
||||
7.840334842744753003862E-4L,
|
||||
-2.294719747873185405699E-4L,
|
||||
-2.681327161876304418288E-3L,
|
||||
3.472222222230075327854E-3L,
|
||||
8.333333333333331800504E-2L,
|
||||
};
|
||||
|
||||
#define MAXSTIR 1024.0L
|
||||
static const long double SQTPI = 2.50662827463100050242E0L;
|
||||
|
||||
/* 1/tgamma(x) = z P(z)
|
||||
* z(x) = 1/x
|
||||
* 0 < x < 0.03125
|
||||
* Peak relative error 4.2e-23
|
||||
*/
|
||||
static const long double S[9] = {
|
||||
-1.193945051381510095614E-3L,
|
||||
7.220599478036909672331E-3L,
|
||||
-9.622023360406271645744E-3L,
|
||||
-4.219773360705915470089E-2L,
|
||||
1.665386113720805206758E-1L,
|
||||
-4.200263503403344054473E-2L,
|
||||
-6.558780715202540684668E-1L,
|
||||
5.772156649015328608253E-1L,
|
||||
1.000000000000000000000E0L,
|
||||
};
|
||||
|
||||
/* 1/tgamma(-x) = z P(z)
|
||||
* z(x) = 1/x
|
||||
* 0 < x < 0.03125
|
||||
* Peak relative error 5.16e-23
|
||||
* Relative error spread = 2.5e-24
|
||||
*/
|
||||
static const long double SN[9] = {
|
||||
1.133374167243894382010E-3L,
|
||||
7.220837261893170325704E-3L,
|
||||
9.621911155035976733706E-3L,
|
||||
-4.219773343731191721664E-2L,
|
||||
-1.665386113944413519335E-1L,
|
||||
-4.200263503402112910504E-2L,
|
||||
6.558780715202536547116E-1L,
|
||||
5.772156649015328608727E-1L,
|
||||
-1.000000000000000000000E0L,
|
||||
};
|
||||
|
||||
static const long double PIL = 3.1415926535897932384626L;
|
||||
|
||||
/* Gamma function computed by Stirling's formula.
|
||||
*/
|
||||
static long double stirf(long double x)
|
||||
{
|
||||
long double y, w, v;
|
||||
|
||||
w = 1.0/x;
|
||||
/* For large x, use rational coefficients from the analytical expansion. */
|
||||
if (x > 1024.0)
|
||||
w = (((((6.97281375836585777429E-5L * w
|
||||
+ 7.84039221720066627474E-4L) * w
|
||||
- 2.29472093621399176955E-4L) * w
|
||||
- 2.68132716049382716049E-3L) * w
|
||||
+ 3.47222222222222222222E-3L) * w
|
||||
+ 8.33333333333333333333E-2L) * w
|
||||
+ 1.0;
|
||||
else
|
||||
w = 1.0 + w * __polevll(w, STIR, 8);
|
||||
y = expl(x);
|
||||
if (x > MAXSTIR) { /* Avoid overflow in pow() */
|
||||
v = powl(x, 0.5L * x - 0.25L);
|
||||
y = v * (v / y);
|
||||
} else {
|
||||
y = powl(x, x - 0.5L) / y;
|
||||
}
|
||||
y = SQTPI * y * w;
|
||||
return y;
|
||||
}
|
||||
|
||||
long double tgammal(long double x)
|
||||
{
|
||||
long double p, q, z;
|
||||
|
||||
if (!isfinite(x))
|
||||
return x + INFINITY;
|
||||
|
||||
q = fabsl(x);
|
||||
if (q > 13.0) {
|
||||
if (x < 0.0) {
|
||||
p = floorl(q);
|
||||
z = q - p;
|
||||
if (z == 0)
|
||||
return 0 / z;
|
||||
if (q > MAXGAML) {
|
||||
z = 0;
|
||||
} else {
|
||||
if (z > 0.5) {
|
||||
p += 1.0;
|
||||
z = q - p;
|
||||
}
|
||||
z = q * sinl(PIL * z);
|
||||
z = fabsl(z) * stirf(q);
|
||||
z = PIL/z;
|
||||
}
|
||||
if (0.5 * p == floorl(q * 0.5))
|
||||
z = -z;
|
||||
} else if (x > MAXGAML) {
|
||||
z = x * 0x1p16383L;
|
||||
} else {
|
||||
z = stirf(x);
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
z = 1.0;
|
||||
while (x >= 3.0) {
|
||||
x -= 1.0;
|
||||
z *= x;
|
||||
}
|
||||
while (x < -0.03125L) {
|
||||
z /= x;
|
||||
x += 1.0;
|
||||
}
|
||||
if (x <= 0.03125L)
|
||||
goto small;
|
||||
while (x < 2.0) {
|
||||
z /= x;
|
||||
x += 1.0;
|
||||
}
|
||||
if (x == 2.0)
|
||||
return z;
|
||||
|
||||
x -= 2.0;
|
||||
p = __polevll(x, P, 7);
|
||||
q = __polevll(x, Q, 8);
|
||||
z = z * p / q;
|
||||
return z;
|
||||
|
||||
small:
|
||||
/* z==1 if x was originally +-0 */
|
||||
if (x == 0 && z != 1)
|
||||
return x / x;
|
||||
if (x < 0.0) {
|
||||
x = -x;
|
||||
q = z / (x * __polevll(x, SN, 8));
|
||||
} else
|
||||
q = z / (x * __polevll(x, S, 8));
|
||||
return q;
|
||||
}
|
||||
|
||||
#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
|
||||
// TODO: broken implementation to make things compile
|
||||
long double tgammal(long double x)
|
||||
{
|
||||
return tgamma(x);
|
||||
}
|
||||
#endif /* long double is long */
|
||||
@@ -91,12 +91,12 @@
|
||||
vga_console:
|
||||
.endobj vga_console,globl,hidden
|
||||
.previous
|
||||
.init.start 305,_init_vga
|
||||
.init.start 101,_init_vga
|
||||
push %rdi
|
||||
push %rsi
|
||||
call _vga_init
|
||||
pop %rsi
|
||||
pop %rdi
|
||||
.init.end 305,_init_vga
|
||||
.init.end 101,_init_vga
|
||||
.yoink sys_writev_vga
|
||||
.yoink sys_readv_vga
|
||||
@@ -33,8 +33,8 @@
|
||||
#ifdef __x86_64__
|
||||
|
||||
/*
|
||||
* @fileoverview Instantiation of routines for emergency or system console
|
||||
* output in graphical video modes.
|
||||
* @fileoverview Instantiation of routines for emergency console output in
|
||||
* graphical video modes.
|
||||
*
|
||||
* @see libc/vga/tty-graph.inc
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
│ OTHER DEALINGS IN THE SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/vga/vga.internal.h"
|
||||
|
||||
@@ -72,4 +75,47 @@ textstartup void _vga_init(void) {
|
||||
}
|
||||
}
|
||||
|
||||
#if !IsTiny()
|
||||
/**
|
||||
* Non-emergency console vprintf(), useful for dumping debugging or
|
||||
* informational messages at program startup.
|
||||
*
|
||||
* @see uprintf()
|
||||
*/
|
||||
void uvprintf(const char *fmt, va_list v) {
|
||||
if (!IsMetal()) {
|
||||
kvprintf(fmt, v);
|
||||
} else {
|
||||
long size = __get_safe_size(8000, 3000);
|
||||
char *buf = alloca(size);
|
||||
CheckLargeStackAllocation(buf, size);
|
||||
size_t count = kvsnprintf(buf, size, fmt, v);
|
||||
if (count >= size) count = size - 1;
|
||||
_TtyWrite(&_vga_tty, buf, count);
|
||||
_klog_serial(buf, count);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-emergency console printf(), useful for dumping debugging or
|
||||
* informational messages at program startup.
|
||||
*
|
||||
* uprintf() is similar to kprintf(), but on bare metal with VGA support, it
|
||||
* uses the normal, fast graphical console, rather than initializing an
|
||||
* emergency console. This makes uprintf() faster — on bare metal — at the
|
||||
* expense of being less crash-proof.
|
||||
*
|
||||
* (The uprintf() function name comes from the FreeBSD kernel.)
|
||||
*
|
||||
* @see kprintf()
|
||||
* @see https://man.freebsd.org/cgi/man.cgi?query=uprintf&sektion=9&n=1
|
||||
*/
|
||||
void uprintf(const char *fmt, ...) {
|
||||
va_list v;
|
||||
va_start(v, fmt);
|
||||
uvprintf(fmt, v);
|
||||
va_end(v);
|
||||
}
|
||||
#endif /* !IsTiny() */
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
@@ -255,8 +255,7 @@ void _TtyGraphMoveLineCells(struct Tty *, size_t, size_t, size_t, size_t,
|
||||
size_t);
|
||||
|
||||
/*
|
||||
* Routines that implement emergency or system console output in graphical
|
||||
* video modes.
|
||||
* Routines that implement emergency console output in graphical video modes.
|
||||
*/
|
||||
void _TtyKlog16Update(struct Tty *);
|
||||
void _TtyKlog16DrawChar(struct Tty *, size_t, size_t, wchar_t);
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
/* pshm_ucase_bounce.c
|
||||
Adapted from SHM_OPEN(3) in Linux Programmer's Manual
|
||||
Licensed under GNU General Public License v2 or later */
|
||||
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/s.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/semaphore.h"
|
||||
|
||||
#define SHM_PATH "/fc7261622dd420d8"
|
||||
#define STRING_SEND "hello"
|
||||
#define STRING_RECV "HELLO"
|
||||
|
||||
struct shmbuf {
|
||||
sem_t sem1; /* POSIX unnamed semaphore */
|
||||
sem_t sem2; /* POSIX unnamed semaphore */
|
||||
size_t cnt; /* Number of bytes used in 'buf' */
|
||||
char buf[256]; /* Data being transferred */
|
||||
};
|
||||
|
||||
atomic_bool *ready;
|
||||
|
||||
wontreturn void Bouncer(void) {
|
||||
|
||||
/* Create shared memory object and set its size to the size
|
||||
of our structure. */
|
||||
int fd = shm_open(SHM_PATH, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
perror("shm_open(bouncer)");
|
||||
exit(1);
|
||||
}
|
||||
if (ftruncate(fd, sizeof(struct shmbuf)) == -1) {
|
||||
perror("ftruncate");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Map the object into the caller's address space. */
|
||||
struct shmbuf *shmp =
|
||||
mmap(NULL, sizeof(*shmp), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (shmp == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Initialize semaphores as process-shared, with value 0. */
|
||||
if (sem_init(&shmp->sem1, 1, 0) == -1) {
|
||||
perror("sem_init-sem1");
|
||||
exit(1);
|
||||
}
|
||||
if (sem_init(&shmp->sem2, 1, 0) == -1) {
|
||||
perror("sem_init-sem2");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Let other process know it's ok to open. */
|
||||
*ready = true;
|
||||
|
||||
/* Wait for 'sem1' to be posted by peer before touching
|
||||
shared memory. */
|
||||
if (sem_wait(&shmp->sem1) == -1) {
|
||||
perror("sem_wait");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Convert data in shared memory into upper case. */
|
||||
for (int j = 0; j < shmp->cnt; j++) {
|
||||
shmp->buf[j] = toupper((unsigned char)shmp->buf[j]);
|
||||
}
|
||||
|
||||
/* Post 'sem2' to tell the peer that it can now
|
||||
access the modified data in shared memory. */
|
||||
if (sem_post(&shmp->sem2) == -1) {
|
||||
perror("sem_post");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
wontreturn void Sender(void) {
|
||||
|
||||
/* Wait for file to exist. */
|
||||
while (!*ready) donothing;
|
||||
|
||||
/* Open the existing shared memory object and map it
|
||||
into the caller's address space. */
|
||||
int fd = shm_open(SHM_PATH, O_RDWR, 0);
|
||||
if (fd == -1) {
|
||||
perror("shm_open(sender)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct shmbuf *shmp =
|
||||
mmap(NULL, sizeof(*shmp), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (shmp == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Copy data into the shared memory object. */
|
||||
shmp->cnt = strlen(STRING_SEND);
|
||||
strcpy(shmp->buf, STRING_SEND);
|
||||
|
||||
/* Tell peer that it can now access shared memory. */
|
||||
if (sem_post(&shmp->sem1) == -1) {
|
||||
perror("sem_post");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Wait until peer says that it has finished accessing
|
||||
the shared memory. */
|
||||
if (sem_wait(&shmp->sem2) == -1) {
|
||||
perror("sem_wait");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Write modified data in shared memory to standard output. */
|
||||
if (strcmp(shmp->buf, STRING_RECV)) {
|
||||
tinyprint(2, program_invocation_name,
|
||||
": received wrong string: ", shmp->buf, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Unlink the shared memory object. Even if the peer process
|
||||
is still using the object, this is okay. The object will
|
||||
be removed only after all open references are closed. */
|
||||
if (shm_unlink(SHM_PATH)) {
|
||||
if (IsWindows() && errno == EACCES) {
|
||||
// TODO(jart): Make unlink() work better on Windows.
|
||||
} else {
|
||||
perror("shm_unlink");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int pid1;
|
||||
int pid2;
|
||||
|
||||
void OnExit(void) {
|
||||
kill(pid1, SIGKILL);
|
||||
kill(pid2, SIGKILL);
|
||||
shm_unlink(SHM_PATH);
|
||||
}
|
||||
|
||||
void OnTimeout(int sig) {
|
||||
tinyprint(2, program_invocation_name, ": test timed out!\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// create synchronization object
|
||||
ready = _mapshared(1);
|
||||
|
||||
// create bouncer
|
||||
pid1 = fork();
|
||||
if (pid1 == -1) {
|
||||
perror("fork");
|
||||
exit(1);
|
||||
}
|
||||
if (!pid1) {
|
||||
Bouncer();
|
||||
}
|
||||
|
||||
// create sender
|
||||
pid2 = fork();
|
||||
if (pid1 == -1) {
|
||||
perror("fork");
|
||||
kill(pid1, SIGKILL);
|
||||
exit(1);
|
||||
}
|
||||
if (!pid2) {
|
||||
Sender();
|
||||
}
|
||||
|
||||
// set limit
|
||||
atexit(OnExit);
|
||||
signal(SIGALRM, OnTimeout);
|
||||
alarm(1);
|
||||
|
||||
// wait for children
|
||||
for (;;) {
|
||||
int child, status;
|
||||
child = wait(&status);
|
||||
if (child == -1) {
|
||||
if (errno == ECHILD) {
|
||||
break;
|
||||
}
|
||||
perror("wait");
|
||||
exit(1);
|
||||
}
|
||||
if (status) {
|
||||
if (WIFEXITED(status)) {
|
||||
exit(WEXITSTATUS(status));
|
||||
} else {
|
||||
raise(WTERMSIG(status));
|
||||
exit(128 + WTERMSIG(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// report success
|
||||
exit(0);
|
||||
}
|
||||
@@ -46,7 +46,8 @@ void RestoreStdout(void) {
|
||||
|
||||
TEST(specialfile, devNull) {
|
||||
ASSERT_SYS(0, 3, creat("/dev/null", 0644));
|
||||
ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & ~O_LARGEFILE);
|
||||
// qemu-aarch64 defines o_largefile wrong
|
||||
ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & ~(O_LARGEFILE | 0x00008000));
|
||||
ASSERT_SYS(0, 2, write(3, "hi", 2));
|
||||
ASSERT_SYS(0, 2, pwrite(3, "hi", 2, 0));
|
||||
ASSERT_SYS(0, 2, pwrite(3, "hi", 2, 2));
|
||||
@@ -63,7 +64,8 @@ TEST(specialfile, devNull) {
|
||||
TEST(specialfile, devNullRead) {
|
||||
char buf[8] = {0};
|
||||
ASSERT_SYS(0, 3, open("/dev/null", O_RDONLY));
|
||||
ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & ~O_LARGEFILE);
|
||||
// qemu-aarch64 defines o_largefile wrong
|
||||
ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & ~(O_LARGEFILE | 0x00008000));
|
||||
ASSERT_SYS(0, 0, read(3, buf, 8));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#define THREADS 8
|
||||
#define ITERATIONS 512
|
||||
|
||||
#define MAX_RECURSIVE_LOCKS 64
|
||||
|
||||
int count;
|
||||
atomic_int started;
|
||||
atomic_int finished;
|
||||
@@ -79,11 +81,18 @@ TEST(pthread_mutex_lock, recursive) {
|
||||
ASSERT_EQ(0, pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE));
|
||||
ASSERT_EQ(0, pthread_mutex_init(&lock, &attr));
|
||||
ASSERT_EQ(0, pthread_mutexattr_destroy(&attr));
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&lock));
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&lock));
|
||||
ASSERT_EQ(0, pthread_mutex_trylock(&lock));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&lock));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&lock));
|
||||
for (int i = 0; i < MAX_RECURSIVE_LOCKS; ++i) {
|
||||
if (i & 1) {
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&lock));
|
||||
} else {
|
||||
ASSERT_EQ(0, pthread_mutex_trylock(&lock));
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(EAGAIN, pthread_mutex_lock(&lock));
|
||||
ASSERT_EQ(EAGAIN, pthread_mutex_trylock(&lock));
|
||||
for (int i = 0; i < MAX_RECURSIVE_LOCKS; ++i) {
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&lock));
|
||||
}
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&lock));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&lock));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&lock));
|
||||
@@ -99,7 +108,7 @@ TEST(pthread_mutex_lock, errorcheck) {
|
||||
ASSERT_EQ(0, pthread_mutexattr_destroy(&attr));
|
||||
ASSERT_EQ(0, pthread_mutex_lock(&lock));
|
||||
ASSERT_EQ(EDEADLK, pthread_mutex_lock(&lock));
|
||||
ASSERT_EQ(EBUSY, pthread_mutex_trylock(&lock));
|
||||
ASSERT_EQ(EDEADLK, pthread_mutex_trylock(&lock));
|
||||
ASSERT_EQ(0, pthread_mutex_unlock(&lock));
|
||||
ASSERT_EQ(0, pthread_mutex_destroy(&lock));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/cxaatexit.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/testlib/subprocess.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
@@ -66,3 +67,21 @@ TEST(pthread_exit, detachedOrphanedChild_runsAtexitHandlers) {
|
||||
pthread_exit(0);
|
||||
EXITS(CHILD);
|
||||
}
|
||||
|
||||
void OnMainThreadExit(void *arg) {
|
||||
_Exit((long)arg);
|
||||
}
|
||||
|
||||
TEST(__cxa_thread_atexit, exit_wontInvokeThreadDestructors) {
|
||||
SPAWN(fork);
|
||||
__cxa_thread_atexit(OnMainThreadExit, (void *)123L, 0);
|
||||
exit(0);
|
||||
EXITS(0);
|
||||
}
|
||||
|
||||
TEST(__cxa_thread_atexit, pthread_exit_willInvokeThreadDestructors) {
|
||||
SPAWN(fork);
|
||||
__cxa_thread_atexit(OnMainThreadExit, (void *)123L, 0);
|
||||
pthread_exit(0);
|
||||
EXITS(123);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ void IgnoreStderr(void) {
|
||||
|
||||
const char *SemPath(const char *name) {
|
||||
static _Thread_local char buf[PATH_MAX];
|
||||
return sem_path_np(name, buf, sizeof(buf));
|
||||
return shm_path_np(name, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void SetUp(void) {
|
||||
|
||||
@@ -6,7 +6,7 @@ if [ $# = 0 ]; then
|
||||
if ! [ $(id -u) = 0 ]; then
|
||||
make -j16 MODE=fastbuild \
|
||||
o/fastbuild/examples/ls.com \
|
||||
o/fastbuild/examples/curl.com \
|
||||
o/fastbuild/tool/curl/curl.com \
|
||||
o/fastbuild/examples/life.com \
|
||||
o/fastbuild/examples/hello.com \
|
||||
o/fastbuild/examples/printargs.com \
|
||||
@@ -14,7 +14,7 @@ if [ $# = 0 ]; then
|
||||
o/fastbuild/tool/build/pledge.com || exit
|
||||
make -j16 MODE=$m \
|
||||
o/$m/examples/ls.com \
|
||||
o/$m/examples/curl.com \
|
||||
o/$m/tool/curl/curl.com \
|
||||
o/$m/examples/life.com \
|
||||
o/$m/examples/hello.com \
|
||||
o/$m/examples/printargs.com \
|
||||
@@ -76,7 +76,7 @@ elif [ "$1" = ape_binfmt_test_suite ]; then
|
||||
checkem
|
||||
|
||||
startit ape binfmt curl.com
|
||||
[ "$(o/fastbuild/tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o/fastbuild/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||
[ "$(o/fastbuild/tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o/fastbuild/tool/curl/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||
checkem
|
||||
|
||||
elif [ "$1" = ape_loader_test_suite ]; then
|
||||
@@ -93,7 +93,7 @@ elif [ "$1" = ape_loader_test_suite ]; then
|
||||
checkem
|
||||
|
||||
startit ape loader curl.com
|
||||
[ "$(o/fastbuild/tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o/fastbuild/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||
[ "$(o/fastbuild/tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o/fastbuild/tool/curl/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||
checkem
|
||||
|
||||
ape/apeinstall.sh >/dev/null 2>&1
|
||||
@@ -116,7 +116,7 @@ elif [ "$1" = ape_assimilated_test_suite ]; then
|
||||
checkem
|
||||
|
||||
startit ape assimilated curl.com
|
||||
cp o/fastbuild/examples/curl.com $t/assimilated
|
||||
cp o/fastbuild/tool/curl/curl.com $t/assimilated
|
||||
o/fastbuild/tool/build/assimilate.com $t/assimilated/curl.com
|
||||
[ "$(o/$m/tool/build/pledge.com -p 'stdio rpath inet dns' $t/assimilated/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||
checkem
|
||||
@@ -133,7 +133,7 @@ elif [ "$1" = ape_native_test_suite ]; then
|
||||
checkem
|
||||
|
||||
startit ape native curl.com
|
||||
[ "$(o/$m/tool/build/pledge.com -p 'stdio rpath inet dns' o/$m/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||
[ "$(o/$m/tool/build/pledge.com -p 'stdio rpath inet dns' o/$m/tool/curl/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||
checkem
|
||||
|
||||
elif [ "$1" = setuid_test_suite ]; then
|
||||
@@ -148,7 +148,7 @@ elif [ "$1" = setuid_test_suite ]; then
|
||||
checkem
|
||||
|
||||
startit setuid curl.com
|
||||
[ "$($t/pledge.com -p 'stdio rpath inet dns' o/$m/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||
[ "$($t/pledge.com -p 'stdio rpath inet dns' o/$m/tool/curl/curl.com https://justine.lol/hello.txt)" = "hello world" ]
|
||||
checkem
|
||||
|
||||
startit setuid getuid
|
||||
|
||||
Vendored
+2
@@ -111,6 +111,7 @@ THIRD_PARTY_LIBCXX_A_HDRS = \
|
||||
third_party/libcxx/regex \
|
||||
third_party/libcxx/scoped_allocator \
|
||||
third_party/libcxx/set \
|
||||
third_party/libcxx/shared_mutex \
|
||||
third_party/libcxx/span \
|
||||
third_party/libcxx/sstream \
|
||||
third_party/libcxx/stack \
|
||||
@@ -163,6 +164,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC = \
|
||||
third_party/libcxx/optional.cc \
|
||||
third_party/libcxx/random.cc \
|
||||
third_party/libcxx/regex.cc \
|
||||
third_party/libcxx/shared_mutex.cc \
|
||||
third_party/libcxx/stdexcept.cc \
|
||||
third_party/libcxx/string.cc \
|
||||
third_party/libcxx/strstream.cc \
|
||||
|
||||
Vendored
+1
-1
@@ -15,7 +15,7 @@
|
||||
|
||||
#ifndef _LIBCPP_HAS_NO_THREADS
|
||||
#if defined(__unix__) && !defined(__ANDROID__) && defined(__ELF__) && defined(_LIBCPP_HAS_COMMENT_LIB_PRAGMA)
|
||||
#pragma comment(lib, "pthread")
|
||||
// #pragma comment(lib, "pthread")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
Vendored
+509
@@ -0,0 +1,509 @@
|
||||
// -*- C++ -*-
|
||||
// clang-format off
|
||||
//===------------------------ shared_mutex --------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP_SHARED_MUTEX
|
||||
#define _LIBCPP_SHARED_MUTEX
|
||||
|
||||
/*
|
||||
shared_mutex synopsis
|
||||
|
||||
// C++1y
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
class shared_mutex // C++17
|
||||
{
|
||||
public:
|
||||
shared_mutex();
|
||||
~shared_mutex();
|
||||
|
||||
shared_mutex(const shared_mutex&) = delete;
|
||||
shared_mutex& operator=(const shared_mutex&) = delete;
|
||||
|
||||
// Exclusive ownership
|
||||
void lock(); // blocking
|
||||
bool try_lock();
|
||||
void unlock();
|
||||
|
||||
// Shared ownership
|
||||
void lock_shared(); // blocking
|
||||
bool try_lock_shared();
|
||||
void unlock_shared();
|
||||
|
||||
typedef implementation-defined native_handle_type; // See 30.2.3
|
||||
native_handle_type native_handle(); // See 30.2.3
|
||||
};
|
||||
|
||||
class shared_timed_mutex
|
||||
{
|
||||
public:
|
||||
shared_timed_mutex();
|
||||
~shared_timed_mutex();
|
||||
|
||||
shared_timed_mutex(const shared_timed_mutex&) = delete;
|
||||
shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
|
||||
|
||||
// Exclusive ownership
|
||||
void lock(); // blocking
|
||||
bool try_lock();
|
||||
template <class Rep, class Period>
|
||||
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
|
||||
template <class Clock, class Duration>
|
||||
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
|
||||
void unlock();
|
||||
|
||||
// Shared ownership
|
||||
void lock_shared(); // blocking
|
||||
bool try_lock_shared();
|
||||
template <class Rep, class Period>
|
||||
bool
|
||||
try_lock_shared_for(const chrono::duration<Rep, Period>& rel_time);
|
||||
template <class Clock, class Duration>
|
||||
bool
|
||||
try_lock_shared_until(const chrono::time_point<Clock, Duration>& abs_time);
|
||||
void unlock_shared();
|
||||
};
|
||||
|
||||
template <class Mutex>
|
||||
class shared_lock
|
||||
{
|
||||
public:
|
||||
typedef Mutex mutex_type;
|
||||
|
||||
// Shared locking
|
||||
shared_lock() noexcept;
|
||||
explicit shared_lock(mutex_type& m); // blocking
|
||||
shared_lock(mutex_type& m, defer_lock_t) noexcept;
|
||||
shared_lock(mutex_type& m, try_to_lock_t);
|
||||
shared_lock(mutex_type& m, adopt_lock_t);
|
||||
template <class Clock, class Duration>
|
||||
shared_lock(mutex_type& m,
|
||||
const chrono::time_point<Clock, Duration>& abs_time);
|
||||
template <class Rep, class Period>
|
||||
shared_lock(mutex_type& m,
|
||||
const chrono::duration<Rep, Period>& rel_time);
|
||||
~shared_lock();
|
||||
|
||||
shared_lock(shared_lock const&) = delete;
|
||||
shared_lock& operator=(shared_lock const&) = delete;
|
||||
|
||||
shared_lock(shared_lock&& u) noexcept;
|
||||
shared_lock& operator=(shared_lock&& u) noexcept;
|
||||
|
||||
void lock(); // blocking
|
||||
bool try_lock();
|
||||
template <class Rep, class Period>
|
||||
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
|
||||
template <class Clock, class Duration>
|
||||
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
|
||||
void unlock();
|
||||
|
||||
// Setters
|
||||
void swap(shared_lock& u) noexcept;
|
||||
mutex_type* release() noexcept;
|
||||
|
||||
// Getters
|
||||
bool owns_lock() const noexcept;
|
||||
explicit operator bool () const noexcept;
|
||||
mutex_type* mutex() const noexcept;
|
||||
};
|
||||
|
||||
template <class Mutex>
|
||||
void swap(shared_lock<Mutex>& x, shared_lock<Mutex>& y) noexcept;
|
||||
|
||||
} // std
|
||||
|
||||
*/
|
||||
|
||||
#include "third_party/libcxx/__config"
|
||||
#include "third_party/libcxx/version"
|
||||
|
||||
_LIBCPP_PUSH_MACROS
|
||||
#include "third_party/libcxx/__undef_macros"
|
||||
|
||||
|
||||
#if _LIBCPP_STD_VER > 11 || defined(_LIBCPP_BUILDING_LIBRARY)
|
||||
|
||||
#include "third_party/libcxx/__mutex_base"
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#ifdef _LIBCPP_HAS_NO_THREADS
|
||||
#error <shared_mutex> is not supported on this single threaded system
|
||||
#else // !_LIBCPP_HAS_NO_THREADS
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
struct _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX _LIBCPP_THREAD_SAFETY_ANNOTATION(capability("shared_mutex"))
|
||||
__shared_mutex_base
|
||||
{
|
||||
mutex __mut_;
|
||||
condition_variable __gate1_;
|
||||
condition_variable __gate2_;
|
||||
unsigned __state_;
|
||||
|
||||
static const unsigned __write_entered_ = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);
|
||||
static const unsigned __n_readers_ = ~__write_entered_;
|
||||
|
||||
__shared_mutex_base();
|
||||
_LIBCPP_INLINE_VISIBILITY ~__shared_mutex_base() = default;
|
||||
|
||||
__shared_mutex_base(const __shared_mutex_base&) = delete;
|
||||
__shared_mutex_base& operator=(const __shared_mutex_base&) = delete;
|
||||
|
||||
// Exclusive ownership
|
||||
void lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_capability()); // blocking
|
||||
bool try_lock() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_capability(true));
|
||||
void unlock() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_capability());
|
||||
|
||||
// Shared ownership
|
||||
void lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(acquire_shared_capability()); // blocking
|
||||
bool try_lock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(try_acquire_shared_capability(true));
|
||||
void unlock_shared() _LIBCPP_THREAD_SAFETY_ANNOTATION(release_shared_capability());
|
||||
|
||||
// typedef implementation-defined native_handle_type; // See 30.2.3
|
||||
// native_handle_type native_handle(); // See 30.2.3
|
||||
};
|
||||
|
||||
|
||||
#if _LIBCPP_STD_VER > 14
|
||||
class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_mutex
|
||||
{
|
||||
__shared_mutex_base __base;
|
||||
public:
|
||||
_LIBCPP_INLINE_VISIBILITY shared_mutex() : __base() {}
|
||||
_LIBCPP_INLINE_VISIBILITY ~shared_mutex() = default;
|
||||
|
||||
shared_mutex(const shared_mutex&) = delete;
|
||||
shared_mutex& operator=(const shared_mutex&) = delete;
|
||||
|
||||
// Exclusive ownership
|
||||
_LIBCPP_INLINE_VISIBILITY void lock() { return __base.lock(); }
|
||||
_LIBCPP_INLINE_VISIBILITY bool try_lock() { return __base.try_lock(); }
|
||||
_LIBCPP_INLINE_VISIBILITY void unlock() { return __base.unlock(); }
|
||||
|
||||
// Shared ownership
|
||||
_LIBCPP_INLINE_VISIBILITY void lock_shared() { return __base.lock_shared(); }
|
||||
_LIBCPP_INLINE_VISIBILITY bool try_lock_shared() { return __base.try_lock_shared(); }
|
||||
_LIBCPP_INLINE_VISIBILITY void unlock_shared() { return __base.unlock_shared(); }
|
||||
|
||||
// typedef __shared_mutex_base::native_handle_type native_handle_type;
|
||||
// _LIBCPP_INLINE_VISIBILITY native_handle_type native_handle() { return __base::unlock_shared(); }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
class _LIBCPP_TYPE_VIS _LIBCPP_AVAILABILITY_SHARED_MUTEX shared_timed_mutex
|
||||
{
|
||||
__shared_mutex_base __base;
|
||||
public:
|
||||
shared_timed_mutex();
|
||||
_LIBCPP_INLINE_VISIBILITY ~shared_timed_mutex() = default;
|
||||
|
||||
shared_timed_mutex(const shared_timed_mutex&) = delete;
|
||||
shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
|
||||
|
||||
// Exclusive ownership
|
||||
void lock();
|
||||
bool try_lock();
|
||||
template <class _Rep, class _Period>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
try_lock_for(const chrono::duration<_Rep, _Period>& __rel_time)
|
||||
{
|
||||
return try_lock_until(chrono::steady_clock::now() + __rel_time);
|
||||
}
|
||||
template <class _Clock, class _Duration>
|
||||
_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
|
||||
bool
|
||||
try_lock_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
|
||||
void unlock();
|
||||
|
||||
// Shared ownership
|
||||
void lock_shared();
|
||||
bool try_lock_shared();
|
||||
template <class _Rep, class _Period>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
try_lock_shared_for(const chrono::duration<_Rep, _Period>& __rel_time)
|
||||
{
|
||||
return try_lock_shared_until(chrono::steady_clock::now() + __rel_time);
|
||||
}
|
||||
template <class _Clock, class _Duration>
|
||||
_LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
|
||||
bool
|
||||
try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& __abs_time);
|
||||
void unlock_shared();
|
||||
};
|
||||
|
||||
template <class _Clock, class _Duration>
|
||||
bool
|
||||
shared_timed_mutex::try_lock_until(
|
||||
const chrono::time_point<_Clock, _Duration>& __abs_time)
|
||||
{
|
||||
unique_lock<mutex> __lk(__base.__mut_);
|
||||
if (__base.__state_ & __base.__write_entered_)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
cv_status __status = __base.__gate1_.wait_until(__lk, __abs_time);
|
||||
if ((__base.__state_ & __base.__write_entered_) == 0)
|
||||
break;
|
||||
if (__status == cv_status::timeout)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
__base.__state_ |= __base.__write_entered_;
|
||||
if (__base.__state_ & __base.__n_readers_)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
cv_status __status = __base.__gate2_.wait_until(__lk, __abs_time);
|
||||
if ((__base.__state_ & __base.__n_readers_) == 0)
|
||||
break;
|
||||
if (__status == cv_status::timeout)
|
||||
{
|
||||
__base.__state_ &= ~__base.__write_entered_;
|
||||
__base.__gate1_.notify_all();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class _Clock, class _Duration>
|
||||
bool
|
||||
shared_timed_mutex::try_lock_shared_until(
|
||||
const chrono::time_point<_Clock, _Duration>& __abs_time)
|
||||
{
|
||||
unique_lock<mutex> __lk(__base.__mut_);
|
||||
if ((__base.__state_ & __base.__write_entered_) || (__base.__state_ & __base.__n_readers_) == __base.__n_readers_)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
cv_status status = __base.__gate1_.wait_until(__lk, __abs_time);
|
||||
if ((__base.__state_ & __base.__write_entered_) == 0 &&
|
||||
(__base.__state_ & __base.__n_readers_) < __base.__n_readers_)
|
||||
break;
|
||||
if (status == cv_status::timeout)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
unsigned __num_readers = (__base.__state_ & __base.__n_readers_) + 1;
|
||||
__base.__state_ &= ~__base.__n_readers_;
|
||||
__base.__state_ |= __num_readers;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class _Mutex>
|
||||
class shared_lock
|
||||
{
|
||||
public:
|
||||
typedef _Mutex mutex_type;
|
||||
|
||||
private:
|
||||
mutex_type* __m_;
|
||||
bool __owns_;
|
||||
|
||||
public:
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
shared_lock() _NOEXCEPT
|
||||
: __m_(nullptr),
|
||||
__owns_(false)
|
||||
{}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
explicit shared_lock(mutex_type& __m)
|
||||
: __m_(_VSTD::addressof(__m)),
|
||||
__owns_(true)
|
||||
{__m_->lock_shared();}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
shared_lock(mutex_type& __m, defer_lock_t) _NOEXCEPT
|
||||
: __m_(_VSTD::addressof(__m)),
|
||||
__owns_(false)
|
||||
{}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
shared_lock(mutex_type& __m, try_to_lock_t)
|
||||
: __m_(_VSTD::addressof(__m)),
|
||||
__owns_(__m.try_lock_shared())
|
||||
{}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
shared_lock(mutex_type& __m, adopt_lock_t)
|
||||
: __m_(_VSTD::addressof(__m)),
|
||||
__owns_(true)
|
||||
{}
|
||||
|
||||
template <class _Clock, class _Duration>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
shared_lock(mutex_type& __m,
|
||||
const chrono::time_point<_Clock, _Duration>& __abs_time)
|
||||
: __m_(_VSTD::addressof(__m)),
|
||||
__owns_(__m.try_lock_shared_until(__abs_time))
|
||||
{}
|
||||
|
||||
template <class _Rep, class _Period>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
shared_lock(mutex_type& __m,
|
||||
const chrono::duration<_Rep, _Period>& __rel_time)
|
||||
: __m_(_VSTD::addressof(__m)),
|
||||
__owns_(__m.try_lock_shared_for(__rel_time))
|
||||
{}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
~shared_lock()
|
||||
{
|
||||
if (__owns_)
|
||||
__m_->unlock_shared();
|
||||
}
|
||||
|
||||
shared_lock(shared_lock const&) = delete;
|
||||
shared_lock& operator=(shared_lock const&) = delete;
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
shared_lock(shared_lock&& __u) _NOEXCEPT
|
||||
: __m_(__u.__m_),
|
||||
__owns_(__u.__owns_)
|
||||
{
|
||||
__u.__m_ = nullptr;
|
||||
__u.__owns_ = false;
|
||||
}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
shared_lock& operator=(shared_lock&& __u) _NOEXCEPT
|
||||
{
|
||||
if (__owns_)
|
||||
__m_->unlock_shared();
|
||||
__m_ = nullptr;
|
||||
__owns_ = false;
|
||||
__m_ = __u.__m_;
|
||||
__owns_ = __u.__owns_;
|
||||
__u.__m_ = nullptr;
|
||||
__u.__owns_ = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void lock();
|
||||
bool try_lock();
|
||||
template <class Rep, class Period>
|
||||
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
|
||||
template <class Clock, class Duration>
|
||||
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
|
||||
void unlock();
|
||||
|
||||
// Setters
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
void swap(shared_lock& __u) _NOEXCEPT
|
||||
{
|
||||
_VSTD::swap(__m_, __u.__m_);
|
||||
_VSTD::swap(__owns_, __u.__owns_);
|
||||
}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
mutex_type* release() _NOEXCEPT
|
||||
{
|
||||
mutex_type* __m = __m_;
|
||||
__m_ = nullptr;
|
||||
__owns_ = false;
|
||||
return __m;
|
||||
}
|
||||
|
||||
// Getters
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
bool owns_lock() const _NOEXCEPT {return __owns_;}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
explicit operator bool () const _NOEXCEPT {return __owns_;}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
mutex_type* mutex() const _NOEXCEPT {return __m_;}
|
||||
};
|
||||
|
||||
template <class _Mutex>
|
||||
void
|
||||
shared_lock<_Mutex>::lock()
|
||||
{
|
||||
if (__m_ == nullptr)
|
||||
__throw_system_error(EPERM, "shared_lock::lock: references null mutex");
|
||||
if (__owns_)
|
||||
__throw_system_error(EDEADLK, "shared_lock::lock: already locked");
|
||||
__m_->lock_shared();
|
||||
__owns_ = true;
|
||||
}
|
||||
|
||||
template <class _Mutex>
|
||||
bool
|
||||
shared_lock<_Mutex>::try_lock()
|
||||
{
|
||||
if (__m_ == nullptr)
|
||||
__throw_system_error(EPERM, "shared_lock::try_lock: references null mutex");
|
||||
if (__owns_)
|
||||
__throw_system_error(EDEADLK, "shared_lock::try_lock: already locked");
|
||||
__owns_ = __m_->try_lock_shared();
|
||||
return __owns_;
|
||||
}
|
||||
|
||||
template <class _Mutex>
|
||||
template <class _Rep, class _Period>
|
||||
bool
|
||||
shared_lock<_Mutex>::try_lock_for(const chrono::duration<_Rep, _Period>& __d)
|
||||
{
|
||||
if (__m_ == nullptr)
|
||||
__throw_system_error(EPERM, "shared_lock::try_lock_for: references null mutex");
|
||||
if (__owns_)
|
||||
__throw_system_error(EDEADLK, "shared_lock::try_lock_for: already locked");
|
||||
__owns_ = __m_->try_lock_shared_for(__d);
|
||||
return __owns_;
|
||||
}
|
||||
|
||||
template <class _Mutex>
|
||||
template <class _Clock, class _Duration>
|
||||
bool
|
||||
shared_lock<_Mutex>::try_lock_until(const chrono::time_point<_Clock, _Duration>& __t)
|
||||
{
|
||||
if (__m_ == nullptr)
|
||||
__throw_system_error(EPERM, "shared_lock::try_lock_until: references null mutex");
|
||||
if (__owns_)
|
||||
__throw_system_error(EDEADLK, "shared_lock::try_lock_until: already locked");
|
||||
__owns_ = __m_->try_lock_shared_until(__t);
|
||||
return __owns_;
|
||||
}
|
||||
|
||||
template <class _Mutex>
|
||||
void
|
||||
shared_lock<_Mutex>::unlock()
|
||||
{
|
||||
if (!__owns_)
|
||||
__throw_system_error(EPERM, "shared_lock::unlock: not locked");
|
||||
__m_->unlock_shared();
|
||||
__owns_ = false;
|
||||
}
|
||||
|
||||
template <class _Mutex>
|
||||
inline _LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
swap(shared_lock<_Mutex>& __x, shared_lock<_Mutex>& __y) _NOEXCEPT
|
||||
{__x.swap(__y);}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // !_LIBCPP_HAS_NO_THREADS
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 11
|
||||
|
||||
_LIBCPP_POP_MACROS
|
||||
|
||||
#endif // _LIBCPP_SHARED_MUTEX
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
// clang-format off
|
||||
//===---------------------- shared_mutex.cpp ------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "third_party/libcxx/__config"
|
||||
#ifndef _LIBCPP_HAS_NO_THREADS
|
||||
|
||||
#include "third_party/libcxx/shared_mutex"
|
||||
#if defined(__unix__) && !defined(__ANDROID__) && defined(__ELF__) && defined(_LIBCPP_HAS_COMMENT_LIB_PRAGMA)
|
||||
#pragma comment(lib, "pthread")
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
// Shared Mutex Base
|
||||
__shared_mutex_base::__shared_mutex_base()
|
||||
: __state_(0)
|
||||
{
|
||||
}
|
||||
|
||||
// Exclusive ownership
|
||||
|
||||
void
|
||||
__shared_mutex_base::lock()
|
||||
{
|
||||
unique_lock<mutex> lk(__mut_);
|
||||
while (__state_ & __write_entered_)
|
||||
__gate1_.wait(lk);
|
||||
__state_ |= __write_entered_;
|
||||
while (__state_ & __n_readers_)
|
||||
__gate2_.wait(lk);
|
||||
}
|
||||
|
||||
bool
|
||||
__shared_mutex_base::try_lock()
|
||||
{
|
||||
unique_lock<mutex> lk(__mut_);
|
||||
if (__state_ == 0)
|
||||
{
|
||||
__state_ = __write_entered_;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
__shared_mutex_base::unlock()
|
||||
{
|
||||
lock_guard<mutex> _(__mut_);
|
||||
__state_ = 0;
|
||||
__gate1_.notify_all();
|
||||
}
|
||||
|
||||
// Shared ownership
|
||||
|
||||
void
|
||||
__shared_mutex_base::lock_shared()
|
||||
{
|
||||
unique_lock<mutex> lk(__mut_);
|
||||
while ((__state_ & __write_entered_) || (__state_ & __n_readers_) == __n_readers_)
|
||||
__gate1_.wait(lk);
|
||||
unsigned num_readers = (__state_ & __n_readers_) + 1;
|
||||
__state_ &= ~__n_readers_;
|
||||
__state_ |= num_readers;
|
||||
}
|
||||
|
||||
bool
|
||||
__shared_mutex_base::try_lock_shared()
|
||||
{
|
||||
unique_lock<mutex> lk(__mut_);
|
||||
unsigned num_readers = __state_ & __n_readers_;
|
||||
if (!(__state_ & __write_entered_) && num_readers != __n_readers_)
|
||||
{
|
||||
++num_readers;
|
||||
__state_ &= ~__n_readers_;
|
||||
__state_ |= num_readers;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
__shared_mutex_base::unlock_shared()
|
||||
{
|
||||
lock_guard<mutex> _(__mut_);
|
||||
unsigned num_readers = (__state_ & __n_readers_) - 1;
|
||||
__state_ &= ~__n_readers_;
|
||||
__state_ |= num_readers;
|
||||
if (__state_ & __write_entered_)
|
||||
{
|
||||
if (num_readers == 0)
|
||||
__gate2_.notify_one();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (num_readers == __n_readers_ - 1)
|
||||
__gate1_.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Shared Timed Mutex
|
||||
// These routines are here for ABI stability
|
||||
shared_timed_mutex::shared_timed_mutex() : __base() {}
|
||||
void shared_timed_mutex::lock() { return __base.lock(); }
|
||||
bool shared_timed_mutex::try_lock() { return __base.try_lock(); }
|
||||
void shared_timed_mutex::unlock() { return __base.unlock(); }
|
||||
void shared_timed_mutex::lock_shared() { return __base.lock_shared(); }
|
||||
bool shared_timed_mutex::try_lock_shared() { return __base.try_lock_shared(); }
|
||||
void shared_timed_mutex::unlock_shared() { return __base.unlock_shared(); }
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // !_LIBCPP_HAS_NO_THREADS
|
||||
Vendored
-10
@@ -37,8 +37,6 @@ void nsync_mu_semaphore_init (nsync_semaphore *s) {
|
||||
return nsync_mu_semaphore_init_gcd (s);
|
||||
} else if (IsNetbsd ()) {
|
||||
return nsync_mu_semaphore_init_sem (s);
|
||||
} else if (IsWindows ()) {
|
||||
return nsync_mu_semaphore_init_win32 (s);
|
||||
} else {
|
||||
return nsync_mu_semaphore_init_futex (s);
|
||||
}
|
||||
@@ -50,8 +48,6 @@ void nsync_mu_semaphore_destroy (nsync_semaphore *s) {
|
||||
return nsync_mu_semaphore_destroy_gcd (s);
|
||||
} else if (IsNetbsd ()) {
|
||||
return nsync_mu_semaphore_destroy_sem (s);
|
||||
} else if (IsWindows ()) {
|
||||
return nsync_mu_semaphore_destroy_win32 (s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +62,6 @@ errno_t nsync_mu_semaphore_p (nsync_semaphore *s) {
|
||||
err = nsync_mu_semaphore_p_gcd (s);
|
||||
} else if (IsNetbsd ()) {
|
||||
err = nsync_mu_semaphore_p_sem (s);
|
||||
} else if (IsWindows ()) {
|
||||
err = nsync_mu_semaphore_p_win32 (s);
|
||||
} else {
|
||||
err = nsync_mu_semaphore_p_futex (s);
|
||||
}
|
||||
@@ -86,8 +80,6 @@ errno_t nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_d
|
||||
err = nsync_mu_semaphore_p_with_deadline_gcd (s, abs_deadline);
|
||||
} else if (IsNetbsd ()) {
|
||||
err = nsync_mu_semaphore_p_with_deadline_sem (s, abs_deadline);
|
||||
} else if (IsWindows ()) {
|
||||
err = nsync_mu_semaphore_p_with_deadline_win32 (s, abs_deadline);
|
||||
} else {
|
||||
err = nsync_mu_semaphore_p_with_deadline_futex (s, abs_deadline);
|
||||
}
|
||||
@@ -101,8 +93,6 @@ void nsync_mu_semaphore_v (nsync_semaphore *s) {
|
||||
return nsync_mu_semaphore_v_gcd (s);
|
||||
} else if (IsNetbsd ()) {
|
||||
return nsync_mu_semaphore_v_sem (s);
|
||||
} else if (IsWindows ()) {
|
||||
return nsync_mu_semaphore_v_win32 (s);
|
||||
} else {
|
||||
return nsync_mu_semaphore_v_futex (s);
|
||||
}
|
||||
|
||||
-6
@@ -22,12 +22,6 @@ errno_t nsync_mu_semaphore_p_gcd(nsync_semaphore *);
|
||||
errno_t nsync_mu_semaphore_p_with_deadline_gcd(nsync_semaphore *, nsync_time);
|
||||
void nsync_mu_semaphore_v_gcd(nsync_semaphore *);
|
||||
|
||||
void nsync_mu_semaphore_init_win32(nsync_semaphore *);
|
||||
void nsync_mu_semaphore_destroy_win32(nsync_semaphore *);
|
||||
errno_t nsync_mu_semaphore_p_win32(nsync_semaphore *);
|
||||
errno_t nsync_mu_semaphore_p_with_deadline_win32(nsync_semaphore *, nsync_time);
|
||||
void nsync_mu_semaphore_v_win32(nsync_semaphore *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_NSYNC_MU_SEMAPHORE_INTERNAL_H_ */
|
||||
-140
@@ -1,140 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2016 Google Inc. │
|
||||
│ │
|
||||
│ Licensed under the Apache License, Version 2.0 (the "License"); │
|
||||
│ you may not use this file except in compliance with the License. │
|
||||
│ You may obtain a copy of the License at │
|
||||
│ │
|
||||
│ http://www.apache.org/licenses/LICENSE-2.0 │
|
||||
│ │
|
||||
│ Unless required by applicable law or agreed to in writing, software │
|
||||
│ distributed under the License is distributed on an "AS IS" BASIS, │
|
||||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │
|
||||
│ See the License for the specific language governing permissions and │
|
||||
│ limitations under the License. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "third_party/nsync/mu_semaphore.h"
|
||||
#include "third_party/nsync/mu_semaphore.internal.h"
|
||||
#include "third_party/nsync/time.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
asm(".ident\t\"\\n\\n\
|
||||
*NSYNC (Apache 2.0)\\n\
|
||||
Copyright 2016 Google, Inc.\\n\
|
||||
https://github.com/google/nsync\"");
|
||||
// clang-format off
|
||||
|
||||
/* Initialize *s; the initial value is 0. */
|
||||
textwindows void nsync_mu_semaphore_init_win32 (nsync_semaphore *s) {
|
||||
int64_t *h = (int64_t *) s;
|
||||
*h = CreateSemaphore (&kNtIsInheritable, 0, 1, NULL);
|
||||
if (!*h) notpossible;
|
||||
}
|
||||
|
||||
/* Releases system resources associated with *s. */
|
||||
textwindows void nsync_mu_semaphore_destroy_win32 (nsync_semaphore *s) {
|
||||
int64_t *h = (int64_t *) s;
|
||||
CloseHandle (*h);
|
||||
}
|
||||
|
||||
/* Wait until the count of *s exceeds 0, and decrement it. */
|
||||
textwindows errno_t nsync_mu_semaphore_p_win32 (nsync_semaphore *s) {
|
||||
errno_t err = 0;
|
||||
int64_t *h = (int64_t *) s;
|
||||
struct PosixThread *pt = _pthread_self ();
|
||||
if (pt) {
|
||||
pt->pt_semaphore = *h;
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
atomic_store_explicit (&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release);
|
||||
}
|
||||
if (_check_cancel() != -1) {
|
||||
WaitForSingleObject (*h, -1u);
|
||||
if (_check_cancel()) {
|
||||
err = ECANCELED;
|
||||
}
|
||||
} else {
|
||||
err = ECANCELED;
|
||||
}
|
||||
if (pt) {
|
||||
atomic_store_explicit (&pt->pt_blocker, PT_BLOCKER_CPU, memory_order_release);
|
||||
pt->pt_semaphore = 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Wait until one of:
|
||||
the count of *s is non-zero, in which case decrement *s and return 0;
|
||||
or abs_deadline expires, in which case return ETIMEDOUT. */
|
||||
textwindows int nsync_mu_semaphore_p_with_deadline_win32 (nsync_semaphore *s, nsync_time abs_deadline) {
|
||||
int result;
|
||||
int64_t *h = (int64_t *) s;
|
||||
struct PosixThread *pt = _pthread_self ();
|
||||
if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) == 0) {
|
||||
if (!nsync_mu_semaphore_p_win32 (s)) {
|
||||
return 0;
|
||||
} else {
|
||||
return ECANCELED;
|
||||
}
|
||||
} else {
|
||||
nsync_time now;
|
||||
now = nsync_time_now ();
|
||||
do {
|
||||
if (nsync_time_cmp (abs_deadline, now) <= 0) {
|
||||
result = WaitForSingleObject (*h, 0);
|
||||
} else {
|
||||
errno_t err = 0;
|
||||
nsync_time delay;
|
||||
delay = nsync_time_sub (abs_deadline, now);
|
||||
if (pt) {
|
||||
pt->pt_semaphore = *h;
|
||||
pt->pt_flags &= ~PT_RESTARTABLE;
|
||||
atomic_store_explicit (&pt->pt_blocker, PT_BLOCKER_SEM, memory_order_release);
|
||||
}
|
||||
if (_check_cancel() != -1) {
|
||||
if (NSYNC_TIME_SEC (delay) > 1000*1000) {
|
||||
result = WaitForSingleObject (*h, 1000*1000);
|
||||
} else {
|
||||
result = WaitForSingleObject (*h,
|
||||
(unsigned) (NSYNC_TIME_SEC (delay) * 1000 +
|
||||
(NSYNC_TIME_NSEC (delay) + 999999) / (1000 * 1000)));
|
||||
}
|
||||
if (_check_cancel()) {
|
||||
err = ECANCELED;
|
||||
}
|
||||
} else {
|
||||
err = ECANCELED;
|
||||
}
|
||||
if (pt) {
|
||||
atomic_store_explicit (&pt->pt_blocker, PT_BLOCKER_CPU, memory_order_release);
|
||||
pt->pt_semaphore = 0;
|
||||
}
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (result == kNtWaitTimeout) {
|
||||
now = nsync_time_now ();
|
||||
}
|
||||
} while (result == kNtWaitTimeout && /* Windows generates early wakeups. */
|
||||
nsync_time_cmp (abs_deadline, now) > 0);
|
||||
}
|
||||
return (result == kNtWaitTimeout ? ETIMEDOUT : 0);
|
||||
}
|
||||
|
||||
/* Ensure that the count of *s is at least 1. */
|
||||
textwindows void nsync_mu_semaphore_v_win32 (nsync_semaphore *s) {
|
||||
int64_t *h = (int64_t *) s;
|
||||
ReleaseSemaphore(*h, 1, NULL);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
+3
-3
@@ -77,7 +77,7 @@ o/$(MODE)/tool/build/%.com.dbg: \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
@$(APELINK)
|
||||
|
||||
o/$(MODE)/tool/build/dso/sandbox.so.zip.o \
|
||||
o/$(MODE)/tool/build/dso/sandbox-$(ARCH).so.zip.o \
|
||||
o/$(MODE)/tool/build/false.com.zip.o \
|
||||
o/$(MODE)/tool/build/echo.com.zip.o \
|
||||
o/$(MODE)/tool/build/cocmd.com.zip.o: private \
|
||||
@@ -99,7 +99,7 @@ o/$(MODE)/tool/build/dso/sandbox.o: \
|
||||
libc/intrin/promises.internal.h \
|
||||
tool/build/build.mk
|
||||
|
||||
o/$(MODE)/tool/build/dso/sandbox.so: \
|
||||
o/$(MODE)/tool/build/dso/sandbox-$(ARCH).so: \
|
||||
o/$(MODE)/tool/build/dso/sandbox.o \
|
||||
o/$(MODE)/libc/calls/pledge-linux.o \
|
||||
o/$(MODE)/libc/sysv/restorert.o
|
||||
@@ -118,7 +118,7 @@ o/$(MODE)/tool/build/dso/sandbox.so: \
|
||||
o/$(MODE)/tool/build/pledge.com.dbg: \
|
||||
$(TOOL_BUILD_DEPS) \
|
||||
o/$(MODE)/tool/build/build.pkg \
|
||||
o/$(MODE)/tool/build/dso/sandbox.so.zip.o \
|
||||
o/$(MODE)/tool/build/dso/sandbox-$(ARCH).so.zip.o \
|
||||
o/$(MODE)/tool/build/pledge.o \
|
||||
$(CRT) \
|
||||
$(APE_NO_MODIFY_SELF)
|
||||
|
||||
@@ -22,10 +22,8 @@
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/*
|
||||
* runs pledge at glibc executable load time, e.g.
|
||||
* strace -vff bash -c '_PLEDGE=4194303,0 LD_PRELOAD=$HOME/sandbox.so ls'
|
||||
*/
|
||||
// runs pledge at glibc executable load time, e.g.
|
||||
// strace -vff bash -c '_PLEDGE=4194303,0 LD_PRELOAD=$HOME/sandbox.so ls'
|
||||
|
||||
__attribute__((__constructor__)) void init(void) {
|
||||
int c, i, j;
|
||||
|
||||
+284
-189
@@ -17,7 +17,7 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/pledge.h"
|
||||
#include "libc/assert.h"
|
||||
#include "ape/ape.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/landlock.h"
|
||||
#include "libc/calls/pledge.internal.h"
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "libc/calls/struct/seccomp.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/struct/sysinfo.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
@@ -35,6 +36,7 @@
|
||||
#include "libc/elf/struct/phdr.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
@@ -42,13 +44,14 @@
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/nexgen32e/kcpuids.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/struct/pollfd.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/sysparam.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/ioprio.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
@@ -75,7 +78,7 @@ __static_yoink("zipos");
|
||||
|
||||
#define USAGE \
|
||||
"\
|
||||
usage: pledge.com [-hnN] PROG ARGS...\n\
|
||||
usage: pledge [-hnN] PROG ARGS...\n\
|
||||
-h show help\n\
|
||||
-g GID call setgid()\n\
|
||||
-u UID call setuid()\n\
|
||||
@@ -116,7 +119,7 @@ usage: pledge.com [-hnN] PROG ARGS...\n\
|
||||
- vminfo: allows /proc/stat, /proc/self/maps, etc.\n\
|
||||
- tmppath: allows /tmp, $TMPPATH, lstat, unlink\n\
|
||||
\n\
|
||||
pledge.com v1.8\n\
|
||||
Cosompolitan Pledge v1.9\n\
|
||||
copyright 2022 justine alexandra roberts tunney\n\
|
||||
notice licenses are embedded in the binary\n\
|
||||
https://twitter.com/justinetunney\n\
|
||||
@@ -130,13 +133,27 @@ the https://justine.lol/pledge/ page for online documentation.\n\
|
||||
\n\
|
||||
"
|
||||
|
||||
#ifdef __x86_64__
|
||||
#define ARCH_NAME "x86_64"
|
||||
#elif defined(__aarch64__)
|
||||
#define ARCH_NAME "aarch64"
|
||||
#else
|
||||
#error "unsupported architecture"
|
||||
#endif
|
||||
|
||||
enum Strategy {
|
||||
kStrategyNull,
|
||||
kStrategyStatic,
|
||||
kStrategyDynamic,
|
||||
kStrategyApe,
|
||||
};
|
||||
|
||||
int g_gflag;
|
||||
int g_uflag;
|
||||
int g_kflag;
|
||||
int g_hflag;
|
||||
bool g_nice;
|
||||
bool g_qflag;
|
||||
bool isdynamic;
|
||||
bool g_noclose;
|
||||
long g_cpuquota;
|
||||
long g_fszquota;
|
||||
@@ -147,19 +164,60 @@ long g_dontdrop;
|
||||
long g_dontunveil;
|
||||
const char *g_test;
|
||||
const char *g_chroot;
|
||||
const char *g_promises;
|
||||
char pledgevar[64];
|
||||
char g_promises[256];
|
||||
char dsopath[PATH_MAX];
|
||||
char tmppath[PATH_MAX];
|
||||
char preloadvar[PATH_MAX];
|
||||
|
||||
struct {
|
||||
int n;
|
||||
char **p;
|
||||
char *p[10000];
|
||||
} unveils;
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
unsigned long HasPromise(unsigned long ipromises, int promise) {
|
||||
return ~ipromises & (1ul << promise);
|
||||
}
|
||||
|
||||
long Atoi(const char *s) {
|
||||
long i;
|
||||
char *ep;
|
||||
errno = 0;
|
||||
i = strtol(s, &ep, 0);
|
||||
if (*ep || errno) {
|
||||
tinyprint(2, program_invocation_name, ": invalid integer: ", s, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
long ParseSiSize(const char *s, long b) {
|
||||
long i;
|
||||
errno = 0;
|
||||
i = sizetol(s, b);
|
||||
if (errno) {
|
||||
tinyprint(2, program_invocation_name, ": invalid size: ", s, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void AddPromise(const char *s) {
|
||||
while (isspace(*s)) ++s;
|
||||
if (!*s) return;
|
||||
if (*g_promises) {
|
||||
strlcat(g_promises, " ", sizeof(g_promises));
|
||||
}
|
||||
if (strlcat(g_promises, s, sizeof(g_promises)) >= sizeof(g_promises)) {
|
||||
tinyprint(2, program_invocation_name, ": too many promises\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
struct sysinfo si;
|
||||
g_promises = 0;
|
||||
bool got_promise_flag = false;
|
||||
g_nfdquota = 64;
|
||||
g_fszquota = 256 * 1000 * 1000;
|
||||
if (!sysinfo(&si)) {
|
||||
@@ -196,46 +254,36 @@ static void GetOpts(int argc, char *argv[]) {
|
||||
g_chroot = optarg;
|
||||
break;
|
||||
case 'g':
|
||||
g_gflag = atoi(optarg);
|
||||
g_gflag = Atoi(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
g_uflag = atoi(optarg);
|
||||
g_uflag = Atoi(optarg);
|
||||
break;
|
||||
case 'C':
|
||||
g_cpuquota = atoi(optarg);
|
||||
g_cpuquota = Atoi(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
g_proquota = atoi(optarg);
|
||||
g_proquota = Atoi(optarg);
|
||||
break;
|
||||
case 'O':
|
||||
g_nfdquota = atoi(optarg);
|
||||
g_nfdquota = Atoi(optarg);
|
||||
break;
|
||||
case 'F':
|
||||
errno = 0;
|
||||
g_fszquota = sizetol(optarg, 1000);
|
||||
if (errno) {
|
||||
kprintf("error: invalid size: -F %s\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
g_fszquota = ParseSiSize(optarg, 1024);
|
||||
break;
|
||||
case 'M':
|
||||
errno = 0;
|
||||
g_memquota = sizetol(optarg, 1024);
|
||||
if (errno) {
|
||||
kprintf("error: invalid size: -F %s\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
g_memquota = ParseSiSize(optarg, 1024);
|
||||
break;
|
||||
case 'p':
|
||||
if (g_promises) {
|
||||
g_promises = xstrcat(g_promises, ' ', optarg);
|
||||
} else {
|
||||
g_promises = optarg;
|
||||
}
|
||||
AddPromise(optarg);
|
||||
got_promise_flag = true;
|
||||
break;
|
||||
case 'v':
|
||||
unveils.p = realloc(unveils.p, ++unveils.n * sizeof(*unveils.p));
|
||||
unveils.p[unveils.n - 1] = optarg;
|
||||
if (unveils.n == ARRAYLEN(unveils.p)) {
|
||||
tinyprint(2, program_invocation_name, ": too many unveils\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
unveils.p[unveils.n++] = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
@@ -246,16 +294,16 @@ static void GetOpts(int argc, char *argv[]) {
|
||||
exit(64);
|
||||
}
|
||||
}
|
||||
if (!g_promises) {
|
||||
g_promises = "stdio rpath";
|
||||
if (!got_promise_flag) {
|
||||
stpcpy(g_promises, "stdio rpath");
|
||||
}
|
||||
}
|
||||
|
||||
const char *prog;
|
||||
const char *g_prog;
|
||||
char pathbuf[PATH_MAX];
|
||||
struct pollfd pfds[256];
|
||||
|
||||
static bool SupportsLandlock(void) {
|
||||
bool SupportsLandlock(void) {
|
||||
int e = errno;
|
||||
bool r = landlock_create_ruleset(0, 0, LANDLOCK_CREATE_RULESET_VERSION) >= 0;
|
||||
errno = e;
|
||||
@@ -265,7 +313,7 @@ static bool SupportsLandlock(void) {
|
||||
int GetPollMaxFds(void) {
|
||||
int n;
|
||||
struct rlimit rl;
|
||||
if (getrlimit(RLIMIT_NOFILE, &rl) != -1) {
|
||||
if (!getrlimit(RLIMIT_NOFILE, &rl)) {
|
||||
n = rl.rlim_cur;
|
||||
} else {
|
||||
n = 64;
|
||||
@@ -274,27 +322,26 @@ int GetPollMaxFds(void) {
|
||||
}
|
||||
|
||||
void NormalizeFileDescriptors(void) {
|
||||
int e, i, n, fd;
|
||||
int i, n, fd;
|
||||
closefrom(3); // faster and more secure if linux 5.9+ or bsd
|
||||
n = GetPollMaxFds();
|
||||
e = errno;
|
||||
closefrom(3); // more secure if linux 5.9+
|
||||
errno = e;
|
||||
for (i = 0; i < n; ++i) {
|
||||
pfds[i].fd = i;
|
||||
pfds[i].events = POLLIN;
|
||||
}
|
||||
if (poll(pfds, n, 0) == -1) {
|
||||
kprintf("error: poll() failed: %m\n");
|
||||
perror("poll");
|
||||
exit(1);
|
||||
}
|
||||
for (i = 0; i < 3; ++i) {
|
||||
if (pfds[i].revents & POLLNVAL) {
|
||||
if ((fd = open("/dev/null", O_RDWR)) == -1) {
|
||||
kprintf("error: open(\"/dev/null\") failed: %m\n");
|
||||
perror("/dev/null");
|
||||
exit(2);
|
||||
}
|
||||
if (fd != i) {
|
||||
kprintf("error: open() is broken: %d vs. %d\n", fd, i);
|
||||
tinyprint(2, program_invocation_name, ": poll() or open() is broken\n",
|
||||
NULL);
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
@@ -302,7 +349,7 @@ void NormalizeFileDescriptors(void) {
|
||||
for (i = 3; i < n; ++i) {
|
||||
if (~pfds[i].revents & POLLNVAL) {
|
||||
if (close(pfds[i].fd) == -1) {
|
||||
kprintf("error: close(%d) failed: %m\n", pfds[i].fd);
|
||||
perror("close");
|
||||
exit(4);
|
||||
}
|
||||
}
|
||||
@@ -354,8 +401,8 @@ bool PathExists(const char *path) {
|
||||
|
||||
void Unveil(const char *path, const char *perm) {
|
||||
if (unveil(path, perm) == -1) {
|
||||
kprintf("error: unveil(%#s, %#s) failed: %m\n", path, perm);
|
||||
_Exit(20);
|
||||
perror(path);
|
||||
exit(20);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,22 +415,62 @@ int UnveilIfExists(const char *path, const char *perm) {
|
||||
} else if (errno == ENOENT) {
|
||||
errno = err;
|
||||
} else {
|
||||
kprintf("error: unveil(%#s, %#s) failed: %m\n", path, perm);
|
||||
_Exit(20);
|
||||
perror(path);
|
||||
exit(20);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
enum Strategy GetStrategy(void) {
|
||||
static enum Strategy strategy;
|
||||
if (strategy == kStrategyNull) {
|
||||
strategy = kStrategyStatic;
|
||||
int fd;
|
||||
if ((fd = open(g_prog, O_RDONLY)) != -1) {
|
||||
union {
|
||||
char magic[8];
|
||||
Elf64_Ehdr ehdr;
|
||||
} hdr = {0};
|
||||
if (pread(fd, &hdr, sizeof(hdr), 0) == sizeof(hdr)) {
|
||||
if (READ64LE(hdr.magic) == READ64LE("MZqFpD='") ||
|
||||
READ64LE(hdr.magic) == READ64LE("jartsr='") ||
|
||||
READ64LE(hdr.magic) == READ64LE("APEDBG='")) {
|
||||
strategy = kStrategyApe;
|
||||
} else if ((IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd()) &&
|
||||
IsElf64Binary(&hdr.ehdr, sizeof(hdr))) {
|
||||
if (hdr.ehdr.e_type == ET_DYN) {
|
||||
strategy = kStrategyDynamic;
|
||||
} else {
|
||||
Elf64_Phdr phdrs[16];
|
||||
int count = MIN(hdr.ehdr.e_phnum, ARRAYLEN(phdrs));
|
||||
int bytes = count * sizeof(Elf64_Phdr);
|
||||
if (pread(fd, phdrs, bytes, hdr.ehdr.e_phoff) == bytes) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (phdrs[i].p_type == PT_INTERP ||
|
||||
phdrs[i].p_type == PT_DYNAMIC) {
|
||||
strategy = kStrategyDynamic;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
return strategy;
|
||||
}
|
||||
|
||||
void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||
const char *p;
|
||||
|
||||
if (g_dontunveil) return;
|
||||
if (!SupportsLandlock()) return;
|
||||
|
||||
Unveil(prog, "rx");
|
||||
Unveil(g_prog, "rx");
|
||||
|
||||
if (isdynamic) {
|
||||
if (GetStrategy() == kStrategyDynamic) {
|
||||
Unveil(dsopath, "rx");
|
||||
UnveilIfExists("/lib", "rx");
|
||||
UnveilIfExists("/lib64", "rx");
|
||||
@@ -391,14 +478,16 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||
UnveilIfExists("/usr/lib64", "rx");
|
||||
UnveilIfExists("/usr/local/lib", "rx");
|
||||
UnveilIfExists("/usr/local/lib64", "rx");
|
||||
UnveilIfExists("/etc/ld-musl-x86_64.path", "r");
|
||||
UnveilIfExists("/etc/ld-musl-" ARCH_NAME ".path", "r");
|
||||
UnveilIfExists("/etc/ld.so.conf", "r");
|
||||
UnveilIfExists("/etc/ld.so.cache", "r");
|
||||
UnveilIfExists("/etc/ld.so.conf.d", "r");
|
||||
UnveilIfExists("/etc/ld.so.preload", "r");
|
||||
// in case musl is symlinked somewhere else
|
||||
UnveilIfExists("/lib/ld-musl-" ARCH_NAME ".so.1", "rx");
|
||||
}
|
||||
|
||||
if (~ipromises & (1ul << PROMISE_STDIO)) {
|
||||
if (HasPromise(ipromises, PROMISE_STDIO)) {
|
||||
UnveilIfExists("/dev/fd", "r");
|
||||
UnveilIfExists("/dev/log", "w");
|
||||
UnveilIfExists("/dev/zero", "r");
|
||||
@@ -422,15 +511,15 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||
UnveilIfExists("/proc/sys/vm/overcommit_memory", "r");
|
||||
}
|
||||
|
||||
if (~ipromises & (1ul << PROMISE_INET)) {
|
||||
if (HasPromise(ipromises, PROMISE_INET)) {
|
||||
UnveilIfExists("/etc/ssl/certs/ca-certificates.crt", "r");
|
||||
}
|
||||
|
||||
if (~ipromises & (1ul << PROMISE_RPATH)) {
|
||||
if (HasPromise(ipromises, PROMISE_RPATH)) {
|
||||
UnveilIfExists("/proc/filesystems", "r");
|
||||
}
|
||||
|
||||
if (~ipromises & (1ul << PROMISE_DNS)) {
|
||||
if (HasPromise(ipromises, PROMISE_DNS)) {
|
||||
UnveilIfExists("/etc/hosts", "r");
|
||||
UnveilIfExists("/etc/hostname", "r");
|
||||
UnveilIfExists("/etc/services", "r");
|
||||
@@ -438,7 +527,7 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||
UnveilIfExists("/etc/resolv.conf", "r");
|
||||
}
|
||||
|
||||
if (~ipromises & (1ul << PROMISE_TTY)) {
|
||||
if (HasPromise(ipromises, PROMISE_TTY)) {
|
||||
UnveilIfExists(ttyname(0), "rw");
|
||||
UnveilIfExists("/dev/tty", "rw");
|
||||
UnveilIfExists("/dev/console", "rw");
|
||||
@@ -447,18 +536,21 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||
UnveilIfExists("/usr/share/terminfo", "r");
|
||||
}
|
||||
|
||||
if (~ipromises & (1ul << PROMISE_PROT_EXEC)) {
|
||||
if (GetStrategy() == kStrategyApe) {
|
||||
if (UnveilIfExists("/usr/bin/ape", "rx") == -1) {
|
||||
char buf[PATH_MAX];
|
||||
if ((p = getenv("TMPDIR"))) {
|
||||
UnveilIfExists(xjoinpaths(p, ".ape"), "rx");
|
||||
UnveilIfExists(
|
||||
__join_paths(buf, sizeof(buf), p, ".ape-" APE_VERSION_STR), "rx");
|
||||
}
|
||||
if ((p = getenv("HOME"))) {
|
||||
UnveilIfExists(xjoinpaths(p, ".ape"), "rx");
|
||||
UnveilIfExists(
|
||||
__join_paths(buf, sizeof(buf), p, ".ape-" APE_VERSION_STR), "rx");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (~ipromises & (1ul << PROMISE_VMINFO)) {
|
||||
if (HasPromise(ipromises, PROMISE_VMINFO)) {
|
||||
UnveilIfExists("/proc/stat", "r");
|
||||
UnveilIfExists("/proc/meminfo", "r");
|
||||
UnveilIfExists("/proc/cpuinfo", "r");
|
||||
@@ -467,7 +559,7 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||
UnveilIfExists("/sys/devices/system/cpu", "r");
|
||||
}
|
||||
|
||||
if (~ipromises & (1ul << PROMISE_TMPPATH)) {
|
||||
if (HasPromise(ipromises, PROMISE_TMPPATH)) {
|
||||
UnveilIfExists("/tmp", "rwc");
|
||||
UnveilIfExists(getenv("TMPPATH"), "rwc");
|
||||
}
|
||||
@@ -489,21 +581,22 @@ void ApplyFilesystemPolicy(unsigned long ipromises) {
|
||||
}
|
||||
|
||||
if (unveil(0, 0) == -1) {
|
||||
kprintf("error: unveil(0, 0) failed: %m\n");
|
||||
_Exit(20);
|
||||
perror("unveil");
|
||||
exit(20);
|
||||
}
|
||||
}
|
||||
|
||||
void DropCapabilities(void) {
|
||||
int e, i;
|
||||
if (!IsLinux()) return;
|
||||
for (e = errno, i = 0;; ++i) {
|
||||
if (prctl(PR_CAPBSET_DROP, i) == -1) {
|
||||
if (errno == EINVAL || errno == EPERM) {
|
||||
errno = e;
|
||||
break;
|
||||
} else {
|
||||
kprintf("error: prctl(PR_CAPBSET_DROP, %d) failed: %m\n", i);
|
||||
_Exit(25);
|
||||
perror("prctl(PR_CAPBSET_DROP)");
|
||||
exit(25);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -533,68 +626,20 @@ int Extract(const char *from, const char *to, int mode) {
|
||||
return close(fdout) | close(fdin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if ELF executable uses dynamic loading magic.
|
||||
*/
|
||||
static bool IsDynamicExecutable(const char *prog) {
|
||||
bool res;
|
||||
Elf64_Ehdr *e;
|
||||
Elf64_Phdr *p;
|
||||
struct stat st;
|
||||
int i, fd, err;
|
||||
fd = -1;
|
||||
err = errno;
|
||||
e = MAP_FAILED;
|
||||
if ((fd = open(prog, O_RDONLY)) == -1) {
|
||||
res = false;
|
||||
goto Finish;
|
||||
}
|
||||
if (fstat(fd, &st) == -1 || st.st_size < 64) {
|
||||
res = false;
|
||||
goto Finish;
|
||||
}
|
||||
if ((e = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
||||
res = false;
|
||||
goto Finish;
|
||||
}
|
||||
if (READ32LE(e->e_ident) != READ32LE(ELFMAG)) {
|
||||
res = false;
|
||||
goto Finish;
|
||||
}
|
||||
if (e->e_type == ET_DYN) {
|
||||
res = true;
|
||||
goto Finish;
|
||||
}
|
||||
for (i = 0; i < e->e_phnum; ++i) {
|
||||
p = GetElfProgramHeaderAddress(e, st.st_size, i);
|
||||
if (p->p_type == PT_INTERP || p->p_type == PT_DYNAMIC) {
|
||||
res = true;
|
||||
goto Finish;
|
||||
}
|
||||
}
|
||||
res = false;
|
||||
goto Finish;
|
||||
Finish:
|
||||
if (e != MAP_FAILED) munmap(e, st.st_size);
|
||||
if (fd != -1) close(fd);
|
||||
errno = err;
|
||||
int CountEnviron(char **ep) {
|
||||
int res = 0;
|
||||
while (*ep++) ++res;
|
||||
return res;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *s;
|
||||
bool hasfunbits;
|
||||
char buf[PATH_MAX];
|
||||
int useruid, usergid;
|
||||
int owneruid, ownergid;
|
||||
int oldfsuid, oldfsgid;
|
||||
unsigned long ipromises;
|
||||
|
||||
if (!IsLinux()) {
|
||||
kprintf("error: this program is only intended for linux\n");
|
||||
exit(5);
|
||||
}
|
||||
|
||||
// parse flags
|
||||
GetOpts(argc, argv);
|
||||
if (g_test) {
|
||||
@@ -612,41 +657,33 @@ int main(int argc, char *argv[]) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
kprintf("error: unknown test: %s\n", g_test);
|
||||
tinyprint(2, g_test, ": unknown test\n", NULL);
|
||||
exit(2);
|
||||
}
|
||||
if (optind == argc) {
|
||||
kprintf("error: too few args\n");
|
||||
tinyprint(2, "error: missing command\n", NULL);
|
||||
write(2, USAGE, sizeof(USAGE) - 1);
|
||||
exit(64);
|
||||
}
|
||||
|
||||
// perform process setup
|
||||
if (!g_noclose) {
|
||||
NormalizeFileDescriptors();
|
||||
}
|
||||
|
||||
if (g_nice) {
|
||||
verynice();
|
||||
}
|
||||
|
||||
if (SetCpuLimit(g_cpuquota) == -1) {
|
||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_CPU");
|
||||
exit(1);
|
||||
perror("setrlimit(RLIMIT_CPU)");
|
||||
}
|
||||
|
||||
if (SetLimit(RLIMIT_FSIZE, g_fszquota, g_fszquota * 1.5) == -1) {
|
||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_FSIZE");
|
||||
exit(1);
|
||||
perror("setrlimit(RLIMIT_FSIZE)");
|
||||
}
|
||||
|
||||
if (SetLimit(RLIMIT_AS, g_memquota, g_memquota) == -1) {
|
||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_AS");
|
||||
exit(1);
|
||||
perror("setrlimit(RLIMIT_AS)");
|
||||
}
|
||||
|
||||
if (SetLimit(RLIMIT_NPROC, g_proquota, g_proquota) == -1) {
|
||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_NPROC");
|
||||
exit(1);
|
||||
perror("setrlimit(RLIMIT_NPROC)");
|
||||
}
|
||||
|
||||
// test for weird chmod bits
|
||||
@@ -664,8 +701,9 @@ int main(int argc, char *argv[]) {
|
||||
// some flags can't be allowed if binary has setuid bits
|
||||
if (hasfunbits) {
|
||||
if (g_uflag || g_gflag) {
|
||||
kprintf("error: setuid flags forbidden on setuid binaries\n");
|
||||
_Exit(6);
|
||||
tinyprint(2, program_invocation_name,
|
||||
": setuid flags forbidden on setuid binaries\n", NULL);
|
||||
exit(6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,8 +712,8 @@ int main(int argc, char *argv[]) {
|
||||
oldfsuid = setfsuid(useruid);
|
||||
oldfsgid = setfsgid(usergid);
|
||||
if (access(g_chroot, R_OK) == -1) {
|
||||
kprintf("error: access(%#s) failed: %m\n", g_chroot);
|
||||
_Exit(7);
|
||||
perror(g_chroot);
|
||||
exit(7);
|
||||
}
|
||||
setfsuid(oldfsuid);
|
||||
setfsgid(oldfsgid);
|
||||
@@ -684,12 +722,12 @@ int main(int argc, char *argv[]) {
|
||||
// change root fs path
|
||||
if (g_chroot) {
|
||||
if (chdir(g_chroot) == -1) {
|
||||
kprintf("error: chdir(%#s) failed: %m\n", g_chroot);
|
||||
_Exit(8);
|
||||
perror(g_chroot);
|
||||
exit(8);
|
||||
}
|
||||
if (chroot(g_chroot) == -1) {
|
||||
kprintf("error: chroot(%#s) failed: %m\n", g_chroot);
|
||||
_Exit(9);
|
||||
perror(g_chroot);
|
||||
exit(9);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,42 +736,63 @@ int main(int argc, char *argv[]) {
|
||||
oldfsuid = setfsuid(useruid);
|
||||
oldfsgid = setfsgid(usergid);
|
||||
}
|
||||
if (!(prog = commandv(argv[optind], pathbuf, sizeof(pathbuf)))) {
|
||||
kprintf("error: command not found: %m\n", argv[optind]);
|
||||
_Exit(10);
|
||||
if (!(g_prog = commandv(argv[optind], pathbuf, sizeof(pathbuf)))) {
|
||||
perror(argv[optind]);
|
||||
exit(10);
|
||||
}
|
||||
if (hasfunbits) {
|
||||
setfsuid(oldfsuid);
|
||||
setfsgid(oldfsgid);
|
||||
}
|
||||
|
||||
// copy environment
|
||||
// to setup child environment
|
||||
// and remove variables we might create
|
||||
unsetenv("_PLEDGE");
|
||||
unsetenv("LD_PRELOAD");
|
||||
int child_environ_count = CountEnviron(environ);
|
||||
int child_environ_capacity = child_environ_count + 2 + 1;
|
||||
int child_environ_bytes = child_environ_capacity * sizeof(char *);
|
||||
char **child_environ = alloca(child_environ_bytes);
|
||||
CheckLargeStackAllocation(child_environ, child_environ_bytes);
|
||||
memcpy(child_environ, environ, child_environ_count * sizeof(char *));
|
||||
bzero(child_environ + child_environ_count,
|
||||
(child_environ_capacity - child_environ_count) * sizeof(char *));
|
||||
|
||||
// figure out where we want the dso
|
||||
if (IsDynamicExecutable(prog)) {
|
||||
isdynamic = true;
|
||||
if (GetStrategy() == kStrategyDynamic) {
|
||||
if ((s = getenv("TMPDIR")) || //
|
||||
(s = getenv("HOME")) || //
|
||||
(s = ".")) {
|
||||
ksnprintf(dsopath, sizeof(dsopath), "%s/sandbox.so", s);
|
||||
strlcpy(dsopath, s, sizeof(dsopath));
|
||||
strlcat(dsopath, "/.pledge-sandbox.so", sizeof(dsopath));
|
||||
if (!FileExistsAndIsNewerThan(dsopath, GetProgramExecutableName())) {
|
||||
ksnprintf(tmppath, sizeof(tmppath), "%s/sandbox.so.%d", s, getpid());
|
||||
if (Extract("/zip/sandbox.so", tmppath, 0755) == -1) {
|
||||
kprintf("error: extract dso failed: %m\n");
|
||||
errno = 0;
|
||||
char pidstr[21];
|
||||
FormatInt64(pidstr, getpid());
|
||||
strlcpy(tmppath, s, sizeof(tmppath));
|
||||
strlcat(tmppath, "/.pledge-sandbox.so.", sizeof(tmppath));
|
||||
strlcat(tmppath, pidstr, sizeof(tmppath));
|
||||
if (Extract("/zip/sandbox-" ARCH_NAME ".so", tmppath, 0755) == -1) {
|
||||
perror(tmppath);
|
||||
exit(1);
|
||||
}
|
||||
if (rename(tmppath, dsopath) == -1) {
|
||||
kprintf("error: rename dso failed: %m\n");
|
||||
perror(dsopath);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
ksnprintf(buf, sizeof(buf), "LD_PRELOAD=%s", dsopath);
|
||||
putenv(buf);
|
||||
stpcpy(preloadvar, "LD_PRELOAD=");
|
||||
strlcat(preloadvar, dsopath, sizeof(preloadvar));
|
||||
child_environ[child_environ_count++] = preloadvar;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_dontdrop) {
|
||||
if (hasfunbits) {
|
||||
kprintf("error: -D flag forbidden on setuid binaries\n");
|
||||
_Exit(6);
|
||||
tinyprint(2, program_invocation_name,
|
||||
": -D flag forbidden on setuid binaries\n", NULL);
|
||||
exit(6);
|
||||
}
|
||||
} else {
|
||||
DropCapabilities();
|
||||
@@ -743,22 +802,22 @@ int main(int argc, char *argv[]) {
|
||||
if (usergid != ownergid) {
|
||||
// setgid binaries must use the gid of the user that ran it
|
||||
if (setgid(usergid) == -1) {
|
||||
kprintf("error: setgid(%d) failed: %m\n", usergid);
|
||||
_Exit(11);
|
||||
perror("setgid");
|
||||
exit(11);
|
||||
}
|
||||
if (getgid() != usergid || getegid() != usergid) {
|
||||
kprintf("error: setgid() broken\n");
|
||||
_Exit(12);
|
||||
tinyprint(2, "error: setgid() broken\n", NULL);
|
||||
exit(12);
|
||||
}
|
||||
} else if (g_gflag) {
|
||||
// otherwise we trust the gid flag
|
||||
if (setgid(g_gflag) == -1) {
|
||||
kprintf("error: setgid(%d) failed: %m\n", g_gflag);
|
||||
_Exit(13);
|
||||
perror("setgid");
|
||||
exit(13);
|
||||
}
|
||||
if (getgid() != g_gflag || getegid() != g_gflag) {
|
||||
kprintf("error: setgid() broken\n");
|
||||
_Exit(14);
|
||||
tinyprint(2, "error: setgid() broken\n", NULL);
|
||||
exit(14);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,30 +825,34 @@ int main(int argc, char *argv[]) {
|
||||
if (useruid != owneruid) {
|
||||
// setuid binaries must use the uid of the user that ran it
|
||||
if (setuid(useruid) == -1) {
|
||||
kprintf("error: setuid(%d) failed: %m\n", useruid);
|
||||
_Exit(15);
|
||||
perror("setuid");
|
||||
exit(15);
|
||||
}
|
||||
if (getuid() != useruid || geteuid() != useruid) {
|
||||
kprintf("error: setuid() broken\n");
|
||||
_Exit(16);
|
||||
tinyprint(2, "error: setuid() broken\n", NULL);
|
||||
exit(16);
|
||||
}
|
||||
} else if (g_uflag) {
|
||||
// otherwise we trust the uid flag
|
||||
if (setuid(g_uflag) == -1) {
|
||||
kprintf("error: setuid(%d) failed: %m\n", g_uflag);
|
||||
_Exit(17);
|
||||
perror("setuid");
|
||||
exit(17);
|
||||
}
|
||||
if (getuid() != g_uflag || geteuid() != g_uflag) {
|
||||
kprintf("error: setuid() broken\n");
|
||||
_Exit(18);
|
||||
tinyprint(2, "error: setuid() broken\n", NULL);
|
||||
exit(18);
|
||||
}
|
||||
}
|
||||
|
||||
// parse requested promises
|
||||
// further changes to g_promises will be *effective* transient promises
|
||||
if (ParsePromises(g_promises, &ipromises, 0) == -1) {
|
||||
kprintf("error: bad promises list: %s\n", g_promises);
|
||||
_Exit(21);
|
||||
tinyprint(2, program_invocation_name, ": bad promises list: ", g_promises,
|
||||
"\n", NULL);
|
||||
exit(21);
|
||||
}
|
||||
|
||||
// perform unveiling
|
||||
ApplyFilesystemPolicy(ipromises);
|
||||
|
||||
// pledge.com uses the return eperm instead of killing the process
|
||||
@@ -802,38 +865,70 @@ int main(int argc, char *argv[]) {
|
||||
__pledge_mode = PLEDGE_PENALTY_RETURN_EPERM;
|
||||
}
|
||||
|
||||
// we need to be able to call execv and mmap the dso
|
||||
// it'll be pledged away once/if the dso gets loaded
|
||||
if (!(~ipromises & (1ul << PROMISE_EXEC))) {
|
||||
g_promises = xstrcat(g_promises, ' ', "exec");
|
||||
// weaken system call policy to allow execution
|
||||
//
|
||||
// 1. we always need to pledge("exec") in order to use execve(). this
|
||||
// is the primary disadvantage to using the `pledge` command to
|
||||
// bolt security onto unsecured programs (as opposed to using
|
||||
// pledge as it was intended, by having the program authors update
|
||||
// their code to invoke the pledge() system call from within their
|
||||
// programs at the appropriate moments)
|
||||
//
|
||||
// 2. we usually need to force pledge("rpath prot_exec") too; dynamic
|
||||
// executables need it mmap() shared objects during initialization
|
||||
// and actually portable executables need it so /usr/bin/ape can
|
||||
// mmap() the ELF program headers. the only time we don't require
|
||||
// `prot_exec` is when launching a native ELF PT_EXEC binaries,
|
||||
// e.g. assimilated actually portable executables.
|
||||
//
|
||||
// 3. in some cases we can remove the `exec` and `prot_exec` promises
|
||||
// later on in the loading process. on musl and glibc systems, we
|
||||
// do that by injecting an LD_PRELOAD DSO which calls pledge()
|
||||
// again with the requested promises.
|
||||
if (!HasPromise(ipromises, PROMISE_EXEC)) {
|
||||
AddPromise("exec");
|
||||
if (!g_qflag) {
|
||||
// TODO(jart): Fix me.
|
||||
// __pledge_mode |= PLEDGE_STDERR_LOGGING;
|
||||
}
|
||||
}
|
||||
if (isdynamic) {
|
||||
g_promises = xstrcat(g_promises, ' ', "prot_exec");
|
||||
if (GetStrategy() != kStrategyStatic) {
|
||||
if (!HasPromise(ipromises, PROMISE_RPATH)) {
|
||||
AddPromise("rpath");
|
||||
}
|
||||
if (!HasPromise(ipromises, PROMISE_PROT_EXEC)) {
|
||||
AddPromise("prot_exec");
|
||||
}
|
||||
}
|
||||
|
||||
// pass arguments to pledge() inside the dso
|
||||
if (isdynamic) {
|
||||
ksnprintf(buf, sizeof(buf), "_PLEDGE=%ld,%ld", ~ipromises, __pledge_mode);
|
||||
putenv(buf);
|
||||
// pass parameters to injected dso
|
||||
if (GetStrategy() == kStrategyDynamic) {
|
||||
char *p = pledgevar;
|
||||
p = stpcpy(p, "_PLEDGE=");
|
||||
p = FormatInt64(p, ~ipromises);
|
||||
p = stpcpy(p, ",");
|
||||
p = FormatInt64(p, __pledge_mode);
|
||||
child_environ[child_environ_count++] = pledgevar;
|
||||
}
|
||||
|
||||
// this limit needs to come last since unveil() opens fds
|
||||
if (SetLimit(RLIMIT_NOFILE, g_nfdquota, g_nfdquota) == -1) {
|
||||
kprintf("error: setrlimit(%s) failed: %m\n", "RLIMIT_NOFILE");
|
||||
perror("setrlimit(RLIMIT_NOFILE)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// apply sandbox
|
||||
if (pledge(g_promises, g_promises) == -1) {
|
||||
kprintf("error: pledge(%#s) failed: %m\n", g_promises);
|
||||
_Exit(19);
|
||||
perror("pledge");
|
||||
exit(19);
|
||||
}
|
||||
|
||||
// launch program
|
||||
sys_execve(prog, argv + optind, environ);
|
||||
kprintf("%s: execve failed: %m\n", prog);
|
||||
if (!IsWindows()) {
|
||||
sys_execve(g_prog, argv + optind, child_environ);
|
||||
} else {
|
||||
sys_execve_nt(g_prog, argv + optind, child_environ);
|
||||
}
|
||||
perror(g_prog);
|
||||
return 127;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*-*- 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/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
|
||||
/**
|
||||
* @fileoverview `verynice foo` launches `foo` as low priority as possible
|
||||
*
|
||||
* This is particularly useful on Linux systems with a spinning disk
|
||||
* where the classic `nice` command doesn't do much.
|
||||
*/
|
||||
|
||||
const char *prog;
|
||||
|
||||
static wontreturn void PrintUsage(int rc, int fd) {
|
||||
tinyprint(fd, "Usage: ", prog, " COMMAND...\n", NULL);
|
||||
exit(rc);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
prog = argv[0];
|
||||
if (!prog) prog = "verynice";
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
PrintUsage(0, 1);
|
||||
default:
|
||||
PrintUsage(1, 2);
|
||||
}
|
||||
}
|
||||
if (optind == argc) {
|
||||
tinyprint(2, prog, ": missing command\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
verynice();
|
||||
execvp(argv[optind], argv + optind);
|
||||
perror(argv[optind]);
|
||||
exit(127);
|
||||
}
|
||||
Reference in new issue
Block a user