[lld] 6cd8511 - [WebAssembly] New-style command support
Dan Gohman via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 19:02:59 PDT 2020
Author: Dan Gohman
Date: 2020-09-30T19:02:40-07:00
New Revision: 6cd8511e5932e4a53b2bb7780f69489355fc7783
URL: https://github.com/llvm/llvm-project/commit/6cd8511e5932e4a53b2bb7780f69489355fc7783
DIFF: https://github.com/llvm/llvm-project/commit/6cd8511e5932e4a53b2bb7780f69489355fc7783.diff
LOG: [WebAssembly] New-style command support
This adds support for new-style command support. In this mode, all exports
are considered command entrypoints, and the linker inserts calls to
`__wasm_call_ctors` and `__wasm_call_dtors` for all such entrypoints.
This enables support for:
- Command entrypoints taking arguments other than strings and return values
other than `int`.
- Multicall executables without requiring on the use of string-based
command-line arguments.
This new behavior is disabled when the input has an explicit call to
`__wasm_call_ctors`, indicating code not expecting new-style command
support.
This change does mean that wasm-ld no longer supports DCE-ing the
`__wasm_call_ctors` function when there are no calls to it. If there are no
calls to it, and there are ctors present, we assume it's wasm-ld's job to
insert the calls. This seems ok though, because if there are ctors present,
the program is expecting them to be called. This change affects the
init-fini-gc.ll test.
Added:
lld/test/wasm/command-exports-no-tors.s
lld/test/wasm/command-exports.s
lld/test/wasm/init-fini-no-gc.ll
Modified:
lld/wasm/Driver.cpp
lld/wasm/InputChunks.h
lld/wasm/MarkLive.cpp
lld/wasm/Symbols.cpp
lld/wasm/Symbols.h
lld/wasm/Writer.cpp
Removed:
lld/test/wasm/init-fini-gc.ll
################################################################################
diff --git a/lld/test/wasm/command-exports-no-tors.s b/lld/test/wasm/command-exports-no-tors.s
new file mode 100644
index 000000000000..e00712bed538
--- /dev/null
+++ b/lld/test/wasm/command-exports-no-tors.s
@@ -0,0 +1,54 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+# RUN: wasm-ld --no-entry %t.o -o %t.wasm
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+# Like command-exports.s, but with no ctors or dtors, so there should be no
+# __wasm_call_ctors, __cxa_atexit, or wrappers.
+
+ .globl foo_i32
+foo_i32:
+ .functype foo_i32 (i32, i32) -> (i32)
+ local.get 0
+ local.get 1
+ i32.add
+ end_function
+
+ .globl foo_f64
+foo_f64:
+ .functype foo_f64 (f64, f64) -> (f64)
+ local.get 0
+ local.get 1
+ f64.add
+ end_function
+
+ .export_name foo_i32, foo_i32
+ .export_name foo_f64, foo_f64
+
+# CHECK: - Type: EXPORT
+# CHECK-NEXT: Exports:
+# CHECK-NEXT: - Name: memory
+# CHECK-NEXT: Kind: MEMORY
+# CHECK-NEXT: Index: 0
+# CHECK-NEXT: - Name: foo_i32
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: Index: 0
+# CHECK-NEXT: - Name: foo_f64
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: Index: 1
+
+# CHECK: - Type: CODE
+
+# CHECK: - Index: 0
+# CHECK-NEXT: Locals: []
+# CHECK-NEXT: Body: 200020016A0B
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Locals: []
+# CHECK-NEXT: Body: 20002001A00B
+
+# CHECK: - Type: CUSTOM
+# CHECK-NEXT: Name: name
+# CHECK-NEXT: FunctionNames:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: Name: foo_i32
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Name: foo_f64
diff --git a/lld/test/wasm/command-exports.s b/lld/test/wasm/command-exports.s
new file mode 100644
index 000000000000..e1b47ce9658f
--- /dev/null
+++ b/lld/test/wasm/command-exports.s
@@ -0,0 +1,113 @@
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t.o %s
+# RUN: wasm-ld --no-entry %t.o -o %t.wasm
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+# This test defines a command with two exported functions, as well as a static
+# constructor and a static destructor. Check that the exports, constructor, and
+# destructor are all set up properly.
+
+ .globl foo_i32
+foo_i32:
+ .functype foo_i32 (i32, i32) -> (i32)
+ local.get 0
+ local.get 1
+ i32.add
+ end_function
+
+ .globl foo_f64
+foo_f64:
+ .functype foo_f64 (f64, f64) -> (f64)
+ local.get 0
+ local.get 1
+ f64.add
+ end_function
+
+ .globl some_ctor
+some_ctor:
+ .functype some_ctor () -> ()
+ end_function
+
+ .globl some_dtor
+some_dtor:
+ .functype some_dtor () -> ()
+ end_function
+
+ .hidden __cxa_atexit
+ .globl __cxa_atexit
+__cxa_atexit:
+ .functype __cxa_atexit (i32, i32, i32) -> (i32)
+ i32.const 0
+ end_function
+
+ .section .text..Lcall_dtors.1,"",@
+.Lcall_dtors.1:
+ .functype .Lcall_dtors.1 (i32) -> ()
+ call some_dtor
+ end_function
+
+ .section .text..Lregister_call_dtors.1,"",@
+.Lregister_call_dtors.1:
+ .functype .Lregister_call_dtors.1 () -> ()
+ block
+ i32.const .Lcall_dtors.1
+ i32.const 0
+ i32.const 0
+ call __cxa_atexit
+ i32.eqz
+ br_if 0
+ unreachable
+.LBB6_2:
+ end_block
+ end_function
+
+ .section .init_array.1,"",@
+ .p2align 2
+ .int32 some_ctor
+ .int32 .Lregister_call_dtors.1
+ .export_name foo_i32, foo_i32
+ .export_name foo_f64, foo_f64
+
+# CHECK: - Type: EXPORT
+# CHECK-NEXT: Exports:
+# CHECK-NEXT: - Name: memory
+# CHECK-NEXT: Kind: MEMORY
+# CHECK-NEXT: Index: 0
+# CHECK-NEXT: - Name: foo_i32
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: Index: 8
+# CHECK-NEXT: - Name: foo_f64
+# CHECK-NEXT: Kind: FUNCTION
+# CHECK-NEXT: Index: 9
+
+# CHECK: - Type: CODE
+
+# CHECK: - Index: 8
+# CHECK-NEXT: Locals: []
+# CHECK-NEXT: Body: 10002000200110010B
+# CHECK-NEXT: - Index: 9
+# CHECK-NEXT: Locals: []
+# CHECK-NEXT: Body: 10002000200110020B
+
+# CHECK: - Type: CUSTOM
+# CHECK-NEXT: Name: name
+# CHECK-NEXT: FunctionNames:
+# CHECK-NEXT: - Index: 0
+# CHECK-NEXT: Name: __wasm_call_ctors
+# CHECK-NEXT: - Index: 1
+# CHECK-NEXT: Name: foo_i32
+# CHECK-NEXT: - Index: 2
+# CHECK-NEXT: Name: foo_f64
+# CHECK-NEXT: - Index: 3
+# CHECK-NEXT: Name: some_ctor
+# CHECK-NEXT: - Index: 4
+# CHECK-NEXT: Name: some_dtor
+# CHECK-NEXT: - Index: 5
+# CHECK-NEXT: Name: __cxa_atexit
+# CHECK-NEXT: - Index: 6
+# CHECK-NEXT: Name: .Lcall_dtors.1
+# CHECK-NEXT: - Index: 7
+# CHECK-NEXT: Name: .Lregister_call_dtors.1
+# CHECK-NEXT: - Index: 8
+# CHECK-NEXT: Name: foo_i32.command_export
+# CHECK-NEXT: - Index: 9
+# CHECK-NEXT: Name: foo_f64.command_export
diff --git a/lld/test/wasm/init-fini-gc.ll b/lld/test/wasm/init-fini-gc.ll
deleted file mode 100644
index 4b2c14bd6858..000000000000
--- a/lld/test/wasm/init-fini-gc.ll
+++ /dev/null
@@ -1,48 +0,0 @@
-; RUN: llc -filetype=obj -o %t.o %s
-; RUN: wasm-ld %t.o -o %t.wasm
-; RUN: obj2yaml %t.wasm | FileCheck %s
-
-; RUN: wasm-ld %t.o -o %t.wasm
-; RUN: obj2yaml %t.wasm | FileCheck %s
-
-; RUN: wasm-ld --export=__wasm_call_ctors %t.o -o %t.export.wasm
-; RUN: obj2yaml %t.export.wasm | FileCheck %s -check-prefix=EXPORT
-
-; Test that the __wasm_call_ctor function if not referenced
-
-target triple = "wasm32-unknown-unknown"
-
-define hidden void @_start() {
-entry:
- ret void
-}
-
-define hidden void @func1() {
-entry:
- ret void
-}
-
-define hidden void @func2() {
-entry:
- ret void
-}
-
-define i32 @__cxa_atexit(i32 %func, i32 %arg, i32 %dso_handle) {
- ret i32 0
-}
-
- at llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [
- { i32, void ()*, i8* } { i32 1, void ()* @func1, i8* null }
-]
-
- at llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [
- { i32, void ()*, i8* } { i32 1, void ()* @func2, i8* null }
-]
-
-; CHECK-NOT: __cxa_atexit
-; CHECK-NOT: __wasm_call_ctors
-
-; EXPORT: __wasm_call_ctors
-; EXPORT: func1
-; EXPORT: func2
-; EXPORT: __cxa_atexit
diff --git a/lld/test/wasm/init-fini-no-gc.ll b/lld/test/wasm/init-fini-no-gc.ll
new file mode 100644
index 000000000000..62415686847e
--- /dev/null
+++ b/lld/test/wasm/init-fini-no-gc.ll
@@ -0,0 +1,85 @@
+; RUN: llc -filetype=obj -o %t.o %s
+; RUN: wasm-ld %t.o -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; RUN: wasm-ld --export=__wasm_call_ctors %t.o -o %t.export.wasm
+; RUN: obj2yaml %t.export.wasm | FileCheck %s -check-prefix=EXPORT
+
+; Test that we emit wrappers and call __wasm_call_ctor when not referenced.
+
+target triple = "wasm32-unknown-unknown"
+
+define hidden void @_start() {
+entry:
+ ret void
+}
+
+define hidden void @func1() {
+entry:
+ ret void
+}
+
+define hidden void @func2() {
+entry:
+ ret void
+}
+
+define hidden i32 @__cxa_atexit(i32 %func, i32 %arg, i32 %dso_handle) {
+ ret i32 0
+}
+
+ at llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [
+ { i32, void ()*, i8* } { i32 1, void ()* @func1, i8* null }
+]
+
+ at llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [
+ { i32, void ()*, i8* } { i32 1, void ()* @func2, i8* null }
+]
+
+; Check that we have exactly the needed exports: `memory` because that's
+; currently on by default, and `_start`, because that's the default entrypoint.
+
+; CHECK: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 7
+
+; Check the body of `_start`'s command-export wrapper.
+
+; CHECK: - Type: CODE
+
+; CHECK: - Index: 7
+; CHECK-NEXT: Locals: []
+; CHECK-NEXT: Body: 100010010B
+
+; Check the symbol table to ensure all the functions are here, and that
+; index 7 above refers to the function we think it does.
+
+; CHECK: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: func1
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: func2
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Name: __cxa_atexit
+; CHECK-NEXT: - Index: 5
+; CHECK-NEXT: Name: .Lcall_dtors.1
+; CHECK-NEXT: - Index: 6
+; CHECK-NEXT: Name: .Lregister_call_dtors.1
+; CHECK-NEXT: - Index: 7
+; CHECK-NEXT: Name: _start.command_export
+
+; EXPORT: __wasm_call_ctors
+; EXPORT: func1
+; EXPORT: func2
+; EXPORT: __cxa_atexit
diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp
index 9b5f6690ebf0..a6d26dcfcc43 100644
--- a/lld/wasm/Driver.cpp
+++ b/lld/wasm/Driver.cpp
@@ -572,7 +572,6 @@ static void createSyntheticSymbols() {
make<SyntheticFunction>(nullSignature, "__wasm_apply_relocs"));
}
-
if (config->isPic) {
WasmSym::stackPointer =
createUndefinedGlobal("__stack_pointer", config->is64.getValueOr(false)
@@ -841,6 +840,29 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
config->entry);
}
+ // If the user code defines a `__wasm_call_dtors` function, remember it so
+ // that we can call it from the command export wrappers. Unlike
+ // `__wasm_call_ctors` which we synthesize, `__wasm_call_dtors` is defined
+ // by libc/etc., because destructors are registered dynamically with
+ // `__cxa_atexit` and friends.
+ if (!config->relocatable && !config->shared &&
+ !WasmSym::callCtors->isUsedInRegularObj &&
+ WasmSym::callCtors->getName() != config->entry &&
+ !config->exportedSymbols.count(WasmSym::callCtors->getName())) {
+ if (Symbol *callDtors = handleUndefined("__wasm_call_dtors")) {
+ if (auto *callDtorsFunc = dyn_cast<DefinedFunction>(callDtors)) {
+ if (callDtorsFunc->signature &&
+ (!callDtorsFunc->signature->Params.empty() ||
+ !callDtorsFunc->signature->Returns.empty())) {
+ error("__wasm_call_dtors must have no argument or return values");
+ }
+ WasmSym::callDtors = callDtorsFunc;
+ } else {
+ error("__wasm_call_dtors must be a function");
+ }
+ }
+ }
+
createOptionalSymbols();
if (errorCount())
diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h
index be91b19ed452..e5671fb89237 100644
--- a/lld/wasm/InputChunks.h
+++ b/lld/wasm/InputChunks.h
@@ -122,7 +122,10 @@ class InputSegment : public InputChunk {
class InputFunction : public InputChunk {
public:
InputFunction(const WasmSignature &s, const WasmFunction *func, ObjFile *f)
- : InputChunk(f, InputChunk::Function), signature(s), function(func) {}
+ : InputChunk(f, InputChunk::Function), signature(s), function(func),
+ exportName(func && func->ExportName.hasValue()
+ ? (*func->ExportName).str()
+ : llvm::Optional<std::string>()) {}
static bool classof(const InputChunk *c) {
return c->kind() == InputChunk::Function ||
@@ -133,8 +136,10 @@ class InputFunction : public InputChunk {
StringRef getName() const override { return function->SymbolName; }
StringRef getDebugName() const override { return function->DebugName; }
llvm::Optional<StringRef> getExportName() const {
- return function ? function->ExportName : llvm::Optional<StringRef>();
+ return exportName.hasValue() ? llvm::Optional<StringRef>(*exportName)
+ : llvm::Optional<StringRef>();
}
+ void setExportName(std::string exportName) { this->exportName = exportName; }
uint32_t getComdat() const override { return function->Comdat; }
uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); }
uint32_t getFunctionCodeOffset() const { return function->CodeOffset; }
@@ -172,6 +177,7 @@ class InputFunction : public InputChunk {
}
const WasmFunction *function;
+ llvm::Optional<std::string> exportName;
llvm::Optional<uint32_t> functionIndex;
llvm::Optional<uint32_t> tableIndex;
uint32_t compressedFuncSize = 0;
diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp
index 2764c88f492c..2766eec07ecb 100644
--- a/lld/wasm/MarkLive.cpp
+++ b/lld/wasm/MarkLive.cpp
@@ -44,6 +44,7 @@ class MarkLive {
void enqueue(Symbol *sym);
void markSymbol(Symbol *sym);
void mark();
+ bool isCallCtorsLive();
// A list of chunks to visit.
SmallVector<InputChunk *, 256> queue;
@@ -58,22 +59,6 @@ void MarkLive::enqueue(Symbol *sym) {
sym->markLive();
if (InputChunk *chunk = sym->getChunk())
queue.push_back(chunk);
-
- // The ctor functions are all referenced by the synthetic callCtors
- // function. However, this function does not contain relocations so we
- // have to manually mark the ctors as live if callCtors itself is live.
- if (sym == WasmSym::callCtors) {
- if (config->isPic)
- enqueue(WasmSym::applyRelocs);
- for (const ObjFile *obj : symtab->objectFiles) {
- const WasmLinkingData &l = obj->getWasmObj()->linkingData();
- for (const WasmInitFunc &f : l.InitFunctions) {
- auto* initSym = obj->getFunctionSymbol(f.Symbol);
- if (!initSym->isDiscarded())
- enqueue(initSym);
- }
- }
- }
}
void MarkLive::run() {
@@ -86,16 +71,29 @@ void MarkLive::run() {
if (sym->isNoStrip() || sym->isExported())
enqueue(sym);
- // For relocatable output, we need to preserve all the ctor functions
- if (config->relocatable) {
- for (const ObjFile *obj : symtab->objectFiles) {
- const WasmLinkingData &l = obj->getWasmObj()->linkingData();
- for (const WasmInitFunc &f : l.InitFunctions)
- enqueue(obj->getFunctionSymbol(f.Symbol));
+ // If we'll be calling the user's `__wasm_call_dtors` function, mark it live.
+ if (Symbol *callDtors = WasmSym::callDtors)
+ enqueue(callDtors);
+
+ // The ctor functions are all referenced by the synthetic callCtors
+ // function. However, this function does not contain relocations so we
+ // have to manually mark the ctors as live.
+ for (const ObjFile *obj : symtab->objectFiles) {
+ const WasmLinkingData &l = obj->getWasmObj()->linkingData();
+ for (const WasmInitFunc &f : l.InitFunctions) {
+ auto *initSym = obj->getFunctionSymbol(f.Symbol);
+ if (!initSym->isDiscarded())
+ enqueue(initSym);
}
}
+ // In Emscripten-style PIC, `__wasm_call_ctors` calls `__wasm_apply_relocs`.
if (config->isPic)
+ enqueue(WasmSym::applyRelocs);
+
+ // If we have any non-discarded init functions, mark `__wasm_call_ctors` as
+ // live so that we assign it an index and call it.
+ if (isCallCtorsLive())
enqueue(WasmSym::callCtors);
if (config->sharedMemory && !config->shared)
@@ -169,5 +167,27 @@ void markLive() {
}
}
+bool MarkLive::isCallCtorsLive() {
+ // In a reloctable link, we don't call `__wasm_call_ctors`.
+ if (config->relocatable)
+ return false;
+
+ // In Emscripten-style PIC, we call `__wasm_call_ctors` which calls
+ // `__wasm_apply_relocs`.
+ if (config->isPic)
+ return true;
+
+ // If there are any init functions, mark `__wasm_call_ctors` live so that
+ // it can call them.
+ for (const ObjFile *file : symtab->objectFiles) {
+ const WasmLinkingData &l = file->getWasmObj()->linkingData();
+ for (const WasmInitFunc &f : l.InitFunctions)
+ if (!file->getFunctionSymbol(f.Symbol)->isDiscarded())
+ return true;
+ }
+
+ return false;
+}
+
} // namespace wasm
} // namespace lld
diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp
index 4b40f95ed52d..d69ef00329c9 100644
--- a/lld/wasm/Symbols.cpp
+++ b/lld/wasm/Symbols.cpp
@@ -66,6 +66,7 @@ std::string toString(wasm::Symbol::Kind kind) {
namespace wasm {
DefinedFunction *WasmSym::callCtors;
+DefinedFunction *WasmSym::callDtors;
DefinedFunction *WasmSym::initMemory;
DefinedFunction *WasmSym::applyRelocs;
DefinedFunction *WasmSym::initTLS;
diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h
index eed481a0b44d..69195e414b47 100644
--- a/lld/wasm/Symbols.h
+++ b/lld/wasm/Symbols.h
@@ -471,6 +471,10 @@ struct WasmSym {
// Function that directly calls all ctors in priority order.
static DefinedFunction *callCtors;
+ // __wasm_call_dtors
+ // Function that calls the libc/etc. cleanup function.
+ static DefinedFunction *callDtors;
+
// __wasm_apply_relocs
// Function that applies relocations to data segment post-instantiation.
static DefinedFunction *applyRelocs;
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 1d669ca7a723..fee87f292c90 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -62,6 +62,8 @@ class Writer {
void createApplyRelocationsFunction();
void createCallCtorsFunction();
void createInitTLSFunction();
+ void createCommandExportWrappers();
+ void createCommandExportWrapper(uint32_t functionIndex, DefinedFunction *f);
void assignIndexes();
void populateSymtab();
@@ -95,6 +97,9 @@ class Writer {
std::vector<WasmInitEntry> initFunctions;
llvm::StringMap<std::vector<InputSection *>> customSectionMapping;
+ // Stable storage for command export wrapper function name strings.
+ std::list<std::string> commandExportWrapperNames;
+
// Elements that are used to construct the final output
std::string header;
std::vector<OutputSection *> outputSections;
@@ -640,6 +645,53 @@ void Writer::calculateTypes() {
out.typeSec->registerType(e->signature);
}
+// In a command-style link, create a wrapper for each exported symbol
+// which calls the constructors and destructors.
+void Writer::createCommandExportWrappers() {
+ // This logic doesn't currently support Emscripten-style PIC mode.
+ assert(!config->isPic);
+
+ // If there are no ctors and there's no libc `__wasm_call_dtors` to
+ // call, don't wrap the exports.
+ if (initFunctions.empty() && WasmSym::callDtors == NULL)
+ return;
+
+ std::vector<DefinedFunction *> toWrap;
+
+ for (Symbol *sym : symtab->getSymbols())
+ if (sym->isExported())
+ if (auto *f = dyn_cast<DefinedFunction>(sym))
+ toWrap.push_back(f);
+
+ for (auto *f : toWrap) {
+ auto funcNameStr = (f->getName() + ".command_export").str();
+ commandExportWrapperNames.push_back(funcNameStr);
+ const std::string &funcName = commandExportWrapperNames.back();
+
+ auto func = make<SyntheticFunction>(*f->getSignature(), funcName);
+ if (f->function->getExportName().hasValue())
+ func->setExportName(f->function->getExportName()->str());
+ else
+ func->setExportName(f->getName().str());
+
+ DefinedFunction *def =
+ symtab->addSyntheticFunction(funcName, f->flags, func);
+ def->markLive();
+
+ def->flags |= WASM_SYMBOL_EXPORTED;
+ def->flags &= ~WASM_SYMBOL_VISIBILITY_HIDDEN;
+ def->forceExport = f->forceExport;
+
+ f->flags |= WASM_SYMBOL_VISIBILITY_HIDDEN;
+ f->flags &= ~WASM_SYMBOL_EXPORTED;
+ f->forceExport = false;
+
+ out.functionSec->addFunction(func);
+
+ createCommandExportWrapper(f->getFunctionIndex(), def);
+ }
+}
+
static void scanRelocations() {
for (ObjFile *file : symtab->objectFiles) {
LLVM_DEBUG(dbgs() << "scanRelocations: " << file->getName() << "\n");
@@ -925,7 +977,10 @@ void Writer::createApplyRelocationsFunction() {
// Create synthetic "__wasm_call_ctors" function based on ctor functions
// in input object.
void Writer::createCallCtorsFunction() {
- if (!WasmSym::callCtors->isLive())
+ // If __wasm_call_ctors isn't referenced, there aren't any ctors, and we
+ // aren't calling `__wasm_apply_relocs` for Emscripten-style PIC, don't
+ // define the `__wasm_call_ctors` function.
+ if (!WasmSym::callCtors->isLive() && initFunctions.empty() && !config->isPic)
return;
// First write the body's contents to a string.
@@ -954,6 +1009,46 @@ void Writer::createCallCtorsFunction() {
createFunction(WasmSym::callCtors, bodyContent);
}
+// Create a wrapper around a function export which calls the
+// static constructors and destructors.
+void Writer::createCommandExportWrapper(uint32_t functionIndex,
+ DefinedFunction *f) {
+ // First write the body's contents to a string.
+ std::string bodyContent;
+ {
+ raw_string_ostream os(bodyContent);
+ writeUleb128(os, 0, "num locals");
+
+ // If we have any ctors, or we're calling `__wasm_apply_relocs` for
+ // Emscripten-style PIC, call `__wasm_call_ctors` which performs those
+ // calls.
+ if (!initFunctions.empty() || config->isPic) {
+ writeU8(os, WASM_OPCODE_CALL, "CALL");
+ writeUleb128(os, WasmSym::callCtors->getFunctionIndex(),
+ "function index");
+ }
+
+ // Call the user's code, leaving any return values on the operand stack.
+ for (size_t i = 0; i < f->signature->Params.size(); ++i) {
+ writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
+ writeUleb128(os, i, "local index");
+ }
+ writeU8(os, WASM_OPCODE_CALL, "CALL");
+ writeUleb128(os, functionIndex, "function index");
+
+ // Call the function that calls the destructors.
+ if (DefinedFunction *callDtors = WasmSym::callDtors) {
+ writeU8(os, WASM_OPCODE_CALL, "CALL");
+ writeUleb128(os, callDtors->getFunctionIndex(), "function index");
+ }
+
+ // End the function, returning the return values from the user's code.
+ writeU8(os, WASM_OPCODE_END, "END");
+ }
+
+ createFunction(f, bodyContent);
+}
+
void Writer::createInitTLSFunction() {
if (!WasmSym::initTLS->isLive())
return;
@@ -1090,6 +1185,18 @@ void Writer::run() {
if (config->isPic)
createApplyRelocationsFunction();
createCallCtorsFunction();
+
+ // Create export wrappers for commands if needed.
+ //
+ // If the input contains a call to `__wasm_call_ctors`, either in one of
+ // the input objects or an explicit export from the command-line, we
+ // assume ctors and dtors are taken care of already.
+ if (!config->relocatable && !config->isPic &&
+ !WasmSym::callCtors->isUsedInRegularObj &&
+ !WasmSym::callCtors->isExported()) {
+ log("-- createCommandExportWrappers");
+ createCommandExportWrappers();
+ }
}
if (!config->relocatable && config->sharedMemory && !config->shared)
More information about the llvm-commits
mailing list