Skip to content

Commit c7750f3

Browse files
Akaricchisbc100
authored andcommitted
Default to 16-byte alignment in malloc
By default we now use `alignof(max_align_t)` in both `emmalloc` and `dlmalloc`. This is a requirement of the C and C++ standards. Developers can opt into the old behviour using `-s MALLOC=dlmalloc-align8` or `-s MALLOC=emmalloc-align8`. Based on #10110 which was authored by @Akaricchi. Fixes: #10072
1 parent 9082822 commit c7750f3

File tree

7 files changed

+82
-7
lines changed

7 files changed

+82
-7
lines changed

ChangeLog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ See docs/process.md for more on how version tagging works.
2020

2121
2.0.25
2222
------
23+
- The alignment of memory returned from the malloc implementations in emscripten
24+
(dlmalloc and emmalloc) defaults to 16 rather than 8. This is technically
25+
correct (since `alignof(max_align_t)` is 16 for the WebAssembly clang target)
26+
and fixes several issues we have seen in the wild. Since some programs can
27+
benefit having a lower alignment we have added `dlmalloc-align8` and
28+
`emmalloc-align8` variants which can use used with the `-s MALLOC` option to
29+
opt into the old (non-compliant) behavior.
2330

2431
2.0.24 - 06/10/2021
2532
-------------------

system/lib/dlmalloc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424
#define USE_SPIN_LOCKS 0 // Ensure we use pthread_mutex_t.
2525
#endif
2626

27+
#ifndef MALLOC_ALIGNMENT
28+
#include <stddef.h>
29+
/* `malloc`ed pointers must be aligned at least as strictly as max_align_t. */
30+
#define MALLOC_ALIGNMENT (__alignof__(max_align_t))
31+
#endif
32+
2733
#endif
2834

2935

system/lib/emmalloc.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
* malloc.
4040
*/
4141

42+
#include <stddef.h>
4243
#include <stdint.h>
4344
#include <unistd.h>
4445
#include <memory.h>
@@ -60,7 +61,9 @@ extern "C"
6061

6162
// Configuration: specifies the minimum alignment that malloc()ed memory outputs. Allocation requests with smaller alignment
6263
// than this will yield an allocation with this much alignment.
63-
#define MALLOC_ALIGNMENT 8
64+
#ifndef MALLOC_ALIGNMENT
65+
#define MALLOC_ALIGNMENT __alignof__(max_align_t)
66+
#endif
6467

6568
#define EMMALLOC_EXPORT __attribute__((weak, __visibility__("default")))
6669

tests/core/test_emmalloc.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ void test_realloc() {
114114
// realloc copies
115115
char* ptr = (char*)malloc(10);
116116
*ptr = 123;
117-
for (int i = 5; i <= 16; i++) {
117+
for (int i = 5; i <= 24; i++) {
118118
char* temp = (char*)realloc(ptr, i);
119119
assert(*temp == 123);
120120
assert(temp == ptr);
@@ -123,7 +123,7 @@ void test_realloc() {
123123
malloc(1);
124124
malloc(100);
125125
{
126-
char* temp = (char*)realloc(ptr, 17);
126+
char* temp = (char*)realloc(ptr, 25);
127127
assert(*temp == 123);
128128
assert(temp != ptr);
129129
ptr = temp;

tests/core/test_malloc_alignment.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2021 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+
#include <assert.h>
9+
#include <stdalign.h>
10+
#include <stdbool.h>
11+
#include <stddef.h>
12+
#include <stdint.h>
13+
#include <stdlib.h>
14+
15+
#ifdef ALIGN8
16+
#define MALLOC_ALIGN 8
17+
#else
18+
#define MALLOC_ALIGN alignof(max_align_t)
19+
#endif
20+
21+
int main(int argc, char **argv) {
22+
bool underaligned = false;
23+
24+
for (int size = 0; size < 16; ++size) {
25+
void *p = malloc(size);
26+
assert(((uintptr_t)p) % MALLOC_ALIGN == 0);
27+
if (((uintptr_t)p) % 16 != 0) {
28+
underaligned = true;
29+
}
30+
}
31+
32+
#if ALIGN8
33+
// Ensure that we have have at least one allocation that is under 16-byte
34+
// alignment when using the align8 variants of malloc.
35+
assert(underaligned);
36+
#endif
37+
return 0;
38+
}

tests/test_core.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8508,6 +8508,19 @@ def test_REVERSE_DEPS(self):
85088508
err = self.expect_fail([EMCC, 'connect.c', '-sREVERSE_DEPS=none'])
85098509
self.assertContained('undefined symbol: ntohs', err)
85108510

8511+
@parameterized({
8512+
'dlmalloc': ['dlmalloc'],
8513+
'emmalloc': ['emmalloc'],
8514+
'dlmalloc_align8': ['dlmalloc-align8'],
8515+
'emmalloc_align8': ['emmalloc-align8'],
8516+
})
8517+
def test_malloc_alignment(self, malloc):
8518+
self.set_setting('MALLOC', malloc)
8519+
if 'align8' in malloc:
8520+
self.emcc_args += ['-DALIGN8']
8521+
self.emcc_args += ['-std=gnu11', '-fno-builtin']
8522+
self.do_runf(test_file('core/test_malloc_alignment.c'))
8523+
85118524

85128525
# Generate tests for everything
85138526
def make_run(name, emcc_args, settings=None, env=None):

tools/system_libs.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,10 +1020,15 @@ class libmalloc(MTLibrary):
10201020
cflags = ['-O2', '-fno-builtin']
10211021

10221022
def __init__(self, **kwargs):
1023+
valid = ['dlmalloc', 'emmalloc', 'emmalloc-debug', 'emmalloc-memvalidate', 'emmalloc-verbose', 'emmalloc-memvalidate-verbose']
1024+
valid += [v + '-align8' for v in valid]
1025+
valid.append('none')
1026+
10231027
self.malloc = kwargs.pop('malloc')
1024-
if self.malloc not in ('dlmalloc', 'emmalloc', 'emmalloc-debug', 'emmalloc-memvalidate', 'emmalloc-verbose', 'emmalloc-memvalidate-verbose', 'none'):
1025-
raise Exception('malloc must be one of "emmalloc[-debug|-memvalidate][-verbose]", "dlmalloc" or "none", see settings.js')
1028+
if self.malloc not in valid:
1029+
shared.exit_with_error(f'invalid MALLOC setting: `{self.malloc}`. must be one of "emmalloc[-debug|-memvalidate][-verbose]", "dlmalloc" or "none", see settings.js')
10261030

1031+
self.align8 = kwargs.pop('align8')
10271032
self.use_errno = kwargs.pop('use_errno')
10281033
self.is_tracing = kwargs.pop('is_tracing')
10291034
self.memvalidate = kwargs.pop('memvalidate')
@@ -1033,7 +1038,7 @@ def __init__(self, **kwargs):
10331038
super().__init__(**kwargs)
10341039

10351040
def get_files(self):
1036-
malloc_base = self.malloc.replace('-memvalidate', '').replace('-verbose', '').replace('-debug', '')
1041+
malloc_base = self.malloc.replace('-memvalidate', '').replace('-verbose', '').replace('-debug', '').replace('-align8', '')
10371042
malloc = shared.path_from_root('system', 'lib', {
10381043
'dlmalloc': 'dlmalloc.c', 'emmalloc': 'emmalloc.cpp',
10391044
}[malloc_base])
@@ -1054,6 +1059,8 @@ def get_cflags(self):
10541059
cflags += ['-DMALLOC_FAILURE_ACTION=', '-DEMSCRIPTEN_NO_ERRNO']
10551060
if self.is_tracing:
10561061
cflags += ['--tracing']
1062+
if self.align8:
1063+
cflags += ['-DMALLOC_ALIGNMENT=8']
10571064
return cflags
10581065

10591066
def get_base_name_prefix(self):
@@ -1075,7 +1082,7 @@ def can_use(self):
10751082

10761083
@classmethod
10771084
def vary_on(cls):
1078-
return super().vary_on() + ['is_debug', 'use_errno', 'is_tracing', 'memvalidate', 'verbose']
1085+
return super().vary_on() + ['is_debug', 'use_errno', 'is_tracing', 'memvalidate', 'verbose', 'align8']
10791086

10801087
@classmethod
10811088
def get_default_variation(cls, **kwargs):
@@ -1084,6 +1091,7 @@ def get_default_variation(cls, **kwargs):
10841091
is_debug=settings.ASSERTIONS >= 2,
10851092
use_errno=settings.SUPPORT_ERRNO,
10861093
is_tracing=settings.EMSCRIPTEN_TRACING,
1094+
align8='align8' in settings.MALLOC,
10871095
memvalidate='memvalidate' in settings.MALLOC,
10881096
verbose='verbose' in settings.MALLOC,
10891097
**kwargs

0 commit comments

Comments
 (0)