diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index b08427f738bb4..cd280aa09964d 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -162,8 +162,6 @@ struct Configuration { bool dll = false; StringRef implib; bool noimplib = false; - std::vector exports; - bool hadExplicitExports; std::set delayLoads; std::map dllOrder; Symbol *delayLoadHelper = nullptr; diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp index 3e9f3f4718386..6a3f8eb21e847 100644 --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -639,22 +639,22 @@ class ExportDirectoryChunk : public NonSectionChunk { class AddressTableChunk : public NonSectionChunk { public: - explicit AddressTableChunk(COFFLinkerContext &ctx, size_t baseOrdinal, + explicit AddressTableChunk(SymbolTable &symtab, size_t baseOrdinal, size_t maxOrdinal) : baseOrdinal(baseOrdinal), size((maxOrdinal - baseOrdinal) + 1), - ctx(ctx) {} + symtab(symtab) {} size_t getSize() const override { return size * 4; } void writeTo(uint8_t *buf) const override { memset(buf, 0, getSize()); - for (const Export &e : ctx.config.exports) { + for (const Export &e : symtab.exports) { assert(e.ordinal >= baseOrdinal && "Export symbol has invalid ordinal"); // Subtract the OrdinalBase to get the index. uint8_t *p = buf + (e.ordinal - baseOrdinal) * 4; uint32_t bit = 0; // Pointer to thumb code must have the LSB set, so adjust it. - if (ctx.config.machine == ARMNT && !e.data) + if (symtab.machine == ARMNT && !e.data) bit = 1; if (e.forwardChunk) { write32le(p, e.forwardChunk->getRVA() | bit); @@ -669,7 +669,7 @@ class AddressTableChunk : public NonSectionChunk { private: size_t baseOrdinal; size_t size; - const COFFLinkerContext &ctx; + const SymbolTable &symtab; }; class NamePointersChunk : public NonSectionChunk { @@ -690,13 +690,13 @@ class NamePointersChunk : public NonSectionChunk { class ExportOrdinalChunk : public NonSectionChunk { public: - explicit ExportOrdinalChunk(const COFFLinkerContext &ctx, size_t baseOrdinal, + explicit ExportOrdinalChunk(const SymbolTable &symtab, size_t baseOrdinal, size_t tableSize) - : baseOrdinal(baseOrdinal), size(tableSize), ctx(ctx) {} + : baseOrdinal(baseOrdinal), size(tableSize), symtab(symtab) {} size_t getSize() const override { return size * 2; } void writeTo(uint8_t *buf) const override { - for (const Export &e : ctx.config.exports) { + for (const Export &e : symtab.exports) { if (e.noname) continue; assert(e.ordinal >= baseOrdinal && "Export symbol has invalid ordinal"); @@ -709,7 +709,7 @@ class ExportOrdinalChunk : public NonSectionChunk { private: size_t baseOrdinal; size_t size; - const COFFLinkerContext &ctx; + const SymbolTable &symtab; }; } // anonymous namespace @@ -920,9 +920,9 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s, } } -void createEdataChunks(COFFLinkerContext &ctx, std::vector &chunks) { +void createEdataChunks(SymbolTable &symtab, std::vector &chunks) { unsigned baseOrdinal = 1 << 16, maxOrdinal = 0; - for (Export &e : ctx.config.exports) { + for (Export &e : symtab.exports) { baseOrdinal = std::min(baseOrdinal, (unsigned)e.ordinal); maxOrdinal = std::max(maxOrdinal, (unsigned)e.ordinal); } @@ -930,15 +930,16 @@ void createEdataChunks(COFFLinkerContext &ctx, std::vector &chunks) { // https://learn.microsoft.com/en-us/cpp/build/reference/export-exports-a-function?view=msvc-170 assert(baseOrdinal >= 1); - auto *dllName = make(sys::path::filename(ctx.config.outputFile)); - auto *addressTab = make(ctx, baseOrdinal, maxOrdinal); + auto *dllName = + make(sys::path::filename(symtab.ctx.config.outputFile)); + auto *addressTab = make(symtab, baseOrdinal, maxOrdinal); std::vector names; - for (Export &e : ctx.config.exports) + for (Export &e : symtab.exports) if (!e.noname) names.push_back(make(e.exportName)); std::vector forwards; - for (Export &e : ctx.config.exports) { + for (Export &e : symtab.exports) { if (e.forwardTo.empty()) continue; e.forwardChunk = make(e.forwardTo); @@ -946,7 +947,8 @@ void createEdataChunks(COFFLinkerContext &ctx, std::vector &chunks) { } auto *nameTab = make(names); - auto *ordinalTab = make(ctx, baseOrdinal, names.size()); + auto *ordinalTab = + make(symtab, baseOrdinal, names.size()); auto *dir = make(baseOrdinal, maxOrdinal, names.size(), dllName, addressTab, nameTab, ordinalTab); diff --git a/lld/COFF/DLL.h b/lld/COFF/DLL.h index 901c974069b47..724a323d62d20 100644 --- a/lld/COFF/DLL.h +++ b/lld/COFF/DLL.h @@ -78,7 +78,7 @@ class DelayLoadContents { }; // Create all chunks for the DLL export table. -void createEdataChunks(COFFLinkerContext &ctx, std::vector &chunks); +void createEdataChunks(SymbolTable &symtab, std::vector &chunks); } // namespace lld::coff diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index b257071c97086..4e0678282eed0 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -458,7 +458,7 @@ void LinkerDriver::parseDirectives(InputFile *file) { // declarations, many object files may end up with having the // same /EXPORT options. In order to save cost of parsing them, // we dedup them first. - if (!directivesExports.insert(e).second) + if (!file->symtab.directivesExports.insert(e).second) continue; Export exp = parseExport(e); @@ -469,7 +469,7 @@ void LinkerDriver::parseDirectives(InputFile *file) { exp.extName = saver().save("_" + exp.extName); } exp.source = ExportSource::Directives; - ctx.config.exports.push_back(exp); + file->symtab.exports.push_back(exp); } // Handle /include: in bulk. @@ -956,7 +956,7 @@ std::string LinkerDriver::getImportName(bool asLib) { void LinkerDriver::createImportLibrary(bool asLib) { llvm::TimeTraceScope timeScope("Create import library"); std::vector exports; - for (Export &e1 : ctx.config.exports) { + for (Export &e1 : ctx.symtab.exports) { COFFShortExport e2; e2.Name = std::string(e1.name); e2.SymbolName = std::string(e1.symbolName); @@ -1069,7 +1069,7 @@ void LinkerDriver::parseModuleDefs(StringRef path) { e2.isPrivate = e1.Private; e2.constant = e1.Constant; e2.source = ExportSource::ModuleDefinition; - ctx.config.exports.push_back(e2); + ctx.symtab.exports.push_back(e2); } } @@ -1222,8 +1222,10 @@ static void findKeepUniqueSections(COFFLinkerContext &ctx) { // Exported symbols could be address-significant in other executables or DSOs, // so we conservatively mark them as address-significant. - for (Export &r : ctx.config.exports) - markAddrsig(r.sym); + ctx.forEachSymtab([](SymbolTable &symtab) { + for (Export &r : symtab.exports) + markAddrsig(r.sym); + }); // Visit the address-significance table in each object file and mark each // referenced symbol as address-significant. @@ -1376,13 +1378,13 @@ void LinkerDriver::maybeCreateECExportThunk(StringRef name, Symbol *&sym) { void LinkerDriver::createECExportThunks() { // Check if EXP+ symbols have corresponding $hp_target symbols and use them // to create export thunks when available. - for (Symbol *s : ctx.symtab.expSymbols) { + for (Symbol *s : ctx.symtabEC->expSymbols) { if (!s->isUsedInRegularObj) continue; assert(s->getName().starts_with("EXP+")); std::string targetName = (s->getName().substr(strlen("EXP+")) + "$hp_target").str(); - Symbol *sym = ctx.symtab.find(targetName); + Symbol *sym = ctx.symtabEC->find(targetName); if (!sym) continue; Defined *targetSym; @@ -1407,7 +1409,7 @@ void LinkerDriver::createECExportThunks() { if (ctx.symtabEC->entry) maybeCreateECExportThunk(ctx.symtabEC->entry->getName(), ctx.symtabEC->entry); - for (Export &e : ctx.config.exports) { + for (Export &e : ctx.symtabEC->exports) { if (!e.data) maybeCreateECExportThunk(e.extName.empty() ? e.name : e.extName, e.sym); } @@ -1430,7 +1432,7 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { if (!ctx.config.dll) return; - if (!ctx.config.exports.empty()) + if (!ctx.symtab.exports.empty()) return; if (args.hasArg(OPT_exclude_all_symbols)) return; @@ -1466,7 +1468,7 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) { if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE)) e.data = true; s->isUsedInRegularObj = true; - ctx.config.exports.push_back(e); + ctx.symtab.exports.push_back(e); }); } @@ -2343,7 +2345,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (!e.extName.empty() && !isDecorated(e.extName)) e.extName = saver().save("_" + e.extName); } - config->exports.push_back(e); + mainSymtab.exports.push_back(e); } } @@ -2355,7 +2357,7 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Handle generation of import library from a def file. if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) { - fixupExports(); + ctx.forEachSymtab([](SymbolTable &symtab) { symtab.fixupExports(); }); if (!config->noimplib) createImportLibrary(/*asLib=*/true); return; @@ -2541,16 +2543,16 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // search for its mangled names. if (symtab.entry) symtab.mangleMaybe(symtab.entry); - }); - // Windows specific -- Make sure we resolve all dllexported symbols. - for (Export &e : config->exports) { - if (!e.forwardTo.empty()) - continue; - e.sym = ctx.symtab.addGCRoot(e.name, !e.data); - if (e.source != ExportSource::Directives) - e.symbolName = ctx.symtab.mangleMaybe(e.sym); - } + // Windows specific -- Make sure we resolve all dllexported symbols. + for (Export &e : symtab.exports) { + if (!e.forwardTo.empty()) + continue; + e.sym = symtab.addGCRoot(e.name, !e.data); + if (e.source != ExportSource::Directives) + e.symbolName = symtab.mangleMaybe(e.sym); + } + }); // Add weak aliases. Weak aliases is a mechanism to give remaining // undefined symbols final chance to be resolved successfully. @@ -2651,7 +2653,9 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { if (errorCount()) return; - config->hadExplicitExports = !config->exports.empty(); + ctx.forEachSymtab([](SymbolTable &symtab) { + symtab.hadExplicitExports = !symtab.exports.empty(); + }); if (config->mingw) { // In MinGW, all symbols are automatically exported if no symbols // are chosen to be exported. @@ -2716,17 +2720,18 @@ void LinkerDriver::linkerMain(ArrayRef argsArr) { // Windows specific -- when we are creating a .dll file, we also // need to create a .lib file. In MinGW mode, we only do that when the // -implib option is given explicitly, for compatibility with GNU ld. - if (!config->exports.empty() || config->dll) { + if (!ctx.symtab.exports.empty() || config->dll) { llvm::TimeTraceScope timeScope("Create .lib exports"); - fixupExports(); + ctx.forEachSymtab([](SymbolTable &symtab) { symtab.fixupExports(); }); if (!config->noimplib && (!config->mingw || !config->implib.empty())) createImportLibrary(/*asLib=*/false); - assignExportOrdinals(); + ctx.forEachSymtab( + [](SymbolTable &symtab) { symtab.assignExportOrdinals(); }); } // Handle /output-def (MinGW specific). if (auto *arg = args.getLastArg(OPT_output_def)) - writeDefFile(ctx, arg->getValue(), config->exports); + writeDefFile(ctx, arg->getValue(), ctx.symtab.exports); // Set extra alignment for .comm symbols for (auto pair : config->alignComm) { diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index 5f65bd7f8d097..12724cbd1eef4 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -182,7 +182,6 @@ class LinkerDriver { std::list> taskQueue; std::vector resources; - llvm::DenseSet directivesExports; llvm::DenseSet excludedSymbols; COFFLinkerContext &ctx; @@ -249,8 +248,6 @@ class LinkerDriver { // Used for dllexported symbols. Export parseExport(StringRef arg); - void fixupExports(); - void assignExportOrdinals(); // Parses a string in the form of "key=value" and check // if value matches previous values for the key. diff --git a/lld/COFF/DriverUtils.cpp b/lld/COFF/DriverUtils.cpp index 19abd4806d53f..4a70c826691d1 100644 --- a/lld/COFF/DriverUtils.cpp +++ b/lld/COFF/DriverUtils.cpp @@ -656,142 +656,6 @@ Export LinkerDriver::parseExport(StringRef arg) { llvm_unreachable(""); } -// Convert stdcall/fastcall style symbols into unsuffixed symbols, -// with or without a leading underscore. (MinGW specific.) -static StringRef killAt(StringRef sym, bool prefix) { - if (sym.empty()) - return sym; - // Strip any trailing stdcall suffix - sym = sym.substr(0, sym.find('@', 1)); - if (!sym.starts_with("@")) { - if (prefix && !sym.starts_with("_")) - return saver().save("_" + sym); - return sym; - } - // For fastcall, remove the leading @ and replace it with an - // underscore, if prefixes are used. - sym = sym.substr(1); - if (prefix) - sym = saver().save("_" + sym); - return sym; -} - -static StringRef exportSourceName(ExportSource s) { - switch (s) { - case ExportSource::Directives: - return "source file (directives)"; - case ExportSource::Export: - return "/export"; - case ExportSource::ModuleDefinition: - return "/def"; - default: - llvm_unreachable("unknown ExportSource"); - } -} - -// Performs error checking on all /export arguments. -// It also sets ordinals. -void LinkerDriver::fixupExports() { - llvm::TimeTraceScope timeScope("Fixup exports"); - // Symbol ordinals must be unique. - std::set ords; - for (Export &e : ctx.config.exports) { - if (e.ordinal == 0) - continue; - if (!ords.insert(e.ordinal).second) - Fatal(ctx) << "duplicate export ordinal: " << e.name; - } - - for (Export &e : ctx.config.exports) { - if (!e.exportAs.empty()) { - e.exportName = e.exportAs; - continue; - } - - StringRef sym = - !e.forwardTo.empty() || e.extName.empty() ? e.name : e.extName; - if (ctx.config.machine == I386 && sym.starts_with("_")) { - // In MSVC mode, a fully decorated stdcall function is exported - // as-is with the leading underscore (with type IMPORT_NAME). - // In MinGW mode, a decorated stdcall function gets the underscore - // removed, just like normal cdecl functions. - if (ctx.config.mingw || !sym.contains('@')) { - e.exportName = sym.substr(1); - continue; - } - } - if (isArm64EC(ctx.config.machine) && !e.data && !e.constant) { - if (std::optional demangledName = - getArm64ECDemangledFunctionName(sym)) { - e.exportName = saver().save(*demangledName); - continue; - } - } - e.exportName = sym; - } - - if (ctx.config.killAt && ctx.config.machine == I386) { - for (Export &e : ctx.config.exports) { - e.name = killAt(e.name, true); - e.exportName = killAt(e.exportName, false); - e.extName = killAt(e.extName, true); - e.symbolName = killAt(e.symbolName, true); - } - } - - // Uniquefy by name. - DenseMap> map( - ctx.config.exports.size()); - std::vector v; - for (Export &e : ctx.config.exports) { - auto pair = map.insert(std::make_pair(e.exportName, std::make_pair(&e, 0))); - bool inserted = pair.second; - if (inserted) { - pair.first->second.second = v.size(); - v.push_back(e); - continue; - } - Export *existing = pair.first->second.first; - if (e == *existing || e.name != existing->name) - continue; - // If the existing export comes from .OBJ directives, we are allowed to - // overwrite it with /DEF: or /EXPORT without any warning, as MSVC link.exe - // does. - if (existing->source == ExportSource::Directives) { - *existing = e; - v[pair.first->second.second] = e; - continue; - } - if (existing->source == e.source) { - Warn(ctx) << "duplicate " << exportSourceName(existing->source) - << " option: " << e.name; - } else { - Warn(ctx) << "duplicate export: " << e.name << " first seen in " - << exportSourceName(existing->source) << ", now in " - << exportSourceName(e.source); - } - } - ctx.config.exports = std::move(v); - - // Sort by name. - llvm::sort(ctx.config.exports, [](const Export &a, const Export &b) { - return a.exportName < b.exportName; - }); -} - -void LinkerDriver::assignExportOrdinals() { - // Assign unique ordinals if default (= 0). - uint32_t max = 0; - for (Export &e : ctx.config.exports) - max = std::max(max, (uint32_t)e.ordinal); - for (Export &e : ctx.config.exports) - if (e.ordinal == 0) - e.ordinal = ++max; - if (max > std::numeric_limits::max()) - Fatal(ctx) << "too many exported symbols (got " << max << ", max " - << Twine(std::numeric_limits::max()) << ")"; -} - // Parses a string in the form of "key=value" and check // if value matches previous values for the same key. void LinkerDriver::checkFailIfMismatch(StringRef arg, InputFile *source) { diff --git a/lld/COFF/MapFile.cpp b/lld/COFF/MapFile.cpp index af87587d143d5..eb98bb484f9f4 100644 --- a/lld/COFF/MapFile.cpp +++ b/lld/COFF/MapFile.cpp @@ -326,7 +326,7 @@ void lld::coff::writeMapFile(COFFLinkerContext &ctx) { os << " Exports\n"; os << "\n"; os << " ordinal name\n\n"; - for (Export &e : ctx.config.exports) { + for (Export &e : ctx.symtab.exports) { os << format(" %7d", e.ordinal) << " " << e.name << "\n"; if (!e.extName.empty() && e.extName != e.name) os << " exported name: " << e.extName << "\n"; diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index bf965e8a2332d..ecccc7d6ed70c 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -1118,6 +1118,141 @@ void SymbolTable::addUndefinedGlob(StringRef arg) { addGCRoot(sym->getName()); } +// Convert stdcall/fastcall style symbols into unsuffixed symbols, +// with or without a leading underscore. (MinGW specific.) +static StringRef killAt(StringRef sym, bool prefix) { + if (sym.empty()) + return sym; + // Strip any trailing stdcall suffix + sym = sym.substr(0, sym.find('@', 1)); + if (!sym.starts_with("@")) { + if (prefix && !sym.starts_with("_")) + return saver().save("_" + sym); + return sym; + } + // For fastcall, remove the leading @ and replace it with an + // underscore, if prefixes are used. + sym = sym.substr(1); + if (prefix) + sym = saver().save("_" + sym); + return sym; +} + +static StringRef exportSourceName(ExportSource s) { + switch (s) { + case ExportSource::Directives: + return "source file (directives)"; + case ExportSource::Export: + return "/export"; + case ExportSource::ModuleDefinition: + return "/def"; + default: + llvm_unreachable("unknown ExportSource"); + } +} + +// Performs error checking on all /export arguments. +// It also sets ordinals. +void SymbolTable::fixupExports() { + llvm::TimeTraceScope timeScope("Fixup exports"); + // Symbol ordinals must be unique. + std::set ords; + for (Export &e : exports) { + if (e.ordinal == 0) + continue; + if (!ords.insert(e.ordinal).second) + Fatal(ctx) << "duplicate export ordinal: " << e.name; + } + + for (Export &e : exports) { + if (!e.exportAs.empty()) { + e.exportName = e.exportAs; + continue; + } + + StringRef sym = + !e.forwardTo.empty() || e.extName.empty() ? e.name : e.extName; + if (machine == I386 && sym.starts_with("_")) { + // In MSVC mode, a fully decorated stdcall function is exported + // as-is with the leading underscore (with type IMPORT_NAME). + // In MinGW mode, a decorated stdcall function gets the underscore + // removed, just like normal cdecl functions. + if (ctx.config.mingw || !sym.contains('@')) { + e.exportName = sym.substr(1); + continue; + } + } + if (isEC() && !e.data && !e.constant) { + if (std::optional demangledName = + getArm64ECDemangledFunctionName(sym)) { + e.exportName = saver().save(*demangledName); + continue; + } + } + e.exportName = sym; + } + + if (ctx.config.killAt && machine == I386) { + for (Export &e : exports) { + e.name = killAt(e.name, true); + e.exportName = killAt(e.exportName, false); + e.extName = killAt(e.extName, true); + e.symbolName = killAt(e.symbolName, true); + } + } + + // Uniquefy by name. + DenseMap> map(exports.size()); + std::vector v; + for (Export &e : exports) { + auto pair = map.insert(std::make_pair(e.exportName, std::make_pair(&e, 0))); + bool inserted = pair.second; + if (inserted) { + pair.first->second.second = v.size(); + v.push_back(e); + continue; + } + Export *existing = pair.first->second.first; + if (e == *existing || e.name != existing->name) + continue; + // If the existing export comes from .OBJ directives, we are allowed to + // overwrite it with /DEF: or /EXPORT without any warning, as MSVC link.exe + // does. + if (existing->source == ExportSource::Directives) { + *existing = e; + v[pair.first->second.second] = e; + continue; + } + if (existing->source == e.source) { + Warn(ctx) << "duplicate " << exportSourceName(existing->source) + << " option: " << e.name; + } else { + Warn(ctx) << "duplicate export: " << e.name << " first seen in " + << exportSourceName(existing->source) << ", now in " + << exportSourceName(e.source); + } + } + exports = std::move(v); + + // Sort by name. + llvm::sort(exports, [](const Export &a, const Export &b) { + return a.exportName < b.exportName; + }); +} + +void SymbolTable::assignExportOrdinals() { + // Assign unique ordinals if default (= 0). + uint32_t max = 0; + for (Export &e : exports) + max = std::max(max, (uint32_t)e.ordinal); + for (Export &e : exports) + if (e.ordinal == 0) + e.ordinal = ++max; + if (max > std::numeric_limits::max()) + Fatal(ctx) << "too many exported symbols (got " << max << ", max " + << Twine(std::numeric_limits::max()) << ")"; +} + Symbol *SymbolTable::addUndefined(StringRef name) { return addUndefined(name, nullptr, false); } diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h index 66bca0d63e5ff..a0acf5db46903 100644 --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -150,6 +150,14 @@ class SymbolTable { // A list of EC EXP+ symbols. std::vector expSymbols; + // A list of DLL exports. + std::vector exports; + llvm::DenseSet directivesExports; + bool hadExplicitExports; + + void fixupExports(); + void assignExportOrdinals(); + // Iterates symbols in non-determinstic hash table order. template void forEachSymbol(T callback) { for (auto &pair : symMap) diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 90b2c083cbfb9..3d95d219a493c 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -1331,11 +1331,11 @@ void Writer::createExportTable() { if (!edataSec->chunks.empty()) { // Allow using a custom built export table from input object files, instead // of having the linker synthesize the tables. - if (ctx.config.hadExplicitExports) + if (ctx.symtab.hadExplicitExports) Warn(ctx) << "literal .edata sections override exports"; - } else if (!ctx.config.exports.empty()) { + } else if (!ctx.symtab.exports.empty()) { std::vector edataChunks; - createEdataChunks(ctx, edataChunks); + createEdataChunks(ctx.symtab, edataChunks); for (Chunk *c : edataChunks) edataSec->addChunk(c); } @@ -1344,7 +1344,7 @@ void Writer::createExportTable() { edataEnd = edataSec->chunks.back(); } // Warn on exported deleting destructor. - for (auto e : ctx.config.exports) + for (auto e : ctx.symtab.exports) if (e.sym && e.sym->getName().starts_with("??_G")) Warn(ctx) << "export of deleting dtor: " << e.sym; } @@ -2061,11 +2061,11 @@ void Writer::createGuardCFTables() { ctx.forEachSymtab([&](SymbolTable &symtab) { if (symtab.entry) maybeAddAddressTakenFunction(addressTakenSyms, symtab.entry); - }); - // Mark exported symbols in executable sections as address-taken. - for (Export &e : config->exports) - maybeAddAddressTakenFunction(addressTakenSyms, e.sym); + // Mark exported symbols in executable sections as address-taken. + for (Export &e : symtab.exports) + maybeAddAddressTakenFunction(addressTakenSyms, e.sym); + }); // For each entry in the .giats table, check if it has a corresponding load // thunk (e.g. because the DLL that defines it will be delay-loaded) and, if diff --git a/lld/test/COFF/arm64x-export.test b/lld/test/COFF/arm64x-export.test new file mode 100644 index 0000000000000..e5d0307e570ef --- /dev/null +++ b/lld/test/COFF/arm64x-export.test @@ -0,0 +1,121 @@ +REQUIRES: aarch64, x86 +RUN: split-file %s %t.dir && cd %t.dir + +RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func.s -o arm64ec-func.obj +RUN: llvm-mc -filetype=obj -triple=aarch64-windows arm64-func.s -o arm64-func.obj +RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func-drectve.s -o arm64ec-drectve.obj +RUN: llvm-mc -filetype=obj -triple=aarch64-windows func-drectve.s -o arm64-drectve.obj +RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj +RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj + + +# A command-line export applies only to EC exports. + +RUN: lld-link -machine:arm64x -dll -out:out-cmd.dll arm64ec-func.obj arm64-func.obj \ +RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj -noentry -export:func + +RUN: llvm-objdump -d out-cmd.dll | FileCheck --check-prefix=DISASM-EC %s +DISASM-EC: Disassembly of section .text: +DISASM-EC-EMPTY: +DISASM-EC-NEXT: 0000000180001000 <.text>: +DISASM-EC-NEXT: 180001000: 52800040 mov w0, #0x2 // =2 +DISASM-EC-NEXT: 180001004: d65f03c0 ret +DISASM-EC-EMPTY: +DISASM-EC-NEXT: Disassembly of section .hexpthk: +DISASM-EC-EMPTY: +DISASM-EC-NEXT: 0000000180002000 <.hexpthk>: +DISASM-EC-NEXT: 180002000: 48 8b c4 movq %rsp, %rax +DISASM-EC-NEXT: 180002003: 48 89 58 20 movq %rbx, 0x20(%rax) +DISASM-EC-NEXT: 180002007: 55 pushq %rbp +DISASM-EC-NEXT: 180002008: 5d popq %rbp +DISASM-EC-NEXT: 180002009: e9 f2 ef ff ff jmp 0x180001000 <.text> +DISASM-EC-NEXT: 18000200e: cc int3 +DISASM-EC-NEXT: 18000200f: cc int3 + +RUN: llvm-readobj --headers --coff-exports out-cmd.dll | FileCheck --check-prefix=EXPORTS-EC %s +EXPORTS-EC: ExportTableRVA: 0x0 +EXPORTS-EC-NEXT: ExportTableSize: 0x0 +EXPORTS-EC-NOT: Name: func + +# Export using the EC .drectve section. + +RUN: lld-link -machine:arm64x -dll -out:out-drectve-ec.dll arm64ec-func.obj arm64-func.obj \ +RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64ec-drectve.obj -noentry +RUN: llvm-objdump -d out-drectve-ec.dll | FileCheck --check-prefix=DISASM-EC %s +RUN: llvm-readobj --headers --coff-exports out-drectve-ec.dll | FileCheck --check-prefix=EXPORTS-EC %s + +# Export using the native .drectve section. + +RUN: lld-link -machine:arm64x -dll -out:out-drectve-native.dll arm64ec-func.obj arm64-func.obj \ +RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64-drectve.obj -noentry + +RUN: llvm-objdump -d out-drectve-native.dll | FileCheck --check-prefix=DISASM-NATIVE %s +DISASM-NATIVE: Disassembly of section .text: +DISASM-NATIVE-EMPTY: +DISASM-NATIVE-NEXT: 0000000180001000 : +DISASM-NATIVE-NEXT: 180001000: 52800020 mov w0, #0x1 // =1 +DISASM-NATIVE-NEXT: 180001004: d65f03c0 ret + +RUN: llvm-readobj --headers --coff-exports out-drectve-native.dll | FileCheck --check-prefix=EXPORTS-NATIVE %s +EXPORTS-NATIVE: ExportTableRVA: 0x2{{.*}} +EXPORTS-NATIVE-NEXT: ExportTableSize: 0x4{{.*}} +EXPORTS-NATIVE: Export { +EXPORTS-NATIVE-NEXT: Ordinal: 1 +EXPORTS-NATIVE-NEXT: Name: func +EXPORTS-NATIVE-NEXT: RVA: 0x1000 +EXPORTS-NATIVE-NEXT: } + +# Export using both the native and EC .drectve sections. + +RUN: lld-link -machine:arm64x -dll -out:out-both.dll arm64ec-func.obj arm64-func.obj \ +RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64-drectve.obj arm64ec-drectve.obj -noentry + +RUN: llvm-objdump -d out-both.dll | FileCheck --check-prefix=DISASM-BOTH %s +DISASM-BOTH: Disassembly of section .text: +DISASM-BOTH-EMPTY: +DISASM-BOTH-NEXT: 0000000180001000 : +DISASM-BOTH-NEXT: 180001000: 52800020 mov w0, #0x1 // =1 +DISASM-BOTH-NEXT: 180001004: d65f03c0 ret +DISASM-BOTH-NEXT: ... +DISASM-BOTH-NEXT: 180002000: 52800040 mov w0, #0x2 // =2 +DISASM-BOTH-NEXT: 180002004: d65f03c0 ret +DISASM-BOTH-EMPTY: +DISASM-BOTH-NEXT: Disassembly of section .hexpthk: +DISASM-BOTH-EMPTY: +DISASM-BOTH-NEXT: 0000000180003000 <.hexpthk>: +DISASM-BOTH-NEXT: 180003000: 48 8b c4 movq %rsp, %rax +DISASM-BOTH-NEXT: 180003003: 48 89 58 20 movq %rbx, 0x20(%rax) +DISASM-BOTH-NEXT: 180003007: 55 pushq %rbp +DISASM-BOTH-NEXT: 180003008: 5d popq %rbp +DISASM-BOTH-NEXT: 180003009: e9 f2 ef ff ff jmp 0x180002000 +DISASM-BOTH-NEXT: 18000300e: cc int3 +DISASM-BOTH-NEXT: 18000300f: cc int3 + +RUN: llvm-readobj --headers --coff-exports out-both.dll | FileCheck --check-prefix=EXPORTS-BOTH %s +EXPORTS-BOTH: ExportTableRVA: 0x4{{.*}} +EXPORTS-BOTH-NEXT: ExportTableSize: 0x4{{.*}} +EXPORTS-BOTH: Export { +EXPORTS-BOTH-NEXT: Ordinal: 1 +EXPORTS-BOTH-NEXT: Name: func +EXPORTS-BOTH-NEXT: RVA: 0x1000 +EXPORTS-BOTH-NEXT: } + +#--- arm64-func.s + .section .text,"xr",discard,func + .globl func + .p2align 2 +func: + mov w0, #1 + ret + +#--- arm64ec-func.s + .section .text,"xr",discard,func + .globl func + .p2align 2 +func: + mov w0, #2 + ret + +#--- func-drectve.s +.section .drectve + .ascii "-export:func"