[LLD][COFF] Separate EC and native exports for ARM64X (#123652)
Store exports in SymbolTable instead of Configuration.
This commit is contained in:
@@ -162,8 +162,6 @@ struct Configuration {
|
||||
bool dll = false;
|
||||
StringRef implib;
|
||||
bool noimplib = false;
|
||||
std::vector<Export> exports;
|
||||
bool hadExplicitExports;
|
||||
std::set<std::string> delayLoads;
|
||||
std::map<std::string, int> dllOrder;
|
||||
Symbol *delayLoadHelper = nullptr;
|
||||
|
||||
@@ -639,22 +639,22 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
private:
|
||||
size_t baseOrdinal;
|
||||
size_t size;
|
||||
const COFFLinkerContext &ctx;
|
||||
const SymbolTable &symtab;
|
||||
};
|
||||
|
||||
class NamePointersChunk : public NonSectionChunk {
|
||||
@@ -690,13 +690,13 @@ private:
|
||||
|
||||
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 @@ public:
|
||||
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<Chunk *> &chunks) {
|
||||
void createEdataChunks(SymbolTable &symtab, std::vector<Chunk *> &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<Chunk *> &chunks) {
|
||||
// https://learn.microsoft.com/en-us/cpp/build/reference/export-exports-a-function?view=msvc-170
|
||||
assert(baseOrdinal >= 1);
|
||||
|
||||
auto *dllName = make<StringChunk>(sys::path::filename(ctx.config.outputFile));
|
||||
auto *addressTab = make<AddressTableChunk>(ctx, baseOrdinal, maxOrdinal);
|
||||
auto *dllName =
|
||||
make<StringChunk>(sys::path::filename(symtab.ctx.config.outputFile));
|
||||
auto *addressTab = make<AddressTableChunk>(symtab, baseOrdinal, maxOrdinal);
|
||||
std::vector<Chunk *> names;
|
||||
for (Export &e : ctx.config.exports)
|
||||
for (Export &e : symtab.exports)
|
||||
if (!e.noname)
|
||||
names.push_back(make<StringChunk>(e.exportName));
|
||||
|
||||
std::vector<Chunk *> forwards;
|
||||
for (Export &e : ctx.config.exports) {
|
||||
for (Export &e : symtab.exports) {
|
||||
if (e.forwardTo.empty())
|
||||
continue;
|
||||
e.forwardChunk = make<StringChunk>(e.forwardTo);
|
||||
@@ -946,7 +947,8 @@ void createEdataChunks(COFFLinkerContext &ctx, std::vector<Chunk *> &chunks) {
|
||||
}
|
||||
|
||||
auto *nameTab = make<NamePointersChunk>(names);
|
||||
auto *ordinalTab = make<ExportOrdinalChunk>(ctx, baseOrdinal, names.size());
|
||||
auto *ordinalTab =
|
||||
make<ExportOrdinalChunk>(symtab, baseOrdinal, names.size());
|
||||
auto *dir =
|
||||
make<ExportDirectoryChunk>(baseOrdinal, maxOrdinal, names.size(), dllName,
|
||||
addressTab, nameTab, ordinalTab);
|
||||
|
||||
@@ -78,7 +78,7 @@ private:
|
||||
};
|
||||
|
||||
// Create all chunks for the DLL export table.
|
||||
void createEdataChunks(COFFLinkerContext &ctx, std::vector<Chunk *> &chunks);
|
||||
void createEdataChunks(SymbolTable &symtab, std::vector<Chunk *> &chunks);
|
||||
|
||||
} // namespace lld::coff
|
||||
|
||||
|
||||
@@ -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<COFFShortExport> 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<const char *> 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<const char *> 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<const char *> 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<const char *> 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<const char *> 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) {
|
||||
|
||||
@@ -182,7 +182,6 @@ private:
|
||||
std::list<std::function<void()>> taskQueue;
|
||||
std::vector<MemoryBufferRef> resources;
|
||||
|
||||
llvm::DenseSet<StringRef> directivesExports;
|
||||
llvm::DenseSet<StringRef> excludedSymbols;
|
||||
|
||||
COFFLinkerContext &ctx;
|
||||
@@ -249,8 +248,6 @@ private:
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -656,142 +656,6 @@ err:
|
||||
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<uint16_t> 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<std::string> 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<StringRef, std::pair<Export *, unsigned>> map(
|
||||
ctx.config.exports.size());
|
||||
std::vector<Export> 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<uint16_t>::max())
|
||||
Fatal(ctx) << "too many exported symbols (got " << max << ", max "
|
||||
<< Twine(std::numeric_limits<uint16_t>::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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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<uint16_t> 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<std::string> 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<StringRef, std::pair<Export *, unsigned>> map(exports.size());
|
||||
std::vector<Export> 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<uint16_t>::max())
|
||||
Fatal(ctx) << "too many exported symbols (got " << max << ", max "
|
||||
<< Twine(std::numeric_limits<uint16_t>::max()) << ")";
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addUndefined(StringRef name) {
|
||||
return addUndefined(name, nullptr, false);
|
||||
}
|
||||
|
||||
@@ -150,6 +150,14 @@ public:
|
||||
// A list of EC EXP+ symbols.
|
||||
std::vector<Symbol *> expSymbols;
|
||||
|
||||
// A list of DLL exports.
|
||||
std::vector<Export> exports;
|
||||
llvm::DenseSet<StringRef> directivesExports;
|
||||
bool hadExplicitExports;
|
||||
|
||||
void fixupExports();
|
||||
void assignExportOrdinals();
|
||||
|
||||
// Iterates symbols in non-determinstic hash table order.
|
||||
template <typename T> void forEachSymbol(T callback) {
|
||||
for (auto &pair : symMap)
|
||||
|
||||
@@ -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<Chunk *> 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
|
||||
|
||||
121
lld/test/COFF/arm64x-export.test
Normal file
121
lld/test/COFF/arm64x-export.test
Normal file
@@ -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 <func>:
|
||||
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 <func>:
|
||||
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 <func+0x1000>
|
||||
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"
|
||||
Reference in New Issue
Block a user