| //===- 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, const SimpleFile &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, |
| SimpleFile *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; |
| } |
| |
| std::error_code EdataPass::perform(SimpleFile &file) { |
| dedupExports(_ctx); |
| assignOrdinals(_ctx); |
| |
| std::vector<TableEntry> entries; |
| if (!getExportedAtoms(_ctx, file, entries)) |
| return std::error_code(); |
| if (entries.empty()) |
| return std::error_code(); |
| |
| 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); |
| 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)); |
| |
| return std::error_code(); |
| } |
| |
| } // namespace pecoff |
| } // namespace lld |