diff --git a/lld/test/wasm/bsymbolic.s b/lld/test/wasm/bsymbolic.s index dc0e0ddcc773a3..07989fc5f437cf 100644 --- a/lld/test/wasm/bsymbolic.s +++ b/lld/test/wasm/bsymbolic.s @@ -1,5 +1,5 @@ // RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %s -o %t.o -// RUN: wasm-ld --no-entry -Bsymbolic %t.o -o %t2.so 2>&1 | FileCheck -check-prefix=WARNING %s +// RUN: wasm-ld --no-entry -Bsymbolic %t.o -o %t.wasm 2>&1 | FileCheck -check-prefix=WARNING %s // WARNING: warning: -Bsymbolic is only meaningful when combined with -shared // RUN: wasm-ld --experimental-pic -shared %t.o -o %t0.so diff --git a/lld/test/wasm/data-segments.ll b/lld/test/wasm/data-segments.ll index 6f6d96a30fa5ce..8c4c4ca796727f 100644 --- a/lld/test/wasm/data-segments.ll +++ b/lld/test/wasm/data-segments.ll @@ -98,27 +98,27 @@ ; PASSIVE-NEXT: Name: __wasm_init_memory ; PASSIVE-PIC: - Type: START -; PASSIVE-PIC-NEXT: StartFunction: 3 +; PASSIVE-PIC-NEXT: StartFunction: 2 ; PASSIVE-PIC-NEXT: - Type: DATACOUNT ; PASSIVE-PIC-NEXT: Count: 1 ; PASSIVE-PIC-NEXT: - Type: CODE ; PASSIVE-PIC-NEXT: Functions: ; PASSIVE-PIC-NEXT: - Index: 0 ; PASSIVE-PIC-NEXT: Locals: [] -; PASSIVE-PIC-NEXT: Body: 10010B +; PASSIVE-PIC-NEXT: Body: 10030B ; PASSIVE-PIC-NEXT: - Index: 1 ; PASSIVE-PIC-NEXT: Locals: [] ; PASSIVE-PIC-NEXT: Body: 0B ; PASSIVE-PIC-NEXT: - Index: 2 -; PASSIVE-PIC-NEXT: Locals: [] -; PASSIVE-PIC-NEXT: Body: 0B -; PASSIVE-PIC-NEXT: - Index: 3 ; PASSIVE-PIC-NEXT: Locals: ; PASSIVE32-PIC-NEXT: - Type: I32 ; PASSIVE64-PIC-NEXT: - Type: I64 ; PASSIVE-PIC-NEXT: Count: 1 ; PASSIVE32-PIC-NEXT: Body: 230141B4CE006A2100200041004101FE480200044020004101427FFE0102001A05410023016A410041B1CE00FC08000020004102FE1702002000417FFE0002001A0BFC09000B ; PASSIVE64-PIC-NEXT: Body: 230142B4CE006A2100200041004101FE480200044020004101427FFE0102001A05420023016A410041B1CE00FC08000020004102FE1702002000417FFE0002001A0BFC09000B +; PASSIVE-PIC-NEXT: - Index: 3 +; PASSIVE-PIC-NEXT: Locals: [] +; PASSIVE-PIC-NEXT: Body: 0B ; PASSIVE-PIC-NEXT: - Type: DATA ; PASSIVE-PIC-NEXT: Segments: ; PASSIVE-PIC-NEXT: - SectionOffset: 4 @@ -130,8 +130,8 @@ ; PASSIVE-PIC-NEXT: - Index: 0 ; PASSIVE-PIC-NEXT: Name: __wasm_call_ctors ; PASSIVE-PIC-NEXT: - Index: 1 -; PASSIVE-PIC-NEXT: Name: __wasm_apply_relocs -; PASSIVE-PIC-NEXT: - Index: 2 ; PASSIVE-PIC-NEXT: Name: __wasm_init_tls -; PASSIVE-PIC-NEXT: - Index: 3 +; PASSIVE-PIC-NEXT: - Index: 2 ; PASSIVE-PIC-NEXT: Name: __wasm_init_memory +; PASSIVE-PIC-NEXT: - Index: 3 +; PASSIVE-PIC-NEXT: Name: __wasm_apply_data_relocs diff --git a/lld/test/wasm/pie.ll b/lld/test/wasm/pie.ll index 20a6c928b6ee29..84555a08f19db5 100644 --- a/lld/test/wasm/pie.ll +++ b/lld/test/wasm/pie.ll @@ -1,6 +1,5 @@ ; RUN: llc -relocation-model=pic -mattr=+mutable-globals -filetype=obj %s -o %t.o ; RUN: wasm-ld --no-gc-sections --allow-undefined --experimental-pic -pie -o %t.wasm %t.o -; RUN: obj2yaml %t.wasm | FileCheck %s target triple = "wasm32-unknown-emscripten" @@ -65,4 +64,51 @@ define void @_start() { ; CHECK-NEXT: GlobalType: I32 ; CHECK-NEXT: GlobalMutable: false +; CHECK: - Type: START +; CHECK-NEXT: StartFunction: 2 + +; 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: __wasm_apply_data_relocs +; CHECK-NEXT: - Index: 2 +; CHECK-NEXT: Name: __wasm_apply_global_relocs + + +; Run the same test with threading support. In this mode +; we expect __wasm_init_memory and __wasm_apply_data_relocs +; to be generated along with __wasm_start as the start +; function. + +; RUN: llc -relocation-model=pic -mattr=+mutable-globals,+atomics,+bulk-memory -filetype=obj %s -o %t.shmem.o +; RUN: wasm-ld --no-gc-sections --shared-memory --allow-undefined --experimental-pic -pie -o %t.shmem.wasm %t.shmem.o +; RUN: obj2yaml %t.shmem.wasm | FileCheck %s --check-prefix=SHMEM + +; SHMEM: - Type: CODE +; SHMEM: - Index: 5 +; SHMEM-NEXT: Locals: [] +; SHMEM-NEXT: Body: 100210040B + +; SHMEM: FunctionNames: +; SHMEM-NEXT: - Index: 0 +; SHMEM-NEXT: Name: __wasm_call_ctors +; SHMEM-NEXT: - Index: 1 +; SHMEM-NEXT: Name: __wasm_init_tls +; SHMEM-NEXT: - Index: 2 +; SHMEM-NEXT: Name: __wasm_init_memory +; SHMEM-NEXT: - Index: 3 +; SHMEM-NEXT: Name: __wasm_apply_data_relocs +; SHMEM-NEXT: - Index: 4 +; SHMEM-NEXT: Name: __wasm_apply_global_relocs +; SHMEM-NEXT: - Index: 5 +; SHMEM-NEXT: Name: __wasm_start +; SHMEM-NEXT: - Index: 6 +; SHMEM-NEXT: Name: foo +; SHMEM-NEXT: - Index: 7 +; SHMEM-NEXT: Name: get_data_address +; SHMEM-NEXT: - Index: 8 +; SHMEM-NEXT: Name: _start diff --git a/lld/test/wasm/weak-undefined-pic.s b/lld/test/wasm/weak-undefined-pic.s index a169fd315f08e2..17dbc54faf7c1a 100644 --- a/lld/test/wasm/weak-undefined-pic.s +++ b/lld/test/wasm/weak-undefined-pic.s @@ -66,7 +66,7 @@ _start: # CHECK-NEXT: - Index: 0 # CHECK-NEXT: Name: __stack_pointer # CHECK-NEXT: - Index: 1 -# CHECK-NEXT: Name: 'undefined_weak:foo' +# CHECK-NEXT: Name: 'GOT.func.internal.undefined_weak:foo' # With `-pie` or `-shared` the resolution should be deferred to the dynamic # linker and the function address should be imported as GOT.func.foo. diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 3be0a0345079b4..0d84914c129515 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -615,14 +615,6 @@ static void createSyntheticSymbols() { "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN, make(nullSignature, "__wasm_call_ctors")); - if (config->isPic) { - // For PIC code we create a synthetic function __wasm_apply_relocs which - // is called from __wasm_call_ctors before the user-level constructors. - WasmSym::applyRelocs = symtab->addSyntheticFunction( - "__wasm_apply_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN, - make(nullSignature, "__wasm_apply_relocs")); - } - if (config->isPic) { WasmSym::stackPointer = createUndefinedGlobal("__stack_pointer", config->is64.getValueOr(false) diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp index acc3c4c04c0c1f..63c12545d5542f 100644 --- a/lld/wasm/MarkLive.cpp +++ b/lld/wasm/MarkLive.cpp @@ -98,9 +98,6 @@ void MarkLive::run() { if (WasmSym::callDtors) enqueue(WasmSym::callDtors); - if (WasmSym::applyRelocs) - enqueue(WasmSym::applyRelocs); - // Enqueue constructors in objects explicitly live from the command-line. for (const ObjFile *obj : symtab->objectFiles) if (obj->isLive()) diff --git a/lld/wasm/Symbols.cpp b/lld/wasm/Symbols.cpp index ffb52c1c5746f9..9e41f7c6281d30 100644 --- a/lld/wasm/Symbols.cpp +++ b/lld/wasm/Symbols.cpp @@ -68,8 +68,10 @@ namespace wasm { DefinedFunction *WasmSym::callCtors; DefinedFunction *WasmSym::callDtors; DefinedFunction *WasmSym::initMemory; -DefinedFunction *WasmSym::applyRelocs; +DefinedFunction *WasmSym::applyDataRelocs; +DefinedFunction *WasmSym::applyGlobalRelocs; DefinedFunction *WasmSym::initTLS; +DefinedFunction *WasmSym::startFunction; DefinedData *WasmSym::dsoHandle; DefinedData *WasmSym::dataEnd; DefinedData *WasmSym::globalBase; diff --git a/lld/wasm/Symbols.h b/lld/wasm/Symbols.h index cfa686998de4a3..4fa1a834db3785 100644 --- a/lld/wasm/Symbols.h +++ b/lld/wasm/Symbols.h @@ -484,14 +484,23 @@ struct WasmSym { // Function that calls the libc/etc. cleanup function. static DefinedFunction *callDtors; - // __wasm_apply_relocs + // __wasm_apply_data_relocs // Function that applies relocations to data segment post-instantiation. - static DefinedFunction *applyRelocs; + static DefinedFunction *applyDataRelocs; + + // __wasm_apply_global_relocs + // Function that applies relocations to data segment post-instantiation. + // Unlike __wasm_apply_data_relocs this needs to run on every thread. + static DefinedFunction *applyGlobalRelocs; // __wasm_init_tls // Function that allocates thread-local storage and initializes it. static DefinedFunction *initTLS; + // Pointer to the function that is to be used in the start section. + // (normally an alias of initMemory, or applyGlobalRelocs). + static DefinedFunction *startFunction; + // __dso_handle // Symbol used in calls to __cxa_atexit to determine current DLL static DefinedData *dsoHandle; diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp index 8e2c7c631f95e0..768a265b3d092b 100644 --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -372,12 +372,13 @@ void ExportSection::writeBody() { } bool StartSection::isNeeded() const { - return WasmSym::initMemory != nullptr; + return WasmSym::startFunction != nullptr; } void StartSection::writeBody() { raw_ostream &os = bodyOutputStream; - writeUleb128(os, WasmSym::initMemory->getFunctionIndex(), "function index"); + writeUleb128(os, WasmSym::startFunction->getFunctionIndex(), + "function index"); } void ElemSection::addEntry(FunctionSymbol *sym) { @@ -624,7 +625,10 @@ void NameSection::writeBody() { } for (Symbol *s : out.globalSec->internalGotSymbols) { writeUleb128(sub.os, s->getGOTIndex(), "global index"); - writeStr(sub.os, toString(*s), "symbol name"); + if (isa(s)) + writeStr(sub.os, "GOT.func.internal." + toString(*s), "symbol name"); + else + writeStr(sub.os, "GOT.data.internal." + toString(*s), "symbol name"); } sub.writeTo(bodyOutputStream); diff --git a/lld/wasm/SyntheticSections.h b/lld/wasm/SyntheticSections.h index 3f52b5dc547e12..b4d833d212a614 100644 --- a/lld/wasm/SyntheticSections.h +++ b/lld/wasm/SyntheticSections.h @@ -217,6 +217,7 @@ class GlobalSection : public SyntheticSection { // specific relocation types combined with linker relaxation which could // transform a `global.get` to an `i32.const`. void addInternalGOTEntry(Symbol *sym); + bool needsRelocations() { return internalGotSymbols.size(); } void generateRelocationCode(raw_ostream &os) const; std::vector dataAddressGlobals; diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index ff9d0dc7a99b48..3f255966f8dbab 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -60,7 +60,9 @@ class Writer { void createSyntheticInitFunctions(); void createInitMemoryFunction(); - void createApplyRelocationsFunction(); + void createStartFunction(); + void createApplyDataRelocationsFunction(); + void createApplyGlobalRelocationsFunction(); void createCallCtorsFunction(); void createInitTLSFunction(); void createCommandExportWrappers(); @@ -296,8 +298,11 @@ void Writer::layoutMemory() { } // Make space for the memory initialization flag - if (WasmSym::initMemoryFlag) { + if (config->sharedMemory && hasPassiveInitializedSegments()) { memoryPtr = alignTo(memoryPtr, 4); + WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol( + "__wasm_init_memory_flag", WASM_SYMBOL_VISIBILITY_HIDDEN); + WasmSym::initMemoryFlag->markLive(); WasmSym::initMemoryFlag->setVirtualAddress(memoryPtr); log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", "__wasm_init_memory_flag", memoryPtr, 4, 4)); @@ -867,19 +872,43 @@ bool Writer::hasPassiveInitializedSegments() { } void Writer::createSyntheticInitFunctions() { + if (config->relocatable) + return; + + static WasmSignature nullSignature = {{}, {}}; + // Passive segments are used to avoid memory being reinitialized on each // thread's instantiation. These passive segments are initialized and // dropped in __wasm_init_memory, which is registered as the start function - if (config->sharedMemory && hasPassiveInitializedSegments()) { - static WasmSignature nullSignature = {{}, {}}; WasmSym::initMemory = symtab->addSyntheticFunction( "__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN, make(nullSignature, "__wasm_init_memory")); WasmSym::initMemory->markLive(); - WasmSym::initMemoryFlag = symtab->addSyntheticDataSymbol( - "__wasm_init_memory_flag", WASM_SYMBOL_VISIBILITY_HIDDEN); - WasmSym::initMemoryFlag->markLive(); + } + + if (config->isPic) { + // For PIC code we create synthetic functions that apply relocations. + // These get called from __wasm_call_ctors before the user-level + // constructors. + WasmSym::applyDataRelocs = symtab->addSyntheticFunction( + "__wasm_apply_data_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN, + make(nullSignature, "__wasm_apply_data_relocs")); + WasmSym::applyDataRelocs->markLive(); + + if (out.globalSec->needsRelocations()) { + WasmSym::applyGlobalRelocs = symtab->addSyntheticFunction( + "__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN, + make(nullSignature, "__wasm_apply_global_relocs")); + WasmSym::applyGlobalRelocs->markLive(); + } + } + + if (WasmSym::applyGlobalRelocs && WasmSym::initMemory) { + WasmSym::startFunction = symtab->addSyntheticFunction( + "__wasm_start", WASM_SYMBOL_VISIBILITY_HIDDEN, + make(nullSignature, "__wasm_start")); + WasmSym::startFunction->markLive(); } } @@ -1042,24 +1071,39 @@ void Writer::createInitMemoryFunction() { createFunction(WasmSym::initMemory, bodyContent); } +void Writer::createStartFunction() { + if (WasmSym::startFunction) { + std::string bodyContent; + { + raw_string_ostream os(bodyContent); + writeUleb128(os, 0, "num locals"); + writeU8(os, WASM_OPCODE_CALL, "CALL"); + writeUleb128(os, WasmSym::initMemory->getFunctionIndex(), + "function index"); + writeU8(os, WASM_OPCODE_CALL, "CALL"); + writeUleb128(os, WasmSym::applyGlobalRelocs->getFunctionIndex(), + "function index"); + writeU8(os, WASM_OPCODE_END, "END"); + } + createFunction(WasmSym::startFunction, bodyContent); + } else if (WasmSym::initMemory) { + WasmSym::startFunction = WasmSym::initMemory; + } else if (WasmSym::applyGlobalRelocs) { + WasmSym::startFunction = WasmSym::applyGlobalRelocs; + } +} + // For -shared (PIC) output, we create create a synthetic function which will // apply any relocations to the data segments on startup. This function is // called __wasm_apply_relocs and is added at the beginning of __wasm_call_ctors // before any of the constructors run. -void Writer::createApplyRelocationsFunction() { - LLVM_DEBUG(dbgs() << "createApplyRelocationsFunction\n"); +void Writer::createApplyDataRelocationsFunction() { + LLVM_DEBUG(dbgs() << "createApplyDataRelocationsFunction\n"); // First write the body's contents to a string. std::string bodyContent; { raw_string_ostream os(bodyContent); writeUleb128(os, 0, "num locals"); - - // First apply relocations to any internalized GOT entries. These - // are the result of relaxation when building with -Bsymbolic. - out.globalSec->generateRelocationCode(os); - - // Next apply any realocation to the data section by reading GOT entry - // globals. for (const OutputSegment *seg : segments) for (const InputSegment *inSeg : seg->inputSegments) inSeg->generateRelocationCode(os); @@ -1067,7 +1111,23 @@ void Writer::createApplyRelocationsFunction() { writeU8(os, WASM_OPCODE_END, "END"); } - createFunction(WasmSym::applyRelocs, bodyContent); + createFunction(WasmSym::applyDataRelocs, bodyContent); +} + +// Similar to createApplyDataRelocationsFunction but generates relocation code +// fro WebAssembly globals. Because these globals are not shared between threads +// these relocation need to run on every thread. +void Writer::createApplyGlobalRelocationsFunction() { + // First write the body's contents to a string. + std::string bodyContent; + { + raw_string_ostream os(bodyContent); + writeUleb128(os, 0, "num locals"); + out.globalSec->generateRelocationCode(os); + writeU8(os, WASM_OPCODE_END, "END"); + } + + createFunction(WasmSym::applyGlobalRelocs, bodyContent); } // Create synthetic "__wasm_call_ctors" function based on ctor functions @@ -1076,7 +1136,8 @@ void Writer::createCallCtorsFunction() { // 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) + if (!WasmSym::callCtors->isLive() && !WasmSym::applyDataRelocs && + initFunctions.empty()) return; // First write the body's contents to a string. @@ -1085,9 +1146,9 @@ void Writer::createCallCtorsFunction() { raw_string_ostream os(bodyContent); writeUleb128(os, 0, "num locals"); - if (config->isPic) { + if (WasmSym::applyDataRelocs) { writeU8(os, WASM_OPCODE_CALL, "CALL"); - writeUleb128(os, WasmSym::applyRelocs->getFunctionIndex(), + writeUleb128(os, WasmSym::applyDataRelocs->getFunctionIndex(), "function index"); } @@ -1099,6 +1160,7 @@ void Writer::createCallCtorsFunction() { writeU8(os, WASM_OPCODE_DROP, "DROP"); } } + writeU8(os, WASM_OPCODE_END, "END"); } @@ -1118,7 +1180,7 @@ void Writer::createCommandExportWrapper(uint32_t functionIndex, // 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) { + if (WasmSym::callCtors->isLive()) { writeU8(os, WASM_OPCODE_CALL, "CALL"); writeUleb128(os, WasmSym::callCtors->getFunctionIndex(), "function index"); @@ -1253,8 +1315,6 @@ void Writer::run() { populateProducers(); log("-- calculateImports"); calculateImports(); - log("-- createSyntheticInitFunctions"); - createSyntheticInitFunctions(); log("-- layoutMemory"); layoutMemory(); @@ -1267,18 +1327,23 @@ void Writer::run() { log("-- scanRelocations"); scanRelocations(); + log("-- createSyntheticInitFunctions"); + createSyntheticInitFunctions(); log("-- assignIndexes"); assignIndexes(); log("-- calculateInitFunctions"); calculateInitFunctions(); if (!config->relocatable) { - if (WasmSym::applyRelocs) - createApplyRelocationsFunction(); + // Create linker synthesized functions + if (WasmSym::applyDataRelocs) + createApplyDataRelocationsFunction(); + if (WasmSym::applyGlobalRelocs) + createApplyGlobalRelocationsFunction(); if (WasmSym::initMemory) createInitMemoryFunction(); + createStartFunction(); - // Create linker synthesized functions createCallCtorsFunction(); // Create export wrappers for commands if needed.