//===- 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 #include #include 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 &exports = ctx.getDllExports(); // Pass 1: find duplicate entries std::set dup; std::map 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 &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 &ret) { std::map 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 getOrdinalBase(std::vector &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(ordinalBase, maxOrdinal); } edata::EdataAtom * EdataPass::createAddressTable(const std::vector &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 &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 &namedEntries, int ordinalBase, int maxOrdinal) { EdataAtom *ret = new (_alloc) EdataAtom(_file, sizeof(export_directory_table_entry)); auto *data = ret->getContents(); 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 &entries, int ordinalBase) { EdataAtom *ret = new (_alloc) EdataAtom(_file, sizeof(uint16_t) * entries.size()); uint16_t *data = ret->getContents(); 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 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 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