diff --git a/lld/lib/ReaderWriter/PECOFF/CMakeLists.txt b/lld/lib/ReaderWriter/PECOFF/CMakeLists.txt index 6cb4d1af058f..1950598885c5 100644 --- a/lld/lib/ReaderWriter/PECOFF/CMakeLists.txt +++ b/lld/lib/ReaderWriter/PECOFF/CMakeLists.txt @@ -1,4 +1,5 @@ add_lld_library(lldPECOFF + EdataPass.cpp IdataPass.cpp PECOFFLinkingContext.cpp Pass.cpp diff --git a/lld/lib/ReaderWriter/PECOFF/EdataPass.cpp b/lld/lib/ReaderWriter/PECOFF/EdataPass.cpp new file mode 100644 index 000000000000..f8a95a20de2e --- /dev/null +++ b/lld/lib/ReaderWriter/PECOFF/EdataPass.cpp @@ -0,0 +1,142 @@ +//===- 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/ReaderWriter/Simple.h" +#include "llvm/Support/Path.h" + +#include + +using lld::pecoff::edata::EdataAtom; +using llvm::object::export_address_table_entry; +using llvm::object::export_directory_table_entry; + +namespace lld { +namespace pecoff { + +static bool +getExportedAtoms(const PECOFFLinkingContext &ctx, MutableFile *file, + std::vector &ret) { + std::map definedAtoms; + for (const DefinedAtom *atom : file->defined()) + definedAtoms[atom->name()] = atom; + + for (StringRef dllExport : ctx.getDllExports()) { + auto it = definedAtoms.find(ctx.decorateSymbol(dllExport)); + if (it == definedAtoms.end()) { + llvm::errs() << "Symbol <" << dllExport + << "> is exported but not defined.\n"; + return false; + } + const DefinedAtom *atom = it->second; + ret.push_back(atom); + } + return true; +} + +static bool compare(const DefinedAtom *a, const DefinedAtom *b) { + return a->name().compare(b->name()) < 0; +} + +edata::EdataAtom * +EdataPass::createAddressTable(const std::vector &atoms) { + EdataAtom *addressTable = new (_alloc) EdataAtom( + _file, sizeof(export_address_table_entry) * atoms.size()); + + size_t offset = 0; + for (const DefinedAtom *atom : atoms) { + addDir32NBReloc(addressTable, atom, offset); + offset += sizeof(export_address_table_entry); + } + return addressTable; +} + +edata::EdataAtom * +EdataPass::createNamePointerTable(const std::vector &atoms, + MutableFile *file) { + EdataAtom *table = new (_alloc) EdataAtom(_file, sizeof(uint32_t) * atoms.size()); + + size_t offset = 0; + for (const DefinedAtom *atom : atoms) { + COFFStringAtom *stringAtom = new (_alloc) COFFStringAtom( + _file, _file.getNextOrdinal(), ".edata", atom->name()); + file->addAtom(*stringAtom); + addDir32NBReloc(table, stringAtom, offset); + offset += sizeof(uint32_t); + } + return table; +} + +edata::EdataAtom *EdataPass::createExportDirectoryTable(size_t numEntries) { + EdataAtom *ret = new (_alloc) EdataAtom(_file, sizeof(export_directory_table_entry)); + auto *data = ret->getContents(); + data->TimeDateStamp = time(nullptr); + data->OrdinalBase = 1; + data->AddressTableEntries = numEntries; + data->NumberOfNamePointers = numEntries; + return ret; +} + +edata::EdataAtom * +EdataPass::createOrdinalTable(const std::vector &atoms, + const std::vector &sortedAtoms) { + EdataAtom *ret = new (_alloc) EdataAtom(_file, sizeof(uint16_t) * atoms.size()); + uint16_t *data = ret->getContents(); + + std::map ordinals; + size_t ordinal = 0; + for (const DefinedAtom *atom : atoms) + ordinals[atom] = ordinal++; + + size_t index = 0; + for (const DefinedAtom *atom : sortedAtoms) + data[index++] = ordinals[atom]; + return ret; +} + +void EdataPass::perform(std::unique_ptr &file) { + std::vector atoms; + if (!getExportedAtoms(_ctx, file.get(), atoms)) + return; + if (atoms.empty()) + return; + + EdataAtom *table = createExportDirectoryTable(atoms.size()); + file->addAtom(*table); + + COFFStringAtom *dllName = new (_alloc) COFFStringAtom( + _file, _file.getNextOrdinal(), + ".edata", llvm::sys::path::filename(_ctx.outputPath())); + file->addAtom(*dllName); + addDir32NBReloc(table, dllName, offsetof(export_directory_table_entry, NameRVA)); + + EdataAtom *addressTable = createAddressTable(atoms); + file->addAtom(*addressTable); + addDir32NBReloc(table, addressTable, + offsetof(export_directory_table_entry, ExportAddressTableRVA)); + + std::vector sortedAtoms(atoms); + std::sort(sortedAtoms.begin(), sortedAtoms.end(), compare); + EdataAtom *namePointerTable = createNamePointerTable(sortedAtoms, file.get()); + file->addAtom(*namePointerTable); + addDir32NBReloc(table, namePointerTable, + offsetof(export_directory_table_entry, NamePointerRVA)); + + EdataAtom *ordinalTable = createOrdinalTable(atoms, sortedAtoms); + file->addAtom(*ordinalTable); + addDir32NBReloc(table, ordinalTable, + offsetof(export_directory_table_entry, OrdinalTableRVA)); +} + +} // namespace pecoff +} // namespace lld diff --git a/lld/lib/ReaderWriter/PECOFF/EdataPass.h b/lld/lib/ReaderWriter/PECOFF/EdataPass.h new file mode 100644 index 000000000000..42714e803151 --- /dev/null +++ b/lld/lib/ReaderWriter/PECOFF/EdataPass.h @@ -0,0 +1,81 @@ +//===- lib/ReaderWriter/PECOFF/EdataPass.h --------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file \brief This linker pass creates atoms for the DLL export +/// information. The defined atoms constructed in this pass will go into .edata +/// section. +/// +/// For the details of the .edata section format, see Microsoft PE/COFF +/// Specification section 5.3, The .edata Section. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_PE_COFF_EDATA_PASS_H +#define LLD_READER_WRITER_PE_COFF_EDATA_PASS_H + +#include "Atoms.h" + +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "lld/ReaderWriter/Simple.h" +#include "llvm/Support/COFF.h" + +#include + +using llvm::COFF::ImportDirectoryTableEntry; + +namespace lld { +namespace pecoff { +namespace edata { + +/// The root class of all edata atoms. +class EdataAtom : public COFFLinkerInternalAtom { +public: + EdataAtom(VirtualFile &file, size_t size) + : COFFLinkerInternalAtom(file, file.getNextOrdinal(), + std::vector(size)) {} + + virtual SectionChoice sectionChoice() const { return sectionCustomRequired; } + virtual StringRef customSectionName() const { return ".edata"; } + virtual ContentType contentType() const { return typeData; } + virtual ContentPermissions permissions() const { return permR__; } + + template T *getContents() const { + return (T *)rawContent().data(); + } +}; + +} // namespace edata + +class EdataPass : public lld::Pass { +public: + EdataPass(const PECOFFLinkingContext &ctx) : _ctx(ctx), _file(ctx) {} + + virtual void perform(std::unique_ptr &file); + +private: + edata::EdataAtom *createExportDirectoryTable(size_t numEntries); + edata::EdataAtom *createAddressTable( + const std::vector &atoms); + edata::EdataAtom *createNamePointerTable( + const std::vector &atoms, MutableFile *file); + edata::EdataAtom *createOrdinalTable( + const std::vector &atoms, + const std::vector &sortedAtoms); + + const PECOFFLinkingContext &_ctx; + VirtualFile _file; + mutable llvm::BumpPtrAllocator _alloc; +}; + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lld/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp b/lld/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp index 1195f19483f6..ceeda806268f 100644 --- a/lld/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp +++ b/lld/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "Atoms.h" +#include "EdataPass.h" #include "GroupedSectionsPass.h" #include "IdataPass.h" #include "LinkerGeneratedSymbolFile.h" @@ -249,6 +250,7 @@ uint32_t PECOFFLinkingContext::getSectionAttributes(StringRef sectionName, void PECOFFLinkingContext::addPasses(PassManager &pm) { pm.add(std::unique_ptr(new pecoff::SetSubsystemPass(*this))); + pm.add(std::unique_ptr(new pecoff::EdataPass(*this))); pm.add(std::unique_ptr(new pecoff::IdataPass(*this))); pm.add(std::unique_ptr(new LayoutPass())); pm.add(std::unique_ptr(new pecoff::GroupedSectionsPass())); diff --git a/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp b/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp index 02bdbffa82f2..c220e271a53a 100644 --- a/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp +++ b/lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp @@ -885,6 +885,9 @@ void ExecutableWriter::build(const File &linkedFile) { if (section->getSectionName() == ".idata.d") dataDirectory->setField(DataDirectoryIndex::IMPORT_TABLE, section->getVirtualAddress(), section->size()); + if (section->getSectionName() == ".edata") + dataDirectory->setField(DataDirectoryIndex::EXPORT_TABLE, + section->getVirtualAddress(), section->size()); } // Now that we know the size and file offset of sections. Set the file diff --git a/lld/test/pecoff/Inputs/export.obj.yaml b/lld/test/pecoff/Inputs/export.obj.yaml new file mode 100644 index 000000000000..3a0a233ef05d --- /dev/null +++ b/lld/test/pecoff/Inputs/export.obj.yaml @@ -0,0 +1,31 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: B800000000506800000000680000000050E80000000050E800000000 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + NumberOfAuxSymbols: 1 + AuxiliaryData: 1C0000000400000000000000000000000000 + - Name: _init + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _exportfn + Value: 8 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/lld/test/pecoff/export.test b/lld/test/pecoff/export.test new file mode 100644 index 000000000000..beb4ae84246f --- /dev/null +++ b/lld/test/pecoff/export.test @@ -0,0 +1,12 @@ +# RUN: yaml2obj %p/Inputs/export.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.dll /dll /subsystem:console /entry:_init \ +# RUN: /export:exportfn -- %t.obj +# RUN: llvm-objdump -s %t.dll | FileCheck %s + +CHECK: Contents of section .edata: +CHECK-NEXT: 1000 00000000 {{........}} 00000000 28100000 +CHECK-NEXT: 1010 01000000 01000000 01000000 3c100000 +CHECK-NEXT: 1020 40100000 4e100000 6578706f 72742e74 +CHECK-NEXT: 1030 6573742e 746d702e 646c6c00 08200000 +CHECK-NEXT: 1040 44100000 5f657870 6f727466 6e000000