/*1* Copyright 2019 The Emscripten Authors. All rights reserved.2* Emscripten is available under two separate licenses, the MIT license and the3* University of Illinois/NCSA Open Source License. Both these licenses can be4* found in the LICENSE file.5*6*/78// libc files are compiled as -std=c99 which doesn't normally declare9// max_align_t.10#if __STDC_VERSION__ < 201112L11#define __NEED_max_align_t12#endif1314#include <errno.h>15#include <limits.h>16#include <stddef.h>17#include <stdint.h>18#ifdef __EMSCRIPTEN_SHARED_MEMORY__ // for error handling, see below19#include <stdio.h>20#include <stdlib.h>21#endif2223#ifdef __EMSCRIPTEN_TRACING__24void emscripten_memprof_sbrk_grow(intptr_t old, intptr_t new);25#else26#define emscripten_memprof_sbrk_grow(...) ((void)0)27#endif2829#include <emscripten/heap.h>3031extern size_t __heap_base;3233static uintptr_t sbrk_val = (uintptr_t)&__heap_base;3435uintptr_t* emscripten_get_sbrk_ptr() {36#ifdef __PIC__37// In relocatable code we may call emscripten_get_sbrk_ptr() during startup,38// potentially *before* the setup of the dynamically-linked __heap_base, when39// using SAFE_HEAP. (SAFE_HEAP instruments *all* memory accesses, so even the40// code doing dynamic linking itself ends up instrumented, which is why we can41// get such an instrumented call before sbrk_val has its proper value.)42if (sbrk_val == 0) {43sbrk_val = (uintptr_t)&__heap_base;44}45#endif46return &sbrk_val;47}4849// Enforce preserving a minimal alignof(maxalign_t) alignment for sbrk.50#define SBRK_ALIGNMENT (__alignof__(max_align_t))5152#ifdef __EMSCRIPTEN_SHARED_MEMORY__53#define READ_SBRK_PTR(sbrk_ptr) (__c11_atomic_load((_Atomic(uintptr_t)*)(sbrk_ptr), __ATOMIC_SEQ_CST))54#else55#define READ_SBRK_PTR(sbrk_ptr) (*(sbrk_ptr))56#endif5758void *_sbrk64(int64_t increment) {59if (increment >= 0) {60increment = (increment + (SBRK_ALIGNMENT-1)) & ~((int64_t)SBRK_ALIGNMENT-1);61} else {62increment = -(-increment & ~((int64_t)SBRK_ALIGNMENT-1));63}6465uintptr_t *sbrk_ptr = (uintptr_t*)emscripten_get_sbrk_ptr();6667// To make sbrk thread-safe, implement a CAS loop to update the68// value of sbrk_ptr.69while (1) {70uintptr_t old_brk = READ_SBRK_PTR(sbrk_ptr);71int64_t new_brk64 = (int64_t)old_brk + increment;72uintptr_t new_brk = (uintptr_t)new_brk64;73// Check for a) an over/underflow, which would indicate that we are74// allocating over maximum addressable memory. and b) if necessary,75// increase the WebAssembly Memory size, and abort if that fails.76if (new_brk < 0 || new_brk64 != (int64_t)new_brk77|| (new_brk > emscripten_get_heap_size() && !emscripten_resize_heap(new_brk))) {78errno = ENOMEM;79return (void*)-1;80}81#ifdef __EMSCRIPTEN_SHARED_MEMORY__82// Attempt to update the dynamic top to new value. Another thread may have83// beat this one to the update, in which case we will need to start over84// by iterating the loop body again.85uintptr_t expected = old_brk;8687__c11_atomic_compare_exchange_strong((_Atomic(uintptr_t)*)sbrk_ptr,88&expected, new_brk, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);8990if (expected != old_brk) continue; // CAS failed, another thread raced in between.91#else92*sbrk_ptr = new_brk;93#endif9495emscripten_memprof_sbrk_grow(old_brk, new_brk);96return (void*)old_brk;97}98}99100void *sbrk(intptr_t increment_) {101#if defined(__wasm64__) // TODO || !defined(wasm2gb)102// In the correct https://linux.die.net/man/2/sbrk spec, sbrk() parameter is103// intended to be treated as signed, meaning that it is not possible in a104// 32-bit program to sbrk alloc (or dealloc) more than 2GB of memory at once.105106// Treat sbrk() parameter as signed.107return _sbrk64((int64_t)increment_);108#else109// BUG: Currently the Emscripten test suite codifies expectations that sbrk()110// values passed to this function are to be treated as unsigned, which means111// that in 2GB and 4GB build modes, it is not possible to shrink memory.112// To satisfy that mode, treat sbrk() parameters in 32-bit builds as unsigned.113// https://github.com/emscripten-core/emscripten/issues/25138114115// Treat sbrk() parameter as unsigned.116return _sbrk64((int64_t)(uintptr_t)increment_);117#endif118}119120int brk(void* ptr) {121#ifdef __EMSCRIPTEN_SHARED_MEMORY__122// FIXME123printf("brk() is not threadsafe yet, https://github.com/emscripten-core/emscripten/issues/10006");124abort();125#else126uintptr_t last = (uintptr_t)sbrk(0);127if (sbrk((uintptr_t)ptr - last) == (void*)-1) {128return -1;129}130return 0;131#endif132}133134135