Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/system/lib/libc/sbrk.c
6162 views
1
/*
2
* Copyright 2019 The Emscripten Authors. All rights reserved.
3
* Emscripten is available under two separate licenses, the MIT license and the
4
* University of Illinois/NCSA Open Source License. Both these licenses can be
5
* found in the LICENSE file.
6
*
7
*/
8
9
// libc files are compiled as -std=c99 which doesn't normally declare
10
// max_align_t.
11
#if __STDC_VERSION__ < 201112L
12
#define __NEED_max_align_t
13
#endif
14
15
#include <errno.h>
16
#include <limits.h>
17
#include <stddef.h>
18
#include <stdint.h>
19
#ifdef __EMSCRIPTEN_SHARED_MEMORY__ // for error handling, see below
20
#include <stdio.h>
21
#include <stdlib.h>
22
#endif
23
24
#ifdef __EMSCRIPTEN_TRACING__
25
void emscripten_memprof_sbrk_grow(intptr_t old, intptr_t new);
26
#else
27
#define emscripten_memprof_sbrk_grow(...) ((void)0)
28
#endif
29
30
#include <emscripten/heap.h>
31
32
extern size_t __heap_base;
33
34
static uintptr_t sbrk_val = (uintptr_t)&__heap_base;
35
36
uintptr_t* emscripten_get_sbrk_ptr() {
37
#ifdef __PIC__
38
// In relocatable code we may call emscripten_get_sbrk_ptr() during startup,
39
// potentially *before* the setup of the dynamically-linked __heap_base, when
40
// using SAFE_HEAP. (SAFE_HEAP instruments *all* memory accesses, so even the
41
// code doing dynamic linking itself ends up instrumented, which is why we can
42
// get such an instrumented call before sbrk_val has its proper value.)
43
if (sbrk_val == 0) {
44
sbrk_val = (uintptr_t)&__heap_base;
45
}
46
#endif
47
return &sbrk_val;
48
}
49
50
// Enforce preserving a minimal alignof(maxalign_t) alignment for sbrk.
51
#define SBRK_ALIGNMENT (__alignof__(max_align_t))
52
53
#ifdef __EMSCRIPTEN_SHARED_MEMORY__
54
#define READ_SBRK_PTR(sbrk_ptr) (__c11_atomic_load((_Atomic(uintptr_t)*)(sbrk_ptr), __ATOMIC_SEQ_CST))
55
#else
56
#define READ_SBRK_PTR(sbrk_ptr) (*(sbrk_ptr))
57
#endif
58
59
void *_sbrk64(int64_t increment) {
60
if (increment >= 0) {
61
increment = (increment + (SBRK_ALIGNMENT-1)) & ~((int64_t)SBRK_ALIGNMENT-1);
62
} else {
63
increment = -(-increment & ~((int64_t)SBRK_ALIGNMENT-1));
64
}
65
66
uintptr_t *sbrk_ptr = (uintptr_t*)emscripten_get_sbrk_ptr();
67
68
// To make sbrk thread-safe, implement a CAS loop to update the
69
// value of sbrk_ptr.
70
while (1) {
71
uintptr_t old_brk = READ_SBRK_PTR(sbrk_ptr);
72
int64_t new_brk64 = (int64_t)old_brk + increment;
73
uintptr_t new_brk = (uintptr_t)new_brk64;
74
// Check for a) an over/underflow, which would indicate that we are
75
// allocating over maximum addressable memory. and b) if necessary,
76
// increase the WebAssembly Memory size, and abort if that fails.
77
if (new_brk < 0 || new_brk64 != (int64_t)new_brk
78
|| (new_brk > emscripten_get_heap_size() && !emscripten_resize_heap(new_brk))) {
79
errno = ENOMEM;
80
return (void*)-1;
81
}
82
#ifdef __EMSCRIPTEN_SHARED_MEMORY__
83
// Attempt to update the dynamic top to new value. Another thread may have
84
// beat this one to the update, in which case we will need to start over
85
// by iterating the loop body again.
86
uintptr_t expected = old_brk;
87
88
__c11_atomic_compare_exchange_strong((_Atomic(uintptr_t)*)sbrk_ptr,
89
&expected, new_brk, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
90
91
if (expected != old_brk) continue; // CAS failed, another thread raced in between.
92
#else
93
*sbrk_ptr = new_brk;
94
#endif
95
96
emscripten_memprof_sbrk_grow(old_brk, new_brk);
97
return (void*)old_brk;
98
}
99
}
100
101
void *sbrk(intptr_t increment_) {
102
#if defined(__wasm64__) // TODO || !defined(wasm2gb)
103
// In the correct https://linux.die.net/man/2/sbrk spec, sbrk() parameter is
104
// intended to be treated as signed, meaning that it is not possible in a
105
// 32-bit program to sbrk alloc (or dealloc) more than 2GB of memory at once.
106
107
// Treat sbrk() parameter as signed.
108
return _sbrk64((int64_t)increment_);
109
#else
110
// BUG: Currently the Emscripten test suite codifies expectations that sbrk()
111
// values passed to this function are to be treated as unsigned, which means
112
// that in 2GB and 4GB build modes, it is not possible to shrink memory.
113
// To satisfy that mode, treat sbrk() parameters in 32-bit builds as unsigned.
114
// https://github.com/emscripten-core/emscripten/issues/25138
115
116
// Treat sbrk() parameter as unsigned.
117
return _sbrk64((int64_t)(uintptr_t)increment_);
118
#endif
119
}
120
121
int brk(void* ptr) {
122
#ifdef __EMSCRIPTEN_SHARED_MEMORY__
123
// FIXME
124
printf("brk() is not threadsafe yet, https://github.com/emscripten-core/emscripten/issues/10006");
125
abort();
126
#else
127
uintptr_t last = (uintptr_t)sbrk(0);
128
if (sbrk((uintptr_t)ptr - last) == (void*)-1) {
129
return -1;
130
}
131
return 0;
132
#endif
133
}
134
135