[PATCH] D11188: [LLD] New ELF implementation

Rafael EspĂ­ndola rafael.espindola at gmail.com
Thu Jul 23 12:58:52 PDT 2015


The attached patch looks good to me.

It is your patch, but I have:

* removed code that had COFF logic.
* removed untested code.
* added a few more tests.
* simplified error handling.
* removed unused dependencies.

Even with the extra tests this patch adds 1255 lines. Yours was 1995.

Cheers,
Rafael



On 22 July 2015 at 20:30, Michael Spencer <bigcheesegs at gmail.com> wrote:
> Bigcheese added a comment.
>
> Mark done
>
>
> http://reviews.llvm.org/D11188
>
>
>
-------------- next part --------------
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0ff47d3..85dcb7a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,99 +1,100 @@
 set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
 
 # Compute the LLD version from the LLVM version.
 string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLD_VERSION
   ${PACKAGE_VERSION})
 message(STATUS "LLD version: ${LLD_VERSION}")
 
 string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" LLD_VERSION_MAJOR
   ${LLD_VERSION})
 string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" LLD_VERSION_MINOR
   ${LLD_VERSION})
 
 # Determine LLD revision and repository.
 # TODO: Figure out a way to get the revision and the repository on windows.
 if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" )
   execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetSourceVersion ${LLD_SOURCE_DIR}
                   OUTPUT_VARIABLE LLD_REVISION)
 
   execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetRepositoryPath ${LLD_SOURCE_DIR}
                   OUTPUT_VARIABLE LLD_REPOSITORY)
   if ( LLD_REPOSITORY )
     # Replace newline characters with spaces
     string(REGEX REPLACE "(\r?\n)+" " " LLD_REPOSITORY ${LLD_REPOSITORY})
     # Remove leading spaces
     STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REPOSITORY "${LLD_REPOSITORY}" )
     # Remove trailing spaces
     string(REGEX REPLACE "(\ )+$" "" LLD_REPOSITORY ${LLD_REPOSITORY})
   endif()
 
   if ( LLD_REVISION )
     # Replace newline characters with spaces
     string(REGEX REPLACE "(\r?\n)+" " " LLD_REVISION ${LLD_REVISION})
     # Remove leading spaces
     STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REVISION "${LLD_REVISION}" )
     # Remove trailing spaces
     string(REGEX REPLACE "(\ )+$" "" LLD_REVISION ${LLD_REVISION})
   endif()
 endif ()
 
 # Configure the Version.inc file.
 configure_file(
   ${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Config/Version.inc.in
   ${CMAKE_CURRENT_BINARY_DIR}/include/lld/Config/Version.inc)
 
 
 if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
   message(FATAL_ERROR "In-source builds are not allowed. CMake would overwrite "
 "the makefiles distributed with LLVM. Please create a directory and run cmake "
 "from there, passing the path to this source directory as the last argument. "
 "This process created the file `CMakeCache.txt' and the directory "
 "`CMakeFiles'. Please delete them.")
 endif()
 
 list (APPEND CMAKE_MODULE_PATH "${LLD_SOURCE_DIR}/cmake/modules")
 
 option(LLD_USE_VTUNE
        "Enable VTune user task tracking."
        OFF)
 if (LLD_USE_VTUNE)
   find_package(VTune)
   if (VTUNE_FOUND)
     include_directories(${VTune_INCLUDE_DIRS})
     list(APPEND LLVM_COMMON_LIBS ${VTune_LIBRARIES})
     add_definitions(-DLLD_HAS_VTUNE)
   endif()
 endif()
 
 
 if (MSVC)
   add_definitions(-wd4530) # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.'
   add_definitions(-wd4062) # Suppress 'warning C4062: enumerator X in switch of enum Y is not handled' from system header.
 endif()
 
 include_directories(BEFORE
   ${CMAKE_CURRENT_BINARY_DIR}/include
   ${CMAKE_CURRENT_SOURCE_DIR}/include
   )
 
 if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
   install(DIRECTORY include/
     DESTINATION include
     FILES_MATCHING
     PATTERN "*.h"
     PATTERN ".svn" EXCLUDE
     )
 endif()
 
 add_subdirectory(lib)
 add_subdirectory(tools)
 
 add_subdirectory(test)
 
 if (LLVM_INCLUDE_TESTS)
   add_subdirectory(unittests)
 endif()
 
 add_subdirectory(docs)
 add_subdirectory(COFF)
+add_subdirectory(ELF)
diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt
new file mode 100644
index 0000000..c2367f3
--- /dev/null
+++ b/ELF/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(ELFOptionsTableGen)
+
+add_llvm_library(lldELF2
+  Chunks.cpp
+  Driver.cpp
+  DriverUtils.cpp
+  InputFiles.cpp
+  SymbolTable.cpp
+  Symbols.cpp
+  Writer.cpp
+
+  LINK_COMPONENTS
+  Object
+  Option
+  Support
+  )
+
+add_dependencies(lldELF2 ELFOptionsTableGen)
diff --git a/ELF/Chunks.cpp b/ELF/Chunks.cpp
new file mode 100644
index 0000000..121fe55
--- /dev/null
+++ b/ELF/Chunks.cpp
@@ -0,0 +1,48 @@
+//===- Chunks.cpp ---------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Chunks.h"
+#include "Driver.h"
+
+using namespace llvm;
+using namespace llvm::ELF;
+
+using namespace lld;
+using namespace lld::elf2;
+
+template <class ELFT>
+SectionChunk<ELFT>::SectionChunk(object::ELFFile<ELFT> *Obj,
+                                 const Elf_Shdr *Header)
+    : Obj(Obj), Header(Header) {
+  // Initialize SectionName.
+  ErrorOr<StringRef> Name = Obj->getSectionName(Header);
+  error(Name);
+  SectionName = *Name;
+
+  Align = Header->sh_addralign;
+}
+
+template <class ELFT> void SectionChunk<ELFT>::writeTo(uint8_t *Buf) {
+  if (Header->sh_type == SHT_NOBITS)
+    return;
+  // Copy section contents from source object file to output file.
+  ArrayRef<uint8_t> Data = *Obj->getSectionContents(Header);
+  memcpy(Buf + FileOff, Data.data(), Data.size());
+
+  // FIXME: Relocations
+}
+
+namespace lld {
+namespace elf2 {
+template class SectionChunk<object::ELF32LE>;
+template class SectionChunk<object::ELF32BE>;
+template class SectionChunk<object::ELF64LE>;
+template class SectionChunk<object::ELF64BE>;
+}
+}
diff --git a/ELF/Chunks.h b/ELF/Chunks.h
new file mode 100644
index 0000000..3080502
--- /dev/null
+++ b/ELF/Chunks.h
@@ -0,0 +1,93 @@
+//===- Chunks.h -----------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_CHUNKS_H
+#define LLD_ELF_CHUNKS_H
+
+#include "lld/Core/LLVM.h"
+#include "llvm/Object/ELF.h"
+
+namespace lld {
+namespace elf2 {
+
+class Defined;
+template <class ELFT> class ObjectFile;
+class OutputSection;
+
+// A Chunk represents a chunk of data that will occupy space in the
+// output (if the resolver chose that). It may or may not be backed by
+// a section of an input file. It could be linker-created data, or
+// doesn't even have actual data (if common or bss).
+class Chunk {
+public:
+  virtual ~Chunk() = default;
+
+  // Returns the size of this chunk (even if this is a common or BSS.)
+  virtual size_t getSize() const = 0;
+
+  // Write this chunk to a mmap'ed file, assuming Buf is pointing to
+  // beginning of the file. Because this function may use VA values
+  // of other chunks for relocations, you need to set them properly
+  // before calling this function.
+  virtual void writeTo(uint8_t *Buf) = 0;
+
+  // The writer sets and uses the addresses.
+  uint64_t getVA() { return VA; }
+  uint64_t getFileOff() { return FileOff; }
+  uint32_t getAlign() { return Align; }
+  void setVA(uint64_t V) { VA = V; }
+  void setFileOff(uint64_t V) { FileOff = V; }
+
+  // Returns the section name if this is a section chunk.
+  // It is illegal to call this function on non-section chunks.
+  virtual StringRef getSectionName() const = 0;
+
+  // An output section has pointers to chunks in the section, and each
+  // chunk has a back pointer to an output section.
+  void setOutputSection(OutputSection *O) { Out = O; }
+  OutputSection *getOutputSection() { return Out; }
+
+protected:
+  // The VA of this chunk in the output. The writer sets a value.
+  uint64_t VA = 0;
+
+  // The offset from beginning of the output file. The writer sets a value.
+  uint64_t FileOff = 0;
+
+  // The output section for this chunk.
+  OutputSection *Out = nullptr;
+
+  // The alignment of this chunk. The writer uses the value.
+  uint32_t Align = 1;
+};
+
+// A chunk corresponding a section of an input file.
+template <class ELFT> class SectionChunk : public Chunk {
+  typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr;
+  typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela;
+  typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel;
+
+public:
+  SectionChunk(llvm::object::ELFFile<ELFT> *Obj, const Elf_Shdr *Header);
+  size_t getSize() const override { return Header->sh_size; }
+  void writeTo(uint8_t *Buf) override;
+  StringRef getSectionName() const override { return SectionName; }
+
+private:
+  // A file this chunk was created from.
+  llvm::object::ELFFile<ELFT> *Obj;
+
+  const Elf_Shdr *Header;
+  StringRef SectionName;
+};
+
+} // namespace elf2
+} // namespace lld
+
+#endif
diff --git a/ELF/Config.h b/ELF/Config.h
new file mode 100644
index 0000000..9687b52
--- /dev/null
+++ b/ELF/Config.h
@@ -0,0 +1,27 @@
+//===- Config.h -----------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_CONFIG_H
+#define LLD_ELF_CONFIG_H
+
+#include "llvm/ADT/StringRef.h"
+
+namespace lld {
+namespace elf2 {
+
+struct Configuration {
+  llvm::StringRef OutputFile;
+};
+
+extern Configuration *Config;
+
+} // namespace elf2
+} // namespace lld
+
+#endif
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
new file mode 100644
index 0000000..1391056
--- /dev/null
+++ b/ELF/Driver.cpp
@@ -0,0 +1,104 @@
+//===- Driver.cpp ---------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Driver.h"
+#include "Config.h"
+#include "Writer.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace llvm;
+
+using namespace lld;
+using namespace lld::elf2;
+
+namespace lld {
+namespace elf2 {
+Configuration *Config;
+LinkerDriver *Driver;
+
+void link(ArrayRef<const char *> Args) {
+  auto C = make_unique<Configuration>();
+  Config = C.get();
+  auto D = make_unique<LinkerDriver>();
+  Driver = D.get();
+  Driver->link(Args.slice(1));
+}
+
+void error(Twine Msg) {
+  errs() << Msg << "\n";
+  exit(1);
+}
+
+void error(std::error_code EC, Twine Prefix) {
+  if (!EC)
+    return;
+  error(Prefix + ": " + EC.message());
+}
+
+void error(std::error_code EC) {
+  if (!EC)
+    return;
+  error(EC.message());
+}
+}
+}
+
+// Opens a file. Path has to be resolved already.
+// Newly created memory buffers are owned by this driver.
+MemoryBufferRef LinkerDriver::openFile(StringRef Path) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile(Path);
+  error(MBOrErr, Twine("cannot open ") + Path);
+  std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
+  MemoryBufferRef MBRef = MB->getMemBufferRef();
+  OwningMBs.push_back(std::move(MB)); // take ownership
+  return MBRef;
+}
+
+static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB) {
+  return std::unique_ptr<InputFile>(new ObjectFile<object::ELF64LE>(MB));
+}
+
+void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
+  // Parse command line options.
+  opt::InputArgList Args = Parser.parse(ArgsArr);
+
+  // Handle -o
+  if (auto *Arg = Args.getLastArg(OPT_output))
+    Config->OutputFile = Arg->getValue();
+  if (Config->OutputFile.empty())
+    error("-o must be specified.");
+
+  // Create a list of input files.
+  std::vector<MemoryBufferRef> Inputs;
+
+  for (auto *Arg : Args.filtered(OPT_INPUT)) {
+    StringRef Path = Arg->getValue();
+    Inputs.push_back(openFile(Path));
+  }
+
+  if (Inputs.empty())
+    error("no input files.");
+
+  // Create a symbol table.
+  SymbolTable<object::ELF64LE> Symtab;
+
+  // Parse all input files and put all symbols to the symbol table.
+  // The symbol table will take care of name resolution.
+  for (MemoryBufferRef MB : Inputs) {
+    std::unique_ptr<InputFile> File = createFile(MB);
+    Symtab.addFile(std::move(File));
+  }
+
+  // Make sure we have resolved all symbols.
+  Symtab.reportRemainingUndefines();
+
+  // Write the result.
+  Writer<object::ELF64LE> Out(&Symtab);
+  Out.write(Config->OutputFile);
+}
diff --git a/ELF/Driver.h b/ELF/Driver.h
new file mode 100644
index 0000000..eefde94
--- /dev/null
+++ b/ELF/Driver.h
@@ -0,0 +1,67 @@
+//===- Driver.h -----------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_DRIVER_H
+#define LLD_ELF_DRIVER_H
+
+#include "lld/Core/LLVM.h"
+#include "llvm/Option/ArgList.h"
+
+namespace lld {
+namespace elf2 {
+
+class LinkerDriver;
+extern LinkerDriver *Driver;
+
+class InputFile;
+
+// Entry point of the ELF linker.
+void link(ArrayRef<const char *> Args);
+
+void error(Twine Msg);
+void error(std::error_code EC, Twine Prefix);
+void error(std::error_code EC);
+template <typename T> void error(const ErrorOr<T> &V, Twine Prefix) {
+  error(V.getError(), Prefix);
+}
+template <typename T> void error(const ErrorOr<T> &V) { error(V.getError()); }
+
+class ArgParser {
+public:
+  // Parses command line options.
+  llvm::opt::InputArgList parse(ArrayRef<const char *> Args);
+};
+
+class LinkerDriver {
+public:
+  void link(ArrayRef<const char *> Args);
+
+private:
+  ArgParser Parser;
+
+  // Opens a file. Path has to be resolved already.
+  MemoryBufferRef openFile(StringRef Path);
+
+  // Driver is the owner of all opened files.
+  // InputFiles have MemoryBufferRefs to them.
+  std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs;
+};
+
+// Create enum with OPT_xxx values for each option in Options.td
+enum {
+  OPT_INVALID = 0,
+#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11) OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+} // namespace elf2
+} // namespace lld
+
+#endif
diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp
new file mode 100644
index 0000000..7bc6fc5
--- /dev/null
+++ b/ELF/DriverUtils.cpp
@@ -0,0 +1,63 @@
+//===- DriverUtils.cpp ----------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains utility functions for the driver. Because there
+// are so many small functions, we created this separate file to make
+// Driver.cpp less cluttered.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Driver.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace llvm;
+
+using namespace lld;
+using namespace lld::elf2;
+
+// Create OptTable
+
+// Create prefix string literals used in Options.td
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in Options.td
+static const opt::OptTable::Info infoTable[] = {
+#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X6, X7, X8, X9, X10)            \
+  {                                                                            \
+    X1, X2, X9, X10, OPT_##ID, opt::Option::KIND##Class, X8, X7, OPT_##GROUP,  \
+        OPT_##ALIAS, X6                                                        \
+  }                                                                            \
+  ,
+#include "Options.inc"
+#undef OPTION
+};
+
+class ELFOptTable : public opt::OptTable {
+public:
+  ELFOptTable() : OptTable(infoTable, array_lengthof(infoTable)) {}
+};
+
+// Parses a given list of options.
+opt::InputArgList ArgParser::parse(ArrayRef<const char *> Argv) {
+  // Make InputArgList from string vectors.
+  ELFOptTable Table;
+  unsigned MissingIndex;
+  unsigned MissingCount;
+
+  opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount);
+  if (MissingCount)
+    error(Twine("missing arg value for \"") + Args.getArgString(MissingIndex) +
+          "\", expected " + Twine(MissingCount) +
+          (MissingCount == 1 ? " argument.\n" : " arguments"));
+  for (auto *Arg : Args.filtered(OPT_UNKNOWN))
+    error(Twine("unknown argument: ") + Arg->getSpelling());
+  return Args;
+}
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
new file mode 100644
index 0000000..2ab8fa2
--- /dev/null
+++ b/ELF/InputFiles.cpp
@@ -0,0 +1,78 @@
+//===- InputFiles.cpp -----------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+#include "Chunks.h"
+#include "Symbols.h"
+#include "Driver.h"
+#include "llvm/ADT/STLExtras.h"
+
+using namespace llvm::ELF;
+
+using namespace lld;
+using namespace lld::elf2;
+
+template <class ELFT> void elf2::ObjectFile<ELFT>::parse() {
+  // Parse a memory buffer as a ELF file.
+  std::error_code EC;
+  ELFObj = llvm::make_unique<ELFFile<ELFT>>(MB.getBuffer(), EC);
+  error(EC);
+
+  // Read section and symbol tables.
+  initializeChunks();
+  initializeSymbols();
+}
+
+template <class ELFT> void elf2::ObjectFile<ELFT>::initializeChunks() {
+  uint64_t Size = ELFObj->getNumSections();
+  Chunks.reserve(Size);
+  for (const Elf_Shdr &Sec : ELFObj->sections()) {
+    if (Sec.sh_flags & SHF_ALLOC) {
+      auto *C = new (Alloc) SectionChunk<ELFT>(this->getObj(), &Sec);
+      Chunks.push_back(C);
+    }
+  }
+}
+
+template <class ELFT> void elf2::ObjectFile<ELFT>::initializeSymbols() {
+  const Elf_Shdr *Symtab = ELFObj->getDotSymtabSec();
+  ErrorOr<StringRef> StringTableOrErr =
+      ELFObj->getStringTableForSymtab(*Symtab);
+  error(StringTableOrErr.getError());
+  StringRef StringTable = *StringTableOrErr;
+
+  Elf_Sym_Range Syms = ELFObj->symbols();
+  Syms = Elf_Sym_Range(Syms.begin() + 1, Syms.end());
+  auto NumSymbols = std::distance(Syms.begin(), Syms.end());
+  SymbolBodies.reserve(NumSymbols);
+  for (const Elf_Sym &Sym : Syms) {
+    if (SymbolBody *Body = createSymbolBody(StringTable, &Sym))
+      SymbolBodies.push_back(Body);
+  }
+}
+
+template <class ELFT>
+SymbolBody *elf2::ObjectFile<ELFT>::createSymbolBody(StringRef StringTable,
+                                                     const Elf_Sym *Sym) {
+  ErrorOr<StringRef> NameOrErr = Sym->getName(StringTable);
+  error(NameOrErr.getError());
+  StringRef Name = *NameOrErr;
+  if (Sym->isUndefined())
+    return new (Alloc) Undefined(Name);
+  return new (Alloc) DefinedRegular<ELFT>(Name);
+}
+
+namespace lld {
+namespace elf2 {
+template class elf2::ObjectFile<llvm::object::ELF32LE>;
+template class elf2::ObjectFile<llvm::object::ELF32BE>;
+template class elf2::ObjectFile<llvm::object::ELF64LE>;
+template class elf2::ObjectFile<llvm::object::ELF64BE>;
+}
+}
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
new file mode 100644
index 0000000..f6ee6b8
--- /dev/null
+++ b/ELF/InputFiles.h
@@ -0,0 +1,78 @@
+//===- InputFiles.h -------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_INPUT_FILES_H
+#define LLD_ELF_INPUT_FILES_H
+
+#include "lld/Core/LLVM.h"
+#include "llvm/Object/ELF.h"
+
+namespace lld {
+namespace elf2 {
+class SymbolBody;
+class Chunk;
+
+// The root class of input files.
+class InputFile {
+public:
+  enum Kind { ObjectKind };
+  Kind kind() const { return FileKind; }
+  virtual ~InputFile() {}
+
+  // Returns symbols defined by this file.
+  virtual ArrayRef<SymbolBody *> getSymbols() = 0;
+
+  // Reads a file (constructors don't do that).
+  virtual void parse() = 0;
+
+protected:
+  explicit InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
+  MemoryBufferRef MB;
+
+private:
+  const Kind FileKind;
+};
+
+// .o file.
+template <class ELFT> class ObjectFile : public InputFile {
+  typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
+  typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
+  typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym_Range Elf_Sym_Range;
+
+public:
+  explicit ObjectFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
+  static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
+  void parse() override;
+  ArrayRef<Chunk *> getChunks() { return Chunks; }
+  ArrayRef<SymbolBody *> getSymbols() override { return SymbolBodies; }
+
+  // Returns the underying ELF file.
+  llvm::object::ELFFile<ELFT> *getObj() { return ELFObj.get(); }
+
+private:
+  void initializeChunks();
+  void initializeSymbols();
+
+  SymbolBody *createSymbolBody(StringRef StringTable, const Elf_Sym *Sym);
+
+  std::unique_ptr<llvm::object::ELFFile<ELFT>> ELFObj;
+  llvm::BumpPtrAllocator Alloc;
+
+  // List of all chunks defined by this file. This includes both section
+  // chunks and non-section chunks for common symbols.
+  std::vector<Chunk *> Chunks;
+
+  // List of all symbols referenced or defined by this file.
+  std::vector<SymbolBody *> SymbolBodies;
+};
+
+} // namespace elf2
+} // namespace lld
+
+#endif
diff --git a/ELF/Options.td b/ELF/Options.td
new file mode 100644
index 0000000..567aecf
--- /dev/null
+++ b/ELF/Options.td
@@ -0,0 +1,8 @@
+include "llvm/Option/OptParser.td"
+
+//===----------------------------------------------------------------------===//
+/// Utility Functions
+//===----------------------------------------------------------------------===//
+
+def output : Separate<["-"], "o">, MetaVarName<"<path>">,
+     HelpText<"Path to file to write output">;
diff --git a/ELF/README.md b/ELF/README.md
new file mode 100644
index 0000000..dea3c6c
--- /dev/null
+++ b/ELF/README.md
@@ -0,0 +1,12 @@
+The New ELF Linker
+==================
+This directory contains a port of the new PE/COFF linker for ELF.
+
+Overall Design
+--------------
+See COFF/README.md for details on the design.
+
+Capabilities
+------------
+This linker can currently generate a valid ELF file that can be run on linux
+from a single input file.
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
new file mode 100644
index 0000000..61001b1
--- /dev/null
+++ b/ELF/SymbolTable.cpp
@@ -0,0 +1,87 @@
+//===- SymbolTable.cpp ----------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolTable.h"
+#include "Driver.h"
+#include "Symbols.h"
+
+using namespace llvm;
+
+using namespace lld;
+using namespace lld::elf2;
+
+template <class ELFT> SymbolTable<ELFT>::SymbolTable() {
+  resolve(new (Alloc) Undefined("_start"));
+}
+
+template <class ELFT>
+void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) {
+  File->parse();
+  InputFile *FileP = File.release();
+  auto *P = cast<ObjectFile<ELFT>>(FileP);
+  addObject(P);
+  return;
+}
+
+template <class ELFT>
+void SymbolTable<ELFT>::addObject(ObjectFile<ELFT> *File) {
+  ObjectFiles.emplace_back(File);
+  for (SymbolBody *Body : File->getSymbols())
+    if (Body->isExternal())
+      resolve(Body);
+}
+
+template <class ELFT> void SymbolTable<ELFT>::reportRemainingUndefines() {
+  for (auto &I : Symtab) {
+    Symbol *Sym = I.second;
+    if (auto *Undef = dyn_cast<Undefined>(Sym->Body))
+      error(Twine("undefined symbol: ") + Undef->getName());
+  }
+}
+
+// This function resolves conflicts if there's an existing symbol with
+// the same name. Decisions are made based on symbol type.
+template <class ELFT> void SymbolTable<ELFT>::resolve(SymbolBody *New) {
+  // Find an existing Symbol or create and insert a new one.
+  StringRef Name = New->getName();
+  Symbol *&Sym = Symtab[Name];
+  if (!Sym) {
+    Sym = new (Alloc) Symbol(New);
+    New->setBackref(Sym);
+    return;
+  }
+  New->setBackref(Sym);
+
+  // compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
+  // equivalent (conflicting), or more preferable, respectively.
+  SymbolBody *Existing = Sym->Body;
+  int comp = Existing->compare(New);
+  if (comp < 0)
+    Sym->Body = New;
+  if (comp == 0)
+    error(Twine("duplicate symbol: ") + Name);
+}
+
+template <class ELFT> std::vector<Chunk *> SymbolTable<ELFT>::getChunks() {
+  std::vector<Chunk *> Res;
+  for (std::unique_ptr<ObjectFile<ELFT>> &File : ObjectFiles) {
+    ArrayRef<Chunk *> V = File->getChunks();
+    Res.insert(Res.end(), V.begin(), V.end());
+  }
+  return Res;
+}
+
+namespace lld {
+namespace elf2 {
+template class SymbolTable<object::ELF32LE>;
+template class SymbolTable<object::ELF32BE>;
+template class SymbolTable<object::ELF64LE>;
+template class SymbolTable<object::ELF64BE>;
+}
+}
diff --git a/ELF/SymbolTable.h b/ELF/SymbolTable.h
new file mode 100644
index 0000000..9b37439
--- /dev/null
+++ b/ELF/SymbolTable.h
@@ -0,0 +1,58 @@
+//===- SymbolTable.h ------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_SYMBOL_TABLE_H
+#define LLD_ELF_SYMBOL_TABLE_H
+
+#include "InputFiles.h"
+#include <unordered_map>
+
+namespace lld {
+namespace elf2 {
+class Defined;
+struct Symbol;
+
+// SymbolTable is a bucket of all known symbols, including defined,
+// undefined, or lazy symbols (the last one is symbols in archive
+// files whose archive members are not yet loaded).
+//
+// We put all symbols of all files to a SymbolTable, and the
+// SymbolTable selects the "best" symbols if there are name
+// conflicts. For example, obviously, a defined symbol is better than
+// an undefined symbol. Or, if there's a conflict between a lazy and a
+// undefined, it'll read an archive member to read a real definition
+// to replace the lazy symbol. The logic is implemented in resolve().
+template <class ELFT> class SymbolTable {
+public:
+  SymbolTable();
+
+  void addFile(std::unique_ptr<InputFile> File);
+
+  // Print an error message on undefined symbols.
+  void reportRemainingUndefines();
+
+  // Returns a list of chunks of selected symbols.
+  std::vector<Chunk *> getChunks();
+
+  // The writer needs to infer the machine type from the object files.
+  std::vector<std::unique_ptr<ObjectFile<ELFT>>> ObjectFiles;
+
+private:
+  void addObject(ObjectFile<ELFT> *File);
+
+  void resolve(SymbolBody *Body);
+
+  std::unordered_map<StringRef, Symbol *> Symtab;
+  llvm::BumpPtrAllocator Alloc;
+};
+
+} // namespace elf2
+} // namespace lld
+
+#endif
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
new file mode 100644
index 0000000..7d9a185
--- /dev/null
+++ b/ELF/Symbols.cpp
@@ -0,0 +1,57 @@
+//===- Symbols.cpp --------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Symbols.h"
+#include "Chunks.h"
+
+using namespace llvm::object;
+
+using namespace lld;
+using namespace lld::elf2;
+
+template <class ELFT>
+DefinedRegular<ELFT>::DefinedRegular(StringRef Name)
+    : Defined(DefinedRegularKind), Name(Name) {}
+
+// Returns 1, 0 or -1 if this symbol should take precedence
+// over the Other, tie or lose, respectively.
+template <class ELFT> int DefinedRegular<ELFT>::compare(SymbolBody *Other) {
+  if (Other->kind() < kind())
+    return -Other->compare(this);
+  auto *R = dyn_cast<DefinedRegular>(Other);
+  if (!R)
+    return 1;
+
+  return 0;
+}
+
+int Defined::compare(SymbolBody *Other) {
+  if (Other->kind() < kind())
+    return -Other->compare(this);
+  if (isa<Defined>(Other))
+    return 0;
+  return 1;
+}
+
+int Undefined::compare(SymbolBody *Other) {
+  if (Other->kind() < kind())
+    return -Other->compare(this);
+  return 1;
+}
+
+template <class ELFT> StringRef DefinedRegular<ELFT>::getName() { return Name; }
+
+namespace lld {
+namespace elf2 {
+template class DefinedRegular<llvm::object::ELF32LE>;
+template class DefinedRegular<llvm::object::ELF32BE>;
+template class DefinedRegular<llvm::object::ELF64LE>;
+template class DefinedRegular<llvm::object::ELF64BE>;
+}
+}
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
new file mode 100644
index 0000000..975302e
--- /dev/null
+++ b/ELF/Symbols.h
@@ -0,0 +1,123 @@
+//===- Symbols.h ----------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_SYMBOLS_H
+#define LLD_ELF_SYMBOLS_H
+
+#include "lld/Core/LLVM.h"
+#include "llvm/Object/ELF.h"
+
+namespace lld {
+namespace elf2 {
+
+using llvm::object::ELFFile;
+
+class Chunk;
+class InputFile;
+class SymbolBody;
+
+// A real symbol object, SymbolBody, is usually accessed indirectly
+// through a Symbol. There's always one Symbol for each symbol name.
+// The resolver updates SymbolBody pointers as it resolves symbols.
+struct Symbol {
+  explicit Symbol(SymbolBody *P) : Body(P) {}
+  SymbolBody *Body;
+};
+
+// The base class for real symbol classes.
+class SymbolBody {
+public:
+  enum Kind {
+    DefinedFirst,
+    DefinedRegularKind,
+    DefinedLast,
+    UndefinedKind,
+  };
+
+  Kind kind() const { return SymbolKind; }
+  virtual ~SymbolBody() {}
+
+  // Returns true if this is an external symbol.
+  virtual bool isExternal() { return true; }
+
+  // Returns the symbol name.
+  virtual StringRef getName() = 0;
+
+  // A SymbolBody has a backreference to a Symbol. Originally they are
+  // doubly-linked. A backreference will never change. But the pointer
+  // in the Symbol may be mutated by the resolver. If you have a
+  // pointer P to a SymbolBody and are not sure whether the resolver
+  // has chosen the object among other objects having the same name,
+  // you can access P->Backref->Body to get the resolver's result.
+  void setBackref(Symbol *P) { Backref = P; }
+  SymbolBody *getReplacement() { return Backref ? Backref->Body : this; }
+
+  // Decides which symbol should "win" in the symbol table, this or
+  // the Other. Returns 1 if this wins, -1 if the Other wins, or 0 if
+  // they are duplicate (conflicting) symbols.
+  virtual int compare(SymbolBody *Other) = 0;
+
+protected:
+  SymbolBody(Kind K) : SymbolKind(K) {}
+
+private:
+  const Kind SymbolKind;
+  Symbol *Backref = nullptr;
+};
+
+// The base class for any defined symbols, including absolute symbols,
+// etc.
+class Defined : public SymbolBody {
+public:
+  Defined(Kind K) : SymbolBody(K) {}
+
+  static bool classof(const SymbolBody *S) {
+    Kind K = S->kind();
+    return DefinedFirst <= K && K <= DefinedLast;
+  }
+
+  int compare(SymbolBody *Other) override;
+};
+
+// Regular defined symbols read from object file symbol tables.
+template <class ELFT> class DefinedRegular : public Defined {
+public:
+  DefinedRegular(StringRef Name);
+
+  static bool classof(const SymbolBody *S) {
+    return S->kind() == DefinedRegularKind;
+  }
+
+  StringRef getName() override;
+  int compare(SymbolBody *Other) override;
+
+private:
+  StringRef Name;
+};
+
+// Undefined symbols.
+class Undefined : public SymbolBody {
+public:
+  explicit Undefined(StringRef N) : SymbolBody(UndefinedKind), Name(N) {}
+
+  static bool classof(const SymbolBody *S) {
+    return S->kind() == UndefinedKind;
+  }
+  StringRef getName() override { return Name; }
+
+  int compare(SymbolBody *Other) override;
+
+private:
+  StringRef Name;
+};
+
+} // namespace elf2
+} // namespace lld
+
+#endif
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
new file mode 100644
index 0000000..28f81ac
--- /dev/null
+++ b/ELF/Writer.cpp
@@ -0,0 +1,161 @@
+//===- Writer.cpp ---------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Writer.h"
+#include "Chunks.h"
+#include "Driver.h"
+
+using namespace llvm;
+using namespace llvm::ELF;
+using namespace llvm::object;
+
+using namespace lld;
+using namespace lld::elf2;
+
+static const int PageSize = 4096;
+
+template <class ELFT> Writer<ELFT>::Writer(SymbolTable<ELFT> *T) : Symtab(T) {}
+template <class ELFT> Writer<ELFT>::~Writer() {}
+
+// The main function of the writer.
+template <class ELFT> void Writer<ELFT>::write(StringRef OutputPath) {
+  createSections();
+  assignAddresses();
+  openFile(OutputPath);
+  writeHeader();
+  writeSections();
+  error(Buffer->commit());
+}
+
+void OutputSection::setVA(uint64_t VA) {
+  Header.sh_addr = VA;
+  for (Chunk *C : Chunks)
+    C->setVA(C->getVA() + VA);
+}
+
+void OutputSection::setFileOffset(uint64_t Off) {
+  if (Header.sh_size == 0)
+    return;
+  Header.sh_offset = Off;
+  for (Chunk *C : Chunks)
+    C->setFileOff(C->getFileOff() + Off);
+}
+
+void OutputSection::addChunk(Chunk *C) {
+  Chunks.push_back(C);
+  C->setOutputSection(this);
+  uint64_t Off = Header.sh_size;
+  Off = RoundUpToAlignment(Off, C->getAlign());
+  C->setVA(Off);
+  C->setFileOff(Off);
+  Off += C->getSize();
+  Header.sh_size = Off;
+}
+
+static int compare(const Chunk *A, const Chunk *B) {
+  return A->getSectionName() < B->getSectionName();
+}
+
+// Create output section objects and add them to OutputSections.
+template <class ELFT> void Writer<ELFT>::createSections() {
+  std::vector<Chunk *> Chunks = Symtab->getChunks();
+  if (Chunks.empty())
+    return;
+  std::sort(Chunks.begin(), Chunks.end(), compare);
+
+  Chunk *Prev = nullptr;
+  OutputSection *Sec = nullptr;
+  for (Chunk *C : Chunks) {
+    if (Prev == nullptr || Prev->getSectionName() != C->getSectionName()) {
+      Sec = new (CAlloc.Allocate()) OutputSection(C->getSectionName());
+      OutputSections.push_back(Sec);
+      Prev = C;
+    }
+    Sec->addChunk(C);
+  }
+}
+
+// Visits all sections to assign incremental, non-overlapping RVAs and
+// file offsets.
+template <class ELFT> void Writer<ELFT>::assignAddresses() {
+  SizeOfHeaders = RoundUpToAlignment(sizeof(Elf_Ehdr_Impl<ELFT>) +
+                                         sizeof(Elf_Shdr_Impl<ELFT>) *
+                                             OutputSections.size(),
+                                     PageSize);
+  uint64_t VA = 0x1000; // The first page is kept unmapped.
+  uint64_t FileOff = SizeOfHeaders;
+  for (OutputSection *Sec : OutputSections) {
+    Sec->setVA(VA);
+    Sec->setFileOffset(FileOff);
+    VA += RoundUpToAlignment(Sec->getSize(), PageSize);
+    FileOff += RoundUpToAlignment(Sec->getSize(), 8);
+  }
+  SizeOfImage = SizeOfHeaders + RoundUpToAlignment(VA - 0x1000, PageSize);
+  FileSize = SizeOfHeaders + RoundUpToAlignment(FileOff - SizeOfHeaders, 8);
+}
+
+template <class ELFT> void Writer<ELFT>::writeHeader() {
+  uint8_t *Buf = Buffer->getBufferStart();
+  auto *EHdr = reinterpret_cast<Elf_Ehdr_Impl<ELFT> *>(Buf);
+  EHdr->e_ident[EI_MAG0] = 0x7F;
+  EHdr->e_ident[EI_MAG1] = 0x45;
+  EHdr->e_ident[EI_MAG2] = 0x4C;
+  EHdr->e_ident[EI_MAG3] = 0x46;
+  EHdr->e_ident[EI_CLASS] = ELFCLASS64;
+  EHdr->e_ident[EI_DATA] = ELFDATA2LSB;
+  EHdr->e_ident[EI_VERSION] = EV_CURRENT;
+  EHdr->e_ident[EI_OSABI] = ELFOSABI_GNU;
+
+  EHdr->e_type = ET_EXEC;
+  EHdr->e_machine = EM_X86_64;
+  EHdr->e_version = EV_CURRENT;
+  EHdr->e_entry = 0x401000;
+  EHdr->e_phoff = sizeof(Elf_Ehdr_Impl<ELFT>);
+  EHdr->e_shoff = 0;
+  EHdr->e_ehsize = sizeof(Elf_Ehdr_Impl<ELFT>);
+  EHdr->e_phentsize = sizeof(Elf_Phdr_Impl<ELFT>);
+  EHdr->e_phnum = 1;
+  EHdr->e_shentsize = sizeof(Elf_Shdr_Impl<ELFT>);
+  EHdr->e_shnum = 0;
+  EHdr->e_shstrndx = 0;
+
+  auto PHdrs = reinterpret_cast<Elf_Phdr_Impl<ELFT> *>(Buf + EHdr->e_phoff);
+  PHdrs->p_type = PT_LOAD;
+  PHdrs->p_flags = PF_R | PF_X;
+  PHdrs->p_offset = 0x0000;
+  PHdrs->p_vaddr = 0x400000;
+  PHdrs->p_paddr = PHdrs->p_vaddr;
+  PHdrs->p_filesz = FileSize;
+  PHdrs->p_memsz = FileSize;
+  PHdrs->p_align = 0x4000;
+}
+
+template <class ELFT> void Writer<ELFT>::openFile(StringRef Path) {
+  std::error_code EC = FileOutputBuffer::create(Path, FileSize, Buffer,
+                                                FileOutputBuffer::F_executable);
+  error(EC, Twine("failed to open ") + Path);
+}
+
+// Write section contents to a mmap'ed file.
+template <class ELFT> void Writer<ELFT>::writeSections() {
+  uint8_t *Buf = Buffer->getBufferStart();
+  for (OutputSection *Sec : OutputSections) {
+    for (Chunk *C : Sec->getChunks())
+      C->writeTo(Buf);
+  }
+}
+
+namespace lld {
+namespace elf2 {
+template class Writer<ELF32LE>;
+template class Writer<ELF32BE>;
+template class Writer<ELF64LE>;
+template class Writer<ELF64BE>;
+}
+}
diff --git a/ELF/Writer.h b/ELF/Writer.h
new file mode 100644
index 0000000..e1066e1
--- /dev/null
+++ b/ELF/Writer.h
@@ -0,0 +1,74 @@
+//===- Writer.h -----------------------------------------------------------===//
+//
+//                             The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_WRITER_H
+#define LLD_ELF_WRITER_H
+
+#include "SymbolTable.h"
+#include "llvm/Support/FileOutputBuffer.h"
+
+namespace lld {
+namespace elf2 {
+// OutputSection represents a section in an output file. It's a
+// container of chunks. OutputSection and Chunk are 1:N relationship.
+// Chunks cannot belong to more than one OutputSections. The writer
+// creates multiple OutputSections and assign them unique,
+// non-overlapping file offsets and VAs.
+class OutputSection {
+public:
+  OutputSection(StringRef Name) : Name(Name), Header({}) {}
+  void setVA(uint64_t);
+  void setFileOffset(uint64_t);
+  void addChunk(Chunk *C);
+  std::vector<Chunk *> &getChunks() { return Chunks; }
+
+  // Returns the size of the section in the output file.
+  uint64_t getSize() { return Header.sh_size; }
+
+  // Set offset into the string table storing this section name.
+  // Used only when the name is longer than 8 bytes.
+  void setStringTableOff(uint32_t V) { StringTableOff = V; }
+
+private:
+  StringRef Name;
+  llvm::ELF::Elf64_Shdr Header;
+  uint32_t StringTableOff = 0;
+  std::vector<Chunk *> Chunks;
+};
+
+// The writer writes a SymbolTable result to a file.
+template <class ELFT> class Writer {
+public:
+  explicit Writer(SymbolTable<ELFT> *T);
+  ~Writer();
+  void write(StringRef Path);
+
+private:
+  void createSections();
+  void assignAddresses();
+  void openFile(StringRef OutputPath);
+  void writeHeader();
+  void writeSections();
+
+  SymbolTable<ELFT> *Symtab;
+  std::unique_ptr<llvm::FileOutputBuffer> Buffer;
+  llvm::SpecificBumpPtrAllocator<OutputSection> CAlloc;
+  std::vector<OutputSection *> OutputSections;
+
+  uint64_t FileSize;
+  uint64_t SizeOfImage;
+  uint64_t SizeOfHeaders;
+
+  std::vector<std::unique_ptr<Chunk>> Chunks;
+};
+
+} // namespace elf2
+} // namespace lld
+
+#endif
diff --git a/include/lld/Driver/Driver.h b/include/lld/Driver/Driver.h
index 160826c..ea2d8b4 100644
--- a/include/lld/Driver/Driver.h
+++ b/include/lld/Driver/Driver.h
@@ -1,168 +1,172 @@
 //===- lld/Driver/Driver.h - Linker Driver Emulator -----------------------===//
 //
 //                             The LLVM Linker
 //
 // This file is distributed under the University of Illinois Open Source
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
 ///
 /// \file
 ///
 /// Interface for Drivers which convert command line arguments into
 /// LinkingContext objects, then perform the link.
 ///
 //===----------------------------------------------------------------------===//
 
 #ifndef LLD_DRIVER_DRIVER_H
 #define LLD_DRIVER_DRIVER_H
 
 #include "lld/Core/LLVM.h"
 #include "lld/Core/Node.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/Support/raw_ostream.h"
 #include <memory>
 #include <set>
 #include <vector>
 
 namespace lld {
 class LinkingContext;
 class CoreLinkingContext;
 class MachOLinkingContext;
 class PECOFFLinkingContext;
 class ELFLinkingContext;
 
 typedef std::vector<std::unique_ptr<File>> FileVector;
 
 FileVector makeErrorFile(StringRef path, std::error_code ec);
 FileVector parseMemberFiles(std::unique_ptr<File> File);
 FileVector loadFile(LinkingContext &ctx, StringRef path, bool wholeArchive);
 
 /// Base class for all Drivers.
 class Driver {
 protected:
 
   /// Performs link using specified options
   static bool link(LinkingContext &context,
                    raw_ostream &diag = llvm::errs());
 
 private:
   Driver() = delete;
 };
 
 /// Driver for "universal" lld tool which can mimic any linker command line
 /// parsing once it figures out which command line flavor to use.
 class UniversalDriver : public Driver {
 public:
   /// Determine flavor and pass control to Driver for that flavor.
   static bool link(llvm::MutableArrayRef<const char *> args,
                    raw_ostream &diag = llvm::errs());
 
 private:
   UniversalDriver() = delete;
 };
 
 /// Driver for gnu/binutil 'ld' command line options.
 class GnuLdDriver : public Driver {
 public:
   /// Parses command line arguments same as gnu/binutils ld and performs link.
   /// Returns true iff an error occurred.
   static bool linkELF(llvm::ArrayRef<const char *> args,
                       raw_ostream &diag = llvm::errs());
 
   /// Uses gnu/binutils style ld command line options to fill in options struct.
   /// Returns true iff there was an error.
   static bool parse(llvm::ArrayRef<const char *> args,
                     std::unique_ptr<ELFLinkingContext> &context,
                     raw_ostream &diag = llvm::errs());
 
   /// Parses a given memory buffer as a linker script and evaluate that.
   /// Public function for testing.
   static std::error_code evalLinkerScript(ELFLinkingContext &ctx,
                                           std::unique_ptr<MemoryBuffer> mb,
                                           raw_ostream &diag, bool nostdlib);
 
   /// A factory method to create an instance of ELFLinkingContext.
   static std::unique_ptr<ELFLinkingContext>
   createELFLinkingContext(llvm::Triple triple);
 
 private:
   static llvm::Triple getDefaultTarget(const char *progName);
   static bool applyEmulation(llvm::Triple &triple,
                              llvm::opt::InputArgList &args,
                              raw_ostream &diag);
   static void addPlatformSearchDirs(ELFLinkingContext &ctx,
                                     llvm::Triple &triple,
                                     llvm::Triple &baseTriple);
 
   GnuLdDriver() = delete;
 };
 
 /// Driver for darwin/ld64 'ld' command line options.
 class DarwinLdDriver : public Driver {
 public:
   /// Parses command line arguments same as darwin's ld and performs link.
   /// Returns true iff there was an error.
   static bool linkMachO(llvm::ArrayRef<const char *> args,
                         raw_ostream &diag = llvm::errs());
 
   /// Uses darwin style ld command line options to update LinkingContext object.
   /// Returns true iff there was an error.
   static bool parse(llvm::ArrayRef<const char *> args,
                     MachOLinkingContext &info,
                     raw_ostream &diag = llvm::errs());
 
 private:
   DarwinLdDriver() = delete;
 };
 
 /// Driver for Windows 'link.exe' command line options
 class WinLinkDriver : public Driver {
 public:
   /// Parses command line arguments same as Windows link.exe and performs link.
   /// Returns true iff there was an error.
   static bool linkPECOFF(llvm::ArrayRef<const char *> args,
                          raw_ostream &diag = llvm::errs());
 
   /// Uses Windows style link command line options to fill in options struct.
   /// Returns true iff there was an error.
   static bool parse(llvm::ArrayRef<const char *> args,
                     PECOFFLinkingContext &info,
                     raw_ostream &diag = llvm::errs(), bool isDirective = false);
 
   // Same as parse(), but restricted to the context of directives.
   static bool parseDirectives(int argc, const char **argv,
                               PECOFFLinkingContext &info,
                               raw_ostream &diag = llvm::errs()) {
     return parse(llvm::makeArrayRef(argv, argc), info, diag, true);
   }
 
 private:
   WinLinkDriver() = delete;
 };
 
 /// Driver for Windows 'link.exe' command line options
 namespace coff {
 bool link(llvm::ArrayRef<const char *> args);
 }
 
+namespace elf2 {
+void link(llvm::ArrayRef<const char *> args);
+}
+
 /// Driver for lld unit tests
 class CoreDriver : public Driver {
 public:
   /// Parses command line arguments same as lld-core and performs link.
   /// Returns true iff there was an error.
   static bool link(llvm::ArrayRef<const char *> args,
                    raw_ostream &diag = llvm::errs());
 
   /// Uses lld-core command line options to fill in options struct.
   /// Returns true iff there was an error.
   static bool parse(llvm::ArrayRef<const char *> args, CoreLinkingContext &info,
                     raw_ostream &diag = llvm::errs());
 
 private:
   CoreDriver() = delete;
 };
 
 } // end namespace lld
 
 #endif
diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt
index cfb3866..fa2fb20 100644
--- a/lib/Driver/CMakeLists.txt
+++ b/lib/Driver/CMakeLists.txt
@@ -1,43 +1,44 @@
 set(LLVM_TARGET_DEFINITIONS UniversalDriverOptions.td)
 tablegen(LLVM UniversalDriverOptions.inc -gen-opt-parser-defs)
 set(LLVM_TARGET_DEFINITIONS GnuLdOptions.td)
 tablegen(LLVM GnuLdOptions.inc -gen-opt-parser-defs)
 set(LLVM_TARGET_DEFINITIONS CoreOptions.td)
 tablegen(LLVM CoreOptions.inc -gen-opt-parser-defs)
 set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td)
 tablegen(LLVM DarwinLdOptions.inc -gen-opt-parser-defs)
 set(LLVM_TARGET_DEFINITIONS WinLinkOptions.td)
 tablegen(LLVM WinLinkOptions.inc -gen-opt-parser-defs)
 add_public_tablegen_target(DriverOptionsTableGen)
 
 add_llvm_library(lldDriver
   CoreDriver.cpp
   DarwinLdDriver.cpp
   Driver.cpp
   GnuLdDriver.cpp
   UniversalDriver.cpp
   WinLinkDriver.cpp
   WinLinkModuleDef.cpp
   LINK_LIBS
     lldConfig
     lldMachO
     lldCOFF
     lldPECOFF
     lldELF
+    lldELF2
     lldAArch64ELFTarget
     lldARMELFTarget
     lldHexagonELFTarget
     lldMipsELFTarget
     lldX86ELFTarget
     lldExampleSubTarget
     lldX86_64ELFTarget
     lldCore
     lldReaderWriter
     lldYAML
     LLVMObject
     LLVMOption
     LLVMSupport
   )
 
 add_dependencies(lldDriver DriverOptionsTableGen)
 
diff --git a/lib/Driver/UniversalDriver.cpp b/lib/Driver/UniversalDriver.cpp
index c09addf..d74b0f3 100644
--- a/lib/Driver/UniversalDriver.cpp
+++ b/lib/Driver/UniversalDriver.cpp
@@ -1,221 +1,226 @@
 //===- lib/Driver/UniversalDriver.cpp -------------------------------------===//
 //
 //                             The LLVM Linker
 //
 // This file is distributed under the University of Illinois Open Source
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
 ///
 /// \file
 ///
 /// Driver for "universal" lld tool which can mimic any linker command line
 /// parsing once it figures out which command line flavor to use.
 ///
 //===----------------------------------------------------------------------===//
 
 #include "lld/Driver/Driver.h"
 #include "lld/Config/Version.h"
 #include "lld/Core/LLVM.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/Option.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Host.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace lld;
 
 namespace {
 
 // Create enum with OPT_xxx values for each option in GnuLdOptions.td
 enum {
   OPT_INVALID = 0,
 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
                HELP, META)                                                     \
   OPT_##ID,
 #include "UniversalDriverOptions.inc"
 #undef OPTION
 };
 
 // Create prefix string literals used in GnuLdOptions.td
 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
 #include "UniversalDriverOptions.inc"
 #undef PREFIX
 
 // Create table mapping all options defined in GnuLdOptions.td
 static const llvm::opt::OptTable::Info infoTable[] = {
 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
                HELPTEXT, METAVAR)                                              \
   {                                                                            \
     PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
         PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS                      \
   }                                                                            \
   ,
 #include "UniversalDriverOptions.inc"
 #undef OPTION
 };
 
 // Create OptTable class for parsing actual command line arguments
 class UniversalDriverOptTable : public llvm::opt::OptTable {
 public:
   UniversalDriverOptTable()
       : OptTable(infoTable, llvm::array_lengthof(infoTable)) {}
 };
 
 enum class Flavor {
   invalid,
   gnu_ld,    // -flavor gnu
+  gnu_ld2,   // -flavor gnu2
   win_link,  // -flavor link
   win_link2, // -flavor link2
   darwin_ld, // -flavor darwin
   core       // -flavor core OR -core
 };
 
 struct ProgramNameParts {
   StringRef _target;
   StringRef _flavor;
 };
 
 } // anonymous namespace
 
 static Flavor strToFlavor(StringRef str) {
   return llvm::StringSwitch<Flavor>(str)
       .Case("gnu", Flavor::gnu_ld)
+      .Case("gnu2", Flavor::gnu_ld2)
       .Case("link", Flavor::win_link)
       .Case("lld-link", Flavor::win_link)
       .Case("link2", Flavor::win_link2)
       .Case("lld-link2", Flavor::win_link2)
       .Case("darwin", Flavor::darwin_ld)
       .Case("core", Flavor::core)
       .Case("ld", Flavor::gnu_ld)
       .Default(Flavor::invalid);
 }
 
 static ProgramNameParts parseProgramName(StringRef programName) {
   SmallVector<StringRef, 3> components;
   llvm::SplitString(programName, components, "-");
   ProgramNameParts ret;
 
   using std::begin;
   using std::end;
 
   // Erase any lld components.
   components.erase(std::remove(components.begin(), components.end(), "lld"),
                    components.end());
 
   // Find the flavor component.
   auto flIter = std::find_if(components.begin(), components.end(),
                              [](StringRef str) -> bool {
     return strToFlavor(str) != Flavor::invalid;
   });
 
   if (flIter != components.end()) {
     ret._flavor = *flIter;
     components.erase(flIter);
   }
 
   // Any remaining component must be the target.
   if (components.size() == 1)
     ret._target = components[0];
 
   return ret;
 }
 
 // Removes the argument from argv along with its value, if exists, and updates
 // argc.
 static void removeArg(llvm::opt::Arg *arg,
                       llvm::MutableArrayRef<const char *> &args) {
   unsigned int numToRemove = arg->getNumValues() + 1;
   auto sub = args.slice(arg->getIndex() + 1);
   std::rotate(sub.begin(), sub.begin() + numToRemove, sub.end());
   args = args.drop_back(numToRemove);
 }
 
 static Flavor getFlavor(llvm::MutableArrayRef<const char *> &args,
                         const llvm::opt::InputArgList &parsedArgs) {
   if (llvm::opt::Arg *argCore = parsedArgs.getLastArg(OPT_core)) {
     removeArg(argCore, args);
     return Flavor::core;
   }
   if (llvm::opt::Arg *argFlavor = parsedArgs.getLastArg(OPT_flavor)) {
     removeArg(argFlavor, args);
     return strToFlavor(argFlavor->getValue());
   }
 
 #if LLVM_ON_UNIX
   if (llvm::sys::path::filename(args[0]).equals("ld")) {
 #if __APPLE__
     // On a Darwin systems, if linker binary is named "ld", use Darwin driver.
     return Flavor::darwin_ld;
 #endif
     // On a ELF based systems, if linker binary is named "ld", use gnu driver.
     return Flavor::gnu_ld;
   }
 #endif
 
   StringRef name = llvm::sys::path::stem(args[0]);
   return strToFlavor(parseProgramName(name)._flavor);
 }
 
 namespace lld {
 
 bool UniversalDriver::link(llvm::MutableArrayRef<const char *> args,
                            raw_ostream &diagnostics) {
   // Parse command line options using GnuLdOptions.td
   UniversalDriverOptTable table;
   unsigned missingIndex;
   unsigned missingCount;
 
   // Program name
   StringRef programName = llvm::sys::path::stem(args[0]);
 
   llvm::opt::InputArgList parsedArgs =
       table.ParseArgs(args.slice(1), missingIndex, missingCount);
 
   if (missingCount) {
     diagnostics << "error: missing arg value for '"
                 << parsedArgs.getArgString(missingIndex) << "' expected "
                 << missingCount << " argument(s).\n";
     return false;
   }
 
   // Handle -help
   if (parsedArgs.getLastArg(OPT_help)) {
     table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false);
     return true;
   }
 
   // Handle -version
   if (parsedArgs.getLastArg(OPT_version)) {
     diagnostics << "LLVM Linker Version: " << getLLDVersion()
                 << getLLDRepositoryVersion() << "\n";
     return true;
   }
 
   Flavor flavor = getFlavor(args, parsedArgs);
 
   // Switch to appropriate driver.
   switch (flavor) {
   case Flavor::gnu_ld:
     return GnuLdDriver::linkELF(args, diagnostics);
+  case Flavor::gnu_ld2:
+    elf2::link(args);
+    return true;
   case Flavor::darwin_ld:
     return DarwinLdDriver::linkMachO(args, diagnostics);
   case Flavor::win_link:
     return WinLinkDriver::linkPECOFF(args, diagnostics);
   case Flavor::win_link2:
     return coff::link(args);
   case Flavor::core:
     return CoreDriver::link(args, diagnostics);
   case Flavor::invalid:
     diagnostics << "Select the appropriate flavor\n";
     table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false);
     return false;
   }
   llvm_unreachable("Unrecognised flavor");
 }
 
 } // end namespace lld
diff --git a/test/elf2/basic.test b/test/elf2/basic.test
new file mode 100644
index 0000000..126b971
--- /dev/null
+++ b/test/elf2/basic.test
@@ -0,0 +1,78 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: lld -flavor gnu2 %t -o %t2
+# RUN: llvm-readobj -file-headers -program-headers %t2 | FileCheck %s
+# REQUIRES: x86
+
+# exits with return code 42 on linux
+.globl _start;
+_start:
+  mov $60, %rax
+  mov $42, %rdi
+  syscall
+
+# CHECK:      ElfHeader {
+# CHECK-NEXT:   Ident {
+# CHECK-NEXT:     Magic: (7F 45 4C 46)
+# CHECK-NEXT:     Class: 64-bit (0x2)
+# CHECK-NEXT:     DataEncoding: LittleEndian (0x1)
+# CHECK-NEXT:     FileVersion: 1
+# CHECK-NEXT:     OS/ABI: GNU/Linux (0x3)
+# CHECK-NEXT:     ABIVersion: 0
+# CHECK-NEXT:     Unused: (00 00 00 00 00 00 00)
+# CHECK-NEXT:   }
+# CHECK-NEXT:   Type: Executable (0x2)
+# CHECK-NEXT:   Machine: EM_X86_64 (0x3E)
+# CHECK-NEXT:   Version: 1
+# CHECK-NEXT:   Entry: 0x401000
+# CHECK-NEXT:   ProgramHeaderOffset: 0x40
+# CHECK-NEXT:   SectionHeaderOffset: 0x0
+# CHECK-NEXT:   Flags [ (0x0)
+# CHECK-NEXT:   ]
+# CHECK-NEXT:   HeaderSize: 64
+# CHECK-NEXT:   ProgramHeaderEntrySize: 56
+# CHECK-NEXT:   ProgramHeaderCount: 1
+# CHECK-NEXT:   SectionHeaderEntrySize: 64
+# CHECK-NEXT:   SectionHeaderCount: 0
+# CHECK-NEXT:    StringTableSectionIndex: 0
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeaders [
+# CHECK-NEXT:   ProgramHeader {
+# CHECK-NEXT:     Type: PT_LOAD (0x1)
+# CHECK-NEXT:     Offset: 0x0
+# CHECK-NEXT:     VirtualAddress: 0x400000
+# CHECK-NEXT:     PhysicalAddress: 0x400000
+# CHECK-NEXT:     FileSize: 4112
+# CHECK-NEXT:     MemSize: 4112
+# CHECK-NEXT:     Flags [ (0x5)
+# CHECK-NEXT:       PF_R (0x4)
+# CHECK-NEXT:       PF_X (0x1)
+# CHECK-NEXT:     ]
+# CHECK-NEXT:     Alignment: 16384
+# CHECK-NEXT:   }
+# CHECK-NEXT: ]
+
+# RUN: not lld -flavor gnu2 %t 2>&1 | FileCheck --check-prefix=NO_O %s
+# NO_O: -o must be specified.
+
+# RUN: not lld -flavor gnu2 %t.foo -o %t2 2>&1 | \
+# RUN:  FileCheck --check-prefix=MISSING %s
+# MISSING: cannot open {{.*}}.foo: No such file or directory
+
+# RUN: not lld -flavor gnu2 -o %t2 2>&1 | \
+# RUN:  FileCheck --check-prefix=NO_INPUT %s
+# NO_INPUT: no input files.
+
+# RUN: mkdir -p %t.dir
+# RUN: not lld -flavor gnu2 %t.dir -o %t2 2>&1 | \
+# RUN:  FileCheck --check-prefix=CANNOT_OPEN %s
+# CANNOT_OPEN: cannot open {{.*}}.dir: Is a directory
+
+# RUN: not lld -flavor gnu2 %t -o 2>&1 | FileCheck --check-prefix=NO_O_VAL %s
+# NO_O_VAL: missing arg value for "-o", expected 1 argument.
+
+# RUN: not lld -flavor gnu2 --foo 2>&1 | FileCheck --check-prefix=UNKNOWN %s
+# UNKNOWN: unknown argument: --foo
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: not lld -flavor gnu2 %t %t -o %t2 2>&1 | FileCheck --check-prefix=DUP %s
+# DUP: duplicate symbol: _start
diff --git a/test/elf2/undef.test b/test/elf2/undef.test
new file mode 100644
index 0000000..2196c1d
--- /dev/null
+++ b/test/elf2/undef.test
@@ -0,0 +1,8 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: not lld -flavor gnu2 %t -o %t2 2>&1 | FileCheck %s
+# CHECK: undefined symbol: foo
+# REQUIRES: x86
+
+  .globl _start;
+_start:
+  call foo


More information about the llvm-commits mailing list