Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -70,4 +70,7 @@ WebAssembly Improvements ------------------------ -* ... +* `__data_end` and `__heap_base` are no longer exported by default, + as it's best to keep them internal when possible. They can be + explicitly exported with `--export=__data_end` and + `--export=__heap_base`, respectively. Index: docs/WebAssembly.rst =================================================================== --- docs/WebAssembly.rst +++ docs/WebAssembly.rst @@ -84,6 +84,13 @@ By default the function table is neither imported nor exported, but defined for internal use only. +.. option:: --emscripten + + Enable Emscripten mode. + +This option enables Emscripten mode, which exports symbols needed by +Emscripten and enables options related to dynamic linking. + Behaviour --------- @@ -109,24 +116,23 @@ and use these stub functions at the otherwise invalid call sites. The default behaviour is to generate these stub function and to produce -a warning. The ``--falal-warnings`` flag can be used to disable this behaviour +a warning. The ``--fatal-warnings`` flag can be used to disable this behaviour and error out if mismatched are found. Imports and Exports ~~~~~~~~~~~~~~~~~~~ -When building a shared library any symbols marked as ``visibility=default`` will -be exported. When building an executable, only the entry point and symbols -flagged as ``WASM_SYMBOL_EXPORTED`` are exported by default. In LLVM the -``WASM_SYMBOL_EXPORTED`` flag is applied to any symbol in the ``llvm.used`` list -which corresponds to ``__attribute__((used))`` in C/C++ sources. +By default, the only symbols exported from the WebAssembly module are the +entry-point symbol and the primary linear memory, named "memory". These ABI +details are not yet stable. In addition, symbols can be exported via the linker command line using -``--export``. +``--export``. ``--export-dynamic`` exports all symbols with "default" +visibility. ``--export-all`` exports all symbols with global binding. -Finally, just like with native ELF linker the ``--export-dynamic`` flag can be -used to export symbol in the executable which are marked as -``visibility=default``. +With ``--emscripten``, the ``--shared`` option implies ``--export-dynamic``, +and symbols marked with ``__attribute__((used))`` are also exported from +the WebAssembly module. Garbage Collection ~~~~~~~~~~~~~~~~~~ Index: test/wasm/export.ll =================================================================== --- test/wasm/export.ll +++ test/wasm/export.ll @@ -1,8 +1,15 @@ +; Test in default mode ; RUN: llc -filetype=obj %s -o %t.o ; RUN: not wasm-ld --export=missing -o %t.wasm %t.o 2>&1 | FileCheck -check-prefix=CHECK-ERROR %s ; RUN: wasm-ld --export=hidden_function -o %t.wasm %t.o ; RUN: obj2yaml %t.wasm | FileCheck %s +; Now test in Emscripten mode +; RUN: llc -filetype=obj %s -o %t.o -mtriple=wasm32-unknown-emscripten +; RUN: not wasm-ld --export=missing -o %t.wasm %t.o 2>&1 | FileCheck -check-prefix=CHECK-ERROR %s +; RUN: wasm-ld --emscripten --export=hidden_function -o %t.wasm %t.o +; RUN: obj2yaml %t.wasm | FileCheck %s --check-prefixes=CHECK,EMSCRIPTEN + @llvm.used = appending global [1 x i8*] [i8* bitcast (i32 ()* @used_function to i8*)], section "llvm.metadata" target triple = "wasm32-unknown-unknown" @@ -43,9 +50,9 @@ ; CHECK-NEXT: - Name: hidden_function ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 0 -; CHECK-NEXT: - Name: used_function -; CHECK-NEXT: Kind: FUNCTION -; CHECK-NEXT: Index: 1 +; EMSCRIPTEN-NEXT: - Name: used_function +; EMSCRIPTEN-NEXT: Kind: FUNCTION +; EMSCRIPTEN-NEXT: Index: 1 ; CHECK-NEXT: - Name: _start ; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Index: 2 Index: test/wasm/pie.ll =================================================================== --- test/wasm/pie.ll +++ test/wasm/pie.ll @@ -1,8 +1,8 @@ ; RUN: llc -relocation-model=pic -filetype=obj %s -o %t.o -; RUN: wasm-ld --no-gc-sections --allow-undefined -pie -o %t.wasm %t.o +; RUN: wasm-ld --emscripten --no-gc-sections --allow-undefined -pie -o %t.wasm %t.o ; RUN: obj2yaml %t.wasm | FileCheck %s -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-emscripten" @data = global i32 2, align 4 @data_external = external global i32 Index: test/wasm/shared-needed.ll =================================================================== --- test/wasm/shared-needed.ll +++ test/wasm/shared-needed.ll @@ -1,13 +1,13 @@ ; RUN: llc -filetype=obj %s -o %t.o ; RUN: llc -filetype=obj %p/Inputs/ret32.ll -o %t.ret32.o -; RUN: wasm-ld -shared -o %t1.so %t.o +; RUN: wasm-ld --emscripten -shared -o %t1.so %t.o ; RUN: obj2yaml %t1.so | FileCheck %s -check-prefix=SO1 -; RUN: wasm-ld -shared -o %t2.so %t1.so %t.ret32.o +; RUN: wasm-ld --emscripten -shared -o %t2.so %t1.so %t.ret32.o ; RUN: obj2yaml %t2.so | FileCheck %s -check-prefix=SO2 -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-emscripten" @data = global i32 2, align 4 Index: test/wasm/shared.ll =================================================================== --- test/wasm/shared.ll +++ test/wasm/shared.ll @@ -1,8 +1,8 @@ ; RUN: llc -relocation-model=pic -filetype=obj %s -o %t.o -; RUN: wasm-ld -shared -o %t.wasm %t.o +; RUN: wasm-ld --emscripten -shared -o %t.wasm %t.o ; RUN: obj2yaml %t.wasm | FileCheck %s -target triple = "wasm32-unknown-unknown" +target triple = "wasm32-unknown-emscripten" @data = hidden global i32 2, align 4 @data_external = external global i32 Index: test/wasm/undefined-data.ll =================================================================== --- test/wasm/undefined-data.ll +++ test/wasm/undefined-data.ll @@ -1,7 +1,7 @@ ; RUN: llc -filetype=obj %s -o %t.o ; RUN: not wasm-ld -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=UNDEF ; RUN: not wasm-ld --allow-undefined -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=ALLOW -; RUN: not wasm-ld --shared -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=SHARED +; RUN: not wasm-ld --emscripten --shared -o %t.wasm %t.o 2>&1 | FileCheck %s -check-prefix=SHARED target triple = "wasm32-unknown-unknown" Index: wasm/Config.h =================================================================== --- wasm/Config.h +++ wasm/Config.h @@ -27,6 +27,7 @@ bool CompressRelocations; bool Demangle; bool DisableVerify; + bool Emscripten; bool EmitRelocs; bool ExportAll; bool ExportDynamic; Index: wasm/Driver.cpp =================================================================== --- wasm/Driver.cpp +++ wasm/Driver.cpp @@ -338,6 +338,7 @@ parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)), "--thinlto-cache-policy: invalid cache policy"); Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u); + Config->Emscripten = Args.hasArg(OPT_emscripten); errorHandler().Verbose = Args.hasArg(OPT_verbose); LLVM_DEBUG(errorHandler().Verbose = true); ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true); @@ -370,6 +371,8 @@ } if (Config->Shared) { + // -shared is only supported in Emscripten mode, where it means the + // following options: Config->ImportMemory = true; Config->ExportDynamic = true; Config->AllowUndefined = true; @@ -411,6 +414,13 @@ if (Config->Pie) error("-r and -pie may not be used together"); } + + if (!Config->Emscripten) { + if (Config->Shared) + error("-shared is currently only supported under Emscripten"); + if (Config->Pie) + error("-pie is currently only supported under Emscripten"); + } } // Force Sym to be entered in the output. Used for -u or equivalent. Index: wasm/LTO.cpp =================================================================== --- wasm/LTO.cpp +++ wasm/LTO.cpp @@ -105,6 +105,7 @@ // be removed. R.Prevailing = !ObjSym.isUndefined() && Sym->getFile() == &F; R.VisibleToRegularObj = Config->Relocatable || Sym->IsUsedInRegularObj || + Sym->isNoStrip() || (R.Prevailing && Sym->isExported()); if (R.Prevailing) undefine(Sym); Index: wasm/MarkLive.cpp =================================================================== --- wasm/MarkLive.cpp +++ wasm/MarkLive.cpp @@ -64,7 +64,7 @@ // We need to preserve any exported symbol for (Symbol *Sym : Symtab->getSymbols()) - if (Sym->isExported()) + if (Sym->isNoStrip() || Sym->isExported()) Enqueue(Sym); // For relocatable output, we need to preserve all the ctor functions Index: wasm/Options.td =================================================================== --- wasm/Options.td +++ wasm/Options.td @@ -34,8 +34,8 @@ def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">; defm export_dynamic: B<"export-dynamic", - "Put symbols in the dynamic symbol table", - "Do not put symbols in the dynamic symbol table (default)">; + "Export all global symbols with visibility \"default\"", + "Do not automatically export global symbols with visibility \"default\" (default)">; def entry: S<"entry">, MetaVarName<"">, HelpText<"Name of entry point symbol">; @@ -158,6 +158,9 @@ def stack_first: F<"stack-first">, HelpText<"Place stack at start of linear memory rather than after data">; +def emscripten: F<"emscripten">, + HelpText<"Enable Emscripten support">; + defm whole_archive: B<"whole-archive", "Force load of all members in a static library", "Do not force load of all members in a static library (default)">; Index: wasm/Symbols.h =================================================================== --- wasm/Symbols.h +++ wasm/Symbols.h @@ -104,6 +104,10 @@ WasmSymbolType getWasmType() const; bool isExported() const; + // Indicates that the symbol is used in an __attribute__((used)) directive + // or similar. + bool isNoStrip() const; + // True if the symbol was used for linking and thus need to be added to the // output file's symbol table. This is true for all symbols except for // unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that Index: wasm/Symbols.cpp =================================================================== --- wasm/Symbols.cpp +++ wasm/Symbols.cpp @@ -138,6 +138,10 @@ return Flags & WASM_SYMBOL_EXPORTED; } +bool Symbol::isNoStrip() const { + return Flags & WASM_SYMBOL_NO_STRIP; +} + uint32_t FunctionSymbol::getFunctionIndex() const { if (auto *F = dyn_cast(this)) return F->Function->getFunctionIndex();