[lld] r239869 - COFF: Support creating DLLs.

Rui Ueyama ruiu at google.com
Tue Jun 16 17:16:33 PDT 2015


Author: ruiu
Date: Tue Jun 16 19:16:33 2015
New Revision: 239869

URL: http://llvm.org/viewvc/llvm-project?rev=239869&view=rev
Log:
COFF: Support creating DLLs.

DLL files are in the same format as executables but they have export tables.
The format of the export table is described in PE/COFF spec section 5.3.

A new class, EdataContents, takes care of creating chunks for export tables.
What we need to do is to parse command line flags for dllexports, and then
instantiate the class to create chunks. For the writer, export table chunks
are opaque data -- it just add chunks to .edata section.

Added:
    lld/trunk/test/COFF/Inputs/export.yaml
    lld/trunk/test/COFF/export.test
Modified:
    lld/trunk/COFF/Config.h
    lld/trunk/COFF/DLL.cpp
    lld/trunk/COFF/DLL.h
    lld/trunk/COFF/Driver.cpp
    lld/trunk/COFF/Driver.h
    lld/trunk/COFF/DriverUtils.cpp
    lld/trunk/COFF/SymbolTable.cpp
    lld/trunk/COFF/Writer.cpp
    lld/trunk/COFF/Writer.h

Modified: lld/trunk/COFF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Config.h?rev=239869&r1=239868&r2=239869&view=diff
==============================================================================
--- lld/trunk/COFF/Config.h (original)
+++ lld/trunk/COFF/Config.h Tue Jun 16 19:16:33 2015
@@ -22,9 +22,21 @@ namespace coff {
 
 using llvm::COFF::WindowsSubsystem;
 using llvm::StringRef;
+class Defined;
 
-class Configuration {
-public:
+// Represents an /export option.
+struct Export {
+  StringRef Name;
+  StringRef ExtName;
+  Defined *Sym = nullptr;
+  uint16_t Ordinal = 0;
+  bool Noname = false;
+  bool Data = false;
+  bool Private = false;
+};
+
+// Global configuration.
+struct Configuration {
   llvm::COFF::MachineTypes MachineType = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
   bool Verbose = false;
   WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
@@ -39,6 +51,10 @@ public:
   std::set<StringRef> NoDefaultLibs;
   bool NoDefaultLibAll = false;
 
+  // True if we are creating a DLL.
+  bool DLL = false;
+  std::vector<Export> Exports;
+
   // Used by /failifmismatch option.
   std::map<StringRef, StringRef> MustMatch;
 

Modified: lld/trunk/COFF/DLL.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DLL.cpp?rev=239869&r1=239868&r2=239869&view=diff
==============================================================================
--- lld/trunk/COFF/DLL.cpp (original)
+++ lld/trunk/COFF/DLL.cpp Tue Jun 16 19:16:33 2015
@@ -23,6 +23,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Object/COFF.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/Path.h"
 
 using namespace llvm;
 using namespace llvm::object;
@@ -31,11 +32,12 @@ using namespace llvm::COFF;
 using llvm::RoundUpToAlignment;
 
 static const size_t LookupChunkSize = sizeof(uint64_t);
-static const size_t DirectoryChunkSize = sizeof(ImportDirectoryTableEntry);
 
 namespace lld {
 namespace coff {
 
+// Import table
+
 // A chunk for the import descriptor table.
 class HintNameChunk : public Chunk {
 public:
@@ -88,10 +90,10 @@ public:
 };
 
 // A chunk for the import descriptor table.
-class DirectoryChunk : public Chunk {
+class ImportDirectoryChunk : public Chunk {
 public:
-  explicit DirectoryChunk(Chunk *N) : DLLName(N) {}
-  size_t getSize() const override { return DirectoryChunkSize; }
+  explicit ImportDirectoryChunk(Chunk *N) : DLLName(N) {}
+  size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
 
   void writeTo(uint8_t *Buf) override {
     auto *E = (coff_import_directory_table_entry *)(Buf + FileOff);
@@ -118,7 +120,7 @@ private:
 };
 
 uint64_t IdataContents::getDirSize() {
-  return Dirs.size() * DirectoryChunkSize;
+  return Dirs.size() * sizeof(ImportDirectoryTableEntry);
 }
 
 uint64_t IdataContents::getIATSize() {
@@ -192,13 +194,120 @@ void IdataContents::create() {
     // Create the import table header.
     if (!DLLNames.count(Name))
       DLLNames[Name] = make_unique<StringChunk>(Name);
-    auto Dir = make_unique<DirectoryChunk>(DLLNames[Name].get());
+    auto Dir = make_unique<ImportDirectoryChunk>(DLLNames[Name].get());
     Dir->LookupTab = Lookups[Base].get();
     Dir->AddressTab = Addresses[Base].get();
     Dirs.push_back(std::move(Dir));
   }
   // Add null terminator.
-  Dirs.push_back(make_unique<NullChunk>(DirectoryChunkSize));
+  Dirs.push_back(make_unique<NullChunk>(sizeof(ImportDirectoryTableEntry)));
+}
+
+// Export table
+// Read Microsoft PE/COFF spec 5.3 for details.
+
+// A chunk for the export descriptor table.
+class ExportDirectoryChunk : public Chunk {
+public:
+  ExportDirectoryChunk(int I, int J, Chunk *D, Chunk *A, Chunk *N, Chunk *O)
+      : MaxOrdinal(I), NameTabSize(J), DLLName(D), AddressTab(A), NameTab(N),
+        OrdinalTab(O) {}
+
+  size_t getSize() const override {
+    return sizeof(export_directory_table_entry);
+  }
+
+  void writeTo(uint8_t *Buf) override {
+    auto *E = (export_directory_table_entry *)(Buf + FileOff);
+    E->NameRVA = DLLName->getRVA();
+    E->OrdinalBase = 0;
+    E->AddressTableEntries = MaxOrdinal + 1;
+    E->NumberOfNamePointers = NameTabSize;
+    E->ExportAddressTableRVA = AddressTab->getRVA();
+    E->NamePointerRVA = NameTab->getRVA();
+    E->OrdinalTableRVA = OrdinalTab->getRVA();
+  }
+
+  uint16_t MaxOrdinal;
+  uint16_t NameTabSize;
+  Chunk *DLLName;
+  Chunk *AddressTab;
+  Chunk *NameTab;
+  Chunk *OrdinalTab;
+};
+
+class AddressTableChunk : public Chunk {
+public:
+  explicit AddressTableChunk(size_t MaxOrdinal) : Size(MaxOrdinal + 1) {}
+  size_t getSize() const override { return Size * 4; }
+
+  void writeTo(uint8_t *Buf) override {
+    for (Export &E : Config->Exports)
+      write32le(Buf + FileOff + E.Ordinal * 4, E.Sym->getRVA());
+  }
+
+private:
+  size_t Size;
+};
+
+class NamePointersChunk : public Chunk {
+public:
+  explicit NamePointersChunk(std::vector<Chunk *> &V) : Chunks(V) {}
+  size_t getSize() const override { return Chunks.size() * 4; }
+
+  void writeTo(uint8_t *Buf) override {
+    uint8_t *P = Buf + FileOff;
+    for (Chunk *C : Chunks) {
+      write32le(P, C->getRVA());
+      P += 4;
+    }
+  }
+
+private:
+  std::vector<Chunk *> Chunks;
+};
+
+class ExportOrdinalChunk : public Chunk {
+public:
+  explicit ExportOrdinalChunk(size_t I) : Size(I) {}
+  size_t getSize() const override { return Size * 2; }
+
+  void writeTo(uint8_t *Buf) override {
+    uint8_t *P = Buf + FileOff;
+    for (Export &E : Config->Exports) {
+      if (E.Noname)
+        continue;
+      write16le(P, E.Ordinal);
+      P += 2;
+    }
+  }
+
+private:
+  size_t Size;
+};
+
+EdataContents::EdataContents() {
+  uint16_t MaxOrdinal = 0;
+  for (Export &E : Config->Exports)
+    MaxOrdinal = std::max(MaxOrdinal, E.Ordinal);
+
+  auto *DLLName = new StringChunk(sys::path::filename(Config->OutputFile));
+  auto *AddressTab = new AddressTableChunk(MaxOrdinal);
+  std::vector<Chunk *> Names;
+  for (Export &E : Config->Exports)
+    if (!E.Noname)
+      Names.push_back(new StringChunk(E.ExtName));
+  auto *NameTab = new NamePointersChunk(Names);
+  auto *OrdinalTab = new ExportOrdinalChunk(Names.size());
+  auto *Dir = new ExportDirectoryChunk(MaxOrdinal, Names.size(), DLLName,
+                                       AddressTab, NameTab, OrdinalTab);
+  Chunks.push_back(std::unique_ptr<Chunk>(Dir));
+  Chunks.push_back(std::unique_ptr<Chunk>(DLLName));
+  Chunks.push_back(std::unique_ptr<Chunk>(AddressTab));
+  Chunks.push_back(std::unique_ptr<Chunk>(NameTab));
+  Chunks.push_back(std::unique_ptr<Chunk>(OrdinalTab));
+  for (Chunk *C : Names)
+    Chunks.push_back(std::unique_ptr<Chunk>(C));
 }
 
 } // namespace coff

Modified: lld/trunk/COFF/DLL.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DLL.h?rev=239869&r1=239868&r2=239869&view=diff
==============================================================================
--- lld/trunk/COFF/DLL.h (original)
+++ lld/trunk/COFF/DLL.h Tue Jun 16 19:16:33 2015
@@ -17,7 +17,7 @@ namespace lld {
 namespace coff {
 
 // Windows-specific.
-// IdataContents creates all chunks for the .idata section.
+// IdataContents creates all chunks for the DLL import table.
 // You are supposed to call add() to add symbols and then
 // call getChunks() to get a list of chunks.
 class IdataContents {
@@ -41,6 +41,14 @@ private:
   std::map<StringRef, std::unique_ptr<Chunk>> DLLNames;
 };
 
+// Windows-specific.
+// EdataContents creates all chunks for the DLL export table.
+class EdataContents {
+public:
+  EdataContents();
+  std::vector<std::unique_ptr<Chunk>> Chunks;
+};
+
 } // namespace coff
 } // namespace lld
 

Modified: lld/trunk/COFF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.cpp?rev=239869&r1=239868&r2=239869&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.cpp (original)
+++ lld/trunk/COFF/Driver.cpp Tue Jun 16 19:16:33 2015
@@ -91,6 +91,14 @@ LinkerDriver::parseDirectives(StringRef
     return EC;
   std::unique_ptr<llvm::opt::InputArgList> Args = std::move(ArgsOrErr.get());
 
+  // Handle /export
+  for (auto *Arg : Args->filtered(OPT_export)) {
+    ErrorOr<Export> E = parseExport(Arg->getValue());
+    if (auto EC = E.getError())
+      return EC;
+    Config->Exports.push_back(E.get());
+  }
+
   // Handle /failifmismatch
   if (auto EC = checkFailIfMismatch(Args.get()))
     return EC;
@@ -180,6 +188,17 @@ std::vector<StringRef> LinkerDriver::get
   return Ret;
 }
 
+static WindowsSubsystem inferSubsystem() {
+  if (Config->DLL)
+    return IMAGE_SUBSYSTEM_WINDOWS_GUI;
+  return StringSwitch<WindowsSubsystem>(Config->EntryName)
+      .Case("mainCRTStartup", IMAGE_SUBSYSTEM_WINDOWS_CUI)
+      .Case("wmainCRTStartup", IMAGE_SUBSYSTEM_WINDOWS_CUI)
+      .Case("WinMainCRTStartup", IMAGE_SUBSYSTEM_WINDOWS_GUI)
+      .Case("wWinMainCRTStartup", IMAGE_SUBSYSTEM_WINDOWS_GUI)
+      .Default(IMAGE_SUBSYSTEM_UNKNOWN);
+}
+
 bool LinkerDriver::link(int Argc, const char *Argv[]) {
   // Needed for LTO.
   llvm::InitializeAllTargetInfos();
@@ -221,6 +240,10 @@ bool LinkerDriver::link(int Argc, const
   if (Args->hasArg(OPT_verbose))
     Config->Verbose = true;
 
+  // Handle /dll
+  if (Args->hasArg(OPT_dll))
+    Config->DLL = true;
+
   // Handle /entry
   if (auto *Arg = Args->getLastArg(OPT_entry))
     Config->EntryName = Arg->getValue();
@@ -318,6 +341,14 @@ bool LinkerDriver::link(int Argc, const
     }
   }
 
+  // Handle /export
+  for (auto *Arg : Args->filtered(OPT_export)) {
+    ErrorOr<Export> E = parseExport(Arg->getValue());
+    if (E.getError())
+      return false;
+    Config->Exports.push_back(E.get());
+  }
+
   // Handle /failifmismatch
   if (auto EC = checkFailIfMismatch(Args.get())) {
     llvm::errs() << "/failifmismatch: " << EC.message() << "\n";
@@ -414,6 +445,15 @@ bool LinkerDriver::link(int Argc, const
     }
   }
 
+  // Windows specific -- Make sure we resolve all dllexported symbols.
+  // (We don't cache the size here because Symtab.resolve() may add
+  // new entries to Config->Exports.)
+  for (size_t I = 0; I < Config->Exports.size(); ++I) {
+    StringRef Sym = Config->Exports[I].Name;
+    Symtab.addUndefined(Sym);
+    Config->GCRoots.insert(Sym);
+  }
+
   // Windows specific -- If entry point name is not given, we need to
   // infer that from user-defined entry name. The symbol table takes
   // care of details.
@@ -441,19 +481,21 @@ bool LinkerDriver::link(int Argc, const
   // Windows specific -- if no /subsystem is given, we need to infer
   // that from entry point name.
   if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
-    Config->Subsystem =
-      StringSwitch<WindowsSubsystem>(Config->EntryName)
-          .Case("mainCRTStartup", IMAGE_SUBSYSTEM_WINDOWS_CUI)
-          .Case("wmainCRTStartup", IMAGE_SUBSYSTEM_WINDOWS_CUI)
-          .Case("WinMainCRTStartup", IMAGE_SUBSYSTEM_WINDOWS_GUI)
-          .Case("wWinMainCRTStartup", IMAGE_SUBSYSTEM_WINDOWS_GUI)
-          .Default(IMAGE_SUBSYSTEM_UNKNOWN);
+    Config->Subsystem = inferSubsystem();
     if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
       llvm::errs() << "subsystem must be defined\n";
       return false;
     }
   }
 
+  // Windows specific -- fix up dllexported symbols.
+  if (!Config->Exports.empty()) {
+    for (Export &E : Config->Exports)
+      E.Sym = Symtab.find(E.Name);
+    if (fixupExports())
+      return false;
+  }
+
   // Write the result.
   Writer Out(&Symtab);
   if (auto EC = Out.write(Config->OutputFile)) {

Modified: lld/trunk/COFF/Driver.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Driver.h?rev=239869&r1=239868&r2=239869&view=diff
==============================================================================
--- lld/trunk/COFF/Driver.h (original)
+++ lld/trunk/COFF/Driver.h Tue Jun 16 19:16:33 2015
@@ -10,6 +10,7 @@
 #ifndef LLD_COFF_DRIVER_H
 #define LLD_COFF_DRIVER_H
 
+#include "Config.h"
 #include "lld/Core/LLVM.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
@@ -115,6 +116,10 @@ std::error_code parseVersion(StringRef A
 std::error_code parseSubsystem(StringRef Arg, WindowsSubsystem *Sys,
                                uint32_t *Major, uint32_t *Minor);
 
+// Used for dllexported symbols.
+ErrorOr<Export> parseExport(StringRef Arg);
+std::error_code fixupExports();
+
 // Parses a string in the form of "key=value" and check
 // if value matches previous values for the key.
 // This feature used in the directive section to reject

Modified: lld/trunk/COFF/DriverUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/DriverUtils.cpp?rev=239869&r1=239868&r2=239869&view=diff
==============================================================================
--- lld/trunk/COFF/DriverUtils.cpp (original)
+++ lld/trunk/COFF/DriverUtils.cpp Tue Jun 16 19:16:33 2015
@@ -114,6 +114,99 @@ std::error_code parseSubsystem(StringRef
   return std::error_code();
 }
 
+// Parse a string in the form of
+// "<name>[=<internalname>][, at ordinal[,NONAME]][,DATA][,PRIVATE]".
+// Used for parsing /export arguments.
+ErrorOr<Export> parseExport(StringRef Arg) {
+  Export E;
+  StringRef Rest;
+  std::tie(E.Name, Rest) = Arg.split(",");
+  if (E.Name.empty())
+    goto err;
+  if (E.Name.find('=') != StringRef::npos) {
+    std::tie(E.ExtName, E.Name) = E.Name.split("=");
+    if (E.Name.empty())
+      goto err;
+  } else {
+    E.ExtName = E.Name;
+  }
+
+  while (!Rest.empty()) {
+    StringRef Tok;
+    std::tie(Tok, Rest) = Rest.split(",");
+    if (Tok.equals_lower("noname")) {
+      if (E.Ordinal == 0)
+        goto err;
+      E.Noname = true;
+      continue;
+    }
+    if (Tok.equals_lower("data")) {
+      E.Data = true;
+      continue;
+    }
+    if (Tok.equals_lower("private")) {
+      E.Private = true;
+      continue;
+    }
+    if (Tok.startswith("@")) {
+      int32_t Ord;
+      if (Tok.substr(1).getAsInteger(0, Ord))
+        goto err;
+      if (Ord <= 0 || 65535 < Ord)
+        goto err;
+      E.Ordinal = Ord;
+      continue;
+    }
+    goto err;
+  }
+  return E;
+
+err:
+  llvm::errs() << "invalid /export: " << Arg << "\n";
+  return make_error_code(LLDError::InvalidOption);
+}
+
+// Performs error checking on all /export arguments.
+// It also sets ordinals.
+std::error_code fixupExports() {
+  // Symbol ordinals must be unique.
+  std::set<uint16_t> Ords;
+  for (Export &E : Config->Exports) {
+    if (E.Ordinal == 0)
+      continue;
+    if (!Ords.insert(E.Ordinal).second) {
+      llvm::errs() << "duplicate export ordinal: " << E.Name << "\n";
+      return make_error_code(LLDError::InvalidOption);
+    }
+  }
+
+  // Uniquefy by name.
+  std::set<StringRef> Names;
+  std::vector<Export> V;
+  for (Export &E : Config->Exports) {
+    if (!Names.insert(E.Name).second) {
+      llvm::errs() << "warning: duplicate /export option: " << E.Name << "\n";
+      continue;
+    }
+    V.push_back(E);
+  }
+  Config->Exports = std::move(V);
+
+  // Sort by name.
+  std::sort(
+      Config->Exports.begin(), Config->Exports.end(),
+      [](const Export &A, const Export &B) { return A.ExtName < B.ExtName; });
+
+  // Assign unique ordinals if default (= 0).
+  uint16_t Max = 0;
+  for (Export &E : Config->Exports)
+    Max = std::max(Max, E.Ordinal);
+  for (Export &E : Config->Exports)
+    if (E.Ordinal == 0)
+      E.Ordinal = ++Max;
+  return std::error_code();
+}
+
 // Parses a string in the form of "key=value" and check
 // if value matches previous values for the same key.
 std::error_code checkFailIfMismatch(llvm::opt::InputArgList *Args) {

Modified: lld/trunk/COFF/SymbolTable.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/SymbolTable.cpp?rev=239869&r1=239868&r2=239869&view=diff
==============================================================================
--- lld/trunk/COFF/SymbolTable.cpp (original)
+++ lld/trunk/COFF/SymbolTable.cpp Tue Jun 16 19:16:33 2015
@@ -189,6 +189,14 @@ Defined *SymbolTable::find(StringRef Nam
 
 // Windows specific -- Link default entry point name.
 ErrorOr<StringRef> SymbolTable::findDefaultEntry() {
+  // If it's DLL, the rule is easy.
+  if (Config->DLL) {
+    StringRef Sym = "_DllMainCRTStartup";
+    if (auto EC = resolve(new (Alloc) Undefined(Sym)))
+      return EC;
+    return Sym;
+  }
+
   // User-defined main functions and their corresponding entry points.
   static const char *Entries[][2] = {
       {"main", "mainCRTStartup"},

Modified: lld/trunk/COFF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Writer.cpp?rev=239869&r1=239868&r2=239869&view=diff
==============================================================================
--- lld/trunk/COFF/Writer.cpp (original)
+++ lld/trunk/COFF/Writer.cpp Tue Jun 16 19:16:33 2015
@@ -42,6 +42,7 @@ std::error_code Writer::write(StringRef
   markLive();
   createSections();
   createImportTables();
+  createExportTable();
   if (Config->Relocatable)
     createSection(".reloc");
   assignAddresses();
@@ -174,6 +175,15 @@ void Writer::createImportTables() {
     Sec->addChunk(C);
 }
 
+void Writer::createExportTable() {
+  if (Config->Exports.empty())
+    return;
+  Edata.reset(new EdataContents());
+  OutputSection *Sec = createSection(".edata");
+  for (std::unique_ptr<Chunk> &C : Edata->Chunks)
+    Sec->addChunk(C.get());
+}
+
 // The Windows loader doesn't seem to like empty sections,
 // so we remove them if any.
 void Writer::removeEmptySections() {
@@ -243,6 +253,8 @@ void Writer::writeHeader() {
   COFF->NumberOfSections = OutputSections.size();
   COFF->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
   COFF->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
+  if (Config->DLL)
+    COFF->Characteristics |= IMAGE_FILE_DLL;
   if (!Config->Relocatable)
     COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
   COFF->SizeOfOptionalHeader =
@@ -292,6 +304,10 @@ void Writer::writeHeader() {
   // Write data directory
   auto *DataDirectory = reinterpret_cast<data_directory *>(Buf);
   Buf += sizeof(*DataDirectory) * NumberfOfDataDirectory;
+  if (OutputSection *Sec = findSection(".edata")) {
+    DataDirectory[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA();
+    DataDirectory[EXPORT_TABLE].Size = Sec->getVirtualSize();
+  }
   if (Idata) {
     DataDirectory[IMPORT_TABLE].RelativeVirtualAddress = Idata->getDirRVA();
     DataDirectory[IMPORT_TABLE].Size = Idata->getDirSize();
@@ -397,6 +413,7 @@ OutputSection *Writer::createSection(Str
                        .Case(".bss", BSS | R | W)
                        .Case(".data", DATA | R | W)
                        .Case(".didat", DATA | R)
+                       .Case(".edata", DATA | R)
                        .Case(".idata", DATA | R)
                        .Case(".rdata", DATA | R)
                        .Case(".reloc", DATA | DISCARDABLE | R)

Modified: lld/trunk/COFF/Writer.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/COFF/Writer.h?rev=239869&r1=239868&r2=239869&view=diff
==============================================================================
--- lld/trunk/COFF/Writer.h (original)
+++ lld/trunk/COFF/Writer.h Tue Jun 16 19:16:33 2015
@@ -78,6 +78,7 @@ private:
   void markLive();
   void createSections();
   void createImportTables();
+  void createExportTable();
   void assignAddresses();
   void removeEmptySections();
   std::error_code openFile(StringRef OutputPath);
@@ -99,6 +100,7 @@ private:
   llvm::SpecificBumpPtrAllocator<BaserelChunk> BAlloc;
   std::vector<OutputSection *> OutputSections;
   std::unique_ptr<IdataContents> Idata;
+  std::unique_ptr<EdataContents> Edata;
 
   uint64_t FileSize;
   uint64_t SizeOfImage;

Added: lld/trunk/test/COFF/Inputs/export.yaml
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/Inputs/export.yaml?rev=239869&view=auto
==============================================================================
--- lld/trunk/test/COFF/Inputs/export.yaml (added)
+++ lld/trunk/test/COFF/Inputs/export.yaml Tue Jun 16 19:16:33 2015
@@ -0,0 +1,51 @@
+---
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: []
+sections:
+  - Name:            .text
+    Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+    Alignment:       4
+    SectionData:     B800000000506800000000680000000050E80000000050E800000000
+  - Name:            .drectve
+    Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+    Alignment:       2147483648
+    SectionData:     2f6578706f72743a6578706f7274666e3300  # /export:exportfn3
+symbols:
+  - Name:            .text
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+    SectionDefinition:
+      Length:          28
+      NumberOfRelocations: 4
+      NumberOfLinenumbers: 0
+      CheckSum:        0
+      Number:          0
+  - Name:            _DllMainCRTStartup
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            exportfn1
+    Value:           8
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            exportfn2
+    Value:           16
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            exportfn3
+    Value:           16
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+...

Added: lld/trunk/test/COFF/export.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/COFF/export.test?rev=239869&view=auto
==============================================================================
--- lld/trunk/test/COFF/export.test (added)
+++ lld/trunk/test/COFF/export.test Tue Jun 16 19:16:33 2015
@@ -0,0 +1,51 @@
+# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
+#
+# RUN: lld -flavor link2 /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2
+# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK1 %s
+
+CHECK1:      Export Table:
+CHECK1:      DLL name: export.test.tmp.dll
+CHECK1:      Ordinal      RVA  Name
+CHECK1-NEXT:       0        0
+CHECK1-NEXT:       1   0x1008  exportfn1
+CHECK1-NEXT:       2   0x1010  exportfn2
+
+# RUN: lld -flavor link2 /out:%t.dll /dll %t.obj /export:exportfn1, at 5 /export:exportfn2
+# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK2 %s
+
+CHECK2:      Export Table:
+CHECK2:      DLL name: export.test.tmp.dll
+CHECK2:      Ordinal      RVA  Name
+CHECK2-NEXT:       0        0
+CHECK2-NEXT:       1        0
+CHECK2-NEXT:       2        0
+CHECK2-NEXT:       3        0
+CHECK2-NEXT:       4        0
+CHECK2-NEXT:       5   0x1008  exportfn1
+CHECK2-NEXT:       6   0x1010  exportfn2
+CHECK2-NEXT:       7   0x1010  exportfn3
+
+# RUN: lld -flavor link2 /out:%t.dll /dll %t.obj /export:exportfn1, at 5,noname /export:exportfn2
+# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK3 %s
+
+CHECK3:      Export Table:
+CHECK3:      DLL name: export.test.tmp.dll
+CHECK3:      Ordinal      RVA  Name
+CHECK3-NEXT:       0        0
+CHECK3-NEXT:       1        0
+CHECK3-NEXT:       2        0
+CHECK3-NEXT:       3        0
+CHECK3-NEXT:       4        0
+CHECK3-NEXT:       5   0x1008
+CHECK3-NEXT:       6   0x1010  exportfn2
+
+# RUN: lld -flavor link2 /out:%t.dll /dll %t.obj /export:f1=exportfn1 /export:f2 at 4=exportfn2
+# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK4 %s
+
+CHECK4:      Export Table:
+CHECK4:      DLL name: export.test.tmp.dll
+CHECK4:      Ordinal      RVA  Name
+CHECK4-NEXT:       0        0
+CHECK4-NEXT:       1   0x1010  exportfn3
+CHECK4-NEXT:       2   0x1008  f1
+CHECK4-NEXT:       3   0x1010  f2





More information about the llvm-commits mailing list