Skip to content
This repository was archived by the owner on Apr 23, 2020. It is now read-only.

Commit 0db522b

Browse files
committed
[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 git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@331467 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent a2c66d3 commit 0db522b

File tree

5 files changed

+85
-19
lines changed

5 files changed

+85
-19
lines changed

test/wasm/stack-first.test

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
; Test that the --stack-first option places the stack at the start of linear
2+
; memory. In this case the --stack-first option is being passed along with a
3+
; stack size of 512. This means (since the stack grows down) the stack pointer
4+
; global should be initialized to 512.
5+
6+
RUN: llc -filetype=obj %p/Inputs/start.ll -o %t.o
7+
8+
RUN: wasm-ld --check-signatures -z stack-size=512 --stack-first --allow-undefined -o %t.wasm %t.o
9+
RUN: obj2yaml %t.wasm | FileCheck %s
10+
11+
CHECK: - Type: GLOBAL
12+
CHECK-NEXT: Globals:
13+
CHECK-NEXT: - Index: 0
14+
CHECK-NEXT: Type: I32
15+
CHECK-NEXT: Mutable: true
16+
CHECK-NEXT: InitExpr:
17+
CHECK-NEXT: Opcode: I32_CONST
18+
CHECK-NEXT: Value: 512
19+
CHECK-NEXT: - Index: 1
20+
CHECK-NEXT: Type: I32
21+
CHECK-NEXT: Mutable: false
22+
CHECK-NEXT: InitExpr:
23+
CHECK-NEXT: Opcode: I32_CONST
24+
CHECK-NEXT: Value: 512
25+
CHECK-NEXT: - Index: 2
26+
CHECK-NEXT: Type: I32
27+
CHECK-NEXT: Mutable: false
28+
CHECK-NEXT: InitExpr:
29+
CHECK-NEXT: Opcode: I32_CONST
30+
CHECK-NEXT: Value: 512
31+
CHECK-NEXT: - Type: EXPORT
32+
CHECK-NEXT: Exports:
33+
CHECK-NEXT: - Name: memory
34+
CHECK-NEXT: Kind: MEMORY
35+
CHECK-NEXT: Index: 0
36+
CHECK-NEXT: - Name: __heap_base
37+
CHECK-NEXT: Kind: GLOBAL
38+
CHECK-NEXT: Index: 1
39+
CHECK-NEXT: - Name: __data_end
40+
CHECK-NEXT: Kind: GLOBAL
41+
CHECK-NEXT: Index: 2
42+

wasm/Config.h

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct Configuration {
2929
bool Relocatable;
3030
bool StripAll;
3131
bool StripDebug;
32+
bool StackFirst;
3233
uint32_t GlobalBase;
3334
uint32_t InitialMemory;
3435
uint32_t MaxMemory;

wasm/Driver.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
298298
Config->SearchPaths = args::getStrings(Args, OPT_L);
299299
Config->StripAll = Args.hasArg(OPT_strip_all);
300300
Config->StripDebug = Args.hasArg(OPT_strip_debug);
301+
Config->StackFirst = Args.hasArg(OPT_stack_first);
301302
errorHandler().Verbose = Args.hasArg(OPT_verbose);
302303
ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
303304

wasm/Options.td

+3
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ def max_memory: J<"max-memory=">,
124124
def no_entry: F<"no-entry">,
125125
HelpText<"Do not output any entry point">;
126126

127+
def stack_first: F<"stack-first">,
128+
HelpText<"Place stack at start of linear memory rather than after data">;
129+
127130
// Aliases
128131
def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
129132
def alias_entry_entry: J<"entry=">, Alias<entry>;

wasm/Writer.cpp

+38-19
Original file line numberDiff line numberDiff line change
@@ -580,22 +580,48 @@ void Writer::writeSections() {
580580

581581
// Fix the memory layout of the output binary. This assigns memory offsets
582582
// to each of the input data sections as well as the explicit stack region.
583-
// The memory layout is as follows, from low to high.
583+
// The default memory layout is as follows, from low to high.
584+
//
584585
// - initialized data (starting at Config->GlobalBase)
585586
// - BSS data (not currently implemented in llvm)
586587
// - explicit stack (Config->ZStackSize)
587588
// - heap start / unallocated
589+
//
590+
// The --stack-first option means that stack is placed before any static data.
591+
// This can be useful since it means that stack overflow traps immediately rather
592+
// than overwriting global data, but also increases code size since all static
593+
// data loads and stores requires larger offsets.
588594
void Writer::layoutMemory() {
595+
createOutputSegments();
596+
589597
uint32_t MemoryPtr = 0;
590-
MemoryPtr = Config->GlobalBase;
591-
log("mem: global base = " + Twine(Config->GlobalBase));
592598

593-
createOutputSegments();
599+
auto PlaceStack = [&]() {
600+
if (Config->Relocatable)
601+
return;
602+
MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
603+
if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
604+
error("stack size must be " + Twine(kStackAlignment) + "-byte aligned");
605+
log("mem: stack size = " + Twine(Config->ZStackSize));
606+
log("mem: stack base = " + Twine(MemoryPtr));
607+
MemoryPtr += Config->ZStackSize;
608+
WasmSym::StackPointer->Global->Global.InitExpr.Value.Int32 = MemoryPtr;
609+
log("mem: stack top = " + Twine(MemoryPtr));
610+
};
611+
612+
if (Config->StackFirst) {
613+
PlaceStack();
614+
} else {
615+
MemoryPtr = Config->GlobalBase;
616+
log("mem: global base = " + Twine(Config->GlobalBase));
617+
}
618+
619+
uint32_t DataStart = MemoryPtr;
594620

595621
// Arbitrarily set __dso_handle handle to point to the start of the data
596622
// segments.
597623
if (WasmSym::DsoHandle)
598-
WasmSym::DsoHandle->setVirtualAddress(MemoryPtr);
624+
WasmSym::DsoHandle->setVirtualAddress(DataStart);
599625

600626
for (OutputSegment *Seg : Segments) {
601627
MemoryPtr = alignTo(MemoryPtr, Seg->Alignment);
@@ -609,22 +635,15 @@ void Writer::layoutMemory() {
609635
if (WasmSym::DataEnd)
610636
WasmSym::DataEnd->setVirtualAddress(MemoryPtr);
611637

612-
log("mem: static data = " + Twine(MemoryPtr - Config->GlobalBase));
638+
log("mem: static data = " + Twine(MemoryPtr - DataStart));
613639

614-
// Stack comes after static data and bss
615-
if (!Config->Relocatable) {
616-
MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
617-
if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
618-
error("stack size must be " + Twine(kStackAlignment) + "-byte aligned");
619-
log("mem: stack size = " + Twine(Config->ZStackSize));
620-
log("mem: stack base = " + Twine(MemoryPtr));
621-
MemoryPtr += Config->ZStackSize;
622-
WasmSym::StackPointer->Global->Global.InitExpr.Value.Int32 = MemoryPtr;
623-
log("mem: stack top = " + Twine(MemoryPtr));
640+
if (!Config->StackFirst)
641+
PlaceStack();
624642

625-
// Set `__heap_base` to directly follow the end of the stack. We don't
626-
// allocate any heap memory up front, but instead really on the malloc/brk
627-
// implementation growing the memory at runtime.
643+
// Set `__heap_base` to directly follow the end of the stack or global data.
644+
// The fact that this comes last means that a malloc/brk implementation
645+
// can grow the heap at runtime.
646+
if (!Config->Relocatable) {
628647
WasmSym::HeapBase->setVirtualAddress(MemoryPtr);
629648
log("mem: heap base = " + Twine(MemoryPtr));
630649
}

0 commit comments

Comments
 (0)