diff --git a/lld/test/wasm/export-all.s b/lld/test/wasm/export-all.s --- a/lld/test/wasm/export-all.s +++ b/lld/test/wasm/export-all.s @@ -46,9 +46,12 @@ # CHECK-NEXT: - Name: __heap_base # CHECK-NEXT: Kind: GLOBAL # CHECK-NEXT: Index: 6 -# CHECK-NEXT: - Name: __memory_base +# CHECK-NEXT: - Name: __heap_end # CHECK-NEXT: Kind: GLOBAL # CHECK-NEXT: Index: 7 -# CHECK-NEXT: - Name: __table_base +# CHECK-NEXT: - Name: __memory_base # CHECK-NEXT: Kind: GLOBAL # CHECK-NEXT: Index: 8 +# CHECK-NEXT: - Name: __table_base +# CHECK-NEXT: Kind: GLOBAL +# CHECK-NEXT: Index: 9 diff --git a/lld/test/wasm/mutable-global-exports.s b/lld/test/wasm/mutable-global-exports.s --- a/lld/test/wasm/mutable-global-exports.s +++ b/lld/test/wasm/mutable-global-exports.s @@ -92,12 +92,15 @@ # CHECK-ALL-NEXT: - Name: __heap_base # CHECK-ALL-NEXT: Kind: GLOBAL # CHECK-ALL-NEXT: Index: 7 -# CHECK-ALL-NEXT: - Name: __memory_base +# CHECK-ALL-NEXT: - Name: __heap_end # CHECK-ALL-NEXT: Kind: GLOBAL # CHECK-ALL-NEXT: Index: 8 -# CHECK-ALL-NEXT: - Name: __table_base +# CHECK-ALL-NEXT: - Name: __memory_base # CHECK-ALL-NEXT: Kind: GLOBAL # CHECK-ALL-NEXT: Index: 9 +# CHECK-ALL-NEXT: - Name: __table_base +# CHECK-ALL-NEXT: Kind: GLOBAL +# CHECK-ALL-NEXT: Index: 10 # CHECK-ALL-NEXT: - Type: CODE # CHECK-ALL: Name: target_features diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -768,6 +768,7 @@ WasmSym::stackHigh = symtab->addOptionalDataSymbol("__stack_high"); WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base"); WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base"); + WasmSym::heapEnd = symtab->addOptionalDataSymbol("__heap_end"); WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base"); WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base"); if (config->is64.value_or(false)) diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -554,6 +554,12 @@ // therefore be used as a backing store for brk()/malloc() implementations. static DefinedData *heapBase; + // __heap_end + // Symbol marking the end of the data, bss and explicit stack. Any linear + // memory following this address is not used by the linked code and can + // therefore be used as a backing store for brk()/malloc() implementations. + static DefinedData *heapEnd; + // __wasm_init_memory_flag // Symbol whose contents are nonzero iff memory has already been initialized. static DefinedData *initMemoryFlag; diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -84,6 +84,7 @@ DefinedData *WasmSym::dataEnd; DefinedData *WasmSym::globalBase; DefinedData *WasmSym::heapBase; +DefinedData *WasmSym::heapEnd; DefinedData *WasmSym::initMemoryFlag; GlobalSymbol *WasmSym::stackPointer; DefinedData *WasmSym::stackLow; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -344,10 +344,20 @@ Twine(maxMemorySetting)); memoryPtr = config->initialMemory; } - out.memorySec->numMemoryPages = - alignTo(memoryPtr, WasmPageSize) / WasmPageSize; + + memoryPtr = alignTo(memoryPtr, WasmPageSize); + + out.memorySec->numMemoryPages = memoryPtr / WasmPageSize; log("mem: total pages = " + Twine(out.memorySec->numMemoryPages)); + if (WasmSym::heapEnd) { + // Set `__heap_end` to follow the end of the statically allocated linear + // memory. The fact that this comes last means that a malloc/brk + // implementation can grow the heap at runtime. + log("mem: heap end = " + Twine(memoryPtr)); + WasmSym::heapEnd->setVA(memoryPtr); + } + if (config->maxMemory != 0) { if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize)) error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned"); @@ -367,7 +377,7 @@ if (config->isPic) max = maxMemorySetting; else - max = alignTo(memoryPtr, WasmPageSize); + max = memoryPtr; } out.memorySec->maxMemoryPages = max / WasmPageSize; log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages));