Bring back gc() function

Renaming gc() to _gc() was a mistake since the better thing to do is put
it behind the _COSMO_SOURCE macro. We need this change because I haven't
wanted to use my amazing garbage collector ever since we renamed it. You
now need to define _COSMO_SOURCE yourself when using amalgamation header
and cosmocc users need to pass the -mcosmo flag to get the gc() function

Some other issues relating to cancelation have been fixed along the way.
We're also now putting cosmocc in a folder named `.cosmocc` so it can be
more safely excluded by grep --exclude-dir=.cosmocc --exclude-dir=o etc.
This commit is contained in:
Justine Tunney 2024-01-08 10:07:35 -08:00
parent 6cb0354e19
commit a4b455185b
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
280 changed files with 1362 additions and 1407 deletions

View file

@ -88,12 +88,14 @@ static ssize_t GetDevUrandom(char *p, size_t n) {
int fd;
ssize_t rc;
BLOCK_SIGNALS;
BLOCK_CANCELATION;
fd = sys_openat(AT_FDCWD, "/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
if (fd != -1) {
rc = sys_read(fd, p, n);
} else {
rc = -1;
}
ALLOW_CANCELATION;
ALLOW_SIGNALS;
return rc;
}

View file

@ -53,7 +53,7 @@ int _mkstemp(char *, int);
* On newer Linux only (c. 2013) it's possible to turn the anonymous
* returned file back into a real file, by doing this:
*
* linkat(AT_FDCWD, _gc(xasprintf("/proc/self/fd/%d", fd)),
* linkat(AT_FDCWD, gc(xasprintf("/proc/self/fd/%d", fd)),
* AT_FDCWD, "real.txt", AT_SYMLINK_FOLLOW)
*
* On the New Technology, temporary files created by this function

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/stat.h"
@ -798,6 +799,7 @@ static void *dlopen_silicon(const char *path, int mode) {
void *cosmo_dlopen(const char *path, int mode) {
void *res;
BLOCK_SIGNALS;
BLOCK_CANCELATION;
if (IsWindows()) {
res = dlopen_nt(path, mode);
} else if (IsXnuSilicon()) {
@ -814,6 +816,7 @@ void *cosmo_dlopen(const char *path, int mode) {
} else {
res = 0;
}
ALLOW_CANCELATION;
ALLOW_SIGNALS;
STRACE("dlopen(%#s, %d) → %p% m", path, mode, res);
return res;

View file

@ -16,6 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/intrin/describebacktrace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/runtime.h"
@ -27,3 +29,14 @@ dontinstrument void __stracef(const char *fmt, ...) {
kvprintf(fmt, v);
va_end(v);
}
#if IsModeDbg()
void report_cancellation_point(void) {
kprintf("error: cancelable raw system call not annotated in wrapper\n"
"choice #1 use BLOCK_CANCELATION / ALLOW_CANCELATION\n"
"choice #2 use BEGIN_CANCELATION_POINT / END_CANCELATION_POINT\n"
"backtrace: %s\n",
DescribeBacktrace(__builtin_frame_address(0)));
__builtin_trap();
}
#endif

View file

@ -3,19 +3,19 @@
#include "libc/macros.internal.h"
COSMOPOLITAN_C_START_
__funline unsigned long __strlen(const char *s) {
forceinline unsigned long __strlen(const char *s) {
unsigned long n = 0;
while (*s++) ++n;
return n;
}
__funline int __strcmp(const char *l, const char *r) {
forceinline int __strcmp(const char *l, const char *r) {
size_t i = 0;
while (l[i] == r[i] && r[i]) ++i;
return (l[i] & 255) - (r[i] & 255);
}
__funline char *__stpcpy(char *d, const char *s) {
forceinline char *__stpcpy(char *d, const char *s) {
size_t i;
for (i = 0;; ++i) {
if (!(d[i] = s[i])) {
@ -24,7 +24,7 @@ __funline char *__stpcpy(char *d, const char *s) {
}
}
__funline void *__repstosb(void *di, char al, size_t cx) {
forceinline void *__repstosb(void *di, char al, size_t cx) {
#if defined(__x86__) && defined(__GNUC__) && !defined(__STRICT_ANSI__)
asm("rep stosb"
: "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di)
@ -40,7 +40,7 @@ __funline void *__repstosb(void *di, char al, size_t cx) {
#endif
}
__funline void *__repmovsb(void *di, const void *si, size_t cx) {
forceinline void *__repmovsb(void *di, const void *si, size_t cx) {
#if defined(__x86__) && defined(__GNUC__) && !defined(__STRICT_ANSI__)
asm("rep movsb"
: "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di)
@ -57,7 +57,7 @@ __funline void *__repmovsb(void *di, const void *si, size_t cx) {
#endif
}
__funline void *__mempcpy(void *d, const void *s, size_t n) {
forceinline void *__mempcpy(void *d, const void *s, size_t n) {
size_t i;
for (i = 0; i < n; ++i) {
((char *)d)[i] = ((const char *)s)[i];
@ -66,7 +66,7 @@ __funline void *__mempcpy(void *d, const void *s, size_t n) {
return (char *)d + n;
}
__funline char *__uintcpy(char p[hasatleast 21], uint64_t x) {
forceinline char *__uintcpy(char p[hasatleast 21], uint64_t x) {
char t;
size_t i, a, b;
i = 0;
@ -85,22 +85,22 @@ __funline char *__uintcpy(char p[hasatleast 21], uint64_t x) {
return p + i;
}
__funline char *__intcpy(char p[hasatleast 21], int64_t x) {
forceinline char *__intcpy(char p[hasatleast 21], int64_t x) {
if (x < 0) *p++ = '-', x = -(uint64_t)x;
return __uintcpy(p, x);
}
__funline char *__fixcpy(char p[hasatleast 17], uint64_t x, uint8_t k) {
forceinline char *__fixcpy(char p[hasatleast 17], uint64_t x, uint8_t k) {
while (k > 0) *p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15];
*p = '\0';
return p;
}
__funline char *__hexcpy(char p[hasatleast 17], uint64_t x) {
forceinline char *__hexcpy(char p[hasatleast 17], uint64_t x) {
return __fixcpy(p, x, ROUNDUP(x ? (__builtin_clzll(x) ^ 63) + 1 : 1, 4));
}
__funline const void *__memchr(const void *s, unsigned char c, size_t n) {
forceinline const void *__memchr(const void *s, unsigned char c, size_t n) {
size_t i;
for (i = 0; i < n; ++i) {
if (((const unsigned char *)s)[i] == c) {
@ -110,7 +110,7 @@ __funline const void *__memchr(const void *s, unsigned char c, size_t n) {
return 0;
}
__funline char *__strstr(const char *haystack, const char *needle) {
forceinline char *__strstr(const char *haystack, const char *needle) {
size_t i;
for (;;) {
for (i = 0;; ++i) {
@ -123,8 +123,8 @@ __funline char *__strstr(const char *haystack, const char *needle) {
return 0;
}
__funline char16_t *__strstr16(const char16_t *haystack,
const char16_t *needle) {
forceinline char16_t *__strstr16(const char16_t *haystack,
const char16_t *needle) {
size_t i;
for (;;) {
for (i = 0;; ++i) {
@ -137,21 +137,21 @@ __funline char16_t *__strstr16(const char16_t *haystack,
return 0;
}
__funline const char *__strchr(const char *s, unsigned char c) {
forceinline const char *__strchr(const char *s, unsigned char c) {
for (;; ++s) {
if ((*s & 255) == c) return s;
if (!*s) return 0;
}
}
__funline unsigned long __atoul(const char *p) {
forceinline unsigned long __atoul(const char *p) {
int c;
unsigned long x = 0;
while ('0' <= (c = *p++) && c <= '9') x *= 10, x += c - '0';
return x;
}
__funline long __atol(const char *p) {
forceinline long __atol(const char *p) {
int s = *p;
unsigned long x;
if (s == '-' || s == '+') ++p;
@ -160,7 +160,7 @@ __funline long __atol(const char *p) {
return x;
}
__funline void *__memset(void *a, int c, unsigned long n) {
forceinline void *__memset(void *a, int c, unsigned long n) {
char *d = a;
unsigned long i;
for (i = 0; i < n; ++i) {
@ -170,7 +170,7 @@ __funline void *__memset(void *a, int c, unsigned long n) {
return d;
}
__funline void *__memcpy(void *a, const void *b, unsigned long n) {
forceinline void *__memcpy(void *a, const void *b, unsigned long n) {
char *d = a;
unsigned long i;
const char *s = b;
@ -181,7 +181,7 @@ __funline void *__memcpy(void *a, const void *b, unsigned long n) {
return d;
}
__funline void *__memmove(void *a, const void *b, unsigned long n) {
forceinline void *__memmove(void *a, const void *b, unsigned long n) {
char *d = a;
unsigned long i;
const char *s = b;

View file

@ -95,6 +95,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
flockfile(f);
strace_enabled(-1);
BLOCK_SIGNALS;
BLOCK_CANCELATION;
// We display TIMESTAMP.MICROS normally. However, when we log multiple
// times in the same second, we display TIMESTAMP+DELTAMICROS instead.
@ -136,6 +137,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
_Exit(22);
}
ALLOW_CANCELATION;
ALLOW_SIGNALS;
strace_enabled(+1);
funlockfile(f);

View file

@ -39,7 +39,7 @@ static void TeardownGc(void) {
if (__tls_enabled) {
t = __get_tls();
if ((g = t->tib_garbages)) {
// exit() currently doesn't use _gclongjmp() like pthread_exit()
// exit() currently doesn't use gclongjmp() like pthread_exit()
// so we need to run the deferred functions manually.
while (g->i) {
--g->i;
@ -88,7 +88,7 @@ static void DeferFunction(struct StackFrame *frame, void *fn, void *arg) {
frame->addr = (intptr_t)__gc;
}
// the gnu extension macros for _gc / _defer point here
// the gnu extension macros for gc / defer point here
void __defer(void *rbp, void *fn, void *arg) {
struct StackFrame *f, *frame = rbp;
f = __builtin_frame_address(0);
@ -96,48 +96,3 @@ void __defer(void *rbp, void *fn, void *arg) {
unassert(PointerNotOwnedByParentStackFrame(f, frame, arg));
DeferFunction(frame, fn, arg);
}
/**
* Frees memory when function returns.
*
* This garbage collector overwrites the return address on the stack so
* that the RET instruction calls a trampoline which calls free(). It's
* loosely analogous to Go's defer keyword rather than a true cycle gc.
*
* const char *s = _gc(strdup("hello"));
* puts(s);
*
* This macro is equivalent to:
*
* _defer(free, ptr)
*
* @warning do not return a gc()'d pointer
* @warning do not realloc() with gc()'d pointer
* @warning be careful about static keyword due to impact of inlining
* @note you should use -fno-omit-frame-pointer
*/
void *(_gc)(void *thing) {
struct StackFrame *frame;
frame = __builtin_frame_address(0);
DeferFunction(frame->next, _weakfree, thing);
return thing;
}
/**
* Calls fn(arg) when function returns.
*
* This garbage collector overwrites the return address on the stack so
* that the RET instruction calls a trampoline which calls free(). It's
* loosely analogous to Go's defer keyword rather than a true cycle gc.
*
* @warning do not return a gc()'d pointer
* @warning do not realloc() with gc()'d pointer
* @warning be careful about static keyword due to impact of inlining
* @note you should use -fno-omit-frame-pointer
*/
void *(_defer)(void *fn, void *arg) {
struct StackFrame *frame;
frame = __builtin_frame_address(0);
DeferFunction(frame->next, fn, arg);
return arg;
}

View file

@ -1,25 +1,23 @@
#ifndef COSMOPOLITAN_LIBC_MEM_GC_H_
#define COSMOPOLITAN_LIBC_MEM_GC_H_
#ifdef _COSMO_SOURCE
COSMOPOLITAN_C_START_
void *_gc(void *);
void *_defer(void *, void *);
void __defer(void *, void *, void *);
void _gclongjmp(void *, int) dontthrow wontreturn;
void _gc_free(void *);
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
#define _gc(THING) _defer((void *)_gc_free, (void *)(THING))
#define _defer(FN, ARG) \
({ \
autotype(ARG) Arg = (ARG); \
/* prevent weird opts like tail call */ \
asm volatile("" : "+g"(Arg) : : "memory"); \
__defer(__builtin_frame_address(0), FN, Arg); \
asm volatile("" : "+g"(Arg) : : "memory"); \
Arg; \
void _gc_free(void *) libcesque;
void __defer(void *, void *, void *) libcesque;
void gclongjmp(void *, int) libcesque wontreturn;
#define gc(THING) defer((void *)_gc_free, (void *)(THING))
#define _gc(THING) defer((void *)_gc_free, (void *)(THING))
#define defer(FN, ARG) \
({ \
autotype(ARG) Arg = (ARG); \
/* prevent weird opts like tail call */ \
__asm__ volatile("" : "+g"(Arg) : : "memory"); \
__defer(__builtin_frame_address(0), FN, Arg); \
__asm__ volatile("" : "+g"(Arg) : : "memory"); \
Arg; \
})
#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) */
COSMOPOLITAN_C_END_
#endif /* _COSMO_SOURCE */
#endif /* COSMOPOLITAN_LIBC_MEM_GC_H_ */

View file

@ -1,9 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_MEM_GC_INTERNAL_H_
#define COSMOPOLITAN_LIBC_MEM_GC_INTERNAL_H_
#include "libc/mem/gc.h"
#define gc(THING) _gc(THING)
#define defer(FN, ARG) _defer(FN, ARG)
#define gclongjmp(JB, ARG) _gclongjmp(JB, ARG)
#endif /* COSMOPOLITAN_LIBC_MEM_GC_INTERNAL_H_ */

View file

@ -30,7 +30,7 @@
// @see examples/ctrlc.c
// @noreturn
.ftrace1
_gclongjmp:
gclongjmp:
.ftrace2
#ifdef __x86_64__
push %rbp
@ -65,4 +65,4 @@ _gclongjmp:
#else
#error "unsupported architecture"
#endif /* __x86_64__ */
.endfn _gclongjmp,globl
.endfn gclongjmp,globl

View file

@ -23,7 +23,7 @@
// @param rdi points to the jmp_buf
// @param esi is returned by setjmp() invocation (coerced nonzero)
// @noreturn
// @see _gclongjmp()
// @see gclongjmp()
// @see siglongjmp()
.ftrace1
longjmp:

View file

@ -25,10 +25,11 @@
// @returnstwice
// @assume system five nexgen32e abi conformant
// @note code built w/ microsoft abi compiler can't call this
// @see longjmp(), _gclongjmp()
// @see longjmp(), gclongjmp()
.ftrace1
setjmp:
.ftrace2
_setjmp:
#ifdef __x86_64__
lea 8(%rsp),%rax
mov %rax,(%rdi)
@ -60,5 +61,5 @@ setjmp:
#else
#error "unsupported architecture"
#endif
.endfn _setjmp,globl
.endfn setjmp,globl
.alias setjmp,_setjmp

View file

@ -58,6 +58,7 @@ static bool IsApeFile(const char *path) {
} else {
bool res = false;
BLOCK_SIGNALS;
BLOCK_CANCELATION;
int fd;
char buf[8];
int flags = O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC;
@ -65,6 +66,7 @@ static bool IsApeFile(const char *path) {
res = sys_pread(fd, buf, 8, 0, 0) == 8 && IsApeLoadable(buf);
sys_close(fd);
}
ALLOW_CANCELATION;
ALLOW_SIGNALS;
return res;
}

View file

@ -201,14 +201,14 @@ int fexecve(int fd, char *const argv[], char *const envp[]) {
}
if (!__isfdkind(fd, kFdZip)) {
bool memfdReq;
BEGIN_CANCELATION_POINT;
BLOCK_SIGNALS;
BLOCK_CANCELATION;
strace_enabled(-1);
memfdReq = ((rc = fcntl(fd, F_GETFD)) != -1) && (rc & FD_CLOEXEC) &&
IsAPEFd(fd);
strace_enabled(+1);
ALLOW_CANCELATION;
ALLOW_SIGNALS;
END_CANCELATION_POINT;
if (rc == -1) {
break;
} else if (!memfdReq) {
@ -221,13 +221,13 @@ int fexecve(int fd, char *const argv[], char *const envp[]) {
}
int newfd;
char *path = alloca(PATH_MAX);
BEGIN_CANCELATION_POINT;
BLOCK_SIGNALS;
BLOCK_CANCELATION;
strace_enabled(-1);
newfd = fd_to_mem_fd(fd, path);
strace_enabled(+1);
ALLOW_CANCELATION;
ALLOW_SIGNALS;
END_CANCELATION_POINT;
if (newfd == -1) {
break;
}
@ -242,13 +242,13 @@ int fexecve(int fd, char *const argv[], char *const envp[]) {
if (!savedErr) {
savedErr = errno;
}
BEGIN_CANCELATION_POINT;
BLOCK_SIGNALS;
BLOCK_CANCELATION;
strace_enabled(-1);
close(newfd);
strace_enabled(+1);
ALLOW_CANCELATION;
ALLOW_SIGNALS;
END_CANCELATION_POINT;
} while (0);
if (savedErr) {
errno = savedErr;

View file

@ -136,7 +136,7 @@ long __get_minsigstksz(void) pureconst;
void __get_main_stack(void **, size_t *, int *);
long __get_safe_size(long, long);
char *__get_tmpdir(void);
__funline int __trace_disabled(int x) {
forceinline int __trace_disabled(int x) {
return 0;
}
#ifndef FTRACE

View file

@ -96,7 +96,7 @@ extern char ape_stack_align[] __attribute__((__weak__));
* since it'll not only ensure stack overflows are detected, it
* will also trigger the stack to grow down safely.
*/
__funline void CheckLargeStackAllocation(void *p, ssize_t n) {
forceinline void CheckLargeStackAllocation(void *p, ssize_t n) {
for (; n > 0; n -= 4096) {
((char *)p)[n - 1] = 0;
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
@ -45,7 +46,9 @@ int getentropy(void *p, size_t n) {
rc = 0;
} else {
BLOCK_SIGNALS;
BLOCK_CANCELATION;
if (__getrandom(p, n, 0) != n) notpossible;
ALLOW_CANCELATION;
ALLOW_SIGNALS;
rc = 0;
}

View file

@ -42,7 +42,7 @@
* On newer Linux only (c. 2013) it's possible to turn the anonymous
* returned file back into a real file, by doing this:
*
* linkat(AT_FDCWD, _gc(xasprintf("/proc/self/fd/%d", fileno(f))),
* linkat(AT_FDCWD, gc(xasprintf("/proc/self/fd/%d", fileno(f))),
* AT_FDCWD, "real.txt", AT_SYMLINK_FOLLOW)
*
* On the New Technology, temporary files created by this function

View file

@ -116,11 +116,11 @@ void _pthread_unref(struct PosixThread *);
void _pthread_unwind(struct PosixThread *);
void _pthread_zombify(struct PosixThread *);
__funline pureconst struct PosixThread *_pthread_self(void) {
forceinline pureconst struct PosixThread *_pthread_self(void) {
return (struct PosixThread *)__get_tls()->tib_pthread;
}
__funline void _pthread_ref(struct PosixThread *pt) {
forceinline void _pthread_ref(struct PosixThread *pt) {
atomic_fetch_add_explicit(&pt->pt_refs, 1, memory_order_relaxed);
}

View file

@ -291,7 +291,7 @@ static errno_t _pthread_cancel_everyone(void) {
* Consider using Cosmopolitan Libc's garbage collector since it will be
* executed when a thread exits due to a cancelation.
*
* void *p = _gc(malloc(123));
* void *p = gc(malloc(123));
* read(0, p, 123);
*
* It's possible to put a thread in asynchronous cancelation mode with

View file

@ -84,8 +84,8 @@ void _pthread_unkey(struct CosmoTib *tib) {
* the callback function that was supplied to pthread_create(). This may
* be used if the thread wishes to exit at any other point in the thread
* lifecycle, in which case this function is responsible for ensuring we
* invoke _gc(), _defer(), and pthread_cleanup_push() callbacks, as well
* as pthread_key_create() destructors.
* invoke gc(), _defer(), and pthread_cleanup_push() callbacks, and also
* pthread_key_create() destructors.
*
* If the current thread is an orphaned thread, or is the main thread
* when no other threads were created, then this will terminated your

View file

@ -65,7 +65,7 @@ void __set_tls(struct CosmoTib *);
*
* This can't be used in privileged functions.
*/
__funline pureconst struct CosmoTib *__get_tls(void) {
forceinline pureconst struct CosmoTib *__get_tls(void) {
#ifdef __chibicc__
return 0;
#elif __x86_64__

View file

@ -11,7 +11,7 @@ COSMOPOLITAN_C_START_
* This should be favored over __get_tls() for .privileged code that
* can't be self-modified by __enable_tls().
*/
__funline struct CosmoTib *__get_tls_privileged(void) {
forceinline struct CosmoTib *__get_tls_privileged(void) {
char *tib, *lin = (char *)0x30;
if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd() || IsMetal()) {
if (!__tls_morphed) {
@ -28,14 +28,14 @@ __funline struct CosmoTib *__get_tls_privileged(void) {
return (struct CosmoTib *)tib;
}
__funline struct CosmoTib *__get_tls_win32(void) {
forceinline struct CosmoTib *__get_tls_win32(void) {
char *tib, *lin = (char *)0x30;
asm("mov\t%%gs:(%1),%0" : "=a"(tib) : "r"(lin) : "memory");
tib = *(char **)(tib + 0x1480 + __tls_index * 8);
return (struct CosmoTib *)tib;
}
__funline void __set_tls_win32(void *tls) {
forceinline void __set_tls_win32(void *tls) {
asm("mov\t%1,%%gs:%0" : "=m"(*((long *)0x1480 + __tls_index)) : "r"(tls));
}

View file

@ -17,7 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/libgen.h"
#include "libc/mem/gc.internal.h"
#include "libc/mem/gc.h"
#include "libc/mem/mem.h"
#include "libc/x/x.h"