Compare commits

..

No commits in common. "master" and "4.0.1" have entirely different histories.

107 changed files with 900 additions and 2046 deletions

View file

@ -1,8 +1,5 @@
name: build
env:
COSMOCC_VERSION: 3.9.2
on:
push:
branches:
@ -22,48 +19,13 @@ jobs:
matrix:
mode: ["", tiny, rel, tinylinux, optlinux]
steps:
- uses: actions/checkout@v4
with:
# Full checkout needed for git-restore-mtime-bare.
fetch-depth: 0
# TODO(jart): fork this action.
- uses: chetan/git-restore-mtime-action@v2
- uses: actions/cache/restore@v4
id: cache
with:
path: |
.cosmocc
o
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
restore-keys: |
${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-
${{ env.COSMOCC_VERSION }}-
- name: Restore mtimes
if: steps.cache.outputs.cache-hit == 'true'
run: |
while read mtime file; do
[ -f "$file" ] && touch -d "@$mtime" "$file"
done < o/.mtimes
- uses: actions/checkout@v3
- name: support ape bins 1
run: sudo cp -a build/bootstrap/ape.elf /usr/bin/ape
run: sudo cp build/bootstrap/ape.elf /usr/bin/ape
- name: support ape bins 2
run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
- name: make matrix
run: V=0 make -j2 MODE=${{ matrix.mode }}
- name: Save mtimes
run: |
find o -type f -exec stat -c "%Y %n" {} \; > o/.mtimes
- uses: actions/cache/save@v4
with:
path: |
.cosmocc
o
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}

View file

@ -77,8 +77,7 @@ COMMA := ,
PWD := $(shell pwd)
# detect wsl2 running cosmopolitan binaries on the host by checking whether:
# - user ran .cosmocc/current/bin/make, in which case make's working directory
# is in wsl
# - user ran build/bootstrap/make, in which case make's working directory is in wsl
# - user ran make, in which case cocmd's working directory is in wsl
ifneq ($(findstring //wsl.localhost/,$(CURDIR) $(PWD)),)
$(warning wsl2 interop is enabled)
@ -90,7 +89,7 @@ UNAME_S := $(shell uname -s)
# apple still distributes a 17 year old version of gnu make
ifeq ($(MAKE_VERSION), 3.81)
$(error please use https://cosmo.zip/pub/cosmos/bin/make)
$(error please use build/bootstrap/make)
endif
LC_ALL = C

View file

@ -3,7 +3,7 @@
[![build](https://github.com/jart/cosmopolitan/actions/workflows/build.yml/badge.svg)](https://github.com/jart/cosmopolitan/actions/workflows/build.yml)
# Cosmopolitan
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C/C++
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C
a build-once run-anywhere language, like Java, except it doesn't need an
interpreter or virtual machine. Instead, it reconfigures stock GCC and
Clang to output a POSIX-approved polyglot format that runs natively on
@ -87,22 +87,15 @@ ape/apeinstall.sh
```
You can now build the mono repo with any modern version of GNU Make. To
bootstrap your build, you can install Cosmopolitan Make from this site:
https://cosmo.zip/pub/cosmos/bin/make
E.g.:
make life easier, we've included one in the cosmocc toolchain, which is
guaranteed to be compatible and furthermore includes our extensions for
doing build system sandboxing.
```sh
curl -LO https://cosmo.zip/pub/cosmos/bin/make
./make -j8
build/bootstrap/make -j8
o//examples/hello
```
After you've built the repo once, you can also use the make from your
cosmocc at `.cosmocc/current/bin/make`. You might even prefer to alias
make to `$COSMO/.cosmocc/current/bin/make`.
Since the Cosmopolitan repository is very large, you might only want to
build one particular thing. Here's an example of a target that can be
compiled relatively quickly, which is a simple POSIX test that only
@ -110,7 +103,7 @@ depends on core LIBC packages.
```sh
rm -rf o//libc o//test
.cosmocc/current/bin/make o//test/posix/signal_test
build/bootstrap/make o//test/posix/signal_test
o//test/posix/signal_test
```
@ -119,21 +112,21 @@ list out each individual one. For example if you wanted to build and run
all the unit tests in the `TEST_POSIX` package, you could say:
```sh
.cosmocc/current/bin/make o//test/posix
build/bootstrap/make o//test/posix
```
Cosmopolitan provides a variety of build modes. For example, if you want
really tiny binaries (as small as 12kb in size) then you'd say:
```sh
.cosmocc/current/bin/make m=tiny
build/bootstrap/make m=tiny
```
You can furthermore cut out the bloat of other operating systems, and
have Cosmopolitan become much more similar to Musl Libc.
```sh
.cosmocc/current/bin/make m=tinylinux
build/bootstrap/make m=tinylinux
```
For further details, see [//build/config.mk](build/config.mk).

View file

@ -259,9 +259,6 @@ SECTIONS {
.debug_ranges 0 : { *(.debug_ranges) }
.debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) }
.debug_names 0 : { *(.debug_names) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.ARM.attributes 0 : { KEEP(*(.ARM.attributes)) KEEP(*(.gnu.attributes)) }
.note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) }

View file

@ -386,13 +386,6 @@ SECTIONS {
_tbss_end = .;
} :Tls
.eh_frame : {
__eh_frame_start = .;
KEEP(*(.eh_frame))
*(.eh_frame.*)
__eh_frame_end = .;
} :Ram
.data . : {
/*BEGIN: Read/Write Data */
#if SupportsWindows()
@ -433,6 +426,11 @@ SECTIONS {
KEEP(*(.dtors))
__fini_array_end = .;
__eh_frame_start = .;
KEEP(*(.eh_frame))
*(.eh_frame.*)
__eh_frame_end = .;
/*BEGIN: Post-Initialization Read-Only */
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
KEEP(*(SORT_BY_NAME(.piro.relo.sort.*)))
@ -441,6 +439,7 @@ SECTIONS {
KEEP(*(.piro.pad.data))
*(.igot.plt)
KEEP(*(.dataepilogue))
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
/*END: NT FORK COPYING */
_edata = .;
@ -520,9 +519,6 @@ SECTIONS {
.debug_rnglists 0 : { *(.debug_rnglists) }
.debug_macro 0 : { *(.debug_macro) }
.debug_addr 0 : { *(.debug_addr) }
.debug_names 0 : { *(.debug_names) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.gnu.attributes 0 : { KEEP(*(.gnu.attributes)) }
.GCC.command.line 0 : { *(.GCC.command.line) }
@ -586,11 +582,11 @@ ape_rom_memsz = ape_rom_filesz;
ape_rom_align = CONSTANT(COMMONPAGESIZE);
ape_rom_rva = RVA(ape_rom_vaddr);
ape_ram_vaddr = ADDR(.eh_frame);
ape_ram_vaddr = ADDR(.data);
ape_ram_offset = ape_ram_vaddr - __executable_start;
ape_ram_paddr = LOADADDR(.eh_frame);
ape_ram_filesz = ADDR(.bss) - ADDR(.eh_frame);
ape_ram_memsz = _end - ADDR(.eh_frame);
ape_ram_paddr = LOADADDR(.data);
ape_ram_filesz = ADDR(.bss) - ADDR(.data);
ape_ram_memsz = _end - ADDR(.data);
ape_ram_align = CONSTANT(COMMONPAGESIZE);
ape_ram_rva = RVA(ape_ram_vaddr);
@ -600,7 +596,7 @@ ape_stack_offset = 0;
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000;
ape_stack_paddr = ape_ram_paddr + ape_ram_filesz;
ape_stack_filesz = 0;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 4 * 1024 * 1024;
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024;
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
ape_note_filesz = ape_note_end - ape_note;

View file

@ -10,8 +10,8 @@ if [ ! -f ape/loader.c ]; then
cd "$COSMO" || exit
fi
if [ -x .cosmocc/current/bin/make ]; then
MAKE=.cosmocc/current/bin/make
if [ -x build/bootstrap/make ]; then
MAKE=build/bootstrap/make
else
MAKE=make
fi

View file

@ -99,8 +99,3 @@ rm -f cosmocc.zip cosmocc.zip.sha256sum
# commit output directory
cd "${OLDPWD}" || die
mv "${OUTPUT_TMP}" "${OUTPUT_DIR}" || die
# update current symlink
BASE=$(basename "${OUTPUT_DIR}")
DIR=$(dirname "${OUTPUT_DIR}")
ln -sfn "$BASE" "$DIR/current"

View file

@ -97,12 +97,12 @@ class shared_ref
size_t use_count() const noexcept
{
return __atomic_load_n(&shared, __ATOMIC_RELAXED) + 1;
return shared + 1;
}
size_t weak_count() const noexcept
{
return __atomic_load_n(&weak, __ATOMIC_RELAXED);
return weak;
}
private:
@ -349,7 +349,7 @@ class shared_ptr
template<typename U>
bool owner_before(const weak_ptr<U>& r) const noexcept
{
return rc < r.rc;
return !r.owner_before(*this);
}
private:
@ -382,34 +382,6 @@ class weak_ptr
rc->keep_weak();
}
weak_ptr(const weak_ptr& r) noexcept : p(r.p), rc(r.rc)
{
if (rc)
rc->keep_weak();
}
template<typename U>
requires __::shared_ptr_compatible<T, U>
weak_ptr(const weak_ptr<U>& r) noexcept : p(r.p), rc(r.rc)
{
if (rc)
rc->keep_weak();
}
weak_ptr(weak_ptr&& r) noexcept : p(r.p), rc(r.rc)
{
r.p = nullptr;
r.rc = nullptr;
}
template<typename U>
requires __::shared_ptr_compatible<T, U>
weak_ptr(weak_ptr<U>&& r) noexcept : p(r.p), rc(r.rc)
{
r.p = nullptr;
r.rc = nullptr;
}
~weak_ptr()
{
if (rc)
@ -438,19 +410,6 @@ class weak_ptr
swap(rc, r.rc);
}
weak_ptr& operator=(weak_ptr r) noexcept
{
swap(r);
return *this;
}
template<typename U>
requires __::shared_ptr_compatible<T, U>
weak_ptr& operator=(weak_ptr<U> r) noexcept
{
weak_ptr<T>(move(r)).swap(*this);
}
shared_ptr<T> lock() const noexcept
{
if (expired())

View file

@ -28,8 +28,8 @@
// @return rdi is rdi+edx
.text.startup
__getntsyspath:
beg
pro
push %rbp
mov %rsp,%rbp
push %rdx
movpp %rdi,%rcx # call f=%rax(p1=%rcx,p2=%rdx)
sub $40,%rsp
@ -55,7 +55,6 @@ __getntsyspath:
jne 2f
movb $'/',-1(%rdi)
2: .loop 1b
epi
leave
ret
end
.endfn __getntsyspath,globl,hidden

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 Justine Alexandra Roberts Tunney
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -16,14 +16,22 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/nt/memory.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/nt/process.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processbasicinformation.h"
/**
* Allocates memory on The New Technology.
*/
textwindows void *VirtualAlloc(void *lpAddress, uint64_t dwSize,
uint32_t flAllocationType, uint32_t flProtect) {
return VirtualAllocEx(GetCurrentProcess(), lpAddress, dwSize,
flAllocationType, flProtect);
textwindows int sys_getppid_nt(void) {
struct NtProcessBasicInformation ProcessInformation;
uint32_t gotsize = 0;
if (!NtError(
NtQueryInformationProcess(GetCurrentProcess(), 0, &ProcessInformation,
sizeof(ProcessInformation), &gotsize)) &&
gotsize >= sizeof(ProcessInformation) &&
ProcessInformation.InheritedFromUniqueProcessId) {
return ProcessInformation.InheritedFromUniqueProcessId;
}
return GetCurrentProcessId();
}

View file

@ -114,8 +114,7 @@ static ssize_t GetDevUrandom(char *p, size_t n, unsigned f) {
ssize_t __getrandom(void *p, size_t n, unsigned f) {
ssize_t rc;
if (IsWindows()) {
ProcessPrng(p, n); // never fails
rc = n;
rc = ProcessPrng(p, n) ? n : __winerr();
} else if (have_getrandom) {
if (IsXnu() || IsOpenbsd()) {
rc = GetRandomBsd(p, n, GetRandomEntropy);
@ -185,7 +184,9 @@ ssize_t __getrandom(void *p, size_t n, unsigned f) {
* @raise EFAULT if the `n` bytes at `p` aren't valid memory
* @raise EINTR if we needed to block and a signal was delivered instead
* @cancelationpoint
* @asyncsignalsafe
* @restartable
* @vforksafe
*/
ssize_t getrandom(void *p, size_t n, unsigned f) {
ssize_t rc;

View file

@ -67,9 +67,10 @@ textstartup void InitializeMetalFile(void) {
size_t size = ROUNDUP(_ezip - __executable_start, 4096);
// TODO(jart): Restore support for ZIPOS on metal.
void *copied_base;
void *addr = sys_mmap_metal(NULL, size, PROT_READ | PROT_WRITE,
struct DirectMap dm;
dm = sys_mmap_metal(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED_linux | MAP_ANONYMOUS_linux, -1, 0);
copied_base = addr;
copied_base = dm.addr;
npassert(copied_base != (void *)-1);
memcpy(copied_base, (void *)(BANE + IMAGE_BASE_PHYSICAL), size);
__ape_com_base = copied_base;

View file

@ -39,15 +39,12 @@
#include "libc/nt/struct/procthreadattributelist.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/nt/struct/startupinfoex.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/proc/ntspawn.h"
#include "libc/stdalign.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
#ifdef __x86_64__
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
struct SpawnBlock {
char16_t path[PATH_MAX];
char16_t cmdline[32767];
@ -67,12 +64,10 @@ static textwindows ssize_t ntspawn_read(intptr_t fh, char *buf, size_t len) {
bool ok;
uint32_t got;
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0)};
ok = overlap.hEvent &&
(ReadFile(fh, buf, len, 0, &overlap) ||
ok = (ReadFile(fh, buf, len, 0, &overlap) ||
GetLastError() == kNtErrorIoPending) &&
GetOverlappedResult(fh, &overlap, &got, true);
if (overlap.hEvent)
__imp_CloseHandle(overlap.hEvent);
CloseHandle(overlap.hEvent);
return ok ? got : -1;
}
@ -92,7 +87,7 @@ static textwindows int ntspawn2(struct NtSpawnArgs *a, struct SpawnBlock *sb) {
if (fh == -1)
return -1;
ssize_t got = ntspawn_read(fh, p, pe - p);
__imp_CloseHandle(fh);
CloseHandle(fh);
if (got < 3)
return enoexec();
pe = p + got;

View file

@ -49,9 +49,11 @@ int sys_openat_metal(int dirfd, const char *file, int flags, unsigned mode) {
if ((fd = __reservefd(-1)) == -1)
return -1;
if (!_weaken(calloc) || !_weaken(free)) {
state = sys_mmap_metal(NULL, ROUNDUP(sizeof(struct MetalFile), 4096),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,
-1, 0);
struct DirectMap dm;
dm = sys_mmap_metal(NULL, ROUNDUP(sizeof(struct MetalFile), 4096),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1,
0);
state = dm.addr;
if (state == (void *)-1)
return -1;
} else {

View file

@ -694,7 +694,6 @@ static const uint16_t kPledgeStdio[] = {
__NR_linux_sched_getaffinity, //
__NR_linux_sched_setaffinity, //
__NR_linux_sigtimedwait, //
__NR_linux_getcpu, //
};
static const uint16_t kPledgeFlock[] = {

View file

@ -997,10 +997,8 @@ textwindows ssize_t ReadBuffer(int fd, void *data, size_t size, int64_t offset,
if (f->kind == kFdDevNull)
return 0;
if (f->kind == kFdDevRandom) {
ProcessPrng(data, size);
return size;
}
if (f->kind == kFdDevRandom)
return ProcessPrng(data, size) ? size : __winerr();
if (f->kind == kFdConsole)
return ReadFromConsole(f, data, size, waitmask);

View file

@ -423,7 +423,7 @@ static int __sigaction(int sig, const struct sigaction *act,
* }
*
* void ContinueOnCrash(void) {
* struct sigaction sa = {.sa_sigaction = OnCrash,
* struct sigaction sa = {.sa_handler = OnSigSegv,
* .sa_flags = SA_SIGINFO | SA_RESETHAND};
* sigaction(SIGSEGV, &sa, 0);
* sigaction(SIGFPE, &sa, 0);

View file

@ -2,9 +2,6 @@
#define COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_
COSMOPOLITAN_C_START_
extern int sys_getppid_nt_cosmo;
extern int sys_getppid_nt_win32;
bool32 sys_isatty(int);
int sys_chdir_nt(const char *);
int sys_dup_nt(int, int, int, int);
@ -40,7 +37,6 @@ int sys_unlinkat_nt(int, const char *, int);
int64_t sys_lseek_nt(int, int64_t, int);
ssize_t sys_read_nt_impl(int, void *, size_t, int64_t);
ssize_t sys_readlinkat_nt(int, const char *, char *, size_t);
void sys_getppid_nt_wipe(int, int);
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_SYSCALL_NT_INTERNAL_H_ */

View file

@ -80,8 +80,7 @@ textwindows int IsWindowsExecutable(int64_t handle, const char16_t *path) {
uint32_t got;
BLOCK_SIGNALS;
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0)};
ok = overlap.hEvent &&
(ReadFile(handle, buf, 2, 0, &overlap) ||
ok = (ReadFile(handle, buf, 2, 0, &overlap) ||
GetLastError() == kNtErrorIoPending) &&
GetOverlappedResult(handle, &overlap, &got, true);
CloseHandle(overlap.hEvent);

View file

@ -47,14 +47,7 @@ __oops_win32:
// @note ape.S and ape-loader both set RCX to XNU on Darwin
// @noreturn
_start:
.cfi_startproc
#if defined(__x86_64__)
.cfi_undefined rip
#elif defined(__aarch64__)
.cfi_undefined x30
#endif /* __x86_64__ */
#if defined(__x86_64__)
#ifdef __x86_64__
#if SupportsFreebsd()
// detect free besiyata dishmaya
@ -166,5 +159,4 @@ _start:
#else
#error "architecture unsupported"
#endif /* __x86_64__ */
.cfi_endproc
.endfn _start,weak,hidden

View file

@ -68,7 +68,6 @@
#define EM_NONE 0
#define EM_M32 1
#define EM_386 3
#define EM_MIPS 8
#define EM_PPC64 21
#define EM_S390 22
#define EM_ARM 40

View file

@ -4,7 +4,7 @@
#define __COSMOPOLITAN_MAJOR__ 4
#define __COSMOPOLITAN_MINOR__ 0
#define __COSMOPOLITAN_PATCH__ 2
#define __COSMOPOLITAN_PATCH__ 1
#define __COSMOPOLITAN__ \
(100000000 * __COSMOPOLITAN_MAJOR__ + 1000000 * __COSMOPOLITAN_MINOR__ + \
__COSMOPOLITAN_PATCH__)

View file

@ -59,7 +59,7 @@ textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
// —Quoth MSDN § Windows Time
//
QueryUnbiasedInterruptTimePrecise(&hectons);
*ts = WindowsDurationToTimeSpec(hectons);
*ts = timespec_fromnanos(hectons * 100);
return 0;
case _CLOCK_MONOTONIC_COARSE:
//
@ -83,7 +83,7 @@ textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
// —Quoth MSDN § QueryUnbiasedInterruptTimePrecise
//
QueryUnbiasedInterruptTime(&hectons);
*ts = WindowsDurationToTimeSpec(hectons);
*ts = timespec_fromnanos(hectons * 100);
return 0;
case _CLOCK_BOOTTIME:
//
@ -95,7 +95,7 @@ textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
// —Quoth MSDN § Interrupt Time
//
QueryInterruptTimePrecise(&hectons);
*ts = WindowsDurationToTimeSpec(hectons);
*ts = timespec_fromnanos(hectons * 100);
return 0;
case _CLOCK_PROCESS_CPUTIME_ID:
GetProcessTimes(GetCurrentProcess(), &ftCreation, &ftExit, &ftKernel,

View file

@ -21,15 +21,16 @@
.privileged
cosmo_futex_thunk:
beg
pro
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov %rcx,%r10
mov __NR_futex,%eax
clc
syscall
jnc 1f
neg %eax
1: pop %rbp
#elif defined(__aarch64__)
ldr x7,=__hostos
ldr w7,[x7]
@ -45,7 +46,5 @@ cosmo_futex_thunk:
#else
#error "unsupported architecture"
#endif /* __x86_64__ */
1: epi
ret
end
1: ret
.endfn cosmo_futex_thunk,globl,hidden

View file

@ -1,32 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 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/describeflags.h"
#include "libc/macros.h"
#include "libc/nt/enum/memflags.h"
static const struct DescribeFlags kNtAllocationTypeFlags[] = {
{kNtMemCommit, "Commit"}, //
{kNtMemReserve, "Reserve"}, //
{kNtMemReset, "Reset"}, //
};
const char *_DescribeNtAllocationType(char buf[48], uint32_t x) {
return _DescribeFlags(buf, 48, kNtAllocationTypeFlags,
ARRAYLEN(kNtAllocationTypeFlags), "kNtMem", x);
}

View file

@ -29,7 +29,6 @@ const char *_DescribeMapping(char[8], int, int) libcesque;
const char *_DescribeMremapFlags(char[30], int) libcesque;
const char *_DescribeMsg(char[16], int) libcesque;
const char *_DescribeMsyncFlags(char[48], int) libcesque;
const char *_DescribeNtAllocationType(char[48], uint32_t);
const char *_DescribeNtConsoleInFlags(char[256], uint32_t) libcesque;
const char *_DescribeNtConsoleOutFlags(char[128], uint32_t) libcesque;
const char *_DescribeNtCreationDisposition(uint32_t) libcesque;
@ -88,7 +87,6 @@ const char *_DescribeWhichPrio(char[12], int) libcesque;
#define DescribeMremapFlags(x) _DescribeMremapFlags(alloca(30), x)
#define DescribeMsg(x) _DescribeMsg(alloca(16), x)
#define DescribeMsyncFlags(x) _DescribeMsyncFlags(alloca(48), x)
#define DescribeNtAllocationType(x) _DescribeNtAllocationType(alloca(48), x)
#define DescribeNtConsoleInFlags(x) _DescribeNtConsoleInFlags(alloca(256), x)
#define DescribeNtConsoleOutFlags(x) _DescribeNtConsoleOutFlags(alloca(128), x)
#define DescribeNtFileAccessFlags(x) _DescribeNtFileAccessFlags(alloca(512), x)

View file

@ -19,6 +19,7 @@
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/metalfile.internal.h"
#include "libc/intrin/directmap.h"
#include "libc/macros.h"
#include "libc/runtime/pc.internal.h"
#include "libc/str/str.h"
@ -31,11 +32,19 @@
static uint64_t sys_mmap_metal_break;
void *sys_mmap_metal(void *vaddr, size_t size, int prot, int flags, int fd,
int64_t off) {
static struct DirectMap bad_mmap(void) {
struct DirectMap res;
res.addr = (void *)-1;
res.maphandle = -1;
return res;
}
struct DirectMap sys_mmap_metal(void *vaddr, size_t size, int prot, int flags,
int fd, int64_t off) {
/* asan runtime depends on this function */
size_t i;
struct mman *mm;
struct DirectMap res;
uint64_t addr, faddr = 0, page, e, *pte, *fdpte, *pml4t;
mm = __get_mm();
pml4t = __get_pml4t();
@ -45,18 +54,18 @@ void *sys_mmap_metal(void *vaddr, size_t size, int prot, int flags, int fd,
struct Fd *sfd;
struct MetalFile *file;
if (off < 0 || fd < 0 || fd >= g_fds.n)
return MAP_FAILED;
return bad_mmap();
sfd = &g_fds.p[fd];
if (sfd->kind != kFdFile)
return MAP_FAILED;
return bad_mmap();
file = (struct MetalFile *)sfd->handle;
/* TODO: allow mapping partial page at end of file, if file size not
* multiple of page size */
if (off > file->size || size > file->size - off)
return MAP_FAILED;
return bad_mmap();
faddr = (uint64_t)file->base + off;
if (faddr % 4096 != 0)
return MAP_FAILED;
return bad_mmap();
}
if (!(flags & MAP_FIXED_linux)) {
if (!addr) {
@ -79,7 +88,7 @@ void *sys_mmap_metal(void *vaddr, size_t size, int prot, int flags, int fd,
if ((flags & MAP_ANONYMOUS_linux)) {
page = __new_page(mm);
if (!page)
return MAP_FAILED;
return bad_mmap();
__clear_page(BANE + page);
e = page | PAGE_RSRV | PAGE_U;
if ((prot & PROT_WRITE))
@ -105,7 +114,9 @@ void *sys_mmap_metal(void *vaddr, size_t size, int prot, int flags, int fd,
break;
}
}
return (void *)addr;
res.addr = (void *)addr;
res.maphandle = -1;
return res;
}
#endif /* __x86_64__ */

122
libc/intrin/directmap-nt.c Normal file
View file

@ -0,0 +1,122 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/errno.h"
#include "libc/intrin/directmap.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/errors.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processmemorycounters.h"
#include "libc/nt/struct/securityattributes.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
textwindows struct DirectMap sys_mmap_nt(void *addr, size_t size, int prot,
int flags, int fd, int64_t off) {
int64_t handle;
if (flags & MAP_ANONYMOUS) {
handle = kNtInvalidHandleValue;
} else {
handle = g_fds.p[fd].handle;
}
// mark map handle as inheritable if fork might need it
const struct NtSecurityAttributes *mapsec;
if ((flags & MAP_TYPE) == MAP_SHARED) {
mapsec = &kNtIsInheritable;
} else {
mapsec = 0;
}
// nt will whine under many circumstances if we change the execute bit
// later using mprotect(). the workaround is to always request execute
// and then virtualprotect() it away until we actually need it. please
// note that open-nt.c always requests an kNtGenericExecute accessmask
int iscow = false;
struct ProtectNt fl;
if (handle != -1) {
if ((flags & MAP_TYPE) != MAP_SHARED) {
// windows has cow pages but they can't propagate across fork()
// that means we only get copy-on-write for the root process :(
fl = (struct ProtectNt){kNtPageExecuteWritecopy,
kNtFileMapCopy | kNtFileMapExecute};
iscow = true;
} else {
if ((g_fds.p[fd].flags & O_ACCMODE) == O_RDONLY) {
fl = (struct ProtectNt){kNtPageExecuteRead,
kNtFileMapRead | kNtFileMapExecute};
} else {
fl = (struct ProtectNt){kNtPageExecuteReadwrite,
kNtFileMapWrite | kNtFileMapExecute};
}
}
} else {
unassert(flags & MAP_ANONYMOUS);
fl = (struct ProtectNt){kNtPageExecuteReadwrite,
kNtFileMapWrite | kNtFileMapExecute};
}
int e = errno;
struct DirectMap dm;
TryAgain:
if ((dm.maphandle = CreateFileMapping(handle, mapsec, fl.flags1,
(size + off) >> 32, (size + off), 0))) {
if ((dm.addr = MapViewOfFileEx(dm.maphandle, fl.flags2, off >> 32, off,
size, addr))) {
uint32_t oldprot;
if (VirtualProtect(dm.addr, size, __prot2nt(prot, iscow), &oldprot))
return dm;
UnmapViewOfFile(dm.addr);
}
CloseHandle(dm.maphandle);
} else if (!(prot & PROT_EXEC) && //
(fl.flags2 & kNtFileMapExecute) && //
GetLastError() == kNtErrorAccessDenied) {
// your file needs to have been O_CREAT'd with exec `mode` bits in
// order to be mapped with executable permission. we always try to
// get execute permission if the kernel will give it to us because
// win32 would otherwise forbid mprotect() from elevating later on
fl.flags2 &= ~kNtFileMapExecute;
switch (fl.flags1) {
case kNtPageExecuteWritecopy:
fl.flags1 = kNtPageWritecopy;
break;
case kNtPageExecuteReadwrite:
fl.flags1 = kNtPageReadwrite;
break;
case kNtPageExecuteRead:
fl.flags1 = kNtPageReadonly;
break;
default:
__builtin_unreachable();
}
errno = e;
goto TryAgain;
}
dm.maphandle = kNtInvalidHandleValue;
dm.addr = (void *)(intptr_t)-1;
return dm;
}

67
libc/intrin/directmap.c Normal file
View file

@ -0,0 +1,67 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/directmap.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/describebacktrace.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/sysv/errfuns.h"
/**
* Obtains memory mapping directly from system.
*
* The mmap() function needs to track memory mappings in order to
* support Windows NT and Address Sanitizer. That memory tracking can be
* bypassed by calling this function. However the caller is responsible
* for passing the magic memory handle on Windows NT to CloseHandle().
*
* @asyncsignalsafe
*/
struct DirectMap sys_mmap(void *addr, size_t size, int prot, int flags, int fd,
int64_t off) {
struct DirectMap d;
if ((__virtualsize += size) >= __virtualmax) {
d.maphandle = kNtInvalidHandleValue;
d.addr = (void *)enomem();
} else if (IsXnuSilicon()) {
long p = _sysret(__syslib->__mmap(addr, size, prot, flags, fd, off));
d.maphandle = kNtInvalidHandleValue;
d.addr = (void *)p;
} else if (!IsWindows() && !IsMetal()) {
d.addr = __sys_mmap(addr, size, prot, flags, fd, off, off);
d.maphandle = kNtInvalidHandleValue;
} else if (IsMetal()) {
d = sys_mmap_metal(addr, size, prot, flags, fd, off);
} else {
d = sys_mmap_nt(addr, size, prot, flags, fd, off);
}
if (d.addr == MAP_FAILED)
__virtualsize -= size;
KERNTRACE("sys_mmap(%.12p, %'zu, %s, %s, %d, %'ld) → {%.12p, %p}% m", addr,
size, DescribeProtFlags(prot), DescribeMapFlags(flags), fd, off,
d.addr, d.maphandle);
return d;
}

View file

@ -2,7 +2,19 @@
#define COSMOPOLITAN_LIBC_INTRIN_DIRECTMAP_H_
COSMOPOLITAN_C_START_
void *sys_mmap_metal(void *, size_t, int, int, int, int64_t) libcesque;
struct ProtectNt {
uint32_t flags1;
uint32_t flags2;
};
struct DirectMap {
void *addr;
int64_t maphandle;
};
struct DirectMap sys_mmap(void *, size_t, int, int, int, int64_t);
struct DirectMap sys_mmap_nt(void *, size_t, int, int, int, int64_t);
struct DirectMap sys_mmap_metal(void *, size_t, int, int, int, int64_t);
int sys_munmap_metal(void *, size_t) libcesque;
int __prot2nt(int, int) libcesque;

View file

@ -46,11 +46,14 @@
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#define OPEN_MAX 16
#ifdef __x86_64__
__static_yoink("_init_fds");
#endif
struct Fds g_fds;
static struct Fd g_fds_static[OPEN_MAX];
static bool TokAtoi(const char **str, long *res) {
int c, d;
@ -89,9 +92,15 @@ textstartup void __init_fds(int argc, char **argv, char **envp) {
fds = &g_fds;
fds->n = 4;
atomic_store_explicit(&fds->f, 3, memory_order_relaxed);
if (_weaken(_extend)) {
fds->p = fds->e = (void *)kMemtrackFdsStart;
fds->e = _extend(fds->p, fds->n * sizeof(*fds->p), fds->e, MAP_PRIVATE,
fds->e =
_weaken(_extend)(fds->p, fds->n * sizeof(*fds->p), fds->e, MAP_PRIVATE,
kMemtrackFdsStart + kMemtrackFdsSize);
} else {
fds->p = g_fds_static;
fds->e = g_fds_static + OPEN_MAX;
}
// inherit standard i/o file descriptors
if (IsMetal()) {
@ -145,7 +154,8 @@ textstartup void __init_fds(int argc, char **argv, char **envp) {
break;
if (!TokAtoi(&fdspec, &protocol))
break;
__ensurefds_unlocked(fd);
if (_weaken(__ensurefds_unlocked))
_weaken(__ensurefds_unlocked)(fd);
struct Fd *f = fds->p + fd;
if (f->handle && f->handle != -1 && f->handle != handle) {
CloseHandle(f->handle);

View file

@ -26,9 +26,7 @@
// @see setcontext()
.ftrace1
getcontext:
beg
.ftrace2
#include "libc/intrin/getcontext.inc"
jmp __getcontextsig
end
.endfn getcontext,globl

View file

@ -18,7 +18,6 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
@ -34,14 +33,10 @@
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/macros.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/memflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/errors.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/stdckdint.h"
#include "libc/stdio/sysparam.h"
@ -85,11 +80,6 @@
} while (0)
#endif
struct DirectMap {
void *addr;
int64_t hand;
};
int __maps_compare(const struct Tree *ra, const struct Tree *rb) {
const struct Map *a = (const struct Map *)MAP_TREE_CONTAINER(ra);
const struct Map *b = (const struct Map *)MAP_TREE_CONTAINER(rb);
@ -294,6 +284,7 @@ void __maps_free(struct Map *map) {
&__maps.freed, &tip, ABA(map, TAG(tip) + 1), memory_order_release,
memory_order_relaxed))
break;
pthread_pause_np();
}
}
@ -430,7 +421,7 @@ void __maps_insert(struct Map *map) {
__maps_check();
}
// adds interval to rbtree
// adds interval to rbtree (no sys_mmap)
bool __maps_track(char *addr, size_t size, int prot, int flags) {
struct Map *map;
if (!(map = __maps_alloc()))
@ -456,132 +447,16 @@ int __maps_untrack(char *addr, size_t size) {
return rc;
}
textwindows dontinline static struct DirectMap sys_mmap_nt(
void *addr, size_t size, int prot, int flags, int fd, int64_t off) {
struct DirectMap dm;
// it's 5x faster
if ((flags & MAP_ANONYMOUS) && (flags & MAP_TYPE) != MAP_SHARED) {
if (!(dm.addr = VirtualAlloc(addr, size, kNtMemReserve | kNtMemCommit,
__prot2nt(prot, false)))) {
dm.addr = MAP_FAILED;
}
dm.hand = MAPS_VIRTUAL;
return dm;
}
int64_t file_handle;
if (flags & MAP_ANONYMOUS) {
file_handle = kNtInvalidHandleValue;
} else {
file_handle = g_fds.p[fd].handle;
}
// mark map handle as inheritable if fork might need it
const struct NtSecurityAttributes *mapsec;
if ((flags & MAP_TYPE) == MAP_SHARED) {
mapsec = &kNtIsInheritable;
} else {
mapsec = 0;
}
// nt will whine under many circumstances if we change the execute bit
// later using mprotect(). the workaround is to always request execute
// and then virtualprotect() it away until we actually need it. please
// note that open-nt.c always requests an kNtGenericExecute accessmask
int iscow = 0;
int page_flags;
int file_flags;
if (file_handle != -1) {
if ((flags & MAP_TYPE) != MAP_SHARED) {
// windows has cow pages but they can't propagate across fork()
// that means we only get copy-on-write for the root process :(
page_flags = kNtPageExecuteWritecopy;
file_flags = kNtFileMapCopy | kNtFileMapExecute;
iscow = 1;
} else {
if ((g_fds.p[fd].flags & O_ACCMODE) == O_RDONLY) {
page_flags = kNtPageExecuteRead;
file_flags = kNtFileMapRead | kNtFileMapExecute;
} else {
page_flags = kNtPageExecuteReadwrite;
file_flags = kNtFileMapWrite | kNtFileMapExecute;
}
}
} else {
page_flags = kNtPageExecuteReadwrite;
file_flags = kNtFileMapWrite | kNtFileMapExecute;
}
int e = errno;
TryAgain:
if ((dm.hand = CreateFileMapping(file_handle, mapsec, page_flags,
(size + off) >> 32, (size + off), 0))) {
if ((dm.addr = MapViewOfFileEx(dm.hand, file_flags, off >> 32, off, size,
addr))) {
uint32_t oldprot;
if (VirtualProtect(dm.addr, size, __prot2nt(prot, iscow), &oldprot))
return dm;
UnmapViewOfFile(dm.addr);
}
CloseHandle(dm.hand);
} else if (!(prot & PROT_EXEC) && //
(file_flags & kNtFileMapExecute) && //
GetLastError() == kNtErrorAccessDenied) {
// your file needs to have been O_CREAT'd with exec `mode` bits in
// order to be mapped with executable permission. we always try to
// get execute permission if the kernel will give it to us because
// win32 would otherwise forbid mprotect() from elevating later on
file_flags &= ~kNtFileMapExecute;
switch (page_flags) {
case kNtPageExecuteWritecopy:
page_flags = kNtPageWritecopy;
break;
case kNtPageExecuteReadwrite:
page_flags = kNtPageReadwrite;
break;
case kNtPageExecuteRead:
page_flags = kNtPageReadonly;
break;
default:
__builtin_unreachable();
}
errno = e;
goto TryAgain;
}
dm.hand = kNtInvalidHandleValue;
dm.addr = (void *)(intptr_t)-1;
return dm;
}
static struct DirectMap sys_mmap(void *addr, size_t size, int prot, int flags,
int fd, int64_t off) {
struct DirectMap d;
if (IsXnuSilicon()) {
long p = _sysret(__syslib->__mmap(addr, size, prot, flags, fd, off));
d.hand = kNtInvalidHandleValue;
d.addr = (void *)p;
} else if (IsWindows()) {
d = sys_mmap_nt(addr, size, prot, flags, fd, off);
} else if (IsMetal()) {
d.addr = sys_mmap_metal(addr, size, prot, flags, fd, off);
d.hand = kNtInvalidHandleValue;
} else {
d.addr = __sys_mmap(addr, size, prot, flags, fd, off, off);
d.hand = kNtInvalidHandleValue;
}
return d;
}
struct Map *__maps_alloc(void) {
struct Map *map;
uintptr_t tip = atomic_load_explicit(&__maps.freed, memory_order_relaxed);
while ((map = (struct Map *)PTR(tip)))
while ((map = (struct Map *)PTR(tip))) {
if (atomic_compare_exchange_weak_explicit(
&__maps.freed, &tip, ABA(map->freed, TAG(tip) + 1),
memory_order_acquire, memory_order_relaxed))
return map;
pthread_pause_np();
}
// we're creating sudden surprise memory. the user might be in the
// middle of carefully planning a fixed memory structure. we don't
// want the system allocator to put our surprise memory inside it,
@ -591,6 +466,8 @@ struct Map *__maps_alloc(void) {
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (sys.addr == MAP_FAILED)
return 0;
if (IsWindows())
CloseHandle(sys.maphandle);
struct MapSlab *slab = sys.addr;
while (!atomic_compare_exchange_weak(&__maps.slabs, &slab->next, slab)) {
}
@ -840,7 +717,7 @@ static void *__mmap_impl(char *addr, size_t size, int prot, int flags, int fd,
map->off = off;
map->prot = prot;
map->flags = flags;
map->hand = res.hand;
map->hand = res.maphandle;
if (IsWindows()) {
map->iscow = (flags & MAP_TYPE) != MAP_SHARED && fd != -1;
map->readonlyfile = (flags & MAP_TYPE) == MAP_SHARED && fd != -1 &&

View file

@ -41,6 +41,8 @@ int sys_munmap(void *p, size_t n) {
} else {
rc = __sys_munmap(p, n);
}
if (!rc)
__virtualsize -= n;
KERNTRACE("sys_munmap(%p, %'zu) → %d", p, n, rc);
return rc;
}

View file

@ -83,11 +83,11 @@ struct SignalFrame {
};
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
__msabi extern typeof(VirtualProtectEx) *const __imp_VirtualProtectEx;
__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
__msabi extern typeof(VirtualQuery) *const __imp_VirtualQuery;
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
atomic_int __sig_worker_state;
extern pthread_mutex_t __sig_worker_lock;
textwindows static bool __sig_ignored_by_default(int sig) {
return sig == SIGURG || //
@ -566,8 +566,7 @@ textwindows wontreturn static void __sig_death(int sig, const char *thing) {
//
forceinline void __sig_reguard(void *page) {
uint32_t old_protect;
__imp_VirtualProtectEx(GetCurrentProcess(),
(void *)((uintptr_t)page & -__pagesize), __pagesize,
__imp_VirtualProtect((void *)((uintptr_t)page & -__pagesize), __pagesize,
kNtPageReadwrite | kNtPageGuard, &old_protect);
}
@ -742,8 +741,7 @@ HAIRY static uint32_t __sig_worker(void *arg) {
STKSZ, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NOFORK);
for (;;) {
// ok sys_execve_nt() might disable this worker
if (~__sig_worker_state & 2) {
_pthread_mutex_lock(&__sig_worker_lock);
// dequeue all pending signals and fire them off. if there's no
// thread that can handle them then __sig_generate will requeue
@ -807,12 +805,10 @@ HAIRY static uint32_t __sig_worker(void *arg) {
}
_pthread_unref(mark);
}
}
// wait until next scheduler quantum
__sig_worker_state |= 1;
_pthread_mutex_unlock(&__sig_worker_lock);
Sleep(POLL_INTERVAL_MS);
__sig_worker_state &= ~1;
}
__builtin_unreachable();
}

View file

@ -30,6 +30,8 @@
// usually better that sigprocmask only strace the user is calling it.
// plus, since we have a very specific use case, this code goes faster
struct Signals __sig;
sigset_t __sig_block(void) {
if (IsWindows() || IsMetal()) {
if (__tls_enabled)

View file

@ -16,8 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/proc/proc.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
int main(int argc, char *argv[]) {
}
// this mutex is needed so execve() can shut down the signal worker
pthread_mutex_t __sig_worker_lock = PTHREAD_MUTEX_INITIALIZER;

View file

@ -31,17 +31,17 @@
// @returnstwice
.ftrace1
swapcontext:
beg
.ftrace2
#include "libc/intrin/getcontext.inc"
#ifdef __x86_64__
pro
cpush %rsi
cpush %rsi
push %rbp
mov %rsp,%rbp
push %rsi
push %rsi
call __swapcontextsig
cpop %rdi
cpop %rdi
epi
pop %rdi
pop %rdi
pop %rbp
test %eax,%eax
jnz 1f
#elif defined(__aarch64__)
@ -56,5 +56,4 @@ swapcontext:
#endif
jmp __tailcontext
1: ret
end
.endfn swapcontext,globl

View file

@ -24,9 +24,9 @@
//
// @return 0 on success, or -1 w/ errno
sys_sched_yield:
beg
#ifdef __x86_64__
pro
push %rbp
mov %rsp,%rbp
xor %eax,%eax
mov __hostos(%rip),%dl
@ -84,16 +84,13 @@ sys_sched_yield:
// fails a positive or negative errno might get returned.
#endif
9: epi
9: leave
ret
#elif defined(__aarch64__)
stp x29,x30,[sp,-32]!
mov x29,sp
.cfi_adjust_cfa_offset 32
.cfi_rel_offset x29,16
.cfi_rel_offset x30,24
mov x3,0
mov x2,0
add x4,sp,16
@ -104,14 +101,10 @@ sys_sched_yield:
mov x16,#0x5d // select(0,0,0,0,&blah) for xnu
svc 0
ldp x29,x30,[sp],32
.cfi_adjust_cfa_offset -32
.cfi_restore x30
.cfi_restore x29
ret
#else
#error "arch unsupported"
#endif
end
.endfn sys_sched_yield,globl
.previous

View file

@ -19,23 +19,32 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/describeflags.h"
#include "libc/intrin/strace.h"
#include "libc/macros.h"
#include "libc/mem/alloca.h"
#include "libc/nt/enum/memflags.h"
#include "libc/nt/memory.h"
#include "libc/nt/thunk/msabi.h"
__msabi extern typeof(VirtualAllocEx) *const __imp_VirtualAllocEx;
/**
* Allocates memory on The New Technology.
*/
textwindows void *VirtualAllocEx(int64_t hProcess, void *lpAddress,
uint64_t dwSize, uint32_t flAllocationType,
uint32_t flProtect) {
static const char *DescribeAllocationType(char buf[48], uint32_t x) {
const struct DescribeFlags kAllocationTypeFlags[] = {
{kNtMemCommit, "Commit"}, //
{kNtMemReserve, "Reserve"}, //
{kNtMemReset, "Reset"}, //
};
return _DescribeFlags(buf, 48, kAllocationTypeFlags,
ARRAYLEN(kAllocationTypeFlags), "kNtMem", x);
}
void *VirtualAllocEx(int64_t hProcess, void *lpAddress, uint64_t dwSize,
uint32_t flAllocationType, uint32_t flProtect) {
void *res = __imp_VirtualAllocEx(hProcess, lpAddress, dwSize,
flAllocationType, flProtect);
if (!res)
__winerr();
NTTRACE("VirtualAllocEx(%ld, %p, %'lu, %s, %s) → %p% m", hProcess, lpAddress,
dwSize, DescribeNtAllocationType(flAllocationType),
dwSize, DescribeAllocationType(alloca(48), flAllocationType),
DescribeNtPageFlags(flProtect), res);
return res;
}

View file

@ -19,3 +19,4 @@
#include "libc/runtime/runtime.h"
size_t __virtualmax = -1;
size_t __virtualsize = 0;

View file

@ -58,8 +58,9 @@ textstartup void *_AcpiOsMapUncachedMemory(uintptr_t phy, size_t n) {
}
textstartup static void *_AcpiOsAllocatePages(size_t n) {
void *addr = sys_mmap_metal(NULL, n, PROT_READ | PROT_WRITE,
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;

View file

@ -12,7 +12,6 @@
#include "libc/nt/files.h"
#include "libc/nt/ipc.h"
#include "libc/nt/memory.h"
#include "libc/nt/nls.h"
#include "libc/nt/paint.h"
#include "libc/nt/process.h"
#include "libc/nt/registry.h"
@ -1421,15 +1420,6 @@
#define HKEY_CURRENT_CONFIG kNtHkeyCurrentConfig
#define HKEY_DYN_DATA kNtHkeyDynData
#define HKEY_CURRENT_USER_LOCAL_SETTINGS kNtHkeyCurrentUserLocalSettings
#define KEY_QUERY_VALUE kNtKeyQueryValue
#define KEY_SET_VALUE kNtKeySetValue
#define KEY_CREATE_SUB_KEY kNtKeyCreateSubKey
#define KEY_ENUMERATE_SUB_KEYS kNtKeyEnumerateSubKeys
#define KEY_NOTIFY kNtKeyNotify
#define KEY_CREATE_LINK kNtKeyCreateLink
#define KEY_WOW64_32KEY kNtWow6432Key
#define KEY_WOW64_64KEY kNtWow6464Key
#define KEY_WOW64_RES kNtWow64Res
#define KEY_READ kNtKeyRead
#define KEY_WRITE kNtKeyWrite
#define KEY_EXECUTE kNtKeyExecute
@ -4301,13 +4291,6 @@
#define MAKE_HRESULT(sev,fac,code) ((HRESULT) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )
#define MAKE_SCODE(sev,fac,code) ((SCODE) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) )
#define CP_ACP 0
#define CP_OEMCP 1
#define CP_MACCP 2
#define CP_THREAD_ACP 3
#define CP_SYMBOL 42
#define CP_UTF7 65000
#define CP_UTF8 65001
#endif /* COSMOPOLITAN_LIBC_COMPAT_INCLUDE_WINDOWS_H_ */

View file

@ -158,60 +158,6 @@
.weak \canonical
.endm
.macro beg
.cfi_startproc
.endm
.macro pro
#if defined(__x86_64__)
push %rbp
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rbp,0
mov %rsp,%rbp
.cfi_def_cfa_register %rbp
#elif defined(__aarch64__)
stp x29,x30,[sp,-16]!
mov x29,sp
.cfi_adjust_cfa_offset 16
.cfi_rel_offset x29,0
.cfi_rel_offset x30,8
#else
#error "unsupported architecture"
#endif
.endm
.macro epi
#if defined(__x86_64__)
.cfi_def_cfa_register %rsp
leave
.cfi_adjust_cfa_offset -8
.cfi_restore %rbp
#elif defined(__aarch64__)
ldp x29,x30,[sp],#16
.cfi_adjust_cfa_offset -16
.cfi_restore x30
.cfi_restore x29
#else
#error "unsupported architecture"
#endif
.endm
.macro end
.cfi_endproc
.endm
.macro cpush reg:req
push \reg
.cfi_adjust_cfa_offset 8
.cfi_rel_offset \reg,0
.endm
.macro cpop reg:req
pop \reg
.cfi_adjust_cfa_offset -8
.cfi_restore \reg
.endm
#ifdef __aarch64__
.macro jmp dest:req
b \dest

View file

@ -7,8 +7,8 @@
// @note public domain
// @see en.wikipedia.org/wiki/Sorting_network
djbsort_avx2:
beg
pro
push %rbp
mov %rsp,%rbp
push %r15
push %r14
push %r13
@ -795,13 +795,11 @@ djbsort_avx2:
pop %r13
pop %r14
pop %r15
epi
pop %rbp
ret
end
.endfn djbsort_avx2,globl,hidden
minmax_vector:
beg
cmp $7,%rdx
jg .L13
.L2: test %rdx,%rdx
@ -840,11 +838,9 @@ minmax_vector:
sub $8,%rdx
jne .L7
ret
end
.endfn minmax_vector
int32_twostages_32:
beg
sub $-128,%rdi
.L17: lea -128(%rdi),%rax
test %rsi,%rsi
@ -870,14 +866,13 @@ int32_twostages_32:
add $512,%rdi
jmp .L17
.L21: ret
end
.endfn int32_twostages_32
int32_threestages:
beg
pro
push %rbp
imul $-24,%rdx,%r8
lea 0(,%rdx,8),%rax
mov %rsp,%rbp
push %r15
push %r14
push %r13
@ -966,13 +961,11 @@ int32_threestages:
pop %r13
pop %r14
pop %r15
epi
pop %rbp
ret
end
.endfn int32_threestages
merge16_finish:
beg
vpminsd %ymm1,%ymm0,%ymm3
vpmaxsd %ymm1,%ymm0,%ymm0
vperm2i128 $32,%ymm0,%ymm3,%ymm2
@ -1001,11 +994,9 @@ merge16_finish:
.L31: vmovdqu %ymm2,(%rdi)
vmovdqu %ymm0,32(%rdi)
ret
end
.endfn merge16_finish
int32_sort_2power:
beg
push %r13
lea 16(%rsp),%r13
andq $-32,%rsp
@ -2084,7 +2075,6 @@ int32_sort_2power:
lea -16(%r13),%rsp
pop %r13
ret
end
.endfn int32_sort_2power
.rodata.cst32

View file

@ -32,8 +32,7 @@
// @param rax,rdx,xmm0,xmm1,st0,st1 is return value
// @see test/libc/runtime/gc_test.c
.ftrace1
__gc: beg
.ftrace2
__gc: .ftrace2
#ifdef __x86_64__
@ -48,7 +47,8 @@ __gc: beg
mov 8(%r8),%r9
mov 16(%r8),%rdi
push 24(%r8)
pro
push %rbp
mov %rsp,%rbp
sub $32,%rsp
mov %rax,-8(%rbp)
mov %rdx,-16(%rbp)
@ -57,7 +57,7 @@ __gc: beg
movdqa -32(%rbp),%xmm0
mov -16(%rbp),%rdx
mov -8(%rbp),%rax
epi
leave
ret
9: ud2
nop
@ -102,5 +102,4 @@ __gc: beg
#endif /* __x86_64__ */
end
.endfn __gc,globl,hidden

View file

@ -31,9 +31,7 @@
// @noreturn
.ftrace1
gclongjmp:
beg
.ftrace2
pro
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
@ -67,5 +65,4 @@ gclongjmp:
#else
#error "unsupported architecture"
#endif /* __x86_64__ */
end
.endfn gclongjmp,globl

View file

@ -26,7 +26,7 @@
// @see gclongjmp()
// @see siglongjmp()
.ftrace1
longjmp:beg
longjmp:
.ftrace2
_longjmp:
#ifdef __x86_64__
@ -61,7 +61,6 @@ _longjmp:
#else
#error "unsupported architecture"
#endif
end
.endfn longjmp,globl
.endfn _longjmp,globl
.alias longjmp,siglongjmp

View file

@ -30,8 +30,8 @@
// @note slower than __sysv2nt
// @see NT2SYSV() macro
__nt2sysv:
beg
pro
push %rbp
mov %rsp,%rbp
sub $256,%rsp
push %rbx
push %rdi
@ -48,7 +48,6 @@ __nt2sysv:
pop %rsi
pop %rdi
pop %rbx
epi
leave
ret
end
.endfn __nt2sysv,globl,hidden

View file

@ -1,18 +0,0 @@
#include "libc/nt/codegen.h"
.imp advapi32,__imp_RegOpenKeyExA,RegOpenKeyExA
.text.windows
.ftrace1
RegOpenKeyExA:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov __imp_RegOpenKeyExA(%rip),%rax
jmp __sysv2nt6
#elif defined(__aarch64__)
mov x0,#0
ret
#endif
.endfn RegOpenKeyExA,globl
.previous

View file

@ -1,16 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_NT_ENUM_KEYACCESS_H_
#define COSMOPOLITAN_LIBC_NT_ENUM_KEYACCESS_H_
#define kNtKeyQueryValue 0x00000001
#define kNtKeySetValue 0x00000002
#define kNtKeyCreateSubKey 0x00000004
#define kNtKeyEnumerateSubKeys 0x00000008
#define kNtKeyNotify 0x00000010
#define kNtKeyCreateLink 0x00000020
#define kNtWow6432Key 0x00000200
#define kNtWow6464Key 0x00000100
#define kNtWow64Res 0x00000300
#define kNtKeyRead 0x00020019
#define kNtKeyWrite 0x00020006
#define kNtKeyExecute 0x00020019

View file

@ -49,7 +49,6 @@ COSMOPOLITAN_C_START_
intptr_t LoadResource(int64_t hModule, int64_t hResInfo);
uint32_t SetHandleCount(uint32_t uNumber);
uint32_t GetLogicalDrives(void);
uint32_t GetLogicalDriveStringsA(uint32_t nBufferLength, char *lpBuffer);
bool32 FlushFileBuffers(int64_t hFile);
int64_t ReOpenFile(int64_t hOriginalFile, uint32_t dwDesiredAccess,
@ -206,7 +205,6 @@ uint32_t GetFinalPathNameByHandle(int64_t hFile, char16_t *out_path,
uint32_t GetFullPathName(const char16_t *lpFileName, uint32_t nBufferLength,
char16_t *lpBuffer, char16_t **lpFilePart);
uint32_t GetShortPathName(const char16_t *lpszLongPath, char16_t *out_lpszShortPath, uint32_t cchBuffer);
bool32 GetOverlappedResult(int64_t hFile, struct NtOverlapped *lpOverlapped,
uint32_t *lpNumberOfBytesTransferred, bool32 bWait);

View file

@ -1,19 +0,0 @@
#include "libc/nt/codegen.h"
.imp kernel32,__imp_GetACP,GetACP
.text.windows
.ftrace1
GetACP:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
sub $32,%rsp
call *__imp_GetACP(%rip)
leave
#elif defined(__aarch64__)
mov x0,#0
#endif
ret
.endfn GetACP,globl
.previous

View file

@ -1,18 +0,0 @@
#include "libc/nt/codegen.h"
.imp kernel32,__imp_GetLogicalDriveStringsA,GetLogicalDriveStringsA
.text.windows
.ftrace1
GetLogicalDriveStringsA:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov __imp_GetLogicalDriveStringsA(%rip),%rax
jmp __sysv2nt
#elif defined(__aarch64__)
mov x0,#0
ret
#endif
.endfn GetLogicalDriveStringsA,globl
.previous

View file

@ -1,19 +0,0 @@
#include "libc/nt/codegen.h"
.imp kernel32,__imp_GetOEMCP,GetOEMCP
.text.windows
.ftrace1
GetOEMCP:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
sub $32,%rsp
call *__imp_GetOEMCP(%rip)
leave
#elif defined(__aarch64__)
mov x0,#0
#endif
ret
.endfn GetOEMCP,globl
.previous

View file

@ -1,18 +0,0 @@
#include "libc/nt/codegen.h"
.imp kernel32,__imp_GetShortPathNameW,GetShortPathNameW
.text.windows
.ftrace1
GetShortPathName:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov __imp_GetShortPathNameW(%rip),%rax
jmp __sysv2nt
#elif defined(__aarch64__)
mov x0,#0
ret
#endif
.endfn GetShortPathName,globl
.previous

View file

@ -1,18 +1,18 @@
#include "libc/nt/codegen.h"
.imp kernel32,__imp_GetCPInfoExW,GetCPInfoExW
.imp kernel32,__imp_VirtualAlloc,VirtualAlloc
.text.windows
.ftrace1
GetCPInfoEx:
VirtualAlloc:
.ftrace2
#ifdef __x86_64__
push %rbp
mov %rsp,%rbp
mov __imp_GetCPInfoExW(%rip),%rax
mov __imp_VirtualAlloc(%rip),%rax
jmp __sysv2nt
#elif defined(__aarch64__)
mov x0,#0
ret
#endif
.endfn GetCPInfoEx,globl
.endfn VirtualAlloc,globl
.previous

View file

@ -9,6 +9,7 @@
# KERNEL32.DLL
#
# Name Actual DLL Arity
imp '' CreateDirectoryW kernel32 2
imp '' CreateFileA kernel32 7
imp '' CreateFileMappingNumaW kernel32 7
@ -129,12 +130,10 @@ imp 'GetFileTime' GetFileTime kernel32 4
imp 'GetFileType' GetFileType kernel32 1
imp 'GetFinalPathNameByHandle' GetFinalPathNameByHandleW kernel32 4
imp 'GetFullPathName' GetFullPathNameW kernel32 4
imp 'GetShortPathName' GetShortPathNameW kernel32 3
imp 'GetHandleInformation' GetHandleInformation kernel32 2
imp 'GetLargestConsoleWindowSize' GetLargestConsoleWindowSize kernel32 1
imp 'GetLastError' GetLastError kernel32 0
imp 'GetLogicalDrives' GetLogicalDrives kernel32 0
imp 'GetLogicalDriveStringsA' GetLogicalDriveStringsA kernel32 2
imp 'GetMaximumProcessorCount' GetMaximumProcessorCount kernel32 1 # Windows 7+
imp 'GetModuleFileName' GetModuleFileNameW kernel32 3
imp 'GetModuleHandle' GetModuleHandleA kernel32 1
@ -188,9 +187,6 @@ imp 'GetVolumeInformationByHandle' GetVolumeInformationByHandleW kernel32
imp 'GetVolumePathName' GetVolumePathNameW kernel32 3
imp 'GetWindowsDirectory' GetWindowsDirectoryW kernel32 2
imp 'GetWindowsDirectoryA' GetWindowsDirectoryA kernel32 2
imp 'GetOEMCP' GetOEMCP kernel32 0
imp 'GetACP' GetACP kernel32 0
imp 'GetCPInfoEx' GetCPInfoExW kernel32 3
imp 'GlobalAlloc' GlobalAlloc kernel32 2
imp 'GlobalFree' GlobalFree kernel32 1
imp 'GlobalLock' GlobalLock kernel32 1
@ -307,6 +303,7 @@ imp 'UnlockFile' UnlockFile kernel32 5
imp 'UnmapViewOfFile2' UnmapViewOfFile2 kernel32 2
imp 'UnmapViewOfFileEx' UnmapViewOfFileEx kernel32 3
imp 'UpdateProcThreadAttribute' UpdateProcThreadAttribute kernel32 7
imp 'VirtualAlloc' VirtualAlloc kernel32 4
imp 'VirtualFree' VirtualFree kernel32 3
imp 'VirtualLock' VirtualLock kernel32 2
imp 'VirtualQuery' VirtualQuery kernel32 3
@ -361,7 +358,6 @@ imp 'RegLoadKey' RegLoadKeyW advapi32 3
imp 'RegNotifyChangeKeyValue' RegNotifyChangeKeyValue advapi32 5
imp 'RegOpenCurrentUser' RegOpenCurrentUser advapi32 2
imp 'RegOpenKeyEx' RegOpenKeyExW advapi32 5
imp 'RegOpenKeyExA' RegOpenKeyExA advapi32 5
imp 'RegOpenUserClassesRoot' RegOpenUserClassesRoot advapi32 4
imp 'RegOverridePredefKey' RegOverridePredefKey advapi32 2
imp 'RegQueryInfoKey' RegQueryInfoKeyW advapi32 12

View file

@ -1,35 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_NT_NLS_H_
#define COSMOPOLITAN_LIBC_NT_NLS_H_
#include "libc/nt/struct/cpinfoex.h"
/* ░░░░
cosmopolitan § new technology » internationalization
*/
COSMOPOLITAN_C_START_
uint32_t GetOEMCP();
uint32_t GetACP();
bool32 GetCPInfoEx(uint32_t CodePage, uint32_t dwFlags, struct NtCpInfoEx *out_lpCPInfoEx) paramsnonnull((3));
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_NT_NLS_H_ */

View file

@ -51,8 +51,6 @@ int RegOpenKey(int64_t hKey, const char16_t *opt_lpSubKey,
int RegOpenKeyEx(int64_t hKey, const char16_t *opt_lpSubKey,
uint32_t opt_ulOptions, int samDesired, int64_t *out_phkResult)
paramsnonnull((5));
int RegOpenKeyExA(int64_t hKey, const char *opt_lpSubKey, uint32_t opt_ulOptions,
int samDesired, int64_t *out_phkResult) paramsnonnull((5));
int RegCloseKey(int64_t hKey);
int RegGetValue(int64_t hkey, const char16_t *opt_lpSubKey,

View file

@ -1,13 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_CPINFOEX_H_
#define COSMOPOLITAN_LIBC_NT_STRUCT_CPINFOEX_H_
struct NtCpInfoEx {
uint32_t MaxCharSize;
uint8_t DefaultChar[2];
uint8_t LeadByte[12];
char16_t UnicodeDefaultChar;
uint32_t CodePage;
char16_t CodePageName[260];
};
#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_CPINFOEX_H_ */

View file

@ -30,7 +30,6 @@ LIBC_PROC_A_DIRECTDEPS = \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_PSAPI \
LIBC_RUNTIME \
LIBC_STR \

View file

@ -22,6 +22,7 @@
#include "libc/fmt/itoa.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/maps.h"
#include "libc/intrin/strace.h"
#include "libc/mem/mem.h"
#include "libc/nt/files.h"
#include "libc/nt/runtime.h"

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
@ -25,23 +24,16 @@
#include "libc/calls/syscall-nt.internal.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.h"
#include "libc/mem/mem.h"
#include "libc/nt/accounting.h"
#include "libc/nt/enum/processaccess.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/errors.h"
#include "libc/nt/files.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/startupinfo.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/proc/describefds.internal.h"
#include "libc/proc/ntspawn.h"
@ -49,19 +41,18 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#ifdef __x86_64__
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
__msabi extern typeof(TerminateProcess) *const __imp_TerminateProcess;
extern atomic_int __sig_worker_state;
extern pthread_mutex_t __sig_worker_lock;
static void sys_execve_nt_abort(sigset_t sigmask) {
__sig_worker_state &= ~2;
_pthread_unlock();
_pthread_mutex_unlock(&__sig_worker_lock);
__sig_unblock(sigmask);
}
@ -70,17 +61,17 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
// execve() needs to be @asyncsignalsafe
sigset_t sigmask = __sig_block();
__sig_worker_state |= 2;
for (;;)
if (__sig_worker_state & 1)
break;
_pthread_mutex_lock(&__sig_worker_lock); // order matters
_pthread_lock(); // order matters
// new process should be a child of our parent
int64_t hParentProcess =
sys_getppid_nt_win32
? OpenProcess(kNtProcessDupHandle | kNtProcessCreateProcess, false,
sys_getppid_nt_win32)
: 0;
int64_t hParentProcess;
int ppid = sys_getppid_nt();
if (!(hParentProcess = OpenProcess(
kNtProcessDupHandle | kNtProcessCreateProcess, false, ppid))) {
sys_execve_nt_abort(sigmask);
return -1;
}
// inherit pid
char pidvar[11 + 21];
@ -90,16 +81,6 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
char maskvar[6 + 21];
FormatUint64(stpcpy(maskvar, "_MASK="), sigmask);
// inherit parent process id
char ppidvar[12 + 21 + 1 + 21 + 1], *p = ppidvar;
p = stpcpy(p, "_COSMO_PPID=");
if (hParentProcess) {
p = FormatUint64(p, sys_getppid_nt_win32);
*p++ = ':';
p = FormatUint64(p, __pid);
setenv("_COSMO_PPID", ppidvar, true);
}
// define stdio handles for the spawned subprocess
struct NtStartupInfo si = {
.cb = sizeof(struct NtStartupInfo),
@ -113,22 +94,13 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
}
}
// which process is responsible for spawning the child?
int64_t hCreatorProcess;
if (hParentProcess) {
hCreatorProcess = hParentProcess;
} else {
hCreatorProcess = GetCurrentProcess();
}
// pass serialized file descriptor table in environment
char *fdspec;
int64_t *lpExplicitHandles;
uint32_t dwExplicitHandleCount;
if (!(fdspec = __describe_fds(g_fds.p, g_fds.n, &si, hCreatorProcess,
if (!(fdspec = __describe_fds(g_fds.p, g_fds.n, &si, hParentProcess,
&lpExplicitHandles, &dwExplicitHandleCount))) {
if (hParentProcess)
__imp_CloseHandle(hParentProcess);
CloseHandle(hParentProcess);
sys_execve_nt_abort(sigmask);
return -1;
}
@ -142,14 +114,12 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
// launch the process
struct NtProcessInformation pi;
int rc = ntspawn(&(struct NtSpawnArgs){
AT_FDCWD, program, argv, envp,
(char *[]){fdspec, maskvar, pidvar, ppidvar, 0}, 0, 0, hCreatorProcess,
lpExplicitHandles, dwExplicitHandleCount, &si, &pi});
__undescribe_fds(hCreatorProcess, lpExplicitHandles, dwExplicitHandleCount);
AT_FDCWD, program, argv, envp, (char *[]){fdspec, maskvar, pidvar, 0}, 0,
0, hParentProcess, lpExplicitHandles, dwExplicitHandleCount, &si, &pi});
__undescribe_fds(hParentProcess, lpExplicitHandles, dwExplicitHandleCount);
if (rc == -1) {
free(fdspec);
if (hParentProcess)
__imp_CloseHandle(hParentProcess);
CloseHandle(hParentProcess);
sys_execve_nt_abort(sigmask);
if (GetLastError() == kNtErrorSharingViolation) {
return etxtbsy();
@ -158,12 +128,10 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
}
}
// check if parent spoofing worked
if (hParentProcess) {
// give child to libc/proc/proc.c worker thread in parent
int64_t handle;
if (DuplicateHandle(GetCurrentProcess(), pi.hProcess, hParentProcess,
&handle, 0, false, kNtDuplicateSameAccess)) {
if (DuplicateHandle(GetCurrentProcess(), pi.hProcess, hParentProcess, &handle,
0, false, kNtDuplicateSameAccess)) {
unassert(!(handle & 0xFFFFFFFFFF000000));
__imp_TerminateProcess(-1, 0x23000000u | handle);
} else {
@ -174,40 +142,4 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
__builtin_unreachable();
}
// we couldn't reparent the new process
STRACE("warning: execve() lingering due to non-cosmo parent process");
// terminate other threads
_pthread_lock();
struct Dll *e;
struct PosixThread *me = _pthread_self();
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
if (pt == me)
continue;
TerminateThread(
atomic_load_explicit(&pt->tib->tib_syshand, memory_order_relaxed),
SIGKILL);
}
// wait for child to terminate and propagate exit code
for (;;) {
uint32_t status;
WaitForSingleObject(pi.hProcess, -1u);
GetExitCodeProcess(pi.hProcess, &status);
if (status != kNtStillActive) {
if ((status & 0xFF000000u) == 0x23000000u) {
// handle child execve()
__imp_CloseHandle(pi.hProcess);
pi.hProcess = status & 0x00FFFFFF;
} else {
// handle child _Exit()
if (status == 0xc9af3d51u)
status = kNtStillActive;
TerminateThisProcess(status);
}
}
}
}
#endif /* __x86_64__ */

View file

@ -57,6 +57,11 @@
* compiled by MSVC or Cygwin is launched instead, then only the stdio
* file descriptors can be passed along.
*
* On Windows, the parent process must be a cosmo program. If you're
* calling execve() from a program that wasn't launched by cosmopolitan
* bash, or some similar program, then ask yourself if what you really
* want is to either (a) call fork() first, or (b) use posix_spawn().
*
* On Windows, `argv` and `envp` can't contain binary strings. They need
* to be valid UTF-8 in order to round-trip the WIN32 API, without being
* corrupted.

View file

@ -46,7 +46,6 @@
#include "libc/nt/winsock.h"
#include "libc/proc/proc.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
@ -90,7 +89,6 @@ textwindows static void sys_fork_nt_child(void) {
// setup runtime
__klog_handle = 0;
__tls_index = __imp_TlsAlloc();
__morph_tls();
__set_tls_win32(__winmain_tib);
__tls_enabled = true;
@ -213,6 +211,8 @@ textwindows static int sys_fork_nt_parent(uint32_t dwCreationFlags) {
// let's go
bool ok = true;
uint32_t child_old_protect;
uint32_t parent_old_protect;
// copy memory manager maps
for (struct MapSlab *slab =
@ -225,12 +225,11 @@ textwindows static int sys_fork_nt_parent(uint32_t dwCreationFlags) {
}
// copy private memory maps
int alloc_prot = -1;
for (struct Map *map = __maps_first(); map; map = __maps_next(map)) {
if ((map->flags & MAP_TYPE) == MAP_SHARED)
continue; // shared memory doesn't need to be copied to subprocess
continue;
if ((map->flags & MAP_NOFORK) && (map->flags & MAP_TYPE) != MAP_FILE)
continue; // ignore things like signal worker stack memory
continue;
if (__maps_isalloc(map)) {
size_t allocsize = map->size;
for (struct Map *m2 = __maps_next(map); m2; m2 = __maps_next(m2)) {
@ -241,44 +240,22 @@ textwindows static int sys_fork_nt_parent(uint32_t dwCreationFlags) {
}
}
if ((map->flags & MAP_NOFORK) && (map->flags & MAP_TYPE) == MAP_FILE) {
// portable executable segment
if (map->prot & PROT_EXEC)
// TODO(jart): write a __remorph_tls() function
continue;
if (!(map->prot & PROT_WRITE)) {
uint32_t child_old_protect;
ok = ok && !!VirtualProtectEx(procinfo.hProcess, map->addr, allocsize,
kNtPageReadwrite, &child_old_protect);
alloc_prot = PROT_READ | PROT_WRITE;
} else {
alloc_prot = map->prot;
}
} else {
// private mapping
uint32_t page_flags;
if (!(alloc_prot & PROT_WRITE)) {
page_flags = kNtPageReadwrite;
alloc_prot = PROT_READ | PROT_WRITE;
} else {
page_flags = __prot2nt(alloc_prot, false);
}
ok = ok && !!VirtualAllocEx(procinfo.hProcess, map->addr, allocsize,
kNtMemReserve | kNtMemCommit, page_flags);
kNtMemReserve | kNtMemCommit,
kNtPageExecuteReadwrite);
}
}
uint32_t parent_old_protect;
if (!(map->prot & PROT_READ))
ok = ok && !!VirtualProtect(map->addr, map->size, kNtPageReadwrite,
&parent_old_protect);
ok = ok &&
!!WriteProcessMemory(procinfo.hProcess, map->addr, map->addr,
(map->size + __pagesize - 1) & -__pagesize, 0);
if (map->prot != alloc_prot) {
uint32_t child_old_protect;
ok = ok && !!WriteProcessMemory(procinfo.hProcess, map->addr, map->addr,
map->size, 0);
ok = ok &&
!!VirtualProtectEx(procinfo.hProcess, map->addr, map->size,
__prot2nt(map->prot, false), &child_old_protect);
}
if (!(map->prot & PROT_READ))
ok = ok && !!VirtualProtect(map->addr, map->size, parent_old_protect,
&parent_old_protect);

View file

@ -16,12 +16,10 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/metasigaltstack.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/syscall-nt.internal.h"
@ -45,7 +43,6 @@
#include "libc/runtime/syslib.internal.h"
#include "libc/stdio/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ss.h"
#include "libc/thread/itimer.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
@ -55,9 +52,9 @@
__msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId;
extern atomic_int __sig_worker_state;
extern pthread_mutex_t __cxa_lock_obj;
extern pthread_mutex_t __pthread_lock_obj;
extern pthread_mutex_t __sig_worker_lock;
void __rand64_lock(void);
void __rand64_unlock(void);
@ -123,7 +120,8 @@ static void fork_prepare(void) {
if (_weaken(__dlopen_lock))
_weaken(__dlopen_lock)();
if (IsWindows())
__proc_lock();
if (_weaken(__proc_lock))
_weaken(__proc_lock)();
if (_weaken(cosmo_stack_lock))
_weaken(cosmo_stack_lock)();
__cxa_lock();
@ -157,7 +155,8 @@ static void fork_parent(void) {
if (_weaken(cosmo_stack_unlock))
_weaken(cosmo_stack_unlock)();
if (IsWindows())
__proc_unlock();
if (_weaken(__proc_unlock))
_weaken(__proc_unlock)();
if (_weaken(__dlopen_unlock))
_weaken(__dlopen_unlock)();
if (_weaken(__localtime_unlock))
@ -168,7 +167,7 @@ static void fork_parent(void) {
_pthread_mutex_unlock(&supreme_lock);
}
static void fork_child(int ppid_win32, int ppid_cosmo) {
static void fork_child(void) {
if (_weaken(__rand64_wipe))
_weaken(__rand64_wipe)();
_pthread_mutex_wipe_np(&__fds_lock_obj);
@ -192,11 +191,9 @@ static void fork_child(int ppid_win32, int ppid_cosmo) {
sys_read_nt_wipe_keystrokes();
__proc_wipe_and_reset();
__itimer_wipe_and_reset();
atomic_init(&__sig_worker_state, 0);
_pthread_mutex_wipe_np(&__sig_worker_lock);
if (_weaken(__sig_init))
_weaken(__sig_init)();
if (_weaken(sys_getppid_nt_wipe))
_weaken(sys_getppid_nt_wipe)(ppid_win32, ppid_cosmo);
}
if (_weaken(_pthread_onfork_child))
_weaken(_pthread_onfork_child)();
@ -205,9 +202,8 @@ static void fork_child(int ppid_win32, int ppid_cosmo) {
int _fork(uint32_t dwCreationFlags) {
struct Dll *e;
int ax, dx, tid, ppid_win32, ppid_cosmo;
ppid_win32 = IsWindows() ? GetCurrentProcessId() : 0;
ppid_cosmo = __pid;
int ax, dx, tid, parent;
parent = __pid;
BLOCK_SIGNALS;
fork_prepare();
if (!IsWindows()) {
@ -227,7 +223,7 @@ int _fork(uint32_t dwCreationFlags) {
// get new thread id
struct CosmoTib *tib = __get_tls();
struct PosixThread *me = (struct PosixThread *)tib->tib_pthread;
struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread;
tid = IsLinux() || IsXnuSilicon() ? dx : sys_gettid();
atomic_init(&tib->tib_ctid, tid);
atomic_init(&tib->tib_ptid, tid);
@ -247,10 +243,10 @@ int _fork(uint32_t dwCreationFlags) {
// turn other threads into zombies
// we can't free() them since we're monopolizing all locks
// we assume the operating system already reclaimed system handles
dll_remove(&_pthread_list, &me->list);
dll_remove(&_pthread_list, &pt->list);
struct Dll *old_threads = _pthread_list;
_pthread_list = 0;
dll_make_first(&_pthread_list, &me->list);
dll_make_first(&_pthread_list, &pt->list);
atomic_init(&_pthread_count, 1);
// get new system thread handle
@ -268,43 +264,25 @@ int _fork(uint32_t dwCreationFlags) {
atomic_init(&tib->tib_sigpending, 0);
// we can't be canceled if the canceler no longer exists
atomic_init(&me->pt_canceled, false);
atomic_init(&pt->pt_canceled, false);
// forget locks
bzero(tib->tib_locks, sizeof(tib->tib_locks));
// xnu fork() doesn't preserve sigaltstack()
if (IsXnu() && me->tib->tib_sigstack_addr) {
struct sigaltstack_bsd ss;
ss.ss_sp = me->tib->tib_sigstack_addr;
ss.ss_size = me->tib->tib_sigstack_size;
ss.ss_flags = me->tib->tib_sigstack_flags;
if (IsXnuSilicon()) {
__syslib->__sigaltstack(&ss, 0);
} else {
sys_sigaltstack(&ss, 0);
}
}
// run user fork callbacks
fork_child(ppid_win32, ppid_cosmo);
fork_child();
// free threads
if (_weaken(_pthread_free)) {
while ((e = dll_first(old_threads))) {
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
pt = POSIXTHREAD_CONTAINER(e);
atomic_init(&pt->tib->tib_syshand, 0);
dll_remove(&old_threads, e);
_weaken(_pthread_free)(pt);
}
}
// reactivate ftrace
/* if (ftrace_stackdigs) */
/* if (_weaken(ftrace_install)) */
/* _weaken(ftrace_install)(); */
STRACE("fork() → 0 (child of %d)", ppid_cosmo);
STRACE("fork() → 0 (child of %d)", parent);
} else {
// this is the parent process
fork_parent();

View file

@ -1,93 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 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/atomic.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/cosmo.h"
#include "libc/dce.h"
#include "libc/fmt/itoa.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/nt/process.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processbasicinformation.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
int sys_getppid_nt_win32;
int sys_getppid_nt_cosmo;
textwindows static int sys_getppid_nt_ntdll(void) {
struct NtProcessBasicInformation ProcessInformation;
uint32_t gotsize = 0;
if (!NtError(
NtQueryInformationProcess(GetCurrentProcess(), 0, &ProcessInformation,
sizeof(ProcessInformation), &gotsize)) &&
gotsize >= sizeof(ProcessInformation) &&
ProcessInformation.InheritedFromUniqueProcessId) {
return ProcessInformation.InheritedFromUniqueProcessId;
}
return 0;
}
static void sys_getppid_nt_extract(const char *str) {
int c;
int win32 = 0;
int cosmo = 0;
if (str) {
for (;;) {
c = *str;
if (!('0' <= c && c <= '9'))
break;
win32 *= 10;
win32 += c - '0';
++str;
}
if (win32 && *str++ == ':') {
for (;;) {
c = *str;
if (!('0' <= c && c <= '9'))
break;
cosmo *= 10;
cosmo += c - '0';
++str;
}
if (win32 == sys_getppid_nt_ntdll()) {
sys_getppid_nt_win32 = win32;
sys_getppid_nt_cosmo = cosmo;
}
}
}
}
__attribute__((__constructor__(90))) static void init(void) {
if (!IsWindows())
return;
sys_getppid_nt_extract(getenv("_COSMO_PPID"));
}
textwindows int sys_getppid_nt(void) {
if (sys_getppid_nt_cosmo)
return sys_getppid_nt_cosmo;
return sys_getppid_nt_ntdll();
}
textwindows void sys_getppid_nt_wipe(int win32, int cosmo) {
sys_getppid_nt_win32 = win32;
sys_getppid_nt_cosmo = cosmo;
}

View file

@ -51,7 +51,6 @@
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/enum/startf.h"
#include "libc/nt/files.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processinformation.h"
#include "libc/nt/struct/startupinfo.h"
@ -60,7 +59,6 @@
#include "libc/proc/posix_spawn.h"
#include "libc/proc/posix_spawn.internal.h"
#include "libc/proc/proc.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/stdio/stdio.h"
@ -398,14 +396,6 @@ static textwindows errno_t posix_spawn_nt_impl(
}
FormatUint64(stpcpy(maskvar, "_MASK="), childmask);
// inherit parent process id
char ppidvar[12 + 21 + 1 + 21 + 1], *p = ppidvar;
p = stpcpy(p, "_COSMO_PPID=");
p = FormatUint64(p, GetCurrentProcessId());
*p++ = ':';
p = FormatUint64(p, __pid);
setenv("_COSMO_PPID", ppidvar, true);
// launch process
int rc = -1;
struct NtProcessInformation procinfo;

View file

@ -131,18 +131,14 @@ textwindows static int __proc_wait(int pid, int *wstatus, int options,
// perform blocking operation
uint32_t wi;
uintptr_t event;
if ((event = CreateEvent(0, 0, 0, 0))) {
struct PosixThread *pt = _pthread_self();
pt->pt_event = event;
pt->pt_blkmask = waitmask;
pt->pt_event = event = CreateEvent(0, 0, 0, 0);
atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_EVENT,
memory_order_release);
wi = WaitForMultipleObjects(2, (intptr_t[2]){hWaitObject, event}, 0, -1u);
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
CloseHandle(event);
} else {
wi = -1u;
}
// log warning if handle unexpectedly closed
if (wi & kNtWaitAbandoned) {

View file

@ -30,18 +30,18 @@
// @param 8(rsp) x6 is arg
// @return tid of child on success, or -errno on error
sys_clone_linux:
beg
pro
#ifdef __x86_64__
cpush %rbx
push %rbp
mov %rsp,%rbp
push %rbx
mov %rcx,%r10
mov 16(%rbp),%rbx
mov $56,%eax // __NR_clone
syscall
test %rax,%rax
jz 2f
0: cpop %rbx
epi
0: pop %rbx
pop %rbp
ret
2: xor %ebp,%ebp // child thread
mov %rbx,%rdi // arg
@ -50,13 +50,15 @@ sys_clone_linux:
mov $60,%eax // __NR_exit(exitcode)
syscall
#elif defined(__aarch64__)
stp x29,x30,[sp,#-16]!
mov x29,sp
mov x8,x3 // swap x3 and x4
mov x3,x4 // swap x3 and x4
mov x4,x8 // swap x3 and x4
mov x8,#220 // __NR_clone
svc #0
cbz x0,2f
epi
ldp x29,x30,[sp],#16
ret
2: mov x29,#0 // wipe backtrace
mov x28,x3 // set cosmo tls
@ -67,5 +69,4 @@ sys_clone_linux:
#else
#error "unsupported architecture"
#endif
end
.endfn sys_clone_linux,globl,hidden

View file

@ -32,8 +32,8 @@
// @param rdx is environ
// @param rcx is auxv
// @noreturn
cosmo: beg
pro
cosmo: push %rbp
mov %rsp,%rbp
mov %edi,%r12d
mov %rsi,%r13
mov %rdx,%r14
@ -104,10 +104,7 @@ cosmo: beg
je 2f
push %rax
push %rax
mov %r12d,%edi
mov %r13,%rsi
mov %r14,%rdx
mov %r15,%rcx
call .Largs
call *(%rax)
pop %rax
pop %rax
@ -115,15 +112,17 @@ cosmo: beg
jmp 1b
// call main()
2: mov %r12d,%edi
mov %r13,%rsi
mov %r14,%rdx
mov %r15,%rcx
2: call .Largs
.weak main
call main
xchg %eax,%edi
call exit
end
.Largs: mov %r12d,%edi
mov %r13,%rsi
mov %r14,%rdx
mov %r15,%rcx
ret
.endfn cosmo,weak
// Enables Thread Local Storage.

View file

@ -28,12 +28,8 @@ ftrace_hook:
cmpl $0,__ftrace(%rip)
jle 1f
.cfi_startproc
push %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
mov %rsp,%rbp
.cfi_def_cfa_register %rbp
and $-16,%rsp
sub $128,%rsp
movdqu %xmm0,-0x80(%rbp)
@ -45,21 +41,13 @@ ftrace_hook:
movdqu %xmm6,-0x20(%rbp)
movdqu %xmm7,-0x10(%rbp)
push %rax
.cfi_offset %rax, -24
push %rcx
.cfi_offset %rcx, -32
push %rdx
.cfi_offset %rdx, -40
push %rdi
.cfi_offset %rdi, -48
push %rsi
.cfi_offset %rsi, -56
push %r8
.cfi_offset %r8, -64
push %r9
.cfi_offset %r9, -72
push %r10
.cfi_offset %r10, -80
call ftracer
movdqu -0x80(%rbp),%xmm0
movdqu -0x70(%rbp),%xmm1
@ -78,20 +66,12 @@ ftrace_hook:
pop %rcx
pop %rax
leave
.cfi_restore %rbp
.cfi_def_cfa %rsp, 8
1: ret
.cfi_endproc
#elif defined(__aarch64__)
stp x29,x30,[sp,-384]!
.cfi_startproc
.cfi_def_cfa_offset 384
.cfi_offset 29, -384 // x29 (fp) is saved at [sp - 384]
.cfi_offset 30, -376 // x30 (lr) is saved at [sp - 376]
mov x29,sp
.cfi_def_cfa_register 29
stp x0,x1,[sp,16]
adrp x0,__ftrace
@ -100,45 +80,18 @@ ftrace_hook:
ble 1f
stp x2,x3,[sp,32]
.cfi_offset 2, -352
.cfi_offset 3, -344
stp x4,x5,[sp,48]
.cfi_offset 4, -336
.cfi_offset 5, -328
stp x6,x7,[sp,64]
.cfi_offset 6, -320
.cfi_offset 7, -312
stp x8,x9,[sp,80]
.cfi_offset 8, -304
.cfi_offset 9, -296
stp x10,x11,[sp,96]
.cfi_offset 10, -288
.cfi_offset 11, -280
stp x12,x13,[sp,112]
.cfi_offset 12, -272
.cfi_offset 13, -264
stp x14,x15,[sp,128]
.cfi_offset 14, -256
.cfi_offset 15, -248
stp x16,x19,[sp,160]
.cfi_offset 16, -224
.cfi_offset 19, -216
stp x20,x21,[sp,176]
.cfi_offset 20, -208
.cfi_offset 21, -200
stp x22,x23,[sp,192]
.cfi_offset 22, -192
.cfi_offset 23, -184
stp x24,x25,[sp,208]
.cfi_offset 24, -176
.cfi_offset 25, -168
stp x26,x27,[sp,224]
.cfi_offset 26, -160
.cfi_offset 27, -152
stp x17,x28,[sp,240]
.cfi_offset 17, -144
.cfi_offset 28, -136
// No CFI directives needed for FP registers
stp q0,q1,[sp,256]
stp q2,q3,[sp,288]
stp q4,q5,[sp,320]
@ -166,12 +119,7 @@ ftrace_hook:
1: ldp x0,x1,[sp,16]
ldp x29,x30,[sp],384
.cfi_restore 29
.cfi_restore 30
.cfi_def_cfa 7, 0 // On some ARM systems the stack pointer is represented by register 7
.cfi_def_cfa_offset 0
ret
.cfi_endproc
#endif /* __x86_64__ */
.endfn ftrace_hook,globl,hidden

View file

@ -23,36 +23,17 @@
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/thread.h"
/**
* Returns true if signal is caused by stack overflow.
* Returns true if signal is most likely a stack overflow.
*/
char __is_stack_overflow(siginfo_t *si, void *arg) {
// sanity check
ucontext_t *uc = arg;
if (!si || !uc)
return false;
if (si->si_signo != SIGSEGV && //
si->si_signo != SIGBUS)
if (si->si_signo != SIGSEGV && si->si_signo != SIGBUS)
return false;
// get stack information
pthread_attr_t attr;
if (pthread_getattr_np(pthread_self(), &attr))
return false;
size_t guardsize;
if (pthread_attr_getguardsize(&attr, &guardsize))
return false;
void *stackaddr;
size_t stacksize;
if (pthread_attr_getstack(&attr, &stackaddr, &stacksize))
return false;
// determine if faulting address is inside guard region
char *x = (char *)si->si_addr;
char *lo = (char *)stackaddr - guardsize;
char *hi = (char *)stackaddr;
return lo <= x && x < hi;
intptr_t sp = uc->uc_mcontext.SP;
intptr_t fp = (intptr_t)si->si_addr;
return ABS(fp - sp) < __pagesize;
}

View file

@ -24,13 +24,12 @@
#include "libc/intrin/kprintf.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/prot.h"
__msabi extern typeof(VirtualProtectEx) *const __imp_VirtualProtectEx;
__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
__funline void __morph_mprotect(void *addr, size_t size, int prot, int ntprot) {
#ifdef __x86_64__
@ -55,7 +54,7 @@ __funline void __morph_mprotect(void *addr, size_t size, int prot, int ntprot) {
}
#endif
} else {
__imp_VirtualProtectEx(GetCurrentProcess(), addr, size, ntprot, &op);
__imp_VirtualProtect(addr, size, ntprot, &op);
}
#elif defined(__aarch64__)
register long r0 asm("x0") = (long)addr;

View file

@ -83,6 +83,7 @@ extern uint64_t kStartTsc;
extern const char kNtSystemDirectory[];
extern const char kNtWindowsDirectory[];
extern size_t __virtualmax;
extern size_t __virtualsize;
extern size_t __stackmax;
extern bool32 __isworker;
/* utilities */

View file

@ -79,7 +79,7 @@ __msabi extern typeof(SetConsoleMode) *const __imp_SetConsoleMode;
__msabi extern typeof(SetConsoleOutputCP) *const __imp_SetConsoleOutputCP;
__msabi extern typeof(SetEnvironmentVariable) *const __imp_SetEnvironmentVariableW;
__msabi extern typeof(SetStdHandle) *const __imp_SetStdHandle;
__msabi extern typeof(VirtualProtectEx) *const __imp_VirtualProtectEx;
__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
// clang-format on
@ -206,11 +206,10 @@ abi wontreturn static void WinInit(const char16_t *cmdline) {
int stackprot = (intptr_t)ape_stack_prot;
if (~stackprot & PROT_EXEC) {
uint32_t old;
__imp_VirtualProtectEx(GetCurrentProcess(), stackaddr, stacksize,
kNtPageReadwrite, &old);
__imp_VirtualProtect(stackaddr, stacksize, kNtPageReadwrite, &old);
}
uint32_t oldattr;
__imp_VirtualProtectEx(GetCurrentProcess(), stackaddr, GetGuardSize(),
__imp_VirtualProtect(stackaddr, GetGuardSize(),
kNtPageReadwrite | kNtPageGuard, &oldattr);
if (_weaken(__maps_stack)) {
struct NtSystemInfo si;

View file

@ -18,19 +18,13 @@
*/
#include "libc/sock/ifaddrs.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/limits.h"
#include "libc/mem/mem.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/ifconf.h"
#include "libc/sock/struct/ifreq.h"
#include "libc/sock/struct/sockaddr6.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/iff.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sio.h"
#include "libc/sysv/consts/sock.h"
@ -42,20 +36,6 @@ struct IfAddr {
struct sockaddr_in bstaddr;
};
struct IfAddr6Info {
int addr_scope;
int addr_flags;
};
struct IfAddr6 {
struct ifaddrs ifaddrs;
char name[IFNAMSIZ];
struct sockaddr_in6 addr;
struct sockaddr_in6 netmask;
struct sockaddr_in6 bstaddr; // unused
struct IfAddr6Info info;
};
/**
* Frees network interface address list.
*/
@ -68,73 +48,6 @@ void freeifaddrs(struct ifaddrs *ifp) {
}
}
// hex repr to network order int
static uint128_t hex2no(const char *str) {
uint128_t res = 0;
const int max_quads = sizeof(uint128_t) * 2;
int i = 0;
while ((i < max_quads) && str[i]) {
uint8_t acc = (((str[i] & 0xF) + (str[i] >> 6)) | ((str[i] >> 3) & 0x8));
acc = acc << 4;
i += 1;
if (str[i]) {
acc = acc | (((str[i] & 0xF) + (str[i] >> 6)) | ((str[i] >> 3) & 0x8));
i += 1;
}
res = (res >> 8) | (((uint128_t)acc) << ((sizeof(uint128_t) - 1) * 8));
}
res = res >> ((max_quads - i) * 4);
return res;
}
/**
* Gets network interface IPv6 address list on linux.
*
* @return 0 on success, or -1 w/ errno
*/
static int getifaddrs_linux_ip6(struct ifconf *conf) {
int fd;
int n = 0;
struct ifreq *ifreq = conf->ifc_req;
const int bufsz = 44 + IFNAMSIZ + 1;
char buf[bufsz + 1]; // one line max size
if ((fd = sys_openat(0, "/proc/net/if_inet6", O_RDONLY, 0)) == -1) {
return -1;
}
while ((n = sys_read(fd, &buf[n], bufsz - n)) &&
((char *)ifreq < (conf->ifc_buf + conf->ifc_len))) {
// flags linux include/uapi/linux/if_addr.h:44
// scope linux include/net/ipv6.h:L99
// *addr, *index, *plen, *scope, *flags, *ifname
char *s[] = {&buf[0], &buf[33], &buf[36], &buf[39], &buf[42], &buf[45]};
int ifnamelen = 0;
while (*s[5] == ' ') {
++s[5];
}
while (s[5][ifnamelen] > '\n') {
++ifnamelen;
}
buf[32] = buf[35] = buf[38] = buf[41] = buf[44] = s[5][ifnamelen] = '\0';
bzero(ifreq, sizeof(*ifreq));
ifreq->ifr_addr.sa_family = AF_INET6;
memcpy(&ifreq->ifr_name, s[5], ifnamelen);
*((uint128_t *)&ifreq->ifr6_addr) = hex2no(s[0]);
ifreq->ifr6_ifindex = hex2no(s[1]);
ifreq->ifr6_prefixlen = hex2no(s[2]);
ifreq->ifr6_scope = hex2no(s[3]);
ifreq->ifr6_flags = hex2no(s[4]);
++ifreq;
int tlen = &s[5][ifnamelen] - &buf[0] + 1;
n = bufsz - tlen;
memcpy(&buf, &buf[tlen], n);
}
conf->ifc_len = (char *)ifreq - conf->ifc_buf;
return sys_close(fd);
}
/**
* Gets network interface address list.
*
@ -142,7 +55,6 @@ static int getifaddrs_linux_ip6(struct ifconf *conf) {
* @see tool/viz/getifaddrs.c for example code
*/
int getifaddrs(struct ifaddrs **out_ifpp) {
// printf("%d\n", sizeof(struct ifreq));
int rc = -1;
int fd;
if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) != -1) {
@ -153,20 +65,12 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
conf.ifc_buf = data;
conf.ifc_len = size;
if (!ioctl(fd, SIOCGIFCONF, &conf)) {
if (IsLinux()) {
struct ifconf confl6;
confl6.ifc_buf = data + conf.ifc_len;
confl6.ifc_len = size - conf.ifc_len;
if ((rc = getifaddrs_linux_ip6(&confl6)))
return rc;
conf.ifc_len += confl6.ifc_len;
}
struct ifaddrs *res = 0;
for (struct ifreq *ifr = (struct ifreq *)data;
(char *)ifr < data + conf.ifc_len; ++ifr) {
uint16_t family = ifr->ifr_addr.sa_family;
if (family == AF_INET) {
if (ifr->ifr_addr.sa_family != AF_INET) {
continue; // TODO(jart): IPv6 support
}
struct IfAddr *addr;
if ((addr = calloc(1, sizeof(struct IfAddr)))) {
memcpy(addr->name, ifr->ifr_name, IFNAMSIZ);
@ -198,44 +102,6 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
addr->ifaddrs.ifa_next = res;
res = (struct ifaddrs *)addr;
}
} else if (family == AF_INET6) {
struct IfAddr6 *addr6;
if ((addr6 = calloc(1, sizeof(struct IfAddr6)))) {
addr6->ifaddrs.ifa_name = addr6->name;
addr6->ifaddrs.ifa_addr = (struct sockaddr *)&addr6->addr;
addr6->ifaddrs.ifa_netmask = (struct sockaddr *)&addr6->netmask;
addr6->ifaddrs.ifa_broadaddr = (struct sockaddr *)&addr6->bstaddr;
addr6->ifaddrs.ifa_data = (void *)&addr6->info;
memcpy(&addr6->name, &ifr->ifr_name, IFNAMSIZ);
addr6->info.addr_flags = ifr->ifr6_flags;
addr6->info.addr_scope = ifr->ifr6_scope;
addr6->addr.sin6_family = AF_INET6;
addr6->addr.sin6_port = 0;
addr6->addr.sin6_flowinfo = 0;
addr6->addr.sin6_scope_id = ifr->ifr6_ifindex;
memcpy(&addr6->addr.sin6_addr, &ifr->ifr6_addr,
sizeof(struct in6_addr));
addr6->netmask.sin6_family = AF_INET6;
addr6->netmask.sin6_port = 0;
addr6->netmask.sin6_flowinfo = 0;
addr6->addr.sin6_scope_id = ifr->ifr6_ifindex;
memcpy(&addr6->netmask.sin6_addr, &ifr->ifr6_addr,
sizeof(struct in6_addr));
*((uint128_t *)&(addr6->netmask.sin6_addr)) &=
(UINT128_MAX >> ifr->ifr6_prefixlen);
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
addr6->ifaddrs.ifa_flags = ifr->ifr_flags;
}
bzero(&addr6->bstaddr, sizeof(struct sockaddr_in6));
addr6->ifaddrs.ifa_next = res;
res = (struct ifaddrs *)addr6;
}
}
}
*out_ifpp = res;
rc = 0;

View file

@ -92,10 +92,9 @@ textwindows dontinline static ssize_t sys_sendfile_nt(
}
struct NtOverlapped ov = {.hEvent = WSACreateEvent(), .Pointer = offset};
cosmo_once(&g_transmitfile.once, transmitfile_init);
if (ov.hEvent &&
(g_transmitfile.lpTransmitFile(oh, ih, uptobytes, 0, &ov, 0, 0) ||
if (g_transmitfile.lpTransmitFile(oh, ih, uptobytes, 0, &ov, 0, 0) ||
WSAGetLastError() == kNtErrorIoPending ||
WSAGetLastError() == WSAEINPROGRESS)) {
WSAGetLastError() == WSAEINPROGRESS) {
if (WSAGetOverlappedResult(oh, &ov, &uptobytes, true, &flags)) {
rc = uptobytes;
if (opt_in_out_inoffset) {

View file

@ -1,7 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_SOCK_STRUCT_IFREQ_H_
#define COSMOPOLITAN_LIBC_SOCK_STRUCT_IFREQ_H_
#include "libc/sock/struct/sockaddr.h"
#include "libc/sock/struct/sockaddr6.h"
COSMOPOLITAN_C_START_
#define IF_NAMESIZE 16
@ -12,14 +11,6 @@ struct ifreq {
char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */
} ifr_ifrn;
union {
struct {
uint16_t sa_family;
uint16_t ifr6_ifindex; /* Interface index */
uint16_t ifr6_flags; /* Flags */
uint8_t ifr6_scope; /* Addr scope */
uint8_t ifr6_prefixlen; /* Prefix length */
struct in6_addr ifr6_addr;
} in6;
struct sockaddr ifru_addr; /* SIOCGIFADDR */
struct sockaddr ifru_dstaddr; /* SIOCGIFDSTADDR */
struct sockaddr ifru_netmask; /* SIOCGIFNETMASK */
@ -38,11 +29,5 @@ struct ifreq {
#define ifr_flags ifr_ifru.ifru_flags /* flags */
#define ifr_ifindex ifr_ifru.ifru_ivalue
#define ifr6_addr ifr_ifru.in6.ifr6_addr /* IP6 Addr */
#define ifr6_scope ifr_ifru.in6.ifr6_scope /* IP6 Addr scope */
#define ifr6_prefixlen ifr_ifru.in6.ifr6_prefixlen /* IP6 Prefix length */
#define ifr6_ifindex ifr_ifru.in6.ifr6_ifindex /* IP6 If index */
#define ifr6_flags ifr_ifru.in6.ifr6_flags /* IP6 If flags */
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_SOCK_STRUCT_IFREQ_H_ */

View file

@ -21,7 +21,6 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/strace.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/sysv/errfuns.h"
int sys_getentropy(void *, size_t) asm("sys_getrandom");
@ -40,8 +39,6 @@ int getentropy(void *p, size_t n) {
rc = eio();
} else if ((!p && n)) {
rc = efault();
} else if (IsXnuSilicon()) {
rc = __syslib->__getentropy(p, n);
} else if (IsXnu() || IsOpenbsd()) {
rc = sys_getentropy(p, n);
} else {

View file

@ -1981,4 +1981,4 @@ syscon misc UL_SETFSIZE 2 2 2 2 2 0 0 0
syscon misc XATTR_CREATE 1 1 2 2 0 0 0 0
syscon misc XATTR_REPLACE 2 2 4 4 0 0 0 0
# https://youtu.be/3SNBXoWs4GM
# https://youtu.be/GUQUD3IMbb4?t=85

View file

@ -102,8 +102,8 @@ __pid: .quad 0
.previous
systemfive_cp:
beg
pro
push %rbp
mov %rsp,%rbp // so backtraces work
systemfive_cancellable: // our pthread_cancel() miracle code
cmpb $0,__tls_enabled(%rip) // inspired by the musl libc design!
je 1f // we handle linux and bsd together!
@ -123,7 +123,7 @@ systemfive_cancellable: // our pthread_cancel() miracle code
clc // no cancellable system calls exist
syscall // that have 7+ args on the bsd OSes
systemfive_cancellable_end: // i/o calls park here for long time
epi
pop %rbp
jnc 2f
neg %rax // turns bsd errno to system v errno
2: cmp $-4095,%rax // but we still check again on eintr
@ -144,13 +144,11 @@ systemfive_cancellable_end: // i/o calls park here for long time
je systemfive_errno // we aren't actually cancelled
jmp 4f // now we are in fact cancelled
systemfive_cancel: // SIGTHR will jump here too
epi
pop %rbp
4: jmp _pthread_cancel_ack // tail call
.weak _pthread_cancel_ack // must be linked if we're cancelled
end
#if IsModeDbg()
not_a_cancellation_point: // need BEGIN/END_CANCELLATION_POINT
beg
nop
.weak report_cancellation_point
5: ezlea report_cancellation_point,cx
@ -159,7 +157,6 @@ not_a_cancellation_point: // need BEGIN/END_CANCELLATION_POINT
call *%rcx
6: ud2
nop
end
#endif
.globl systemfive_cancellable_end
.globl systemfive_cancellable
@ -169,20 +166,19 @@ not_a_cancellation_point: // need BEGIN/END_CANCELLATION_POINT
.Lanchorpoint:
#if SupportsLinux() || SupportsMetal()
systemfive_linux:
beg
and $0xfff,%eax // remove nonlinux bits from ordinal
cmp $0xfff,%eax // checks if unsupported by platform
je systemfive_enosys // never taken branches cost nothing
btr $11,%eax // 0x800 means a call is cancellable
jc systemfive_cp // it is handled by the holiest code
mov %rcx,%r10 // syscall instruction clobbers %rcx
pro // linux never reads args from stack
push %rbp // linux never reads args from stack
mov %rsp,%rbp // having frame will help backtraces
syscall // this is known as a context switch
epi // next we check to see if it failed
pop %rbp // next we check to see if it failed
cmp $-4095,%rax // system five nexgen32e abi § a.2.1
jae systemfive_error // encodes errno as neg return value
ret
end
.endfn systemfive_linux,globl,hidden
systemfive_error:
neg %eax
@ -190,35 +186,27 @@ systemfive_error:
.endfn systemfive_error,globl,hidden
#endif
systemfive_errno:
beg
xchg %eax,%ecx
call __errno_location
mov %ecx,(%rax) // normalize to c library convention
push $-1 // negative one is only error result
pop %rax // the push pop is to save code size
ret
end
.endfn systemfive_errno,globl,hidden
systemfive_enosys:
beg
mov ENOSYS(%rip),%eax
jmp systemfive_errno
end
.endfn systemfive_enosys,globl,hidden
#if SupportsNetbsd()
systemfive_netbsd:
beg
shr $4*13,%rax
jmp systemfive_bsdscrub
end
.endfn systemfive_netbsd,globl,hidden
#endif
#if SupportsOpenbsd()
systemfive_openbsd:
beg
shr $4*10,%rax
jmp systemfive_bsdscrub
end
.endfn systemfive_openbsd,globl,hidden
#endif
#if SupportsFreebsd()
@ -234,7 +222,6 @@ systemfive_bsdscrub:
// 𝑠𝑙𝑖𝑑𝑒
.endfn systemfive_bsdscrub,globl,hidden
systemfive_bsd:
beg
cmp $0xfff,%ax
je systemfive_enosys
btr $11,%eax // checks/reset the 800 cancellable bit
@ -243,7 +230,6 @@ systemfive_bsd:
syscall // bsd will need arg on stack sometimes
jc systemfive_errno // bsd sets carry flag if %rax is errno
ret
end
.endfn systemfive_bsd
#endif
#if SupportsXnu()

View file

@ -205,9 +205,11 @@ int pthread_mutex_unlock(pthread_mutex_t *) dontthrow paramsnonnull();
int pthread_mutex_wipe_np(pthread_mutex_t *) libcesque paramsnonnull();
int pthread_mutexattr_destroy(pthread_mutexattr_t *) libcesque paramsnonnull();
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *) libcesque paramsnonnull();
int pthread_mutexattr_getrobust(const pthread_mutexattr_t *, int *) libcesque paramsnonnull();
int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *) libcesque paramsnonnull();
int pthread_mutexattr_init(pthread_mutexattr_t *) libcesque paramsnonnull();
int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int) libcesque paramsnonnull();
int pthread_mutexattr_setrobust(const pthread_mutexattr_t *, int) libcesque paramsnonnull();
int pthread_mutexattr_settype(pthread_mutexattr_t *, int) libcesque paramsnonnull();
int pthread_once(pthread_once_t *, void (*)(void)) paramsnonnull();
int pthread_orphan_np(void) libcesque;

View file

@ -167,6 +167,7 @@ void _StartTty(struct Tty *tty, unsigned char type, unsigned short yp,
unsigned short startx, unsigned char yc, unsigned char xc,
void *fb, unsigned init_flags) {
unsigned short yn, xn, xs = xp * sizeof(TtyCanvasColor);
struct DirectMap dm;
bzero(tty, sizeof(struct Tty));
SetYp(tty, yp);
SetXp(tty, xp);
@ -182,9 +183,9 @@ void _StartTty(struct Tty *tty, unsigned char type, unsigned short yp,
tty->canvas = fb;
xs = xsfb;
} else {
void *addr = sys_mmap_metal(NULL, (size_t)yp * xs, PROT_READ | PROT_WRITE,
dm = sys_mmap_metal(NULL, (size_t)yp * xs, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr == (void *)-1) {
if (dm.addr == (void *)-1) {
/*
* We are a bit low on memory. Try to go on anyway, & initialize
* our tty as an emergency console.
@ -193,7 +194,7 @@ void _StartTty(struct Tty *tty, unsigned char type, unsigned short yp,
tty->canvas = fb;
xs = xsfb;
} else
tty->canvas = addr;
tty->canvas = dm.addr;
}
}
SetYn(tty, yn);

View file

@ -51,9 +51,6 @@ void SetUpOnce(void) {
// ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath", 0));
}
// TODO(jart): fix this test
#if 0
TEST(cachestat, testCachestatOnDevices) {
const char *const files[] = {
"/dev/zero", "/dev/null", "/dev/urandom", "/proc/version", "/proc",
@ -67,8 +64,6 @@ TEST(cachestat, testCachestatOnDevices) {
}
}
#endif
TEST(cachestat, testCachestatAfterWrite) {
size_t size = 4 * pagesize;
char *data = gc(xmalloc(size));

View file

@ -0,0 +1,242 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 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 "dsp/core/core.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/timespec.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/directmap.h"
#include "libc/intrin/safemacros.h"
#include "libc/limits.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/rand.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/rlimit.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
#include "libc/time.h"
#include "libc/x/xsigaction.h"
#include "libc/x/xspawn.h"
#define MEM (64 * 1024 * 1024)
static char tmpname[PATH_MAX];
void OnSigxcpu(int sig) {
ASSERT_EQ(SIGXCPU, sig);
_Exit(0);
}
void OnSigxfsz(int sig) {
unlink(tmpname);
ASSERT_EQ(SIGXFSZ, sig);
_Exit(0);
}
TEST(setrlimit, testCpuLimit) {
int wstatus;
struct rlimit rlim;
struct timespec start;
double matrices[3][3][3];
if (IsWindows())
return; // of course it doesn't work on windows
if (IsXnu())
return; // TODO(jart): it worked before
if (IsOpenbsd())
return; // TODO(jart): fix flake
ASSERT_NE(-1, (wstatus = xspawn(0)));
if (wstatus == -2) {
ASSERT_EQ(0, xsigaction(SIGXCPU, OnSigxcpu, 0, 0, 0));
ASSERT_EQ(0, getrlimit(RLIMIT_CPU, &rlim));
rlim.rlim_cur = 1; // set soft limit to one second
ASSERT_EQ(0, setrlimit(RLIMIT_CPU, &rlim));
start = timespec_real();
do {
matmul3(matrices[0], matrices[1], matrices[2]);
matmul3(matrices[0], matrices[1], matrices[2]);
matmul3(matrices[0], matrices[1], matrices[2]);
matmul3(matrices[0], matrices[1], matrices[2]);
} while (timespec_sub(timespec_real(), start).tv_sec < 5);
_Exit(1);
}
EXPECT_TRUE(WIFEXITED(wstatus));
EXPECT_FALSE(WIFSIGNALED(wstatus));
EXPECT_EQ(0, WEXITSTATUS(wstatus));
EXPECT_EQ(0, WTERMSIG(wstatus));
}
TEST(setrlimit, testFileSizeLimit) {
char junkdata[512];
int i, fd, wstatus;
struct rlimit rlim;
if (IsWindows())
return; /* of course it doesn't work on windows */
ASSERT_NE(-1, (wstatus = xspawn(0)));
if (wstatus == -2) {
ASSERT_EQ(0, xsigaction(SIGXFSZ, OnSigxfsz, 0, 0, 0));
ASSERT_EQ(0, getrlimit(RLIMIT_FSIZE, &rlim));
rlim.rlim_cur = 1024 * 1024; /* set soft limit to one megabyte */
ASSERT_EQ(0, setrlimit(RLIMIT_FSIZE, &rlim));
snprintf(tmpname, sizeof(tmpname), "%s/%s.%d",
firstnonnull(getenv("TMPDIR"), "/tmp"),
firstnonnull(program_invocation_short_name, "unknown"), getpid());
ASSERT_NE(-1, (fd = open(tmpname, O_RDWR | O_CREAT | O_TRUNC, 0644)));
rngset(junkdata, 512, lemur64, -1);
for (i = 0; i < 5 * 1024 * 1024 / 512; ++i) {
ASSERT_EQ(512, write(fd, junkdata, 512));
}
close(fd);
unlink(tmpname);
_Exit(1);
}
EXPECT_TRUE(WIFEXITED(wstatus));
EXPECT_FALSE(WIFSIGNALED(wstatus));
EXPECT_EQ(0, WEXITSTATUS(wstatus));
EXPECT_EQ(0, WTERMSIG(wstatus));
}
int SetMemoryLimit(size_t n) {
struct rlimit rlim = {0};
getrlimit(RLIMIT_AS, &rlim);
rlim.rlim_cur = n;
rlim.rlim_max = n;
return setrlimit(RLIMIT_AS, &rlim);
}
TEST(setrlimit, testMemoryLimit) {
char *p;
bool gotsome;
int i, wstatus;
ASSERT_NE(-1, (wstatus = xspawn(0)));
if (wstatus == -2) {
ASSERT_EQ(0, SetMemoryLimit(MEM));
for (gotsome = false, i = 0; i < (MEM * 2) / getpagesize(); ++i) {
p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (p != MAP_FAILED) {
gotsome = true;
} else {
ASSERT_TRUE(gotsome);
ASSERT_EQ(ENOMEM, errno);
_Exit(0);
}
rngset(p, getpagesize(), lemur64, -1);
}
_Exit(1);
}
EXPECT_TRUE(WIFEXITED(wstatus));
EXPECT_FALSE(WIFSIGNALED(wstatus));
EXPECT_EQ(0, WEXITSTATUS(wstatus));
EXPECT_EQ(0, WTERMSIG(wstatus));
}
TEST(setrlimit, testVirtualMemoryLimit) {
char *p;
int i, wstatus;
ASSERT_NE(-1, (wstatus = xspawn(0)));
if (wstatus == -2) {
ASSERT_EQ(0, setrlimit(RLIMIT_AS, &(struct rlimit){MEM, MEM}));
for (i = 0; i < (MEM * 2) / getpagesize(); ++i) {
if ((p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)) ==
MAP_FAILED) {
ASSERT_EQ(ENOMEM, errno);
_Exit(0);
}
rngset(p, getpagesize(), lemur64, -1);
}
_Exit(1);
}
EXPECT_TRUE(WIFEXITED(wstatus));
EXPECT_FALSE(WIFSIGNALED(wstatus));
EXPECT_EQ(0, WEXITSTATUS(wstatus));
EXPECT_EQ(0, WTERMSIG(wstatus));
}
TEST(setrlimit, testDataMemoryLimit) {
char *p;
int i, wstatus;
if (IsXnu())
return; /* doesn't work on darwin */
if (IsNetbsd())
return; /* doesn't work on netbsd */
if (IsFreebsd())
return; /* doesn't work on freebsd */
if (IsLinux())
return; /* doesn't work on gnu/systemd */
if (IsWindows())
return; /* of course it doesn't work on windows */
ASSERT_NE(-1, (wstatus = xspawn(0)));
if (wstatus == -2) {
ASSERT_EQ(0, setrlimit(RLIMIT_DATA, &(struct rlimit){MEM, MEM}));
for (i = 0; i < (MEM * 2) / getpagesize(); ++i) {
p = sys_mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)
.addr;
if (p == MAP_FAILED) {
ASSERT_EQ(ENOMEM, errno);
_Exit(0);
}
rngset(p, getpagesize(), lemur64, -1);
}
_Exit(1);
}
EXPECT_TRUE(WIFEXITED(wstatus));
EXPECT_FALSE(WIFSIGNALED(wstatus));
EXPECT_EQ(0, WEXITSTATUS(wstatus));
EXPECT_EQ(0, WTERMSIG(wstatus));
}
TEST(setrlimit, testPhysicalMemoryLimit) {
/* RLIMIT_RSS doesn't work on gnu/systemd */
/* RLIMIT_RSS doesn't work on darwin */
/* RLIMIT_RSS doesn't work on freebsd */
/* RLIMIT_RSS doesn't work on netbsd */
/* RLIMIT_RSS doesn't work on openbsd */
/* of course it doesn't work on windows */
}
wontreturn void OnVfork(void *ctx) {
struct rlimit *rlim;
rlim = ctx;
rlim->rlim_cur -= 1;
ASSERT_EQ(0, getrlimit(RLIMIT_CPU, rlim));
_Exit(0);
}
TEST(setrlimit, isVforkSafe) {
int ws;
struct rlimit rlim[2];
if (IsWindows())
return; /* of course it doesn't work on windows */
ASSERT_EQ(0, getrlimit(RLIMIT_CPU, rlim));
ASSERT_NE(-1, (ws = xvspawn(OnVfork, rlim, 0)));
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_FALSE(WIFSIGNALED(ws));
EXPECT_EQ(0, WEXITSTATUS(ws));
EXPECT_EQ(0, WTERMSIG(ws));
ASSERT_EQ(0, getrlimit(RLIMIT_CPU, rlim + 1));
EXPECT_EQ(rlim[0].rlim_cur, rlim[1].rlim_cur);
EXPECT_EQ(rlim[0].rlim_max, rlim[1].rlim_max);
}

View file

@ -59,7 +59,7 @@ void CrashHandler(int sig, siginfo_t *si, void *ctx) {
kprintf("kprintf avoids overflowing %G si_addr=%lx sp=%lx\n", si->si_signo,
si->si_addr, ((ucontext_t *)ctx)->uc_mcontext.SP);
smashed_stack = true;
// unassert(__is_stack_overflow(si, ctx)); // fuzzy with main thread
unassert(__is_stack_overflow(si, ctx));
longjmp(recover, 123);
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigaltstack.h"
#include "libc/calls/struct/siginfo.h"
@ -41,9 +40,8 @@
volatile bool smashed_stack;
void CrashHandler(int sig, siginfo_t *si, void *ctx) {
void CrashHandler(int sig) {
smashed_stack = true;
unassert(__is_stack_overflow(si, ctx));
pthread_exit((void *)123L);
}
@ -65,7 +63,7 @@ void *MyPosixThread(void *arg) {
ASSERT_SYS(0, 0, sigaltstack(&ss, 0));
sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = CrashHandler;
sa.sa_handler = CrashHandler;
sigaction(SIGBUS, &sa, 0);
sigaction(SIGSEGV, &sa, 0);
exit(StackOverflow(1));

View file

@ -16,28 +16,22 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/ss.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#include <cosmo.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
/**
* stack overflow test #5
* - make sure fork() preserves sigaltstack()
* - make sure fork() preserves guard page status
* stack overflow recovery technique #5
* use the cosmo posix threads extensions
*/
jmp_buf recover;
sig_atomic_t smashed_stack;
void CrashHandler(int sig, siginfo_t *si, void *ctx) {
unassert(__is_stack_overflow(si, ctx));
longjmp(recover, 123);
void CrashHandler(int sig) {
smashed_stack = true;
pthread_exit(0);
}
int StackOverflow(int d) {
@ -50,40 +44,42 @@ int StackOverflow(int d) {
}
void *MyPosixThread(void *arg) {
int pid;
unassert(__get_tls()->tib_sigstack_addr);
unassert((pid = fork()) != -1);
if (!pid) {
int jumpcode;
if (!(jumpcode = setjmp(recover))) {
StackOverflow(1);
_Exit(1);
}
unassert(123 == jumpcode);
} else {
int ws;
unassert(wait(&ws) != -1);
unassert(!ws);
pthread_exit(0);
}
exit(StackOverflow(1));
return 0;
}
int main() {
struct sigaction sa;
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = CrashHandler;
unassert(!sigaction(SIGBUS, &sa, 0));
unassert(!sigaction(SIGSEGV, &sa, 0));
// choose the most dangerously small size possible
size_t sigstacksize = sysconf(_SC_MINSIGSTKSZ) + 2048;
pthread_t th;
// setup signal handler
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK;
sa.sa_handler = CrashHandler;
if (sigaction(SIGBUS, &sa, 0))
return 1;
if (sigaction(SIGSEGV, &sa, 0))
return 2;
// create thread with signal stack
pthread_t id;
pthread_attr_t attr;
unassert(!pthread_attr_init(&attr));
unassert(!pthread_attr_setguardsize(&attr, getpagesize()));
unassert(!pthread_attr_setsigaltstacksize_np(&attr, SIGSTKSZ));
unassert(!pthread_create(&th, &attr, MyPosixThread, 0));
unassert(!pthread_attr_destroy(&attr));
unassert(!pthread_join(th, 0));
if (pthread_attr_init(&attr))
return 3;
if (pthread_attr_setguardsize(&attr, getpagesize()))
return 4;
if (pthread_attr_setsigaltstacksize_np(&attr, sigstacksize))
return 5;
if (pthread_create(&id, &attr, MyPosixThread, 0))
return 6;
if (pthread_attr_destroy(&attr))
return 7;
if (pthread_join(id, 0))
return 8;
if (!smashed_stack)
return 9;
CheckForMemoryLeaks();
}

View file

@ -116,42 +116,6 @@ TEST(mmap, fixedTaken) {
EXPECT_SYS(0, 0, munmap(p, 1));
}
TEST(mmap, anon_rw_to_rx) {
char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, 1, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
ASSERT_SYS(0, 0, mprotect(p, 1, PROT_READ | PROT_EXEC));
ASSERT_SYS(0, 0, munmap(p, 1));
}
TEST(mmap, anon_rw_fork_to_rx) {
char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, 1, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
SPAWN(fork);
ASSERT_SYS(0, 0, mprotect(p, 1, PROT_READ | PROT_EXEC));
EXITS(0);
ASSERT_SYS(0, 0, munmap(p, 1));
}
TEST(mmap, anon_r_to_rw) {
char *p;
ASSERT_NE(MAP_FAILED,
(p = mmap(0, 1, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
ASSERT_SYS(0, 0, mprotect(p, 1, PROT_READ | PROT_WRITE));
ASSERT_SYS(0, 0, munmap(p, 1));
}
TEST(mmap, anon_r_fork_to_rw) {
char *p;
ASSERT_NE(MAP_FAILED,
(p = mmap(0, 1, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
SPAWN(fork);
ASSERT_SYS(0, 0, mprotect(p, 1, PROT_READ | PROT_WRITE));
EXITS(0);
ASSERT_SYS(0, 0, munmap(p, 1));
}
TEST(mmap, hint) {
char *p;

View file

@ -31,7 +31,6 @@ TEST_LIBC_PROC_DIRECTDEPS = \
LIBC_NT_KERNEL32 \
LIBC_PROC \
LIBC_RUNTIME \
LIBC_LOG \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSV \
@ -91,7 +90,6 @@ o/$(MODE)/test/libc/proc/execve_test.dbg: \
o/$(MODE)/test/libc/proc/execve_test.o \
o/$(MODE)/test/libc/calls/life-nomod.zip.o \
o/$(MODE)/test/libc/proc/execve_test_prog1.zip.o \
o/$(MODE)/test/libc/proc/execve_test_prog2.zip.o \
o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \
o/$(MODE)/test/libc/mem/prog/sock.elf.zip.o \
o/$(MODE)/test/libc/proc/proc.pkg \
@ -121,7 +119,6 @@ o/$(MODE)/test/libc/proc/life.dbg: \
o/$(MODE)/test/libc/proc/life.zip.o \
o/$(MODE)/test/libc/proc/execve_test_prog1.zip.o \
o/$(MODE)/test/libc/proc/execve_test_prog2.zip.o \
o/$(MODE)/test/libc/proc/life-pe.zip.o: private \
ZIPOBJ_FLAGS += \
-B

View file

@ -53,12 +53,12 @@ TEST(execve, testArgPassing) {
char ibuf[12], buf[8];
const char *prog = "./execve_test_prog1";
testlib_extract("/zip/execve_test_prog1", prog, 0755);
testlib_extract("/zip/execve_test_prog2", "execve_test_prog2", 0755);
for (i = 0; i < N; ++i) {
FormatInt32(ibuf, i);
GenBuf(buf, i);
SPAWN(vfork);
execl(prog, prog, "-", ibuf, buf, NULL);
execve(prog, (char *const[]){(char *)prog, "-", ibuf, buf, 0},
(char *const[]){0});
kprintf("execve failed: %m\n");
EXITS(0);
}

View file

@ -18,7 +18,6 @@
*/
#include "libc/calls/calls.h"
#include "libc/fmt/conv.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
void GenBuf(char buf[8], int x) {
@ -41,15 +40,5 @@ int main(int argc, char *argv[]) {
tinyprint(2, "error: buf check failed\n", NULL);
return 10;
}
const char *prog = "./execve_test_prog2";
if (!fork()) {
execl(prog, prog, NULL);
_Exit(127);
}
int ws;
if (wait(&ws) == -1)
return 30;
if (ws)
return 31;
return 0;
}

View file

@ -151,32 +151,6 @@ TEST(fork, preservesTlsMemory) {
EXITS(0);
}
TEST(fork, privateExtraPageData_getsCopiedByFork) {
char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, 1, PROT_WRITE | PROT_READ,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)));
p[0] = 1;
p[1] = 2;
SPAWN(fork);
ASSERT_EQ(1, p[0]);
ASSERT_EQ(2, p[1]);
EXITS(0);
ASSERT_SYS(0, 0, munmap(p, 1));
}
TEST(fork, sharedExtraPageData_getsResurrectedByFork) {
char *p;
ASSERT_NE(MAP_FAILED, (p = mmap(0, 1, PROT_WRITE | PROT_READ,
MAP_ANONYMOUS | MAP_SHARED, -1, 0)));
p[0] = 1;
p[1] = 2;
SPAWN(fork);
ASSERT_EQ(1, p[0]);
ASSERT_EQ(2, p[1]);
EXITS(0);
ASSERT_SYS(0, 0, munmap(p, 1));
}
#define CHECK_TERMSIG \
if (WIFSIGNALED(ws)) { \
kprintf("%s:%d: error: forked life subprocess terminated with %G\n", \

View file

@ -32,7 +32,7 @@
#include <unistd.h>
// clang-format off
// sh -c '.cosmocc/current/bin/make -j8 V=1 o//test/posix/sigchld_test.runs'
// sh -c 'build/bootstrap/make -j8 V=1 o//test/posix/sigchld_test.runs'
// clang-format on
void Assert(const char *file, int line, bool ok) {

View file

@ -85,13 +85,6 @@
" executable will self-modify its header on\n" \
" the first run, to use the platform format\n" \
"\n" \
" -k KERNEL test for maching kernel name [repeatable]\n" \
" when set, the shell script for subsequent\n" \
" loader executables will check if uname -s\n" \
" output matches the kernel string, only if\n" \
" the loader executable architecture is not\n" \
" an architecture in the input binary list\n" \
"\n" \
" -M PATH bundle ape loader source code file for m1\n" \
" processors running the xnu kernel so that\n" \
" it can be compiled on the fly by xcode\n" \
@ -220,7 +213,6 @@ struct Loader {
char *ddarg_size1;
char *ddarg_skip2;
char *ddarg_size2;
const char *kernel;
};
struct Loaders {
@ -252,7 +244,6 @@ static struct Inputs inputs;
static char ape_heredoc[15];
static enum Strategy strategy;
static struct Loaders loaders;
static const char *loader_kernel;
static const char *custom_sh_code;
static bool force_bypass_binfmt_misc;
static bool generate_debuggable_binary;
@ -988,19 +979,13 @@ static void AddLoader(const char *path) {
if (loaders.n == ARRAYLEN(loaders.p)) {
Die(prog, "too many loaders");
}
struct Loader *loader = &loaders.p[loaders.n++];
loader->path = path;
loader->kernel = loader_kernel;
}
static void SetLoaderKernel(const char *kernel) {
loader_kernel = kernel;
loaders.p[loaders.n++].path = path;
}
static void GetOpts(int argc, char *argv[]) {
int opt, bits;
bool got_support_vector = false;
while ((opt = getopt(argc, argv, "hvgsGBo:l:k:S:M:V:")) != -1) {
while ((opt = getopt(argc, argv, "hvgsGBo:l:S:M:V:")) != -1) {
switch (opt) {
case 'o':
outpath = optarg;
@ -1024,10 +1009,6 @@ static void GetOpts(int argc, char *argv[]) {
HashInputString("-l");
AddLoader(optarg);
break;
case 'k':
HashInputString("-k");
SetLoaderKernel(optarg);
break;
case 'S':
HashInputString("-S");
HashInputString(optarg);
@ -1649,28 +1630,6 @@ static char *GenerateScriptIfMachine(char *p, struct Input *in) {
}
}
static char *GenerateScriptIfLoaderMachine(char *p, struct Loader *loader) {
if (loader->machine == EM_NEXGEN32E) {
p = stpcpy(p, "if [ \"$m\" = x86_64 ] || [ \"$m\" = amd64 ]");
} else if (loader->machine == EM_AARCH64) {
p = stpcpy(p, "if [ \"$m\" = aarch64 ] || [ \"$m\" = arm64 ] || [ \"$m\" = evbarm ]");
} else if (loader->machine == EM_PPC64) {
p = stpcpy(p, "if [ \"$m\" = ppc64le ]");
} else if (loader->machine == EM_MIPS) {
p = stpcpy(p, "if [ \"$m\" = mips64 ]");
} else {
Die(loader->path, "unsupported cpu architecture");
}
if (loader->kernel) {
p = stpcpy(p, " && [ \"$k\" = ");
p = stpcpy(p, loader->kernel);
p = stpcpy(p, " ]");
}
return stpcpy(p, "; then\n");
}
static char *FinishGeneratingDosHeader(char *p) {
p = WRITE16LE(p, 0x1000); // 10: MZ: lowers upper bound load / 16
p = WRITE16LE(p, 0xf800); // 12: MZ: roll greed on bss
@ -1920,16 +1879,8 @@ int main(int argc, char *argv[]) {
for (j = i + 1; j < loaders.n; ++j) {
if (loaders.p[i].os == loaders.p[j].os &&
loaders.p[i].machine == loaders.p[j].machine) {
if (!loaders.p[i].kernel && !loaders.p[j].kernel) {
Die(prog, "multiple ape loaders specified for the same platform");
}
if (loaders.p[i].kernel != NULL &&
loaders.p[j].kernel != NULL &&
strcmp(loaders.p[i].kernel, loaders.p[j].kernel) == 0) {
Die(prog, "multiple ape loaders specified for the same platform "
"with matching kernels");
}
}
}
}
@ -2239,36 +2190,6 @@ int main(int argc, char *argv[]) {
gotsome = true;
}
}
// extract the ape loader for non-input architectures
// if the user requested a host kernel check, get the host kernel
if (loader_kernel) {
p = stpcpy(p, "k=$(uname -s 2>/dev/null) || k=unknown\n");
}
for (i = 0; i < loaders.n; ++i) {
struct Loader *loader = loaders.p + i;
if (loader->used) {
continue;
}
loader->used = true;
p = GenerateScriptIfLoaderMachine(p, loader);
p = stpcpy(p, "mkdir -p \"${t%/*}\" ||exit\n"
"dd if=\"$o\"");
p = stpcpy(p, " skip=");
loader->ddarg_skip2 = p;
p = GenerateDecimalOffsetRelocation(p);
p = stpcpy(p, " count=");
loader->ddarg_size2 = p;
p = GenerateDecimalOffsetRelocation(p);
p = stpcpy(p, " bs=1 2>/dev/null | gzip -dc >\"$t.$$\" ||exit\n"
"chmod 755 \"$t.$$\" ||exit\n"
"mv -f \"$t.$$\" \"$t\" ||exit\n");
p = stpcpy(p, "exec \"$t\" \"$o\" \"$@\"\n"
"fi\n");
gotsome = true;
}
// close if-statements
if (inputs.n && (support_vector & _HOSTXNU)) {
if (!gotsome) {
p = stpcpy(p, "true\n");

View file

@ -1,283 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2024 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/elf/def.h"
#include "libc/elf/elf.h"
#include "libc/elf/scalar.h"
#include "libc/elf/struct/ehdr.h"
#include "libc/elf/struct/phdr.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/likely.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.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 "third_party/getopt/getopt.internal.h"
#define VERSION \
"renamestr v0.1\n" \
"https://github.com/jart/cosmopolitan\n"
#define MANUAL \
" -f FROM -t TO INPUT \n" \
"\n" \
"DESCRIPTION\n" \
"\n" \
" in-place string replacement in ELF binary .rodata\n" \
"\n" \
" this program may be used to replace strings in the\n" \
" .rodata sections of ELF binaries, in-place.\n" \
"\n" \
"FLAGS\n" \
"\n" \
" -h show usage\n" \
"\n" \
" -v show version\n" \
"\n" \
" -f FROM source string to replace\n" \
"\n" \
" -t TO target string replacement. must be shorter\n" \
" than FROM string for replacement to work\n" \
"\n" \
" INPUT ELF binary containing strings to replace\n" \
"\n"
static const char *prog;
static const char *exepath;
static Elf64_Shdr *rodata;
static char *rostart;
static char *roend;
static int exefd;
static wontreturn void Die(const char *thing, const char *reason) {
tinyprint(2, thing, ": ", reason, "\n", NULL);
exit(1);
}
static wontreturn void DieSys(const char *thing) {
perror(thing);
exit(1);
}
static wontreturn void ShowUsage(int rc, int fd) {
tinyprint(fd, "USAGE\n\n ", prog, MANUAL, NULL);
exit(rc);
}
static void Pwrite(const void *data, size_t size, uint64_t offset) {
ssize_t rc;
const char *p, *e;
for (p = data, e = p + size; p < e; p += (size_t)rc, offset += (size_t)rc) {
if ((rc = pwrite(exefd, p, e - p, offset)) == -1) {
DieSys(exepath);
}
}
}
struct String {
const char *str;
size_t len;
};
struct Param {
struct String from;
struct String to;
int count;
char *roloc;
};
struct Params {
int n;
struct Param p[4];
};
static struct Params params;
static void GetOpts(int argc, char *argv[]) {
int opt;
bool partial = false;
params.n = 0;
struct Param *param;
while ((opt = getopt(argc, argv, "hvf:t:")) != -1) {
if (params.n >= ARRAYLEN(params.p)) {
param = NULL;
} else {
param = &(params.p[params.n]);
}
switch (opt) {
case 'f':
if (!param) {
Die(prog, "too many replacements provided");
}
if (param->from.str) {
Die(prog, "from string already provided");
}
param->from.str = optarg;
param->from.len = strlen(optarg);
partial = !partial;
break;
case 't':
if (!param) {
Die(prog, "too many replacements provided");
}
if (param->to.str) {
Die(prog, "to string already provided");
}
param->to.str = optarg;
param->to.len = strlen(optarg);
partial = !partial;
break;
case 'v':
tinyprint(0, VERSION, NULL);
exit(0);
case 'h':
ShowUsage(0, 1);
default:
ShowUsage(1, 2);
}
if (param->from.str && param->to.str) {
if (param->from.len < param->to.len) {
Die(prog, "to.str longer than from.str, cannot replace");
}
params.n++;
}
}
if (params.n == 0) {
Die(prog, "no replacements provided");
}
if (partial) {
Die(prog, "partial replacement provided");
}
if (optind == argc) {
Die(prog, "missing input argument");
}
if (optind != argc - 1) {
Die(prog, "too many args");
}
exepath = argv[optind];
}
struct Input {
union {
char *map;
Elf64_Ehdr *elf;
unsigned char *umap;
};
size_t size;
const char *path;
};
static struct Input input;
static void OpenInput(const char *path) {
int fd;
if ((fd = open(path, O_RDWR)) == -1)
DieSys(path);
if ((input.size = lseek(fd, 0, SEEK_END)) == -1)
DieSys(path);
input.path = path;
input.map = mmap(0, input.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (input.map == MAP_FAILED)
DieSys(path);
if (!IsElf64Binary(input.elf, input.size))
Die(path, "not an elf64 binary");
exefd = fd;
}
static void ReplaceString(struct Param *param) {
size_t len;
char *x = (char *)memchr(param->roloc, 0, roend - param->roloc);
memmove(param->roloc, param->to.str, param->to.len);
if (UNLIKELY(x == NULL)) {
len = roend - param->roloc;
memmove(param->roloc + param->to.len, param->roloc + param->from.len,
len - param->from.len);
} else {
len = x - param->roloc;
memmove(param->roloc + param->to.len, param->roloc + param->from.len,
len + 1 - param->from.len);
}
param->roloc += param->to.len;
}
int main(int argc, char *argv[]) {
#ifdef MODE_DBG
ShowCrashReports();
#endif
prog = argv[0];
if (!prog)
prog = "renamestr";
GetOpts(argc, argv);
OpenInput(exepath);
rodata = FindElfSectionByName(
input.elf, input.size,
GetElfSectionNameStringTable(input.elf, input.size), ".rodata");
if (!rodata)
Die(exepath, "doesn't have .rodata");
rostart = GetElfSectionAddress(input.elf, input.size, rodata);
if (!rostart)
Die(prog, "could not get to start of .rodata");
roend = rostart + rodata->sh_size;
#ifdef MODE_DBG
kprintf("elf file to process: %s\n", exepath);
kprintf("file size is %ld\n", input.size);
#endif
for (int i = 0; i < params.n; ++i) {
struct Param *param = &(params.p[i]);
param->roloc = rostart;
param->count = 0;
#ifdef MODE_DBG
kprintf("need to replace '%s' with '%s'\n", param->from.str, param->to.str);
#endif
}
#define NEXT_ROLOC(z) \
memmem((z)->roloc, roend - (z)->roloc, (z)->from.str, (z)->from.len)
for (int i = 0; i < params.n; ++i) {
struct Param *param = &(params.p[i]);
for (param->roloc = NEXT_ROLOC(param); param->roloc != NULL;
param->roloc = NEXT_ROLOC(param)) {
ReplaceString(param);
param->count++;
}
}
#undef NEXT_ROLOC
Pwrite(input.map, input.size, 0);
if (close(exefd)) {
Die(prog, "unable to close file after writing");
}
for (int i = 0; i < params.n; ++i) {
struct Param *param = &(params.p[i]);
printf("'%s' -> '%s': %d replacements\n", param->from.str, param->to.str,
param->count);
}
return 0;
}

0
tool/cosmocc/bin/cosmoranlib Executable file → Normal file
View file

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