mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-30 06:20:28 +00:00
Improve memory safety
This commit makes numerous refinements to cosmopolitan memory handling. The default stack size has been reduced from 2mb to 128kb. A new macro is now provided so you can easily reconfigure the stack size to be any value you want. Work around the breaking change by adding to your main: STATIC_STACK_SIZE(0x00200000); // 2mb stack If you're not sure how much stack you need, then you can use: STATIC_YOINK("stack_usage_logging"); After which you can `sort -nr o/$MODE/stack.log`. Based on the unit test suite, nothing in the Cosmopolitan repository (except for Python) needs a stack size greater than 30kb. There are also new macros for detecting the size and address of the stack at runtime, e.g. GetStackAddr(). We also now support sigaltstack() so if you want to see nice looking crash reports whenever a stack overflow happens, you can put this in main(): ShowCrashReports(); Under `make MODE=dbg` and `make MODE=asan` the unit testing framework will now automatically print backtraces of memory allocations when things like memory leaks happen. Bugs are now fixed in ASAN global variable overrun detection. The memtrack and asan runtimes also handle edge cases now. The new tools helped to identify a few memory leaks, which are fixed by this change. This change should fix an issue reported in #288 with ARG_MAX limits. Fixing this doubled the performance of MKDEPS.COM and AR.COM yet again.
This commit is contained in:
parent
a0b39f886c
commit
226aaf3547
317 changed files with 6474 additions and 3993 deletions
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 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,18 +16,23 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
.source __FILE__
|
||||
#include "libc/mem/mem.h"
|
||||
|
||||
.initbss 202,_init_posix_memalign
|
||||
hook_posix_memalign:
|
||||
.quad 0
|
||||
.endobj hook_posix_memalign,globl,hidden
|
||||
.previous
|
||||
|
||||
.init.start 202,_init_posix_memalign
|
||||
.hidden dlposix_memalign
|
||||
ezlea dlposix_memalign,ax
|
||||
stosq
|
||||
yoink free
|
||||
.init.end 202,_init_posix_memalign
|
||||
/**
|
||||
* Same as memalign(a, n) but requires IS2POW(a).
|
||||
*
|
||||
* @param n number of bytes needed
|
||||
* @return memory address, or NULL w/ errno
|
||||
* @throw EINVAL if !IS2POW(a)
|
||||
* @see pvalloc()
|
||||
*/
|
||||
void *aligned_alloc(size_t a, size_t n) {
|
||||
if (IS2POW(a)) {
|
||||
return memalign(a, n);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
}
|
312
libc/mem/arena.c
312
libc/mem/arena.c
|
@ -22,37 +22,39 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/arena.h"
|
||||
#include "libc/mem/hook/hook.internal.h"
|
||||
#include "libc/nexgen32e/bsf.h"
|
||||
#include "libc/nexgen32e/bsr.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define BASE ((char *)0x30000000)
|
||||
#define LIMIT ((char *)0x50000000)
|
||||
|
||||
#define BASE 0x50000000
|
||||
#define SIZE 0x2ffe0000
|
||||
#define P(i) ((void *)(intptr_t)(i))
|
||||
#define EXCHANGE(HOOK, SLOT) \
|
||||
__arena_hook((intptr_t *)weaken(HOOK), (intptr_t *)(&(SLOT)))
|
||||
|
||||
static struct Arena {
|
||||
bool once;
|
||||
uint8_t depth;
|
||||
unsigned size;
|
||||
unsigned offset[16];
|
||||
size_t size;
|
||||
size_t depth;
|
||||
size_t offset[16];
|
||||
void (*free)(void *);
|
||||
void *(*malloc)(size_t);
|
||||
void *(*calloc)(size_t, size_t);
|
||||
void *(*memalign)(size_t, size_t);
|
||||
void *(*realloc)(void *, size_t);
|
||||
void *(*realloc_in_place)(void *, size_t);
|
||||
void *(*valloc)(size_t);
|
||||
void *(*pvalloc)(size_t);
|
||||
int (*malloc_trim)(size_t);
|
||||
size_t (*malloc_usable_size)(const void *);
|
||||
size_t (*bulk_free)(void *[], size_t);
|
||||
int (*malloc_trim)(size_t);
|
||||
} __arena;
|
||||
|
||||
static wontreturn void __arena_die(void) {
|
||||
|
@ -61,103 +63,202 @@ static wontreturn void __arena_die(void) {
|
|||
}
|
||||
|
||||
static wontreturn void __arena_not_implemented(void) {
|
||||
__printf("not implemented");
|
||||
assert(!"not implemented");
|
||||
__arena_die();
|
||||
}
|
||||
|
||||
static void __arena_free(void *p) {
|
||||
if (!p) return;
|
||||
forceinline void __arena_check(void) {
|
||||
assert(__arena.depth);
|
||||
assert((intptr_t)BASE + __arena.offset[__arena.depth - 1] <= (intptr_t)p &&
|
||||
(intptr_t)p < (intptr_t)BASE + __arena.offset[__arena.depth]);
|
||||
}
|
||||
|
||||
forceinline void __arena_check_pointer(void *p) {
|
||||
assert(BASE + __arena.offset[__arena.depth - 1] <= (uintptr_t)p &&
|
||||
(uintptr_t)p < BASE + __arena.offset[__arena.depth]);
|
||||
}
|
||||
|
||||
forceinline bool __arena_is_arena_pointer(void *p) {
|
||||
return BASE <= (uintptr_t)p && (uintptr_t)p < BASE + SIZE;
|
||||
}
|
||||
|
||||
forceinline size_t __arena_get_size(void *p) {
|
||||
return *(const size_t *)((const char *)p - sizeof(size_t));
|
||||
}
|
||||
|
||||
static void __arena_free(void *p) {
|
||||
__arena_check();
|
||||
if (p) {
|
||||
__arena_check_pointer(p);
|
||||
if (!(BASE <= (uintptr_t)p && (uintptr_t)p < BASE + SIZE)) {
|
||||
__arena.free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t __arena_bulk_free(void *p[], size_t n) {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (p[i]) __arena_free(p[i]);
|
||||
__arena_free(p[i]);
|
||||
p[i] = 0;
|
||||
}
|
||||
bzero(p, n * sizeof(void *));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static noinline bool __arena_grow(size_t offset, size_t request) {
|
||||
size_t greed;
|
||||
greed = __arena.size + 1;
|
||||
do {
|
||||
greed += greed >> 1;
|
||||
greed = ROUNDUP(greed, FRAMESIZE);
|
||||
} while (greed < offset + request);
|
||||
if (greed <= SIZE) {
|
||||
if (mmap(P(BASE + __arena.size), greed - __arena.size,
|
||||
PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED,
|
||||
-1, 0) != MAP_FAILED) {
|
||||
__arena.size = greed;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
enomem();
|
||||
}
|
||||
if (weaken(__oom_hook)) {
|
||||
weaken(__oom_hook)(request);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void *__arena_alloc(size_t a, size_t n) {
|
||||
size_t o;
|
||||
if (!n) n = 1;
|
||||
o = ROUNDUP(__arena.offset[__arena.depth] + sizeof(size_t), a);
|
||||
if (o + n >= n) {
|
||||
if (n <= sizeof(size_t)) {
|
||||
n = sizeof(size_t);
|
||||
} else {
|
||||
n = ROUNDUP(n, sizeof(size_t));
|
||||
}
|
||||
if (o + n <= SIZE) {
|
||||
if (UNLIKELY(o + n > __arena.size)) {
|
||||
if (!__arena_grow(o, n)) return 0;
|
||||
}
|
||||
__arena.offset[__arena.depth] = o + n;
|
||||
*(size_t *)(BASE + o - sizeof(size_t)) = n;
|
||||
return (void *)(BASE + o);
|
||||
}
|
||||
}
|
||||
enomem();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *__arena_malloc(size_t n) {
|
||||
char *ptr;
|
||||
size_t need, greed;
|
||||
assert(__arena.depth);
|
||||
if (!n) n = 1;
|
||||
if (n < LIMIT - BASE) {
|
||||
need = __arena.offset[__arena.depth] + n;
|
||||
need = ROUNDUP(need, __BIGGEST_ALIGNMENT__);
|
||||
if (UNLIKELY(need > __arena.size)) {
|
||||
greed = __arena.size + 1;
|
||||
do {
|
||||
greed += greed >> 1;
|
||||
greed = ROUNDUP(greed, FRAMESIZE);
|
||||
} while (need > greed);
|
||||
if (greed < LIMIT - BASE &&
|
||||
mmap(BASE + __arena.size, greed - __arena.size,
|
||||
PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED,
|
||||
-1, 0) != MAP_FAILED) {
|
||||
__arena.size = greed;
|
||||
__arena_check();
|
||||
return __arena_alloc(16, n);
|
||||
}
|
||||
|
||||
static void *__arena_calloc(size_t n, size_t z) {
|
||||
__arena_check();
|
||||
if (__builtin_mul_overflow(n, z, &n)) n = -1;
|
||||
return __arena_alloc(16, n);
|
||||
}
|
||||
|
||||
static void *__arena_memalign(size_t a, size_t n) {
|
||||
__arena_check();
|
||||
if (a <= sizeof(size_t)) {
|
||||
return __arena_alloc(8, n);
|
||||
} else {
|
||||
return __arena_alloc(2ul << bsrl(a - 1), n);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t __arena_malloc_usable_size(const void *p) {
|
||||
__arena_check();
|
||||
__arena_check_pointer(p);
|
||||
if (__arena_is_arena_pointer(p)) {
|
||||
return __arena_get_size(p);
|
||||
} else {
|
||||
return __arena.malloc_usable_size(p);
|
||||
}
|
||||
}
|
||||
|
||||
static void *__arena_realloc(void *p, size_t n) {
|
||||
char *q;
|
||||
size_t m, o, z;
|
||||
__arena_check();
|
||||
if (p) {
|
||||
__arena_check_pointer(p);
|
||||
if (__arena_is_arena_pointer(p)) {
|
||||
if (n) {
|
||||
if ((m = __arena_get_size(p)) >= n) {
|
||||
return p;
|
||||
} else if (n <= SIZE) {
|
||||
z = 2ul << bsrl(n - 1);
|
||||
if (__arena.offset[__arena.depth] - m == (o = (intptr_t)p - BASE)) {
|
||||
if (UNLIKELY(o + z > __arena.size)) {
|
||||
if (o + z <= SIZE) {
|
||||
if (!__arena_grow(o, z)) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
enomem();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
__arena.offset[__arena.depth] = o + z;
|
||||
*(size_t *)((char *)p - sizeof(size_t)) = z;
|
||||
return p;
|
||||
} else if ((q = __arena_alloc(1ul << bsfl((intptr_t)p), z))) {
|
||||
memmove(q, p, m);
|
||||
return q;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
enomem();
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return __arena.realloc(p, n);
|
||||
}
|
||||
} else {
|
||||
if (n <= 16) {
|
||||
n = 16;
|
||||
} else {
|
||||
n = 2ul << bsrl(n - 1);
|
||||
}
|
||||
return __arena_alloc(16, n);
|
||||
}
|
||||
}
|
||||
|
||||
static void *__arena_realloc_in_place(void *p, size_t n) {
|
||||
char *q;
|
||||
size_t m, z;
|
||||
__arena_check();
|
||||
if (p) {
|
||||
__arena_check_pointer(p);
|
||||
if (__arena_is_arena_pointer(p)) {
|
||||
if (n) {
|
||||
if ((m = __arena_get_size(p)) >= n) {
|
||||
return p;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return __arena.realloc_in_place(p, n);
|
||||
}
|
||||
ptr = BASE + __arena.offset[__arena.depth];
|
||||
__arena.offset[__arena.depth] = need;
|
||||
return ptr;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void *__arena_calloc(size_t n, size_t z) {
|
||||
if (__builtin_mul_overflow(n, z, &n)) n = -1;
|
||||
return __arena_malloc(n);
|
||||
}
|
||||
|
||||
static void *__arena_memalign(size_t a, size_t n) {
|
||||
if (a <= __BIGGEST_ALIGNMENT__) {
|
||||
return __arena_malloc(n);
|
||||
} else {
|
||||
__arena_not_implemented();
|
||||
}
|
||||
}
|
||||
|
||||
static void *__arena_realloc(void *p, size_t n) {
|
||||
if (p) {
|
||||
if (n) {
|
||||
__arena_not_implemented();
|
||||
} else {
|
||||
__arena_free(p);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return __arena_malloc(n);
|
||||
}
|
||||
}
|
||||
|
||||
static int __arena_malloc_trim(size_t n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *__arena_realloc_in_place(void *p, size_t n) {
|
||||
__arena_not_implemented();
|
||||
}
|
||||
|
||||
static void *__arena_valloc(size_t n) {
|
||||
__arena_not_implemented();
|
||||
}
|
||||
|
||||
static void *__arena_pvalloc(size_t n) {
|
||||
__arena_not_implemented();
|
||||
}
|
||||
|
||||
static size_t __arena_malloc_usable_size(const void *p) {
|
||||
__arena_not_implemented();
|
||||
}
|
||||
|
||||
static void __arena_hook(intptr_t *h, intptr_t *f) {
|
||||
intptr_t t;
|
||||
if (h) {
|
||||
|
@ -169,42 +270,32 @@ static void __arena_hook(intptr_t *h, intptr_t *f) {
|
|||
|
||||
static void __arena_install(void) {
|
||||
EXCHANGE(hook_free, __arena.free);
|
||||
EXCHANGE(hook_realloc, __arena.realloc);
|
||||
EXCHANGE(hook_realloc, __arena.realloc);
|
||||
EXCHANGE(hook_malloc, __arena.malloc);
|
||||
EXCHANGE(hook_calloc, __arena.calloc);
|
||||
EXCHANGE(hook_realloc, __arena.realloc);
|
||||
EXCHANGE(hook_memalign, __arena.memalign);
|
||||
EXCHANGE(hook_realloc_in_place, __arena.realloc_in_place);
|
||||
EXCHANGE(hook_valloc, __arena.valloc);
|
||||
EXCHANGE(hook_pvalloc, __arena.pvalloc);
|
||||
EXCHANGE(hook_malloc_trim, __arena.malloc_trim);
|
||||
EXCHANGE(hook_malloc_usable_size, __arena.malloc_usable_size);
|
||||
EXCHANGE(hook_bulk_free, __arena.bulk_free);
|
||||
EXCHANGE(hook_malloc_trim, __arena.malloc_trim);
|
||||
EXCHANGE(hook_realloc_in_place, __arena.realloc_in_place);
|
||||
EXCHANGE(hook_malloc_usable_size, __arena.malloc_usable_size);
|
||||
}
|
||||
|
||||
static void __arena_destroy(void) {
|
||||
if (__arena.depth) {
|
||||
__arena_install();
|
||||
}
|
||||
if (__arena.size) {
|
||||
munmap(BASE, __arena.size);
|
||||
}
|
||||
if (__arena.depth) __arena_install();
|
||||
if (__arena.size) munmap(P(BASE), __arena.size);
|
||||
bzero(&__arena, sizeof(__arena));
|
||||
}
|
||||
|
||||
static void __arena_init(void) {
|
||||
__arena.free = __arena_free;
|
||||
__arena.realloc = __arena_realloc;
|
||||
__arena.realloc = __arena_realloc;
|
||||
__arena.malloc = __arena_malloc;
|
||||
__arena.calloc = __arena_calloc;
|
||||
__arena.realloc = __arena_realloc;
|
||||
__arena.memalign = __arena_memalign;
|
||||
__arena.realloc_in_place = __arena_realloc_in_place;
|
||||
__arena.valloc = __arena_valloc;
|
||||
__arena.pvalloc = __arena_pvalloc;
|
||||
__arena.malloc_trim = __arena_malloc_trim;
|
||||
__arena.malloc_usable_size = __arena_malloc_usable_size;
|
||||
__arena.bulk_free = __arena_bulk_free;
|
||||
__arena.malloc_trim = __arena_malloc_trim;
|
||||
__arena.realloc_in_place = __arena_realloc_in_place;
|
||||
__arena.malloc_usable_size = __arena_malloc_usable_size;
|
||||
atexit(__arena_destroy);
|
||||
}
|
||||
|
||||
|
@ -215,24 +306,27 @@ void __arena_push(void) {
|
|||
}
|
||||
if (!__arena.depth) {
|
||||
__arena_install();
|
||||
} else if (__arena.depth == ARRAYLEN(__arena.offset) - 1) {
|
||||
__printf("too many arenas");
|
||||
__arena_die();
|
||||
} else {
|
||||
assert(__arena.depth < ARRAYLEN(__arena.offset) - 1);
|
||||
}
|
||||
__arena.offset[__arena.depth + 1] = __arena.offset[__arena.depth];
|
||||
++__arena.depth;
|
||||
}
|
||||
|
||||
void __arena_pop(void) {
|
||||
unsigned greed;
|
||||
assert(__arena.depth);
|
||||
bzero(BASE + __arena.offset[__arena.depth - 1],
|
||||
__arena.offset[__arena.depth] - __arena.offset[__arena.depth - 1]);
|
||||
size_t a, b, greed;
|
||||
__arena_check();
|
||||
if (!--__arena.depth) __arena_install();
|
||||
greed = __arena.offset[__arena.depth];
|
||||
a = __arena.offset[__arena.depth];
|
||||
b = __arena.offset[__arena.depth + 1];
|
||||
greed = a;
|
||||
greed += FRAMESIZE;
|
||||
greed <<= 1;
|
||||
if (__arena.size > greed) {
|
||||
munmap(BASE + greed, __arena.size - greed);
|
||||
munmap(P(BASE + greed), __arena.size - greed);
|
||||
__arena.size = greed;
|
||||
b = MIN(b, greed);
|
||||
a = MIN(b, a);
|
||||
}
|
||||
bzero(P(BASE + a), b - a);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/buffer.h"
|
||||
|
||||
/* TODO(jart): delete */
|
||||
|
||||
#define kGuard PAGESIZE
|
||||
#define kGrain FRAMESIZE
|
||||
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/bits/likely.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/gc.internal.h"
|
||||
#include "libc/runtime/gc.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
forceinline bool PointerNotOwnedByParentStackFrame(struct StackFrame *frame,
|
||||
struct StackFrame *parent,
|
||||
|
@ -31,14 +33,28 @@ forceinline bool PointerNotOwnedByParentStackFrame(struct StackFrame *frame,
|
|||
((intptr_t)ptr < (intptr_t)parent));
|
||||
}
|
||||
|
||||
static void __garbage_destroy(void) {
|
||||
if (weaken(free)) {
|
||||
weaken(free)(__garbage.p);
|
||||
}
|
||||
bzero(&__garbage, sizeof(__garbage));
|
||||
}
|
||||
|
||||
void __deferer(struct StackFrame *frame, void *fn, void *arg) {
|
||||
size_t n2;
|
||||
struct Garbage *p2;
|
||||
if (UNLIKELY(__garbage.i == __garbage.n)) {
|
||||
p2 = __garbage.p;
|
||||
n2 = __garbage.n + (__garbage.n >> 1);
|
||||
p2 = malloc(n2 * sizeof(*__garbage.p));
|
||||
memcpy(p2, __garbage.p, __garbage.n * sizeof(*__garbage.p));
|
||||
if (__garbage.p != __garbage.initmem) free(__garbage.p);
|
||||
if (__garbage.p != __garbage.initmem) {
|
||||
if (!weaken(realloc)) return;
|
||||
if (!(p2 = weaken(realloc)(p2, n2 * sizeof(*p2)))) return;
|
||||
} else {
|
||||
if (!weaken(malloc)) return;
|
||||
if (!(p2 = weaken(malloc)(n2 * sizeof(*p2)))) return;
|
||||
memcpy(p2, __garbage.p, __garbage.n * sizeof(*p2));
|
||||
atexit(__garbage_destroy);
|
||||
}
|
||||
__garbage.p = p2;
|
||||
__garbage.n = n2;
|
||||
}
|
||||
|
@ -59,11 +75,11 @@ void __deferer(struct StackFrame *frame, void *fn, void *arg) {
|
|||
* @return arg
|
||||
*/
|
||||
void __defer(struct StackFrame *frame, void *fn, void *arg) {
|
||||
struct StackFrame *f2;
|
||||
struct StackFrame *f;
|
||||
if (!arg) return;
|
||||
f2 = __builtin_frame_address(0);
|
||||
f = __builtin_frame_address(0);
|
||||
assert(__garbage.n);
|
||||
assert(f2->next == frame);
|
||||
assert(PointerNotOwnedByParentStackFrame(f2, frame, arg));
|
||||
assert(f->next == frame);
|
||||
assert(PointerNotOwnedByParentStackFrame(f, frame, arg));
|
||||
__deferer(frame, fn, arg);
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ extern void *(*hook_calloc)(size_t, size_t);
|
|||
extern void *(*hook_memalign)(size_t, size_t);
|
||||
extern void *(*hook_realloc)(void *, size_t);
|
||||
extern void *(*hook_realloc_in_place)(void *, size_t);
|
||||
extern void *(*hook_valloc)(size_t);
|
||||
extern void *(*hook_pvalloc)(size_t);
|
||||
extern int (*hook_malloc_trim)(size_t);
|
||||
extern size_t (*hook_malloc_usable_size)(const void *);
|
||||
extern size_t (*hook_bulk_free)(void *[], size_t);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
COSMOPOLITAN_C_START_
|
||||
|
||||
int PutEnvImpl(char *, bool) hidden;
|
||||
void __freeenv(void *) hidden;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
.source __FILE__
|
||||
|
||||
// Allocates uninitialized memory.
|
||||
//
|
||||
|
@ -32,9 +31,7 @@
|
|||
// fail. The maximum supported value of n differs across systems, but is
|
||||
// in all cases less than the maximum representable value of a size_t.
|
||||
//
|
||||
// @param rdi is number of bytes needed
|
||||
// @param rdi is number of bytes needed, coerced to 1+
|
||||
// @return new memory, or NULL w/ errno
|
||||
// @note malloc(0) → malloc(32)
|
||||
// @see dlmalloc()
|
||||
malloc: jmp *hook_malloc(%rip)
|
||||
.endfn malloc,globl
|
||||
|
|
|
@ -15,15 +15,17 @@ void free(void *) libcesque;
|
|||
void *malloc(size_t) attributeallocsize((1)) mallocesque;
|
||||
void *calloc(size_t, size_t) attributeallocsize((1, 2)) mallocesque;
|
||||
void *memalign(size_t, size_t) attributeallocalign((1))
|
||||
attributeallocsize((2)) mallocesque;
|
||||
attributeallocsize((2)) returnspointerwithnoaliases libcesque nodiscard;
|
||||
void *realloc(void *, size_t) reallocesque;
|
||||
void *realloc_in_place(void *, size_t);
|
||||
void *realloc_in_place(void *, size_t) reallocesque;
|
||||
void *reallocarray(void *, size_t, size_t) nodiscard;
|
||||
void *valloc(size_t) attributeallocsize((1)) vallocesque;
|
||||
void *pvalloc(size_t) attributeallocsize((1)) mallocesque;
|
||||
void *pvalloc(size_t) vallocesque;
|
||||
char *strdup(const char *) paramsnonnull() mallocesque;
|
||||
char *strndup(const char *, size_t) paramsnonnull() mallocesque;
|
||||
int posix_memalign(void **, size_t, size_t); /* wut */
|
||||
void *aligned_alloc(size_t, size_t) attributeallocsize((1))
|
||||
attributeallocsize((2)) returnspointerwithnoaliases libcesque nodiscard;
|
||||
int posix_memalign(void **, size_t, size_t);
|
||||
bool __grow(void *, size_t *, size_t, size_t) paramsnonnull((1, 2)) libcesque;
|
||||
|
||||
int malloc_trim(size_t);
|
||||
|
|
|
@ -18,22 +18,18 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/notice.inc"
|
||||
.source __FILE__
|
||||
|
||||
// Allocates aligned memory.
|
||||
//
|
||||
// Returns a pointer to a newly allocated chunk of n bytes, aligned in
|
||||
// accord with the alignment argument. The alignment argument should be
|
||||
// a power of two. If the argument is not a power of two, the nearest
|
||||
// greater power is used. 8-byte alignment is guaranteed by normal
|
||||
// malloc calls, so don't bother calling memalign with an argument of 8
|
||||
// or less.
|
||||
// accord with the alignment argument. The alignment argument shall be
|
||||
// rounded up to the nearest two power and higher 2 powers may be used
|
||||
// if the allocator imposes a minimum alignment requirement.
|
||||
//
|
||||
// @param rdi is alignment in bytes
|
||||
// @param rsi (newsize) is number of bytes needed
|
||||
// @param rdi is alignment in bytes, coerced to 1+ w/ 2-power roundup
|
||||
// @param rsi is number of bytes needed, coerced to 1+
|
||||
// @return rax is memory address, or NULL w/ errno
|
||||
// @note overreliance on memalign is a sure way to fragment space
|
||||
// @see dlmemalign()
|
||||
// @see valloc(), pvalloc()
|
||||
memalign:
|
||||
jmp *hook_memalign(%rip)
|
||||
.endfn memalign,globl
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 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,21 +16,43 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
.source __FILE__
|
||||
#include "libc/mem/mem.h"
|
||||
|
||||
// Allocates aligned memory the POSIX way.
|
||||
//
|
||||
// Allocates a chunk of n bytes, aligned in accord with the alignment
|
||||
// argument. Differs from memalign only in that it (1) assigns the
|
||||
// allocated memory to *pp rather than returning it, (2) fails and
|
||||
// returns EINVAL if the alignment is not a power of two (3) fails and
|
||||
// returns ENOMEM if memory cannot be allocated.
|
||||
//
|
||||
// @param rdi is void **pp
|
||||
// @param rsi is size_t align
|
||||
// @param rdx is size_t size
|
||||
// @return eax
|
||||
posix_memalign:
|
||||
jmp *hook_posix_memalign(%rip)
|
||||
.endfn posix_memalign,globl
|
||||
/**
|
||||
* Allocates aligned memory, the POSIX way.
|
||||
*
|
||||
* Allocates a chunk of n bytes, aligned in accord with the alignment
|
||||
* argument. Differs from memalign() only in that it:
|
||||
*
|
||||
* 1. Assigns the allocated memory to *pp rather than returning it
|
||||
* 2. Fails and returns EINVAL if the alignment is not a power of two
|
||||
* 3. Fails and returns ENOMEM if memory cannot be allocated
|
||||
*
|
||||
* @param pp receives pointer, only on success
|
||||
* @param alignment must be 2-power multiple of sizeof(void *)
|
||||
* @param bytes is number of bytes to allocate
|
||||
* @return return 0 or EINVAL or ENOMEM w/o setting errno
|
||||
* @see memalign()
|
||||
*/
|
||||
int posix_memalign(void **pp, size_t alignment, size_t bytes) {
|
||||
int e;
|
||||
void *m;
|
||||
size_t q, r;
|
||||
q = alignment / sizeof(void *);
|
||||
r = alignment % sizeof(void *);
|
||||
if (!r && q && IS2POW(q)) {
|
||||
e = errno;
|
||||
m = memalign(alignment, bytes);
|
||||
errno = e;
|
||||
if (m) {
|
||||
*pp = m;
|
||||
return 0;
|
||||
} else {
|
||||
return ENOMEM;
|
||||
}
|
||||
} else {
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
|
@ -43,6 +43,12 @@ static void PutEnvInit(void) {
|
|||
atexit(PutEnvDestroy);
|
||||
}
|
||||
|
||||
void __freeenv(void *p) {
|
||||
if (once) {
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
int PutEnvImpl(char *s, bool overwrite) {
|
||||
char *p;
|
||||
unsigned i, namelen;
|
||||
|
@ -62,7 +68,10 @@ int PutEnvImpl(char *s, bool overwrite) {
|
|||
goto replace;
|
||||
}
|
||||
}
|
||||
if (i + 1 >= MAX_VARS) goto fail;
|
||||
if (i + 1 >= MAX_VARS) {
|
||||
free(s);
|
||||
return enomem();
|
||||
}
|
||||
environ[i + 1] = NULL;
|
||||
replace:
|
||||
free(environ[i]);
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/notice.inc"
|
||||
.source __FILE__
|
||||
|
||||
// Equivalent to valloc(minimum-page-that-holds(n)), that is,
|
||||
// round up n to nearest pagesize.
|
||||
//
|
||||
// @param rdi is number of bytes needed
|
||||
// @return rax is memory address, or NULL w/ errno
|
||||
// @see dlpvalloc()
|
||||
pvalloc:jmp *hook_pvalloc(%rip)
|
||||
.endfn pvalloc,globl
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 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 │
|
||||
|
@ -17,17 +17,15 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
.source __FILE__
|
||||
#include "libc/mem/mem.h"
|
||||
|
||||
.initbss 202,_init_pvalloc
|
||||
hook_pvalloc:
|
||||
.quad 0
|
||||
.endobj hook_pvalloc,globl,hidden
|
||||
.previous
|
||||
|
||||
.init.start 202,_init_pvalloc
|
||||
.hidden dlpvalloc
|
||||
ezlea dlpvalloc,ax
|
||||
stosq
|
||||
yoink free
|
||||
.init.end 202,_init_pvalloc
|
||||
/**
|
||||
* Equivalent to memalign(PAGESIZE, ROUNDUP(n, PAGESIZE)).
|
||||
*
|
||||
* @param n number of bytes needed
|
||||
* @return memory address, or NULL w/ errno
|
||||
* @see valloc()
|
||||
*/
|
||||
void *pvalloc(size_t n) {
|
||||
return memalign(PAGESIZE, ROUNDUP(n, PAGESIZE));
|
||||
}
|
|
@ -23,6 +23,5 @@
|
|||
|
||||
nodiscard void *unhexstr(const char *hexdigs) {
|
||||
assert(strlen(hexdigs) % 2 == 0);
|
||||
return unhexbuf(memalign(__BIGGEST_ALIGNMENT__, strlen(hexdigs) / 2),
|
||||
strlen(hexdigs) / 2, hexdigs);
|
||||
return unhexbuf(malloc(strlen(hexdigs) / 2), strlen(hexdigs) / 2, hexdigs);
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/notice.inc"
|
||||
.source __FILE__
|
||||
|
||||
// Equivalent to memalign(4096, n).
|
||||
//
|
||||
// @param rdi is number of bytes needed
|
||||
// @return rax is memory address, or NULL w/ errno
|
||||
// @see dlvalloc()
|
||||
valloc: jmp *hook_valloc(%rip)
|
||||
.endfn valloc,globl
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 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,18 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
.source __FILE__
|
||||
#include "libc/mem/mem.h"
|
||||
|
||||
.initbss 202,_init_valloc
|
||||
hook_valloc:
|
||||
.quad 0
|
||||
.endobj hook_valloc,globl,hidden
|
||||
.previous
|
||||
|
||||
.init.start 202,_init_valloc
|
||||
.hidden dlvalloc
|
||||
ezlea dlvalloc,ax
|
||||
stosq
|
||||
yoink free
|
||||
.init.end 202,_init_valloc
|
||||
/**
|
||||
* Equivalent to memalign(PAGESIZE, n).
|
||||
*
|
||||
* @param n number of bytes needed
|
||||
* @return memory address, or NULL w/ errno
|
||||
* @see pvalloc()
|
||||
*/
|
||||
void *valloc(size_t n) {
|
||||
return memalign(PAGESIZE, n);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue