[lld][WebAssembly] Align __heap_base
authorAyke van Laethem <aykevanlaethem@gmail.com>
Wed, 21 Jul 2021 21:35:10 +0000 (23:35 +0200)
committerAyke van Laethem <aykevanlaethem@gmail.com>
Sat, 24 Jul 2021 12:03:26 +0000 (14:03 +0200)
__heap_base was not aligned. In practice, it will often be aligned
simply because it follows the stack, but when the stack is placed at the
beginning (with the --stack-first option), the __heap_base might be
unaligned. It could even be byte-aligned.

At least wasi-libc appears to expect that __heap_base is aligned:
https://github.com/WebAssembly/wasi-libc/blob/659ff414560721b1660a19685110e484a081c3d4/dlmalloc/src/malloc.c#L5224

While WebAssembly itself does not appear to require any alignment for
memory accesses, it is sometimes required when sharing a pointer
externally. For example, WASI might expect alignment up to 8:
https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-timestamp-u64

This issue got introduced with the addition of the --stack-first flag:
https://reviews.llvm.org/D46141
I suspect the lack of alignment wasn't intentional here.

Differential Revision: https://reviews.llvm.org/D106499

lld/test/wasm/Inputs/stack-first.s [new file with mode: 0644]
lld/test/wasm/stack-first.test
lld/wasm/Writer.cpp

diff --git a/lld/test/wasm/Inputs/stack-first.s b/lld/test/wasm/Inputs/stack-first.s
new file mode 100644 (file)
index 0000000..a2a356b
--- /dev/null
@@ -0,0 +1,11 @@
+  .globl  _start
+_start:
+  .functype _start () -> ()
+  end_function
+
+  .globl     someByte
+  .type      someByte,@object
+  .section  .data.someByte,"",@
+someByte:
+  .int8 42
+  .size someByte, 1
index 9d7f077..72e1a00 100644 (file)
@@ -2,9 +2,10 @@
 ; memory.  In this case the --stack-first option is being passed along with a
 ; stack size of 512.  This means (since the stack grows down) the stack pointer
 ; global should be initialized to 512.
+; Also test that __heap_base is still aligned with the --stack-first option.
 
-RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/start.s -o %t.o
-RUN: wasm-ld -z stack-size=512 --stack-first --export=__data_end --export=__heap_base -o %t.wasm %t.o
+RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %p/Inputs/stack-first.s -o %t.o
+RUN: wasm-ld -z stack-size=512 --stack-first --export=__data_end --export=__heap_base --export=someByte -o %t.wasm %t.o
 RUN: obj2yaml %t.wasm | FileCheck %s
 
 CHECK:        - Type:            GLOBAL
@@ -26,7 +27,13 @@ CHECK-NEXT:         Type:            I32
 CHECK-NEXT:         Mutable:         false
 CHECK-NEXT:         InitExpr:        
 CHECK-NEXT:           Opcode:          I32_CONST
-CHECK-NEXT:           Value:           512
+CHECK-NEXT:           Value:           513
+CHECK-NEXT:       - Index:           3
+CHECK-NEXT:         Type:            I32
+CHECK-NEXT:         Mutable:         false
+CHECK-NEXT:         InitExpr:        
+CHECK-NEXT:           Opcode:          I32_CONST
+CHECK-NEXT:           Value:           528
 CHECK-NEXT:   - Type:            EXPORT
 CHECK-NEXT:     Exports:         
 CHECK-NEXT:       - Name:            memory
@@ -35,9 +42,12 @@ CHECK-NEXT:         Index:           0
 CHECK-NEXT:       - Name:            _start
 CHECK-NEXT:         Kind:            FUNCTION
 CHECK-NEXT:         Index:           0
-CHECK-NEXT:       - Name:            __data_end
+CHECK-NEXT:       - Name:            someByte
 CHECK-NEXT:         Kind:            GLOBAL
 CHECK-NEXT:         Index:           1
-CHECK-NEXT:       - Name:            __heap_base
+CHECK-NEXT:       - Name:            __data_end
 CHECK-NEXT:         Kind:            GLOBAL
 CHECK-NEXT:         Index:           2
+CHECK-NEXT:       - Name:            __heap_base
+CHECK-NEXT:         Kind:            GLOBAL
+CHECK-NEXT:         Index:           3
index e94ea17..cf9356b 100644 (file)
@@ -43,6 +43,7 @@ using namespace llvm::wasm;
 namespace lld {
 namespace wasm {
 static constexpr int stackAlignment = 16;
+static constexpr int heapAlignment = 16;
 
 namespace {
 
@@ -310,9 +311,12 @@ void Writer::layoutMemory() {
     placeStack();
 
   if (WasmSym::heapBase) {
-    // Set `__heap_base` to directly follow the end of the stack or global data.
-    // The fact that this comes last means that a malloc/brk implementation
-    // can grow the heap at runtime.
+    // Set `__heap_base` to follow the end of the stack or global data. The
+    // fact that this comes last means that a malloc/brk implementation can
+    // grow the heap at runtime.
+    // We'll align the heap base here because memory allocators might expect
+    // __heap_base to be aligned already.
+    memoryPtr = alignTo(memoryPtr, heapAlignment);
     log("mem: heap base   = " + Twine(memoryPtr));
     WasmSym::heapBase->setVA(memoryPtr);
   }