//===- lib/ReaderWriter/PECOFF/IdataPass.cpp ------------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "IdataPass.h" #include "Pass.h" #include "lld/Core/File.h" #include "lld/Core/Pass.h" #include "lld/Core/Simple.h" #include "llvm/Support/COFF.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include #include #include #include namespace lld { namespace pecoff { namespace idata { IdataAtom::IdataAtom(Context &context, std::vector data) : COFFLinkerInternalAtom(context.dummyFile, context.dummyFile.getNextOrdinal(), data) { context.file.addAtom(*this); } HintNameAtom::HintNameAtom(Context &context, uint16_t hint, StringRef importName) : IdataAtom(context, assembleRawContent(hint, importName)), _importName(importName) {} std::vector HintNameAtom::assembleRawContent(uint16_t hint, StringRef importName) { size_t size = llvm::RoundUpToAlignment(sizeof(hint) + importName.size() + 1, 2); std::vector ret(size); ret[importName.size()] = 0; ret[importName.size() - 1] = 0; *reinterpret_cast(&ret[0]) = hint; std::memcpy(&ret[2], importName.data(), importName.size()); return ret; } std::vector ImportTableEntryAtom::assembleRawContent(uint32_t contents) { std::vector ret(4); *reinterpret_cast(&ret[0]) = contents; return ret; } // Creates atoms for an import lookup table. The import lookup table is an // array of pointers to hint/name atoms. The array needs to be terminated with // the NULL entry. void ImportDirectoryAtom::addRelocations( Context &context, StringRef loadName, const std::vector &sharedAtoms) { // Create parallel arrays. The contents of the two are initially the // same. The PE/COFF loader overwrites the import address tables with the // pointers to the referenced items after loading the executable into // memory. std::vector importLookupTables = createImportTableAtoms(context, sharedAtoms, false, ".idata.t"); std::vector importAddressTables = createImportTableAtoms(context, sharedAtoms, true, ".idata.a"); addDir32NBReloc(this, importLookupTables[0], offsetof(ImportDirectoryTableEntry, ImportLookupTableRVA)); addDir32NBReloc(this, importAddressTables[0], offsetof(ImportDirectoryTableEntry, ImportAddressTableRVA)); auto *atom = new (_alloc) COFFStringAtom(context.dummyFile, context.dummyFile.getNextOrdinal(), ".idata", loadName); context.file.addAtom(*atom); addDir32NBReloc(this, atom, offsetof(ImportDirectoryTableEntry, NameRVA)); } std::vector ImportDirectoryAtom::createImportTableAtoms( Context &context, const std::vector &sharedAtoms, bool shouldAddReference, StringRef sectionName) const { std::vector ret; for (COFFSharedLibraryAtom *atom : sharedAtoms) { ImportTableEntryAtom *entry = nullptr; if (atom->importName().empty()) { // Import by ordinal uint32_t hint = (1U << 31) | atom->hint(); entry = new (_alloc) ImportTableEntryAtom(context, hint, sectionName); } else { // Import by name entry = new (_alloc) ImportTableEntryAtom(context, 0, sectionName); HintNameAtom *hintName = new (_alloc) HintNameAtom(context, atom->hint(), atom->importName()); addDir32NBReloc(entry, hintName); } ret.push_back(entry); if (shouldAddReference) atom->setImportTableEntry(entry); } // Add the NULL entry. ret.push_back(new (_alloc) ImportTableEntryAtom(context, 0, sectionName)); return ret; } } // namespace idata void IdataPass::perform(std::unique_ptr &file) { if (file->sharedLibrary().empty()) return; idata::Context context(*file, _dummyFile); std::map > sharedAtoms = groupByLoadName(*file); for (auto i : sharedAtoms) { StringRef loadName = i.first; std::vector &atoms = i.second; new (_alloc) idata::ImportDirectoryAtom(context, loadName, atoms); } // All atoms, including those of tyep NullImportDirectoryAtom, are added to // context.file in the IdataAtom's constructor. new (_alloc) idata::NullImportDirectoryAtom(context); replaceSharedLibraryAtoms(context); } std::map > IdataPass::groupByLoadName(MutableFile &file) { std::map uniqueAtoms; for (const SharedLibraryAtom *atom : file.sharedLibrary()) uniqueAtoms[atom->name()] = (COFFSharedLibraryAtom *)atom; std::map > ret; for (auto i : uniqueAtoms) { COFFSharedLibraryAtom *atom = i.second; ret[atom->loadName()].push_back(atom); } return ret; } /// Transforms a reference to a COFFSharedLibraryAtom to a real reference. void IdataPass::replaceSharedLibraryAtoms(idata::Context &context) { for (const DefinedAtom *atom : context.file.defined()) { for (const Reference *ref : *atom) { const Atom *target = ref->target(); auto *sharedAtom = dyn_cast(target); if (!sharedAtom) continue; auto *coffSharedAtom = (COFFSharedLibraryAtom *)sharedAtom; const DefinedAtom *entry = coffSharedAtom->getImportTableEntry(); const_cast(ref)->setTarget(entry); } } } } // namespace pecoff } // namespace lld