Compare commits

...
16 Commits
Author SHA1 Message Date
Justine Tunney 55d5d6bbc0 Release Cosmopolitan v3.0.1 2023-11-01 09:23:43 -07:00
Justine Tunney d259e573b6 Remove *NSYNC WIN32 semaphores
This implementation doesn't work as well as WIN32 futexes. This code
path was only added back when we were having issues with set context
however that's been solved so we can go back to the much better code
2023-11-01 07:18:58 -07:00
Justine Tunney 7b284f6bda Fix bugs and regressions in the pledge command
This change gets the pledge (formerly pledge.com) command back in tip
top shape for a 3.0.1 cosmos release. It now runs on all platforms, even
though it's mostly a no-op on ones that lack the kernel security stuff.
The binary footprint is now smaller, since it no longer needs to link
malloc. It's also now able to be built as a fat binary.
2023-11-01 06:08:58 -07:00
Justine Tunney b0e3d89942 Work around qemu-aarch64 bug
Qemu appears to define O_LARGEFILE as having its x86-64 value, which is
an easy mistake to make since this is one of the few magic numbers that
Linux special-cases for AARCH64.
2023-11-01 00:24:48 -07:00
Justine Tunney 0b1acce680 Introduce shm_open() and shm_unlink() 2023-10-31 23:57:52 -07:00
Justine Tunney fadb64a2bf Introduce pthread_rwlock_try{rd,wr}lock
This also changes recursive mutexes to favor cpu over scheduler yield.
2023-10-31 22:13:08 -07:00
Justine Tunney a1e1e821cb Introduce GNU/BSD timeval macros 2023-10-31 21:58:49 -07:00
Justine Tunney 2af7c802b6 Stub out getutline and pututline 2023-10-31 21:58:39 -07:00
Justine Tunney 3b791d2f44 Introduce lgammal, tgammal, erfl, and erfcl
git://git.musl-libc.org/musl
79bdacff83a6bd5b70ff5ae5eb8b6de82c2f7c30
2023-10-31 21:58:29 -07:00
Justine Tunney b4084dd6c6 Introduce libcxx <shared_mutex>
[email protected]:llvm-mirror/libcxx.git
78d6a7767ed57b50122a161b91f59f19c9bd0d19
2023-10-31 21:57:28 -07:00
Justine Tunney ee82f90bba Introduce __cxa_thread_atexit() 2023-10-31 20:04:31 -07:00
Justine Tunney a699cda5c8 Make cosmocc -Wl,--version do the right thing
Our `cosmocc` wrapper script will now report the linker version, the
same way as the `cc` command. The `fatcosmocc` command won't do this
because we wouldn't want programs to parse out the path of the amd64
linker command and use it explicitly.

Fixes #917
2023-10-31 18:20:28 -07:00
Justine Tunney f86ffa1a25 Fix aarch64 build breakage caused by ed17d3008 2023-10-31 18:17:32 -07:00
Justine Tunney a09f62141d Fix bug in login_tty() 2023-10-31 18:03:13 -07:00
tkchia ed17d3008b [metal] Add a uprintf() routine, for non-emergency boot logging (#905)
* [metal] Add a uprintf() routine, for non-emergency boot logging
* [metal] _Really_ push forward timing of VGA TTY initialization
* [metal] Do something useful with uprintf()
* [metal] Locate some ACPI tables, for later hardware detection

Specifically the code now tries to find the ACPI RSDP,
RSDT/XSDT, FADT, & MADT tables, whether in legacy BIOS
bootup mode or in a UEFI bootup.  These are useful for
figuring out how to (re)enable asynchronous interrupts
in legacy 8259 PIC mode.
2023-10-25 14:32:20 -07:00
Justine Tunney 062b2d776e Mention cosmocc v0.0.14 in Makefile
https://justine.lol/cosmocc-0.0.14.zip
https://github.com/ahgamut/superconfigure/releases/tag/z0.0.14
2023-10-16 00:07:33 -07:00
91 changed files with 3805 additions and 881 deletions

No files matched your search

+8 -5
View File
@@ -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 \
+2
View File
@@ -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
+1
View File
@@ -46,6 +46,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_DNS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_IRQ \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
+2
View File
@@ -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
View File
@@ -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;
+2
View File
@@ -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))
+4 -3
View File
@@ -16,6 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/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
View File
@@ -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;
+3 -5
View File
@@ -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) {
+2 -1
View File
@@ -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='");
}
+2
View File
@@ -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);
}
}
+2 -2
View File
@@ -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
+3
View File
@@ -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,
+4 -1
View File
@@ -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.
+45
View File
@@ -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);
}
+28
View File
@@ -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);
}
+1
View File
@@ -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;
+1 -1
View File
@@ -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 */
+23
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+2
View File
@@ -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")
+5 -1
View File
@@ -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__)
+3
View File
@@ -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) */
+2
View File
@@ -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.
-2
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+4 -3
View File
@@ -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;
+21 -15
View File
@@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 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;
}
+1 -1
View File
@@ -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);
+2 -1
View File
@@ -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();
}
}
+2 -1
View File
@@ -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;
}
+43
View File
@@ -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
+80
View File
@@ -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__ */
+51
View File
@@ -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
+81
View File
@@ -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__ */
+46
View File
@@ -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
+243
View File
@@ -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__ */
+232
View File
@@ -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_ */
+57
View File
@@ -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)
+4
View File
@@ -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_ */
+1
View File
@@ -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 \
+2
View File
@@ -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);
+12
View File
@@ -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_
+26
View File
@@ -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.
*/
+3 -3
View File
@@ -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));
}
+6 -3
View File
@@ -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
View File
@@ -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);
+69
View File
@@ -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;
}
}
}
+1
View File
@@ -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_
+23
View File
@@ -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);
}
+47
View File
@@ -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;
}
+1
View File
@@ -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");
+4
View File
@@ -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();
+37
View File
@@ -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;
}
}
+37
View File
@@ -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;
}
}
+4 -8
View File
@@ -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();
-1
View File
@@ -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) */
+5
View File
@@ -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
+381
View File
@@ -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 */
-318
View File
@@ -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;
}
+4 -1
View File
@@ -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
+4
View File
@@ -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
+388
View File
@@ -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 */
+4
View File
@@ -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
+314
View File
@@ -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 */
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
*/
+46
View File
@@ -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__ */
+1 -2
View File
@@ -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);
+217
View File
@@ -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);
}
+4 -2
View File
@@ -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));
}
+15 -6
View File
@@ -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));
}
+19
View File
@@ -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);
}
+1 -1
View File
@@ -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) {
+7 -7
View File
@@ -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
+2
View File
@@ -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 \
+1 -1
View File
@@ -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
+509
View File
@@ -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
+119
View File
@@ -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
-10
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+2 -4
View File
@@ -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
View File
@@ -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;
}
+61
View File
@@ -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);
}