[WebAssembly] Add --stack-first option which places the shadow stack at start of linear memory

Fixes https://bugs.llvm.org/show_bug.cgi?id=37181

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

llvm-svn: 331467
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 261fbc1..45abb3e 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -580,22 +580,48 @@
 
 // Fix the memory layout of the output binary.  This assigns memory offsets
 // to each of the input data sections as well as the explicit stack region.
-// The memory layout is as follows, from low to high.
+// The default memory layout is as follows, from low to high.
+//
 //  - initialized data (starting at Config->GlobalBase)
 //  - BSS data (not currently implemented in llvm)
 //  - explicit stack (Config->ZStackSize)
 //  - heap start / unallocated
+//
+// The --stack-first option means that stack is placed before any static data.
+// This can be useful since it means that stack overflow traps immediately rather
+// than overwriting global data, but also increases code size since all static
+// data loads and stores requires larger offsets.
 void Writer::layoutMemory() {
-  uint32_t MemoryPtr = 0;
-  MemoryPtr = Config->GlobalBase;
-  log("mem: global base = " + Twine(Config->GlobalBase));
-
   createOutputSegments();
 
+  uint32_t MemoryPtr = 0;
+
+  auto PlaceStack = [&]() {
+    if (Config->Relocatable)
+      return;
+    MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
+    if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
+      error("stack size must be " + Twine(kStackAlignment) + "-byte aligned");
+    log("mem: stack size  = " + Twine(Config->ZStackSize));
+    log("mem: stack base  = " + Twine(MemoryPtr));
+    MemoryPtr += Config->ZStackSize;
+    WasmSym::StackPointer->Global->Global.InitExpr.Value.Int32 = MemoryPtr;
+    log("mem: stack top   = " + Twine(MemoryPtr));
+  };
+
+  if (Config->StackFirst) {
+    PlaceStack();
+  } else {
+    MemoryPtr = Config->GlobalBase;
+    log("mem: global base = " + Twine(Config->GlobalBase));
+  }
+
+  uint32_t DataStart = MemoryPtr;
+
   // Arbitrarily set __dso_handle handle to point to the start of the data
   // segments.
   if (WasmSym::DsoHandle)
-    WasmSym::DsoHandle->setVirtualAddress(MemoryPtr);
+    WasmSym::DsoHandle->setVirtualAddress(DataStart);
 
   for (OutputSegment *Seg : Segments) {
     MemoryPtr = alignTo(MemoryPtr, Seg->Alignment);
@@ -609,22 +635,15 @@
   if (WasmSym::DataEnd)
     WasmSym::DataEnd->setVirtualAddress(MemoryPtr);
 
-  log("mem: static data = " + Twine(MemoryPtr - Config->GlobalBase));
+  log("mem: static data = " + Twine(MemoryPtr - DataStart));
 
-  // Stack comes after static data and bss
+  if (!Config->StackFirst)
+    PlaceStack();
+
+  // 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.
   if (!Config->Relocatable) {
-    MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
-    if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
-      error("stack size must be " + Twine(kStackAlignment) + "-byte aligned");
-    log("mem: stack size  = " + Twine(Config->ZStackSize));
-    log("mem: stack base  = " + Twine(MemoryPtr));
-    MemoryPtr += Config->ZStackSize;
-    WasmSym::StackPointer->Global->Global.InitExpr.Value.Int32 = MemoryPtr;
-    log("mem: stack top   = " + Twine(MemoryPtr));
-
-    // Set `__heap_base` to directly follow the end of the stack.  We don't
-    // allocate any heap memory up front, but instead really on the malloc/brk
-    // implementation growing the memory at runtime.
     WasmSym::HeapBase->setVirtualAddress(MemoryPtr);
     log("mem: heap base   = " + Twine(MemoryPtr));
   }