diff options
author | Sam Clegg <sbc@chromium.org> | 2024-01-18 15:01:21 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-18 15:01:21 -0800 |
commit | 184c22dd3aa3513244401fcced9a447c2577e2d3 (patch) | |
tree | ad138af3597d107b78dac5bf1feda759211fd2dc /lld | |
parent | f2684959145faaddc3717ea3d3fc4dbfb5c1a061 (diff) | |
download | llvm-184c22dd3aa3513244401fcced9a447c2577e2d3.zip llvm-184c22dd3aa3513244401fcced9a447c2577e2d3.tar.gz llvm-184c22dd3aa3513244401fcced9a447c2577e2d3.tar.bz2 |
[lld][WebAssembly] Move linker global state in to context object. NFC (#78629)
See lld/ELF/Config.h
Diffstat (limited to 'lld')
-rw-r--r-- | lld/wasm/Config.h | 22 | ||||
-rw-r--r-- | lld/wasm/Driver.cpp | 28 | ||||
-rw-r--r-- | lld/wasm/InputChunks.cpp | 6 | ||||
-rw-r--r-- | lld/wasm/InputFiles.cpp | 2 | ||||
-rw-r--r-- | lld/wasm/LTO.cpp | 2 | ||||
-rw-r--r-- | lld/wasm/MarkLive.cpp | 2 | ||||
-rw-r--r-- | lld/wasm/OutputSections.cpp | 6 | ||||
-rw-r--r-- | lld/wasm/OutputSegment.h | 2 | ||||
-rw-r--r-- | lld/wasm/Relocations.cpp | 4 | ||||
-rw-r--r-- | lld/wasm/SymbolTable.cpp | 5 | ||||
-rw-r--r-- | lld/wasm/SyntheticSections.cpp | 12 | ||||
-rw-r--r-- | lld/wasm/Writer.cpp | 38 |
12 files changed, 64 insertions, 65 deletions
diff --git a/lld/wasm/Config.h b/lld/wasm/Config.h index 104d470..cc8a1dc 100644 --- a/lld/wasm/Config.h +++ b/lld/wasm/Config.h @@ -71,6 +71,11 @@ struct Configuration { uint64_t initialHeap; uint64_t initialMemory; uint64_t maxMemory; + // 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 + // runtime). + uint64_t tableBase; uint64_t zStackSize; unsigned ltoPartitions; unsigned ltoo; @@ -96,11 +101,13 @@ struct Configuration { std::optional<std::vector<std::string>> features; std::optional<std::vector<std::string>> extraFeatures; llvm::SmallVector<uint8_t, 0> buildIdVector; +}; - // The following config options do not directly correspond to any - // particular command line options, and should probably be moved to separate - // Ctx struct as in ELF/Config.h +// The only instance of Configuration struct. +extern Configuration *config; +// The Ctx object hold all other (non-configuration) global state. +struct Ctx { // True if we are creating position-independent code. bool isPic; @@ -108,12 +115,6 @@ struct Configuration { // requires it to be allocated to table number 0. bool legacyFunctionTable = false; - // 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 - // runtime). - uint32_t tableBase = 0; - // Will be set to true if bss data segments should be emitted. In most cases // this is not necessary. bool emitBssSegments = false; @@ -124,8 +125,7 @@ struct Configuration { whyExtractRecords; }; -// The only instance of Configuration struct. -extern Configuration *config; +extern Ctx ctx; } // namespace lld::wasm diff --git a/lld/wasm/Driver.cpp b/lld/wasm/Driver.cpp index 60c6e5c..05a9686 100644 --- a/lld/wasm/Driver.cpp +++ b/lld/wasm/Driver.cpp @@ -45,6 +45,7 @@ using namespace llvm::wasm; namespace lld::wasm { Configuration *config; +Ctx ctx; namespace { @@ -308,7 +309,7 @@ static std::optional<std::string> searchLibraryBaseName(StringRef name) { for (StringRef dir : config->searchPaths) { // Currently we don't enable dynamic linking at all unless -shared or -pie // are used, so don't even look for .so files in that case.. - if (config->isPic && !config->isStatic) + if (ctx.isPic && !config->isStatic) if (std::optional<std::string> s = findFile(dir, "lib" + name + ".so")) return s; if (std::optional<std::string> s = findFile(dir, "lib" + name + ".a")) @@ -571,9 +572,9 @@ static void readConfigs(opt::InputArgList &args) { // This function initialize such members. See Config.h for the details // of these values. static void setConfigs() { - config->isPic = config->pie || config->shared; + ctx.isPic = config->pie || config->shared; - if (config->isPic) { + if (ctx.isPic) { if (config->exportTable) error("-shared/-pie is incompatible with --export-table"); config->importTable = true; @@ -680,7 +681,7 @@ static void checkOptions(opt::InputArgList &args) { warn("-Bsymbolic is only meaningful when combined with -shared"); } - if (config->isPic) { + if (ctx.isPic) { if (config->globalBase) error("--global-base may not be used with -shared/-pie"); if (config->tableBase) @@ -707,7 +708,7 @@ static Symbol *handleUndefined(StringRef name, const char *option) { if (auto *lazySym = dyn_cast<LazySymbol>(sym)) { lazySym->extract(); if (!config->whyExtract.empty()) - config->whyExtractRecords.emplace_back(option, sym->getFile(), *sym); + ctx.whyExtractRecords.emplace_back(option, sym->getFile(), *sym); } return sym; @@ -722,8 +723,7 @@ static void handleLibcall(StringRef name) { MemoryBufferRef mb = lazySym->getMemberBuffer(); if (isBitcode(mb)) { if (!config->whyExtract.empty()) - config->whyExtractRecords.emplace_back("<libcall>", sym->getFile(), - *sym); + ctx.whyExtractRecords.emplace_back("<libcall>", sym->getFile(), *sym); lazySym->extract(); } } @@ -742,7 +742,7 @@ static void writeWhyExtract() { } os << "reference\textracted\tsymbol\n"; - for (auto &entry : config->whyExtractRecords) { + for (auto &entry : ctx.whyExtractRecords) { os << std::get<0>(entry) << '\t' << toString(std::get<1>(entry)) << '\t' << toString(std::get<2>(entry)) << '\n'; } @@ -811,7 +811,7 @@ static void createSyntheticSymbols() { bool is64 = config->is64.value_or(false); - if (config->isPic) { + if (ctx.isPic) { WasmSym::stackPointer = createUndefinedGlobal("__stack_pointer", config->is64.value_or(false) ? &mutableGlobalTypeI64 @@ -850,7 +850,7 @@ static void createSyntheticSymbols() { "__wasm_init_tls")); } - if (config->isPic || + if (ctx.isPic || config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) { // For PIC code, or when dynamically importing addresses, we create // synthetic functions that apply relocations. These get called from @@ -871,7 +871,7 @@ static void createOptionalSymbols() { if (!config->shared) WasmSym::dataEnd = symtab->addOptionalDataSymbol("__data_end"); - if (!config->isPic) { + if (!ctx.isPic) { WasmSym::stackLow = symtab->addOptionalDataSymbol("__stack_low"); WasmSym::stackHigh = symtab->addOptionalDataSymbol("__stack_high"); WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base"); @@ -967,8 +967,8 @@ static void processStubLibraries() { depsAdded = true; lazy->extract(); if (!config->whyExtract.empty()) - config->whyExtractRecords.emplace_back(stub_file->getName(), - sym->getFile(), *sym); + ctx.whyExtractRecords.emplace_back(stub_file->getName(), + sym->getFile(), *sym); } } } @@ -1305,7 +1305,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) { sym->forceExport = true; } - if (!config->relocatable && !config->isPic) { + if (!config->relocatable && !ctx.isPic) { // Add synthetic dummies for weak undefined functions. Must happen // after LTO otherwise functions may not yet have signatures. symtab->handleWeakUndefines(); diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp index f55f54a..2074dd5 100644 --- a/lld/wasm/InputChunks.cpp +++ b/lld/wasm/InputChunks.cpp @@ -378,7 +378,7 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const { uint64_t offset = getVA(rel.Offset) - getInputSectionOffset(); Symbol *sym = file->getSymbol(rel); - if (!config->isPic && sym->isDefined()) + if (!ctx.isPic && sym->isDefined()) continue; LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type) @@ -390,7 +390,7 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const { writeSleb128(os, offset, "offset"); // In PIC mode we need to add the __memory_base - if (config->isPic) { + if (ctx.isPic) { writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); if (isTLS()) writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "tls_base"); @@ -417,7 +417,7 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const { writeU8(os, opcode_reloc_add, "ADD"); } } else { - assert(config->isPic); + assert(ctx.isPic); const GlobalSymbol* baseSymbol = WasmSym::memoryBase; if (rel.Type == R_WASM_TABLE_INDEX_I32 || rel.Type == R_WASM_TABLE_INDEX_I64) diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp index 97c8858..19c76e4 100644 --- a/lld/wasm/InputFiles.cpp +++ b/lld/wasm/InputFiles.cpp @@ -351,7 +351,7 @@ void ObjFile::addLegacyIndirectFunctionTableIfNeeded( // We assume that this compilation unit has unrelocatable references to // this table. - config->legacyFunctionTable = true; + ctx.legacyFunctionTable = true; } static bool shouldMerge(const WasmSection &sec) { diff --git a/lld/wasm/LTO.cpp b/lld/wasm/LTO.cpp index 4b22e4c..e523f0f 100644 --- a/lld/wasm/LTO.cpp +++ b/lld/wasm/LTO.cpp @@ -55,7 +55,7 @@ static std::unique_ptr<lto::LTO> createLTO() { if (config->relocatable) c.RelocModel = std::nullopt; - else if (config->isPic) + else if (ctx.isPic) c.RelocModel = Reloc::PIC_; else c.RelocModel = Reloc::Static; diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp index a59a80a..773af2f 100644 --- a/lld/wasm/MarkLive.cpp +++ b/lld/wasm/MarkLive.cpp @@ -187,7 +187,7 @@ bool MarkLive::isCallCtorsLive() { // In Emscripten-style PIC, we call `__wasm_call_ctors` which calls // `__wasm_apply_data_relocs`. - if (config->isPic) + if (ctx.isPic) return true; // If there are any init functions, mark `__wasm_call_ctors` live so that diff --git a/lld/wasm/OutputSections.cpp b/lld/wasm/OutputSections.cpp index 9c4383c..3974146 100644 --- a/lld/wasm/OutputSections.cpp +++ b/lld/wasm/OutputSections.cpp @@ -107,7 +107,7 @@ void DataSection::finalizeContents() { }); #endif - assert((config->sharedMemory || !config->isPic || config->extendedConst || + assert((config->sharedMemory || !ctx.isPic || config->extendedConst || activeCount <= 1) && "output segments should have been combined by now"); @@ -124,7 +124,7 @@ void DataSection::finalizeContents() { if (segment->initFlags & WASM_DATA_SEGMENT_HAS_MEMINDEX) writeUleb128(os, 0, "memory index"); if ((segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0) { - if (config->isPic && config->extendedConst) { + if (ctx.isPic && config->extendedConst) { writeU8(os, WASM_OPCODE_GLOBAL_GET, "global get"); writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "literal (global index)"); @@ -136,7 +136,7 @@ void DataSection::finalizeContents() { } else { WasmInitExpr initExpr; initExpr.Extended = false; - if (config->isPic) { + if (ctx.isPic) { assert(segment->startVA == 0); initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET; initExpr.Inst.Value.Global = WasmSym::memoryBase->getGlobalIndex(); diff --git a/lld/wasm/OutputSegment.h b/lld/wasm/OutputSegment.h index e1e43c1..701df1f 100644 --- a/lld/wasm/OutputSegment.h +++ b/lld/wasm/OutputSegment.h @@ -27,7 +27,7 @@ public: // to the output binary. However if the memory is imported, and // we can't use memory.fill during startup (due to lack of bulk // memory feature) then we include BSS segments verbatim. - bool requiredInBinary() const { return !isBss || config->emitBssSegments; } + bool requiredInBinary() const { return !isBss || ctx.emitBssSegments; } bool isTLS() const { return name == ".tdata"; } diff --git a/lld/wasm/Relocations.cpp b/lld/wasm/Relocations.cpp index ce41cdc..fcffacb 100644 --- a/lld/wasm/Relocations.cpp +++ b/lld/wasm/Relocations.cpp @@ -19,7 +19,7 @@ using namespace llvm::wasm; namespace lld::wasm { static bool requiresGOTAccess(const Symbol *sym) { - if (!config->isPic && + if (!ctx.isPic && config->unresolvedSymbols != UnresolvedPolicy::ImportDynamic) return false; if (sym->isHidden() || sym->isLocal()) @@ -141,7 +141,7 @@ void scanRelocations(InputChunk *chunk) { break; } - if (config->isPic || + if (ctx.isPic || (sym->isUndefined() && config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) { switch (reloc.Type) { diff --git a/lld/wasm/SymbolTable.cpp b/lld/wasm/SymbolTable.cpp index 1563e66..9dd599e 100644 --- a/lld/wasm/SymbolTable.cpp +++ b/lld/wasm/SymbolTable.cpp @@ -535,8 +535,7 @@ Symbol *SymbolTable::addUndefinedFunction(StringRef name, } else { lazy->extract(); if (!config->whyExtract.empty()) - config->whyExtractRecords.emplace_back(toString(file), s->getFile(), - *s); + ctx.whyExtractRecords.emplace_back(toString(file), s->getFile(), *s); } } else { auto existingFunction = dyn_cast<FunctionSymbol>(s); @@ -774,7 +773,7 @@ void SymbolTable::addLazy(ArchiveFile *file, const Archive::Symbol *sym) { const InputFile *oldFile = s->getFile(); file->addMember(sym); if (!config->whyExtract.empty()) - config->whyExtractRecords.emplace_back(toString(oldFile), s->getFile(), *s); + ctx.whyExtractRecords.emplace_back(toString(oldFile), s->getFile(), *s); } bool SymbolTable::addComdat(StringRef name) { diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp index 3a9c147..f95174f 100644 --- a/lld/wasm/SyntheticSections.cpp +++ b/lld/wasm/SyntheticSections.cpp @@ -55,7 +55,7 @@ public: } // namespace bool DylinkSection::isNeeded() const { - return config->isPic || + return ctx.isPic || config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic || !symtab->sharedFiles.empty(); } @@ -174,7 +174,7 @@ void ImportSection::addGOTEntry(Symbol *sym) { return; LLVM_DEBUG(dbgs() << "addGOTEntry: " << toString(*sym) << "\n"); sym->setGOTIndex(numImportedGlobals++); - if (config->isPic) { + if (ctx.isPic) { // Any symbol that is assigned an normal GOT entry must be exported // otherwise the dynamic linker won't be able create the entry that contains // it. @@ -319,7 +319,7 @@ void TableSection::addTable(InputTable *table) { return; // Some inputs require that the indirect function table be assigned to table // number 0. - if (config->legacyFunctionTable && + if (ctx.legacyFunctionTable && isa<DefinedTable>(WasmSym::indirectFunctionTable) && cast<DefinedTable>(WasmSym::indirectFunctionTable)->table == table) { if (out.importSec->getNumImportedTables()) { @@ -474,7 +474,7 @@ void GlobalSection::writeBody() { // In the case of dynamic linking, unless we have 'extended-const' // available, these global must to be mutable since they get updated to // the correct runtime value during `__wasm_apply_global_relocs`. - if (!config->extendedConst && config->isPic && !sym->isTLS()) + if (!config->extendedConst && ctx.isPic && !sym->isTLS()) mutable_ = true; // With multi-theadeding any TLS globals must be mutable since they get // set during `__wasm_apply_global_tls_relocs` @@ -487,7 +487,7 @@ void GlobalSection::writeBody() { bool useExtendedConst = false; uint32_t globalIdx; int64_t offset; - if (config->extendedConst && config->isPic) { + if (config->extendedConst && ctx.isPic) { if (auto *d = dyn_cast<DefinedData>(sym)) { if (!sym->isTLS()) { globalIdx = WasmSym::memoryBase->getGlobalIndex(); @@ -582,7 +582,7 @@ void ElemSection::writeBody() { WasmInitExpr initExpr; initExpr.Extended = false; - if (config->isPic) { + if (ctx.isPic) { initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET; initExpr.Inst.Value.Global = (config->is64.value_or(false) ? WasmSym::tableBase32 diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp index 805018c..7c19a1b 100644 --- a/lld/wasm/Writer.cpp +++ b/lld/wasm/Writer.cpp @@ -332,7 +332,7 @@ void Writer::layoutMemory() { uint64_t memoryPtr = 0; auto placeStack = [&]() { - if (config->relocatable || config->isPic) + if (config->relocatable || ctx.isPic) return; memoryPtr = alignTo(memoryPtr, stackAlignment); if (WasmSym::stackLow) @@ -415,7 +415,7 @@ void Writer::layoutMemory() { uint64_t staticDataSize = memoryPtr - dataStart; log("mem: static data = " + Twine(staticDataSize)); - if (config->isPic) + if (ctx.isPic) out.dylinkSec->memSize = staticDataSize; if (!config->stackFirst) @@ -489,7 +489,7 @@ void Writer::layoutMemory() { 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 (config->isPic) + if (ctx.isPic) max = maxMemorySetting; else max = memoryPtr; @@ -568,7 +568,7 @@ void Writer::populateTargetFeatures() { SmallSet<std::string, 8> &allowed = out.targetFeaturesSec->features; bool tlsUsed = false; - if (config->isPic) { + if (ctx.isPic) { // This should not be necessary because all PIC objects should // contain the mutable-globals feature. // TODO (https://github.com/llvm/llvm-project/issues/51681) @@ -683,7 +683,7 @@ done: // the bss segments, so these segments need to exist in the binary. if (config->emitRelocs || (config->memoryImport.has_value() && !allowed.count("bulk-memory"))) - config->emitBssSegments = true; + ctx.emitBssSegments = true; if (allowed.count("extended-const")) config->extendedConst = true; @@ -734,18 +734,18 @@ static bool shouldImport(Symbol *sym) { return true; if (!sym->isUndefined()) return false; - if (sym->isWeak() && !config->relocatable && !config->isPic) + if (sym->isWeak() && !config->relocatable && !ctx.isPic) return false; // In PIC mode we only need to import functions when they are called directly. // Indirect usage all goes via GOT imports. - if (config->isPic) { + if (ctx.isPic) { if (auto *f = dyn_cast<UndefinedFunction>(sym)) if (!f->isCalledDirectly) return false; } - if (config->isPic || config->relocatable || config->importUndefined || + if (ctx.isPic || config->relocatable || config->importUndefined || config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) return true; if (config->allowUndefinedSymbols.count(sym->getName()) != 0) @@ -873,7 +873,7 @@ void Writer::calculateTypes() { // which calls the constructors and destructors. void Writer::createCommandExportWrappers() { // This logic doesn't currently support Emscripten-style PIC mode. - assert(!config->isPic); + assert(!ctx.isPic); // If there are no ctors and there's no libc `__wasm_call_dtors` to // call, don't wrap the exports. @@ -1072,7 +1072,7 @@ void Writer::combineOutputSegments() { // This restriction does not apply when the extended const extension is // available: https://github.com/WebAssembly/extended-const assert(!config->extendedConst); - assert(config->isPic && !config->sharedMemory); + assert(ctx.isPic && !config->sharedMemory); if (segments.size() <= 1) return; OutputSegment *combined = make<OutputSegment>(".data"); @@ -1174,7 +1174,7 @@ void Writer::createSyntheticInitFunctions() { } } - if (config->isPic && out.globalSec->needsRelocations()) { + if (ctx.isPic && out.globalSec->needsRelocations()) { WasmSym::applyGlobalRelocs = symtab->addSyntheticFunction( "__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN, make<SyntheticFunction>(nullSignature, "__wasm_apply_global_relocs")); @@ -1257,7 +1257,7 @@ void Writer::createInitMemoryFunction() { // (i32.const 1) auto writeGetFlagAddress = [&]() { - if (config->isPic) { + if (ctx.isPic) { writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get"); writeUleb128(os, 0, "local 0"); } else { @@ -1267,7 +1267,7 @@ void Writer::createInitMemoryFunction() { if (config->sharedMemory) { // With PIC code we cache the flag address in local 0 - if (config->isPic) { + if (ctx.isPic) { writeUleb128(os, 1, "num local decls"); writeUleb128(os, 2, "local count"); writeU8(os, is64 ? WASM_TYPE_I64 : WASM_TYPE_I32, "address type"); @@ -1317,7 +1317,7 @@ void Writer::createInitMemoryFunction() { // instructions take as their first argument the destination // address. writePtrConst(os, s->startVA, is64, "destination address"); - if (config->isPic) { + if (ctx.isPic) { writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET"); writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "__memory_base"); @@ -1329,7 +1329,7 @@ void Writer::createInitMemoryFunction() { // global. This allows the runtime to use this static copy of the // TLS data for the first/main thread. if (config->sharedMemory && s->isTLS()) { - if (config->isPic) { + if (ctx.isPic) { // Cache the result of the addionion in local 0 writeU8(os, WASM_OPCODE_LOCAL_TEE, "local.tee"); writeUleb128(os, 1, "local 1"); @@ -1339,7 +1339,7 @@ void Writer::createInitMemoryFunction() { writeU8(os, WASM_OPCODE_GLOBAL_SET, "GLOBAL_SET"); writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "__tls_base"); - if (config->isPic) { + if (ctx.isPic) { writeU8(os, WASM_OPCODE_LOCAL_GET, "local.tee"); writeUleb128(os, 1, "local 1"); } @@ -1687,7 +1687,7 @@ void Writer::createSyntheticSectionsPostLayout() { void Writer::run() { // For PIC code the table base is assigned dynamically by the loader. // For non-PIC, we start at 1 so that accessing table index 0 always traps. - if (!config->isPic) { + if (!ctx.isPic) { if (WasmSym::definedTableBase) WasmSym::definedTableBase->setVA(config->tableBase); if (WasmSym::definedTableBase32) @@ -1734,7 +1734,7 @@ void Writer::run() { // `__memory_base` import. Unless we support the extended const expression we // can't do addition inside the constant expression, so we much combine the // segments into a single one that can live at `__memory_base`. - if (config->isPic && !config->extendedConst && !config->sharedMemory) { + if (ctx.isPic && !config->extendedConst && !config->sharedMemory) { // In shared memory mode all data segments are passive and initialized // via __wasm_init_memory. log("-- combineOutputSegments"); @@ -1779,7 +1779,7 @@ void Writer::run() { // 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 && + if (!config->relocatable && !ctx.isPic && !WasmSym::callCtors->isUsedInRegularObj && !WasmSym::callCtors->isExported()) { log("-- createCommandExportWrappers"); |