[lld] r197212 - [PECOFF] Create .edata section for the DLL export table.

Rui Ueyama ruiu at google.com
Thu Dec 12 22:58:28 PST 2013


Author: ruiu
Date: Fri Dec 13 00:58:27 2013
New Revision: 197212

URL: http://llvm.org/viewvc/llvm-project?rev=197212&view=rev
Log:
[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.

Added:
    lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp
    lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.h
    lld/trunk/test/pecoff/Inputs/export.obj.yaml
    lld/trunk/test/pecoff/export.test
Modified:
    lld/trunk/lib/ReaderWriter/PECOFF/CMakeLists.txt
    lld/trunk/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp
    lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp

Modified: lld/trunk/lib/ReaderWriter/PECOFF/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/CMakeLists.txt?rev=197212&r1=197211&r2=197212&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/PECOFF/CMakeLists.txt (original)
+++ lld/trunk/lib/ReaderWriter/PECOFF/CMakeLists.txt Fri Dec 13 00:58:27 2013
@@ -1,4 +1,5 @@
 add_lld_library(lldPECOFF
+  EdataPass.cpp
   IdataPass.cpp
   PECOFFLinkingContext.cpp
   Pass.cpp

Added: lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp?rev=197212&view=auto
==============================================================================
--- lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp (added)
+++ lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.cpp Fri Dec 13 00:58:27 2013
@@ -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 <ctime>
+
+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<const DefinedAtom *> &ret) {
+  std::map<StringRef, const DefinedAtom *> 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<const DefinedAtom *> &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<const DefinedAtom *> &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<export_directory_table_entry>();
+  data->TimeDateStamp = time(nullptr);
+  data->OrdinalBase = 1;
+  data->AddressTableEntries = numEntries;
+  data->NumberOfNamePointers = numEntries;
+  return ret;
+}
+
+edata::EdataAtom *
+EdataPass::createOrdinalTable(const std::vector<const DefinedAtom *> &atoms,
+                              const std::vector<const DefinedAtom *> &sortedAtoms) {
+  EdataAtom *ret = new (_alloc) EdataAtom(_file, sizeof(uint16_t) * atoms.size());
+  uint16_t *data = ret->getContents<uint16_t>();
+
+  std::map<const DefinedAtom *, size_t> 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<MutableFile> &file) {
+  std::vector<const DefinedAtom *> 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<const DefinedAtom *> 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

Added: lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.h?rev=197212&view=auto
==============================================================================
--- lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.h (added)
+++ lld/trunk/lib/ReaderWriter/PECOFF/EdataPass.h Fri Dec 13 00:58:27 2013
@@ -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 <map>
+
+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<uint8_t>(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<typename T> 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<MutableFile> &file);
+
+private:
+  edata::EdataAtom *createExportDirectoryTable(size_t numEntries);
+  edata::EdataAtom *createAddressTable(
+    const std::vector<const DefinedAtom *> &atoms);
+  edata::EdataAtom *createNamePointerTable(
+    const std::vector<const DefinedAtom *> &atoms, MutableFile *file);
+  edata::EdataAtom *createOrdinalTable(
+    const std::vector<const DefinedAtom *> &atoms,
+    const std::vector<const DefinedAtom *> &sortedAtoms);
+
+  const PECOFFLinkingContext &_ctx;
+  VirtualFile _file;
+  mutable llvm::BumpPtrAllocator _alloc;
+};
+
+} // namespace pecoff
+} // namespace lld
+
+#endif

Modified: lld/trunk/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp?rev=197212&r1=197211&r2=197212&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp (original)
+++ lld/trunk/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp Fri Dec 13 00:58:27 2013
@@ -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::getSectio
 
 void PECOFFLinkingContext::addPasses(PassManager &pm) {
   pm.add(std::unique_ptr<Pass>(new pecoff::SetSubsystemPass(*this)));
+  pm.add(std::unique_ptr<Pass>(new pecoff::EdataPass(*this)));
   pm.add(std::unique_ptr<Pass>(new pecoff::IdataPass(*this)));
   pm.add(std::unique_ptr<Pass>(new LayoutPass()));
   pm.add(std::unique_ptr<Pass>(new pecoff::GroupedSectionsPass()));

Modified: lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp?rev=197212&r1=197211&r2=197212&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp (original)
+++ lld/trunk/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp Fri Dec 13 00:58:27 2013
@@ -885,6 +885,9 @@ void ExecutableWriter::build(const File
     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

Added: lld/trunk/test/pecoff/Inputs/export.obj.yaml
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/pecoff/Inputs/export.obj.yaml?rev=197212&view=auto
==============================================================================
--- lld/trunk/test/pecoff/Inputs/export.obj.yaml (added)
+++ lld/trunk/test/pecoff/Inputs/export.obj.yaml Fri Dec 13 00:58:27 2013
@@ -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
+...

Added: lld/trunk/test/pecoff/export.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/pecoff/export.test?rev=197212&view=auto
==============================================================================
--- lld/trunk/test/pecoff/export.test (added)
+++ lld/trunk/test/pecoff/export.test Fri Dec 13 00:58:27 2013
@@ -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





More information about the llvm-commits mailing list