diff --git a/lld/docs/WebAssembly.rst b/lld/docs/WebAssembly.rst index 3f554de46d38a..1dd05d67983c7 100644 --- a/lld/docs/WebAssembly.rst +++ b/lld/docs/WebAssembly.rst @@ -135,6 +135,10 @@ WebAssembly-specific options: Maximum size of the linear memory. Default: unlimited. +.. option:: --no-growable-memory + + Set maximum size of the linear memory to its initial size, disallowing memory growth. + By default the function table is neither imported nor exported, but defined for internal use only. diff --git a/lld/test/wasm/data-layout.s b/lld/test/wasm/data-layout.s index 2a447aad62216..a68bc032e4840 100644 --- a/lld/test/wasm/data-layout.s +++ b/lld/test/wasm/data-layout.s @@ -103,6 +103,22 @@ local_struct_internal_ptr: # CHECK-MAX-NEXT: Minimum: 0x2 # CHECK-MAX-NEXT: Maximum: 0x2 +# RUN: wasm-ld --no-entry --initial-memory=327680 --no-growable-memory \ +# RUN: -o %t_max.wasm %t.hello32.o +# RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-NO-GROWTH + +# CHECK-NO-GROWTH: - Type: MEMORY +# CHECK-NO-GROWTH-NEXT: Memories: +# CHECK-NO-GROWTH-NEXT: - Flags: [ HAS_MAX ] +# CHECK-NO-GROWTH-NEXT: Minimum: 0x5 +# CHECK-NO-GROWTH-NEXT: Maximum: 0x5 + +# RUN: not wasm-ld --max-memory=262144 --no-growable-memory \ +# RUN: --no-entry -o %t_max.wasm %t.hello32.o 2>&1 \ +# RUN: | FileCheck %s --check-prefix CHECK-NO-GROWTH-COMPAT-ERROR + +# CHECK-NO-GROWTH-COMPAT-ERROR: --max-memory is incompatible with --no-growable-memory + # RUN: wasm-ld -no-gc-sections --allow-undefined --no-entry --shared-memory \ # RUN: --features=atomics,bulk-memory --initial-memory=131072 \ # RUN: --max-memory=131072 -o %t_max.wasm %t32.o %t.hello32.o diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h index 97c508bda6a1c..266348fef4031 100644 --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -78,6 +78,7 @@ struct Configuration { uint64_t initialHeap; uint64_t initialMemory; uint64_t maxMemory; + bool noGrowableMemory; // The table offset at which to place function addresses. We reserve zero // for the null function pointer. This gets set to 1 for executables and 0 // for shared libraries (since they always added to a dynamic offset at diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 635f19f78b15e..df7d4d1cc3d67 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -542,9 +542,15 @@ static void readConfigs(opt::InputArgList &args) { config->initialHeap = args::getInteger(args, OPT_initial_heap, 0); config->initialMemory = args::getInteger(args, OPT_initial_memory, 0); config->maxMemory = args::getInteger(args, OPT_max_memory, 0); + config->noGrowableMemory = args.hasArg(OPT_no_growable_memory); config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize); + if (config->maxMemory != 0 && config->noGrowableMemory) { + // Erroring out here is simpler than defining precedence rules. + error("--max-memory is incompatible with --no-growable-memory"); + } + // Default value of exportDynamic depends on `-shared` config->exportDynamic = args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, config->shared); diff --git a/lld/wasm/Options.td b/lld/wasm/Options.td index 8190717cef63b..70b5aadc26c2a 100644 --- a/lld/wasm/Options.td +++ b/lld/wasm/Options.td @@ -230,6 +230,9 @@ def initial_memory: JJ<"initial-memory=">, def max_memory: JJ<"max-memory=">, HelpText<"Maximum size of the linear memory">; +def no_growable_memory: FF<"no-growable-memory">, + HelpText<"Set maximum size of the linear memory to its initial size">; + def no_entry: FF<"no-entry">, HelpText<"Do not output any entry point">; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index d1a06c9ac9c2a..55eff995fb8a1 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -473,6 +473,7 @@ void Writer::layoutMemory() { WasmSym::heapEnd->setVA(memoryPtr); } + uint64_t maxMemory = 0; if (config->maxMemory != 0) { if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize)) error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned"); @@ -481,20 +482,23 @@ void Writer::layoutMemory() { if (config->maxMemory > maxMemorySetting) error("maximum memory too large, cannot be greater than " + Twine(maxMemorySetting)); + + maxMemory = config->maxMemory; + } else if (config->noGrowableMemory) { + maxMemory = memoryPtr; } - // Check max if explicitly supplied or required by shared memory - if (config->maxMemory != 0 || config->sharedMemory) { - uint64_t max = config->maxMemory; - if (max == 0) { - // If no maxMemory config was supplied but we are building with - // shared memory, we need to pick a sensible upper limit. - if (ctx.isPic) - max = maxMemorySetting; - else - max = memoryPtr; - } - out.memorySec->maxMemoryPages = max / WasmPageSize; + // If no maxMemory config was supplied but we are building with + // shared memory, we need to pick a sensible upper limit. + if (config->sharedMemory && maxMemory == 0) { + if (ctx.isPic) + maxMemory = maxMemorySetting; + else + maxMemory = memoryPtr; + } + + if (maxMemory != 0) { + out.memorySec->maxMemoryPages = maxMemory / WasmPageSize; log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages)); } }