Skip to content

Commit a7af7c0

Browse files
committed
Port emmalloc to wasi-libc.
- Avoid using Emscripten-specific functions - Avoid using a constructor. - Add support for allocating memory at `__heap_base`. - Adjust the max-align value. - Disable functions that wasi-libc doesn't currently publish. - Add `__libc_` aliases.
1 parent f8eaf00 commit a7af7c0

2 files changed

Lines changed: 134 additions & 23 deletions

File tree

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ DLMALLOC_DIR = $(CURDIR)/dlmalloc
4343
DLMALLOC_SRC_DIR = $(DLMALLOC_DIR)/src
4444
DLMALLOC_SOURCES = $(DLMALLOC_SRC_DIR)/dlmalloc.c
4545
DLMALLOC_INC = $(DLMALLOC_DIR)/include
46+
EMMALLOC_DIR = $(CURDIR)/emmalloc
47+
EMMALLOC_SOURCES = $(EMMALLOC_DIR)/emmalloc.c
4648
LIBC_BOTTOM_HALF_DIR = $(CURDIR)/libc-bottom-half
4749
LIBC_BOTTOM_HALF_CLOUDLIBC_SRC = $(LIBC_BOTTOM_HALF_DIR)/cloudlibc/src
4850
LIBC_BOTTOM_HALF_CLOUDLIBC_SRC_INC = $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC)/include
@@ -312,10 +314,13 @@ CFLAGS += -isystem "$(SYSROOT_INC)"
312314
# the build tree.
313315
objs = $(patsubst $(CURDIR)/%.c,$(OBJDIR)/%.o,$(1))
314316
DLMALLOC_OBJS = $(call objs,$(DLMALLOC_SOURCES))
317+
EMMALLOC_OBJS = $(call objs,$(EMMALLOC_SOURCES))
315318
LIBC_BOTTOM_HALF_ALL_OBJS = $(call objs,$(LIBC_BOTTOM_HALF_ALL_SOURCES))
316319
LIBC_TOP_HALF_ALL_OBJS = $(call objs,$(LIBC_TOP_HALF_ALL_SOURCES))
317320
ifeq ($(MALLOC_IMPL),dlmalloc)
318321
LIBC_OBJS += $(DLMALLOC_OBJS)
322+
else ifeq ($(MALLOC_IMPL),emmalloc)
323+
LIBC_OBJS += $(EMMALLOC_OBJS)
319324
else ifeq ($(MALLOC_IMPL),none)
320325
# No object files to add.
321326
else

emmalloc/emmalloc.c

Lines changed: 129 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,25 @@
4747
#include <memory.h>
4848
#include <assert.h>
4949
#include <malloc.h>
50-
#include <emscripten/heap.h>
51-
#include <emscripten/threading.h>
50+
#include <limits.h>
51+
#include <stdlib.h>
5252

5353
#ifdef __EMSCRIPTEN_TRACING__
5454
#include <emscripten/trace.h>
5555
#endif
5656

57+
// Defind by the linker to have the address of the start of the heap.
58+
extern unsigned char __heap_base;
59+
5760
// Behavior of right shifting a signed integer is compiler implementation defined.
5861
static_assert((((int32_t)0x80000000U) >> 31) == -1, "This malloc implementation requires that right-shifting a signed integer produces a sign-extending (arithmetic) shift!");
5962

6063
// Configuration: specifies the minimum alignment that malloc()ed memory outputs. Allocation requests with smaller alignment
6164
// than this will yield an allocation with this much alignment.
6265
#define MALLOC_ALIGNMENT alignof(max_align_t)
63-
static_assert(alignof(max_align_t) == 8, "max_align_t must be correct");
66+
static_assert(alignof(max_align_t) == 16, "max_align_t must be correct");
6467

65-
#define EMMALLOC_EXPORT __attribute__((weak, __visibility__("default")))
68+
#define EMMALLOC_EXPORT __attribute__((weak))
6669

6770
#define MIN(x, y) ((x) < (y) ? (x) : (y))
6871
#define MAX(x, y) ((x) > (y) ? (x) : (y))
@@ -138,7 +141,72 @@ static RootRegion *listOfAllRegions = NULL;
138141
// when adding and removing elements from the linked list, i.e. we are guaranteed that
139142
// the sentinel node is always fixed and there, and the actual free region list elements
140143
// start at freeRegionBuckets[i].next each.
141-
static Region freeRegionBuckets[NUM_FREE_BUCKETS];
144+
static Region freeRegionBuckets[NUM_FREE_BUCKETS] = {
145+
{ .prev = &freeRegionBuckets[0], .next = &freeRegionBuckets[0] },
146+
{ .prev = &freeRegionBuckets[1], .next = &freeRegionBuckets[1] },
147+
{ .prev = &freeRegionBuckets[2], .next = &freeRegionBuckets[2] },
148+
{ .prev = &freeRegionBuckets[3], .next = &freeRegionBuckets[3] },
149+
{ .prev = &freeRegionBuckets[4], .next = &freeRegionBuckets[4] },
150+
{ .prev = &freeRegionBuckets[5], .next = &freeRegionBuckets[5] },
151+
{ .prev = &freeRegionBuckets[6], .next = &freeRegionBuckets[6] },
152+
{ .prev = &freeRegionBuckets[7], .next = &freeRegionBuckets[7] },
153+
{ .prev = &freeRegionBuckets[8], .next = &freeRegionBuckets[8] },
154+
{ .prev = &freeRegionBuckets[9], .next = &freeRegionBuckets[9] },
155+
{ .prev = &freeRegionBuckets[10], .next = &freeRegionBuckets[10] },
156+
{ .prev = &freeRegionBuckets[11], .next = &freeRegionBuckets[11] },
157+
{ .prev = &freeRegionBuckets[12], .next = &freeRegionBuckets[12] },
158+
{ .prev = &freeRegionBuckets[13], .next = &freeRegionBuckets[13] },
159+
{ .prev = &freeRegionBuckets[14], .next = &freeRegionBuckets[14] },
160+
{ .prev = &freeRegionBuckets[15], .next = &freeRegionBuckets[15] },
161+
{ .prev = &freeRegionBuckets[16], .next = &freeRegionBuckets[16] },
162+
{ .prev = &freeRegionBuckets[17], .next = &freeRegionBuckets[17] },
163+
{ .prev = &freeRegionBuckets[18], .next = &freeRegionBuckets[18] },
164+
{ .prev = &freeRegionBuckets[19], .next = &freeRegionBuckets[19] },
165+
{ .prev = &freeRegionBuckets[20], .next = &freeRegionBuckets[20] },
166+
{ .prev = &freeRegionBuckets[21], .next = &freeRegionBuckets[21] },
167+
{ .prev = &freeRegionBuckets[22], .next = &freeRegionBuckets[22] },
168+
{ .prev = &freeRegionBuckets[23], .next = &freeRegionBuckets[23] },
169+
{ .prev = &freeRegionBuckets[24], .next = &freeRegionBuckets[24] },
170+
{ .prev = &freeRegionBuckets[25], .next = &freeRegionBuckets[25] },
171+
{ .prev = &freeRegionBuckets[26], .next = &freeRegionBuckets[26] },
172+
{ .prev = &freeRegionBuckets[27], .next = &freeRegionBuckets[27] },
173+
{ .prev = &freeRegionBuckets[28], .next = &freeRegionBuckets[28] },
174+
{ .prev = &freeRegionBuckets[29], .next = &freeRegionBuckets[29] },
175+
{ .prev = &freeRegionBuckets[30], .next = &freeRegionBuckets[30] },
176+
{ .prev = &freeRegionBuckets[31], .next = &freeRegionBuckets[31] },
177+
{ .prev = &freeRegionBuckets[32], .next = &freeRegionBuckets[32] },
178+
{ .prev = &freeRegionBuckets[33], .next = &freeRegionBuckets[33] },
179+
{ .prev = &freeRegionBuckets[34], .next = &freeRegionBuckets[34] },
180+
{ .prev = &freeRegionBuckets[35], .next = &freeRegionBuckets[35] },
181+
{ .prev = &freeRegionBuckets[36], .next = &freeRegionBuckets[36] },
182+
{ .prev = &freeRegionBuckets[37], .next = &freeRegionBuckets[37] },
183+
{ .prev = &freeRegionBuckets[38], .next = &freeRegionBuckets[38] },
184+
{ .prev = &freeRegionBuckets[39], .next = &freeRegionBuckets[39] },
185+
{ .prev = &freeRegionBuckets[40], .next = &freeRegionBuckets[40] },
186+
{ .prev = &freeRegionBuckets[41], .next = &freeRegionBuckets[41] },
187+
{ .prev = &freeRegionBuckets[42], .next = &freeRegionBuckets[42] },
188+
{ .prev = &freeRegionBuckets[43], .next = &freeRegionBuckets[43] },
189+
{ .prev = &freeRegionBuckets[44], .next = &freeRegionBuckets[44] },
190+
{ .prev = &freeRegionBuckets[45], .next = &freeRegionBuckets[45] },
191+
{ .prev = &freeRegionBuckets[46], .next = &freeRegionBuckets[46] },
192+
{ .prev = &freeRegionBuckets[47], .next = &freeRegionBuckets[47] },
193+
{ .prev = &freeRegionBuckets[48], .next = &freeRegionBuckets[48] },
194+
{ .prev = &freeRegionBuckets[49], .next = &freeRegionBuckets[49] },
195+
{ .prev = &freeRegionBuckets[50], .next = &freeRegionBuckets[50] },
196+
{ .prev = &freeRegionBuckets[51], .next = &freeRegionBuckets[51] },
197+
{ .prev = &freeRegionBuckets[52], .next = &freeRegionBuckets[52] },
198+
{ .prev = &freeRegionBuckets[53], .next = &freeRegionBuckets[53] },
199+
{ .prev = &freeRegionBuckets[54], .next = &freeRegionBuckets[54] },
200+
{ .prev = &freeRegionBuckets[55], .next = &freeRegionBuckets[55] },
201+
{ .prev = &freeRegionBuckets[56], .next = &freeRegionBuckets[56] },
202+
{ .prev = &freeRegionBuckets[57], .next = &freeRegionBuckets[57] },
203+
{ .prev = &freeRegionBuckets[58], .next = &freeRegionBuckets[58] },
204+
{ .prev = &freeRegionBuckets[59], .next = &freeRegionBuckets[59] },
205+
{ .prev = &freeRegionBuckets[60], .next = &freeRegionBuckets[60] },
206+
{ .prev = &freeRegionBuckets[61], .next = &freeRegionBuckets[61] },
207+
{ .prev = &freeRegionBuckets[62], .next = &freeRegionBuckets[62] },
208+
{ .prev = &freeRegionBuckets[63], .next = &freeRegionBuckets[63] },
209+
};
142210

143211
// A bitmask that tracks the population status for each of the 64 distinct memory regions:
144212
// a zero at bit position i means that the free list bucket i is empty. This bitmask is
@@ -347,6 +415,7 @@ static void link_to_free_list(Region *freeRegion)
347415
freeRegionBucketsUsed |= ((BUCKET_BITMASK_T)1) << bucketIndex;
348416
}
349417

418+
#if 0
350419
static void dump_memory_regions()
351420
{
352421
ASSERT_MALLOC_IS_ACQUIRED();
@@ -458,6 +527,7 @@ int emmalloc_validate_memory_regions()
458527
MALLOC_RELEASE();
459528
return memoryError;
460529
}
530+
#endif
461531

462532
static bool claim_more_memory(size_t numBytes)
463533
{
@@ -469,20 +539,38 @@ static bool claim_more_memory(size_t numBytes)
469539
validate_memory_regions();
470540
#endif
471541

472-
// Claim memory via sbrk
473-
uint8_t *startPtr = (uint8_t*)sbrk(numBytes);
474-
if ((intptr_t)startPtr == -1)
475-
{
542+
uint8_t *startPtr;
543+
uint8_t *endPtr;
544+
do {
545+
// If this is the first time we're called, see if we can use
546+
// the initial heap memory set up by wasm-ld.
547+
if (!listOfAllRegions) {
548+
unsigned char *heap_end = sbrk(0);
549+
if (numBytes <= (size_t)(heap_end - &__heap_base)) {
550+
startPtr = &__heap_base;
551+
endPtr = heap_end;
552+
break;
553+
}
554+
}
555+
556+
// Round numBytes up to the nearest page size.
557+
numBytes = (numBytes + (PAGE_SIZE-1)) & -PAGE_SIZE;
558+
559+
// Claim memory via sbrk
560+
startPtr = (uint8_t*)sbrk(numBytes);
561+
if ((intptr_t)startPtr == -1)
562+
{
476563
#ifdef EMMALLOC_VERBOSE
477-
MAIN_THREAD_ASYNC_EM_ASM(console.error('claim_more_memory: sbrk failed!'));
564+
MAIN_THREAD_ASYNC_EM_ASM(console.error('claim_more_memory: sbrk failed!'));
478565
#endif
479-
return false;
480-
}
566+
return false;
567+
}
481568
#ifdef EMMALLOC_VERBOSE
482-
MAIN_THREAD_ASYNC_EM_ASM(console.log('claim_more_memory: claimed 0x' + ($0>>>0).toString(16) + ' - 0x' + ($1>>>0).toString(16) + ' (' + ($2>>>0) + ' bytes) via sbrk()'), startPtr, startPtr + numBytes, numBytes);
569+
MAIN_THREAD_ASYNC_EM_ASM(console.log('claim_more_memory: claimed 0x' + ($0>>>0).toString(16) + ' - 0x' + ($1>>>0).toString(16) + ' (' + ($2>>>0) + ' bytes) via sbrk()'), startPtr, startPtr + numBytes, numBytes);
483570
#endif
484-
assert(HAS_ALIGNMENT(startPtr, alignof(size_t)));
485-
uint8_t *endPtr = startPtr + numBytes;
571+
assert(HAS_ALIGNMENT(startPtr, alignof(size_t)));
572+
endPtr = startPtr + numBytes;
573+
} while (0);
486574

487575
// Create a sentinel region at the end of the new heap block
488576
Region *endSentinelRegion = (Region*)(endPtr - sizeof(Region));
@@ -535,6 +623,7 @@ static bool claim_more_memory(size_t numBytes)
535623
return true;
536624
}
537625

626+
#if 0
538627
// Initialize emmalloc during static initialization.
539628
// See system/lib/README.md for static constructor ordering.
540629
__attribute__((constructor(47)))
@@ -562,6 +651,7 @@ void emmalloc_blank_slate_from_orbit()
562651
initialize_emmalloc_heap();
563652
MALLOC_RELEASE();
564653
}
654+
#endif
565655

566656
static void *attempt_allocate(Region *freeRegion, size_t alignment, size_t size)
567657
{
@@ -796,19 +886,21 @@ static void *allocate_memory(size_t alignment, size_t size)
796886
return 0;
797887
}
798888

889+
static
799890
void *emmalloc_memalign(size_t alignment, size_t size)
800891
{
801892
MALLOC_ACQUIRE();
802893
void *ptr = allocate_memory(alignment, size);
803894
MALLOC_RELEASE();
804895
return ptr;
805896
}
806-
extern __typeof(emmalloc_memalign) emscripten_builtin_memalign __attribute__((alias("emmalloc_memalign")));
807897

898+
#if 0
808899
void * EMMALLOC_EXPORT memalign(size_t alignment, size_t size)
809900
{
810901
return emmalloc_memalign(alignment, size);
811902
}
903+
#endif
812904

813905
void * EMMALLOC_EXPORT aligned_alloc(size_t alignment, size_t size)
814906
{
@@ -817,18 +909,18 @@ void * EMMALLOC_EXPORT aligned_alloc(size_t alignment, size_t size)
817909
return emmalloc_memalign(alignment, size);
818910
}
819911

912+
static
820913
void *emmalloc_malloc(size_t size)
821914
{
822915
return emmalloc_memalign(MALLOC_ALIGNMENT, size);
823916
}
824-
extern __typeof(emmalloc_malloc) emscripten_builtin_malloc __attribute__((alias("emmalloc_malloc")));
825-
extern __typeof(emmalloc_malloc) __libc_malloc __attribute__((alias("emmalloc_malloc")));
826917

827918
void * EMMALLOC_EXPORT malloc(size_t size)
828919
{
829920
return emmalloc_malloc(size);
830921
}
831922

923+
static
832924
size_t emmalloc_usable_size(void *ptr)
833925
{
834926
if (!ptr)
@@ -854,6 +946,7 @@ size_t EMMALLOC_EXPORT malloc_usable_size(void *ptr)
854946
return emmalloc_usable_size(ptr);
855947
}
856948

949+
static
857950
void emmalloc_free(void *ptr)
858951
{
859952
#ifdef EMMALLOC_MEMVALIDATE
@@ -923,12 +1016,10 @@ void emmalloc_free(void *ptr)
9231016
emmalloc_validate_memory_regions();
9241017
#endif
9251018
}
926-
extern __typeof(emmalloc_free) emscripten_builtin_free __attribute__((alias("emmalloc_free")));
927-
extern __typeof(emmalloc_free) __libc_free __attribute__((alias("emmalloc_free")));
9281019

9291020
void EMMALLOC_EXPORT free(void *ptr)
9301021
{
931-
return emmalloc_free(ptr);
1022+
emmalloc_free(ptr);
9321023
}
9331024

9341025
// Can be called to attempt to increase or decrease the size of the given region
@@ -1005,6 +1096,7 @@ static int acquire_and_attempt_region_resize(Region *region, size_t size)
10051096
return success;
10061097
}
10071098

1099+
static
10081100
void *emmalloc_aligned_realloc(void *ptr, size_t alignment, size_t size)
10091101
{
10101102
#ifdef EMMALLOC_VERBOSE
@@ -1058,11 +1150,14 @@ void *emmalloc_aligned_realloc(void *ptr, size_t alignment, size_t size)
10581150
return newptr;
10591151
}
10601152

1153+
#if 0
10611154
void * EMMALLOC_EXPORT aligned_realloc(void *ptr, size_t alignment, size_t size)
10621155
{
10631156
return emmalloc_aligned_realloc(ptr, alignment, size);
10641157
}
1158+
#endif
10651159

1160+
#if 0
10661161
// realloc_try() is like realloc(), but only attempts to try to resize the existing memory
10671162
// area. If resizing the existing memory area fails, then realloc_try() will return 0
10681163
// (the original memory block is not freed or modified). If resizing succeeds, previous
@@ -1140,25 +1235,29 @@ void *emmalloc_aligned_realloc_uninitialized(void *ptr, size_t alignment, size_t
11401235
free(ptr);
11411236
return emmalloc_memalign(alignment, size);
11421237
}
1238+
#endif
11431239

1240+
static
11441241
void *emmalloc_realloc(void *ptr, size_t size)
11451242
{
11461243
return emmalloc_aligned_realloc(ptr, MALLOC_ALIGNMENT, size);
11471244
}
1148-
extern __typeof(emmalloc_realloc) __libc_realloc __attribute__((alias("emmalloc_realloc")));
11491245

11501246
void * EMMALLOC_EXPORT realloc(void *ptr, size_t size)
11511247
{
11521248
return emmalloc_realloc(ptr, size);
11531249
}
11541250

1251+
#if 0
11551252
// realloc_uninitialized() is like realloc(), but old memory contents
11561253
// will be undefined after reallocation. (old memory is not preserved in any case)
11571254
void *emmalloc_realloc_uninitialized(void *ptr, size_t size)
11581255
{
11591256
return emmalloc_aligned_realloc_uninitialized(ptr, MALLOC_ALIGNMENT, size);
11601257
}
1258+
#endif
11611259

1260+
static
11621261
int emmalloc_posix_memalign(void **memptr, size_t alignment, size_t size)
11631262
{
11641263
assert(memptr);
@@ -1173,6 +1272,7 @@ int EMMALLOC_EXPORT posix_memalign(void **memptr, size_t alignment, size_t size)
11731272
return emmalloc_posix_memalign(memptr, alignment, size);
11741273
}
11751274

1275+
static
11761276
void *emmalloc_calloc(size_t num, size_t size)
11771277
{
11781278
size_t bytes = num*size;
@@ -1181,13 +1281,13 @@ void *emmalloc_calloc(size_t num, size_t size)
11811281
memset(ptr, 0, bytes);
11821282
return ptr;
11831283
}
1184-
extern __typeof(emmalloc_calloc) __libc_calloc __attribute__((alias("emmalloc_calloc")));
11851284

11861285
void * EMMALLOC_EXPORT calloc(size_t num, size_t size)
11871286
{
11881287
return emmalloc_calloc(num, size);
11891288
}
11901289

1290+
#if 0
11911291
static int count_linked_list_size(Region *list)
11921292
{
11931293
int size = 1;
@@ -1427,3 +1527,9 @@ size_t emmalloc_compute_free_dynamic_memory_fragmentation_map(size_t freeMemoryS
14271527
size_t emmalloc_unclaimed_heap_memory(void) {
14281528
return emscripten_get_heap_max() - (size_t)sbrk(0);
14291529
}
1530+
#endif
1531+
1532+
// Define these to satisfy musl references.
1533+
void *__libc_malloc(size_t) __attribute__((alias("malloc")));
1534+
void __libc_free(void *) __attribute__((alias("free")));
1535+
void *__libc_calloc(size_t nmemb, size_t size) __attribute__((alias("calloc")));

0 commit comments

Comments
 (0)