diff --git a/system/lib/emmalloc.c b/system/lib/emmalloc.c index 3ea657ba9171d..61a71710fd86d 100644 --- a/system/lib/emmalloc.c +++ b/system/lib/emmalloc.c @@ -37,6 +37,13 @@ * - Debugging and logging directly uses console.log via uses EM_ASM, not * printf etc., to minimize any risk of debugging or logging depending on * malloc. + * + * Exporting: + * + * - By default we declare not only emmalloc_malloc, emmalloc_free, etc. but + * also the standard library methods like malloc, free, and some aliases. + * You can override this by defining EMMALLOC_NO_STD_EXPORTS, in which case + * we only declare the emalloc_* ones but not the standard ones. */ #include @@ -63,7 +70,13 @@ static_assert((((int32_t)0x80000000U) >> 31) == -1, "This malloc implementation #define MALLOC_ALIGNMENT alignof(max_align_t) static_assert(alignof(max_align_t) == 8, "max_align_t must be correct"); +#ifdef EMMALLOC_NO_STD_EXPORTS +#define EMMALLOC_EXPORT +#define EMMALLOC_ALIAS(ALIAS, ORIGINAL) +#else #define EMMALLOC_EXPORT __attribute__((weak, __visibility__("default"))) +#define EMMALLOC_ALIAS(ALIAS, ORIGINAL) extern __typeof(ORIGINAL) ALIAS __attribute__((alias(#ORIGINAL))); +#endif #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) @@ -812,31 +825,37 @@ void *emmalloc_memalign(size_t alignment, size_t size) MALLOC_RELEASE(); return ptr; } -extern __typeof(emmalloc_memalign) emscripten_builtin_memalign __attribute__((alias("emmalloc_memalign"))); +EMMALLOC_ALIAS(emscripten_builtin_memalign, emmalloc_memalign); +#ifndef EMMALLOC_NO_STD_EXPORTS void * EMMALLOC_EXPORT memalign(size_t alignment, size_t size) { return emmalloc_memalign(alignment, size); } +#endif +#ifndef EMMALLOC_NO_STD_EXPORTS void * EMMALLOC_EXPORT aligned_alloc(size_t alignment, size_t size) { if ((alignment % sizeof(void *) != 0) || (size % alignment) != 0) return 0; return emmalloc_memalign(alignment, size); } +#endif void *emmalloc_malloc(size_t size) { return emmalloc_memalign(MALLOC_ALIGNMENT, size); } -extern __typeof(emmalloc_malloc) emscripten_builtin_malloc __attribute__((alias("emmalloc_malloc"))); -extern __typeof(emmalloc_malloc) __libc_malloc __attribute__((alias("emmalloc_malloc"))); +EMMALLOC_ALIAS(emscripten_builtin_malloc, emmalloc_malloc); +EMMALLOC_ALIAS(__libc_malloc, emmalloc_malloc); +#ifndef EMMALLOC_NO_STD_EXPORTS void * EMMALLOC_EXPORT malloc(size_t size) { return emmalloc_malloc(size); } +#endif size_t emmalloc_usable_size(void *ptr) { @@ -858,10 +877,12 @@ size_t emmalloc_usable_size(void *ptr) return size - REGION_HEADER_SIZE; } +#ifndef EMMALLOC_NO_STD_EXPORTS size_t EMMALLOC_EXPORT malloc_usable_size(void *ptr) { return emmalloc_usable_size(ptr); } +#endif void emmalloc_free(void *ptr) { @@ -932,13 +953,15 @@ void emmalloc_free(void *ptr) emmalloc_validate_memory_regions(); #endif } -extern __typeof(emmalloc_free) emscripten_builtin_free __attribute__((alias("emmalloc_free"))); -extern __typeof(emmalloc_free) __libc_free __attribute__((alias("emmalloc_free"))); +EMMALLOC_ALIAS(emscripten_builtin_free, emmalloc_free); +EMMALLOC_ALIAS(__libc_free, emmalloc_free); +#ifndef EMMALLOC_NO_STD_EXPORTS void EMMALLOC_EXPORT free(void *ptr) { return emmalloc_free(ptr); } +#endif // Can be called to attempt to increase or decrease the size of the given region // to a new size (in-place). Returns 1 if resize succeeds, and 0 on failure. @@ -1067,10 +1090,12 @@ void *emmalloc_aligned_realloc(void *ptr, size_t alignment, size_t size) return newptr; } +#ifndef EMMALLOC_NO_STD_EXPORTS void * EMMALLOC_EXPORT aligned_realloc(void *ptr, size_t alignment, size_t size) { return emmalloc_aligned_realloc(ptr, alignment, size); } +#endif // realloc_try() is like realloc(), but only attempts to try to resize the existing memory // area. If resizing the existing memory area fails, then realloc_try() will return 0 @@ -1154,12 +1179,14 @@ void *emmalloc_realloc(void *ptr, size_t size) { return emmalloc_aligned_realloc(ptr, MALLOC_ALIGNMENT, size); } -extern __typeof(emmalloc_realloc) __libc_realloc __attribute__((alias("emmalloc_realloc"))); +EMMALLOC_ALIAS(__libc_realloc, emmalloc_realloc); +#ifndef EMMALLOC_NO_STD_EXPORTS void * EMMALLOC_EXPORT realloc(void *ptr, size_t size) { return emmalloc_realloc(ptr, size); } +#endif // realloc_uninitialized() is like realloc(), but old memory contents // will be undefined after reallocation. (old memory is not preserved in any case) @@ -1177,10 +1204,12 @@ int emmalloc_posix_memalign(void **memptr, size_t alignment, size_t size) return *memptr ? 0 : 12/*ENOMEM*/; } +#ifndef EMMALLOC_NO_STD_EXPORTS int EMMALLOC_EXPORT posix_memalign(void **memptr, size_t alignment, size_t size) { return emmalloc_posix_memalign(memptr, alignment, size); } +#endif void *emmalloc_calloc(size_t num, size_t size) { @@ -1190,12 +1219,14 @@ void *emmalloc_calloc(size_t num, size_t size) memset(ptr, 0, bytes); return ptr; } -extern __typeof(emmalloc_calloc) __libc_calloc __attribute__((alias("emmalloc_calloc"))); +EMMALLOC_ALIAS(__libc_calloc, emmalloc_calloc); +#ifndef EMMALLOC_NO_STD_EXPORTS void * EMMALLOC_EXPORT calloc(size_t num, size_t size) { return emmalloc_calloc(num, size); } +#endif static int count_linked_list_size(Region *list) { @@ -1286,12 +1317,14 @@ struct mallinfo emmalloc_mallinfo() return info; } +#ifndef EMMALLOC_NO_STD_EXPORTS struct mallinfo EMMALLOC_EXPORT mallinfo() { return emmalloc_mallinfo(); } +#endif -// Note! This function is not fully multithreadin safe: while this function is running, other threads should not be +// Note! This function is not fully multithreading safe: while this function is running, other threads should not be // allowed to call sbrk()! static int trim_dynamic_heap_reservation(size_t pad) { @@ -1352,10 +1385,12 @@ int emmalloc_trim(size_t pad) return success; } +#ifndef EMMALLOC_NO_STD_EXPORTS int EMMALLOC_EXPORT malloc_trim(size_t pad) { return emmalloc_trim(pad); } +#endif size_t emmalloc_dynamic_heap_size() { diff --git a/test/other/test_emmalloc_in_addition.c b/test/other/test_emmalloc_in_addition.c new file mode 100644 index 0000000000000..6219e9b094151 --- /dev/null +++ b/test/other/test_emmalloc_in_addition.c @@ -0,0 +1,36 @@ +#include +#include +#include + +int main() { + // Verify we can call both malloc and emmalloc_malloc, and that those are + // different functions, unless TEST_EMMALLOC_IS_MALLOC is set (in that case, + // emmalloc is malloc because we let emmalloc define the standard exports like + // malloc). + + // We have allocated nothing so far, but there may be some initial allocation + // from startup. + size_t initial = emmalloc_dynamic_heap_size(); + emscripten_console_logf("initial: %zu\n", initial); + + const size_t ONE_MB = 1024 * 1024; + void* one = malloc(ONE_MB); + assert(one); +#ifndef TEST_EMMALLOC_IS_MALLOC + // We have allocated using malloc, but not emmalloc, so emmalloc reports no + // change in usage. + assert(emmalloc_dynamic_heap_size() == initial); +#else + // malloc == emmalloc_malloc, so emmalloc will report additional usage (of the + // size of the allocation, or perhaps more if it overallocated as an + // optimization). + assert(emmalloc_dynamic_heap_size() >= initial + ONE_MB); +#endif + + void* two = emmalloc_malloc(ONE_MB); + assert(two); + // We have allocated using emmalloc, so now emmalloc definitely reports usage. + assert(emmalloc_dynamic_heap_size() >= initial + ONE_MB); + + emscripten_console_log("success"); +} diff --git a/test/test_other.py b/test/test_other.py index a97b638db594f..19609da19fd0b 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -14033,3 +14033,18 @@ def test_hello_world_argv(self): def test_arguments_global(self): self.emcc(test_file('hello_world_argv.c'), ['-sENVIRONMENT=web', '-sSTRICT', '--closure=1', '-O2']) + + @parameterized({ + 'no_std_exp': (['-DEMMALLOC_NO_STD_EXPORTS'],), + # When we let emmalloc build with the standard exports like malloc, + # emmalloc == malloc. + 'with_std_exp': (['-DTEST_EMMALLOC_IS_MALLOC'],), + }) + def test_emmalloc_in_addition(self, args): + # Test that we can use emmalloc in addition to another malloc impl. When we + # build emmalloc using -DEMMALLOC_NO_STD_EXPORTS it will not export malloc + # etc., and only provide the emmalloc_malloc etc. family of functions that + # we can use. + emmalloc = path_from_root('system', 'lib', 'emmalloc.c') + self.run_process([EMCC, test_file('other/test_emmalloc_in_addition.c'), emmalloc] + args) + self.assertContained('success', self.run_js('a.out.js'))