From c91c24e33d37e3ab9c0578ec9edaba39cd5826f7 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Fri, 13 Dec 2013 06:58:27 +0000 Subject: [PATCH] [PECOFF] Create .edata section for the DLL export table. This is the first patch to emit data for the DLL export table. The DLL export table is the data used by the Windows loader to find the address of exported function from DLL. With this patch, LLD is able to emit a valid DLL export table which the Windows loader can interpret and load. The data structure of the DLL export table is described in the Microsoft PE/COFF Specification, section 5.3. DLL support is not complete yet; the linker needs to emit an import library for a DLL, otherwise the linker cannot link against the DLL. We also do not support export-only-by-ordinal yet. llvm-svn: 197212 --- lld/lib/ReaderWriter/PECOFF/CMakeLists.txt | 1 + lld/lib/ReaderWriter/PECOFF/EdataPass.cpp | 142 ++++++++++++++++++ lld/lib/ReaderWriter/PECOFF/EdataPass.h | 81 ++++++++++ .../PECOFF/PECOFFLinkingContext.cpp | 2 + lld/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp | 3 + lld/test/pecoff/Inputs/export.obj.yaml | 31 ++++ lld/test/pecoff/export.test | 12 ++ 7 files changed, 272 insertions(+) create mode 100644 lld/lib/ReaderWriter/PECOFF/EdataPass.cpp create mode 100644 lld/lib/ReaderWriter/PECOFF/EdataPass.h create mode 100644 lld/test/pecoff/Inputs/export.obj.yaml create mode 100644 lld/test/pecoff/export.test 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