summaryrefslogtreecommitdiff
path: root/lib/ReaderWriter/PECOFF/EdataPass.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ReaderWriter/PECOFF/EdataPass.cpp')
-rw-r--r--lib/ReaderWriter/PECOFF/EdataPass.cpp227
1 files changed, 227 insertions, 0 deletions
diff --git a/lib/ReaderWriter/PECOFF/EdataPass.cpp b/lib/ReaderWriter/PECOFF/EdataPass.cpp
new file mode 100644
index 0000000000000..ad79f171f3c9f
--- /dev/null
+++ b/lib/ReaderWriter/PECOFF/EdataPass.cpp
@@ -0,0 +1,227 @@
+//===- lib/ReaderWriter/PECOFF/EdataPass.cpp ------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Pass.h"
+#include "EdataPass.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Pass.h"
+#include "lld/Core/Simple.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Path.h"
+#include <climits>
+#include <ctime>
+#include <utility>
+
+using lld::pecoff::edata::EdataAtom;
+using lld::pecoff::edata::TableEntry;
+using llvm::object::export_address_table_entry;
+using llvm::object::export_directory_table_entry;
+
+namespace lld {
+namespace pecoff {
+
+typedef PECOFFLinkingContext::ExportDesc ExportDesc;
+
+// dedupExports removes duplicate export entries. If two exports are
+// referring the same symbol, they are considered duplicates.
+// This could happen if the same symbol name is specified as an argument
+// to /export more than once, or an unmangled and mangled name of the
+// same symbol are given to /export. In the latter case, we choose
+// unmangled (shorter) name.
+static void dedupExports(PECOFFLinkingContext &ctx) {
+ std::vector<ExportDesc> &exports = ctx.getDllExports();
+ // Pass 1: find duplicate entries
+ std::set<const ExportDesc *> dup;
+ std::map<StringRef, ExportDesc *> map;
+ for (ExportDesc &exp : exports) {
+ if (!exp.externalName.empty())
+ continue;
+ StringRef symbol = exp.getRealName();
+ auto it = map.find(symbol);
+ if (it == map.end()) {
+ map[symbol] = &exp;
+ } else if (symbol.size() < it->second->getRealName().size()) {
+ map[symbol] = &exp;
+ dup.insert(it->second);
+ } else {
+ dup.insert(&exp);
+ }
+ }
+ // Pass 2: remove duplicate entries
+ auto pred = [&](const ExportDesc &exp) {
+ return dup.count(&exp) == 1;
+ };
+ exports.erase(std::remove_if(exports.begin(), exports.end(), pred),
+ exports.end());
+}
+
+static void assignOrdinals(PECOFFLinkingContext &ctx) {
+ std::vector<ExportDesc> &exports = ctx.getDllExports();
+ int maxOrdinal = -1;
+ for (ExportDesc &desc : exports)
+ maxOrdinal = std::max(maxOrdinal, desc.ordinal);
+
+ std::sort(exports.begin(), exports.end(),
+ [](const ExportDesc &a, const ExportDesc &b) {
+ return a.getExternalName().compare(b.getExternalName()) < 0;
+ });
+
+ int nextOrdinal = (maxOrdinal == -1) ? 1 : (maxOrdinal + 1);
+ for (ExportDesc &desc : exports)
+ if (desc.ordinal == -1)
+ desc.ordinal = nextOrdinal++;
+}
+
+static bool getExportedAtoms(PECOFFLinkingContext &ctx, MutableFile *file,
+ std::vector<TableEntry> &ret) {
+ std::map<StringRef, const DefinedAtom *> definedAtoms;
+ for (const DefinedAtom *atom : file->defined())
+ definedAtoms[atom->name()] = atom;
+
+ for (PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports()) {
+ auto it = definedAtoms.find(desc.getRealName());
+ if (it == definedAtoms.end()) {
+ llvm::errs() << "Symbol <" << desc.name
+ << "> is exported but not defined.\n";
+ return false;
+ }
+ const DefinedAtom *atom = it->second;
+
+ // One can export a symbol with a different name than the symbol
+ // name used in DLL. If such name is specified, use it in the
+ // .edata section.
+ ret.push_back(TableEntry(ctx.undecorateSymbol(desc.getExternalName()),
+ desc.ordinal, atom, desc.noname));
+ }
+ std::sort(ret.begin(), ret.end(),
+ [](const TableEntry &a, const TableEntry &b) {
+ return a.exportName.compare(b.exportName) < 0;
+ });
+
+ return true;
+}
+
+static std::pair<int, int> getOrdinalBase(std::vector<TableEntry> &entries) {
+ int ordinalBase = INT_MAX;
+ int maxOrdinal = -1;
+ for (TableEntry &e : entries) {
+ ordinalBase = std::min(ordinalBase, e.ordinal);
+ maxOrdinal = std::max(maxOrdinal, e.ordinal);
+ }
+ return std::pair<int, int>(ordinalBase, maxOrdinal);
+}
+
+edata::EdataAtom *
+EdataPass::createAddressTable(const std::vector<TableEntry> &entries,
+ int ordinalBase, int maxOrdinal) {
+ EdataAtom *addressTable =
+ new (_alloc) EdataAtom(_file, sizeof(export_address_table_entry) *
+ (maxOrdinal - ordinalBase + 1));
+
+ for (const TableEntry &e : entries) {
+ int index = e.ordinal - ordinalBase;
+ size_t offset = index * sizeof(export_address_table_entry);
+ addDir32NBReloc(addressTable, e.atom, _ctx.getMachineType(), offset);
+ }
+ return addressTable;
+}
+
+edata::EdataAtom *
+EdataPass::createNamePointerTable(const PECOFFLinkingContext &ctx,
+ const std::vector<TableEntry> &entries,
+ MutableFile *file) {
+ EdataAtom *table =
+ new (_alloc) EdataAtom(_file, sizeof(uint32_t) * entries.size());
+
+ size_t offset = 0;
+ for (const TableEntry &e : entries) {
+ auto *stringAtom = new (_alloc) COFFStringAtom(
+ _file, _stringOrdinal++, ".edata", e.exportName);
+ file->addAtom(*stringAtom);
+ addDir32NBReloc(table, stringAtom, _ctx.getMachineType(), offset);
+ offset += sizeof(uint32_t);
+ }
+ return table;
+}
+
+edata::EdataAtom *EdataPass::createExportDirectoryTable(
+ const std::vector<edata::TableEntry> &namedEntries, int ordinalBase,
+ int maxOrdinal) {
+ EdataAtom *ret =
+ new (_alloc) EdataAtom(_file, sizeof(export_directory_table_entry));
+ auto *data = ret->getContents<export_directory_table_entry>();
+ data->TimeDateStamp = time(nullptr);
+ data->OrdinalBase = ordinalBase;
+ data->AddressTableEntries = maxOrdinal - ordinalBase + 1;
+ data->NumberOfNamePointers = namedEntries.size();
+ return ret;
+}
+
+edata::EdataAtom *
+EdataPass::createOrdinalTable(const std::vector<TableEntry> &entries,
+ int ordinalBase) {
+ EdataAtom *ret =
+ new (_alloc) EdataAtom(_file, sizeof(uint16_t) * entries.size());
+ uint16_t *data = ret->getContents<uint16_t>();
+ int i = 0;
+ for (const TableEntry &e : entries)
+ data[i++] = e.ordinal - ordinalBase;
+ return ret;
+}
+
+void EdataPass::perform(std::unique_ptr<MutableFile> &file) {
+ dedupExports(_ctx);
+ assignOrdinals(_ctx);
+
+ std::vector<TableEntry> entries;
+ if (!getExportedAtoms(_ctx, file.get(), entries))
+ return;
+ if (entries.empty())
+ return;
+
+ int ordinalBase, maxOrdinal;
+ std::tie(ordinalBase, maxOrdinal) = getOrdinalBase(entries);
+
+ std::vector<TableEntry> namedEntries;
+ for (TableEntry &e : entries)
+ if (!e.noname)
+ namedEntries.push_back(e);
+
+ EdataAtom *table =
+ createExportDirectoryTable(namedEntries, ordinalBase, maxOrdinal);
+ file->addAtom(*table);
+
+ COFFStringAtom *dllName =
+ new (_alloc) COFFStringAtom(_file, _stringOrdinal++, ".edata",
+ llvm::sys::path::filename(_ctx.outputPath()));
+ file->addAtom(*dllName);
+ addDir32NBReloc(table, dllName, _ctx.getMachineType(),
+ offsetof(export_directory_table_entry, NameRVA));
+
+ EdataAtom *addressTable =
+ createAddressTable(entries, ordinalBase, maxOrdinal);
+ file->addAtom(*addressTable);
+ addDir32NBReloc(
+ table, addressTable, _ctx.getMachineType(),
+ offsetof(export_directory_table_entry, ExportAddressTableRVA));
+
+ EdataAtom *namePointerTable =
+ createNamePointerTable(_ctx, namedEntries, file.get());
+ file->addAtom(*namePointerTable);
+ addDir32NBReloc(table, namePointerTable, _ctx.getMachineType(),
+ offsetof(export_directory_table_entry, NamePointerRVA));
+
+ EdataAtom *ordinalTable = createOrdinalTable(namedEntries, ordinalBase);
+ file->addAtom(*ordinalTable);
+ addDir32NBReloc(table, ordinalTable, _ctx.getMachineType(),
+ offsetof(export_directory_table_entry, OrdinalTableRVA));
+}
+
+} // namespace pecoff
+} // namespace lld