Skip to content

Commit

Permalink
[ELF2] Add a new ELF linker based on the new PE/COFF linker.
Browse files Browse the repository at this point in the history
Differential Revision: http://reviews.llvm.org/D11188

llvm-svn: 243161
  • Loading branch information
Bigcheese committed Jul 24, 2015
1 parent b9e045a commit 84487f1
Show file tree
Hide file tree
Showing 23 changed files with 1,255 additions and 0 deletions.
1 change: 1 addition & 0 deletions lld/CMakeLists.txt
Expand Up @@ -97,3 +97,4 @@ endif()

add_subdirectory(docs)
add_subdirectory(COFF)
add_subdirectory(ELF)
20 changes: 20 additions & 0 deletions lld/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)
48 changes: 48 additions & 0 deletions lld/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>;
}
}
93 changes: 93 additions & 0 deletions lld/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
27 changes: 27 additions & 0 deletions lld/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
104 changes: 104 additions & 0 deletions lld/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);
}
67 changes: 67 additions & 0 deletions lld/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

0 comments on commit 84487f1

Please sign in to comment.