Skip to content

Commit

Permalink
COFF: Add a new PE/COFF port.
Browse files Browse the repository at this point in the history
This is an initial patch for a section-based COFF linker.

The patch has 2300 lines of code including comments and blank lines.
Before diving into details, you want to start from reading README
because it should give you an overview of the design.

All important things are written in the README file, so I write
summary here.

- The linker is already able to self-link on Windows.

- It's significantly faster than the existing implementation.
  The existing one takes 5 seconds to link LLD on my machine,
  while the new one only takes 1.2 seconds, even though the new
  one is not multi-threaded yet. (And a proof-of-concept multi-
  threaded version was able to link it in 0.5 seconds.)

- It uses much less memory (250MB vs. 2GB virtual memory space
  to self-host).

- IMHO the new code is much simpler and easier to read than
  the existing PE/COFF port.

http://reviews.llvm.org/D10036

llvm-svn: 238458
  • Loading branch information
rui314 committed May 28, 2015
1 parent fb76b00 commit 411c636
Show file tree
Hide file tree
Showing 28 changed files with 2,720 additions and 0 deletions.
1 change: 1 addition & 0 deletions lld/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,4 @@ if (LLVM_INCLUDE_TESTS)
endif()

add_subdirectory(docs)
add_subdirectory(COFF)
14 changes: 14 additions & 0 deletions lld/COFF/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
set(LLVM_TARGET_DEFINITIONS Options.td)
tablegen(LLVM Options.inc -gen-opt-parser-defs)
add_public_tablegen_target(COFFOptionsTableGen)

add_llvm_library(lldCOFF
Chunks.cpp
Driver.cpp
InputFiles.cpp
SymbolTable.cpp
Symbols.cpp
Writer.cpp
)

add_dependencies(lldCOFF COFFOptionsTableGen)
207 changes: 207 additions & 0 deletions lld/COFF/Chunks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
//===- 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 "InputFiles.h"
#include "Writer.h"
#include "lld/Core/Error.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/COFF.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::COFF;
using llvm::RoundUpToAlignment;

namespace lld {
namespace coff {

SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H, uint32_t SI)
: File(F), Header(H), SectionIndex(SI) {
// Initialize SectionName.
File->getCOFFObj()->getSectionName(Header, SectionName);
// Bit [20:24] contains section alignment.
unsigned Shift = ((Header->Characteristics & 0xF00000) >> 20) - 1;
Align = uint32_t(1) << Shift;
}

const uint8_t *SectionChunk::getData() const {
assert(hasData());
ArrayRef<uint8_t> Data;
File->getCOFFObj()->getSectionContents(Header, Data);
return Data.data();
}

// Returns true if this chunk should be considered as a GC root.
bool SectionChunk::isRoot() {
// COMDAT sections are live only when they are referenced by something else.
if (isCOMDAT())
return false;

// Associative sections are live if their parent COMDATs are live,
// and vice versa, so they are not considered live by themselves.
if (IsAssocChild)
return false;

// Only code is subject of dead-stripping.
return !(Header->Characteristics & IMAGE_SCN_CNT_CODE);
}

void SectionChunk::markLive() {
if (Live)
return;
Live = true;

// Mark all symbols listed in the relocation table for this section.
for (const auto &I : getSectionRef().relocations()) {
const coff_relocation *Rel = File->getCOFFObj()->getCOFFRelocation(I);
SymbolBody *B = File->getSymbolBody(Rel->SymbolTableIndex);
if (auto *Def = dyn_cast<Defined>(B))
Def->markLive();
}

// Mark associative sections if any.
for (Chunk *C : AssocChildren)
C->markLive();
}

void SectionChunk::addAssociative(SectionChunk *Child) {
Child->IsAssocChild = true;
AssocChildren.push_back(Child);
}

void SectionChunk::applyRelocations(uint8_t *Buf) {
for (const auto &I : getSectionRef().relocations()) {
const coff_relocation *Rel = File->getCOFFObj()->getCOFFRelocation(I);
applyReloc(Buf, Rel);
}
}

static void add16(uint8_t *P, int32_t V) { write16le(P, read16le(P) + V); }
static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }
static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); }

// Implements x64 PE/COFF relocations.
void SectionChunk::applyReloc(uint8_t *Buf, const coff_relocation *Rel) {
using namespace llvm::COFF;
uint8_t *Off = Buf + FileOff + Rel->VirtualAddress;
SymbolBody *Body = File->getSymbolBody(Rel->SymbolTableIndex);
uint64_t S = cast<Defined>(Body)->getRVA();
uint64_t P = RVA + Rel->VirtualAddress;
switch (Rel->Type) {
case IMAGE_REL_AMD64_ADDR32: add32(Off, S + Config->ImageBase); break;
case IMAGE_REL_AMD64_ADDR64: add64(Off, S + Config->ImageBase); break;
case IMAGE_REL_AMD64_ADDR32NB: add32(Off, S); break;
case IMAGE_REL_AMD64_REL32: add32(Off, S - P - 4); break;
case IMAGE_REL_AMD64_REL32_1: add32(Off, S - P - 5); break;
case IMAGE_REL_AMD64_REL32_2: add32(Off, S - P - 6); break;
case IMAGE_REL_AMD64_REL32_3: add32(Off, S - P - 7); break;
case IMAGE_REL_AMD64_REL32_4: add32(Off, S - P - 8); break;
case IMAGE_REL_AMD64_REL32_5: add32(Off, S - P - 9); break;
case IMAGE_REL_AMD64_SECTION: add16(Off, Out->getSectionIndex()); break;
case IMAGE_REL_AMD64_SECREL: add32(Off, S - Out->getRVA()); break;
default:
llvm::report_fatal_error("Unsupported relocation type");
}
}

bool SectionChunk::hasData() const {
return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
}

uint32_t SectionChunk::getPermissions() const {
return Header->Characteristics & PermMask;
}

bool SectionChunk::isCOMDAT() const {
return Header->Characteristics & IMAGE_SCN_LNK_COMDAT;
}

// Prints "Discarded <symbol>" for all external function symbols.
void SectionChunk::printDiscardedMessage() {
uint32_t E = File->getCOFFObj()->getNumberOfSymbols();
for (uint32_t I = 0; I < E; ++I) {
auto SrefOrErr = File->getCOFFObj()->getSymbol(I);
COFFSymbolRef Sym = SrefOrErr.get();
if (uint32_t(Sym.getSectionNumber()) != SectionIndex)
continue;
if (!Sym.isFunctionDefinition())
continue;
StringRef SymbolName;
File->getCOFFObj()->getSymbolName(Sym, SymbolName);
llvm::dbgs() << "Discarded " << SymbolName << " from "
<< File->getShortName() << "\n";
I += Sym.getNumberOfAuxSymbols();
}
}

SectionRef SectionChunk::getSectionRef() {
DataRefImpl Ref;
Ref.p = uintptr_t(Header);
return SectionRef(Ref, File->getCOFFObj());
}

uint32_t CommonChunk::getPermissions() const {
using namespace llvm::COFF;
return IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ |
IMAGE_SCN_MEM_WRITE;
}

StringChunk::StringChunk(StringRef S) : Data(S.size() + 1) {
memcpy(Data.data(), S.data(), S.size());
Data[S.size()] = 0;
}

void ImportThunkChunk::applyRelocations(uint8_t *Buf) {
uint32_t Operand = ImpSymbol->getRVA() - RVA - getSize();
// The first two bytes are a JMP instruction. Fill its operand.
write32le(Buf + FileOff + 2, Operand);
}

HintNameChunk::HintNameChunk(StringRef Name)
: Data(RoundUpToAlignment(Name.size() + 4, 2)) {
memcpy(&Data[2], Name.data(), Name.size());
}

void LookupChunk::applyRelocations(uint8_t *Buf) {
write32le(Buf + FileOff, HintName->getRVA());
}

void DirectoryChunk::applyRelocations(uint8_t *Buf) {
auto *E = (coff_import_directory_table_entry *)(Buf + FileOff);
E->ImportLookupTableRVA = LookupTab->getRVA();
E->NameRVA = DLLName->getRVA();
E->ImportAddressTableRVA = AddressTab->getRVA();
}

ImportTable::ImportTable(StringRef N,
std::vector<DefinedImportData *> &Symbols) {
DLLName = new StringChunk(N);
DirTab = new DirectoryChunk(DLLName);
for (DefinedImportData *S : Symbols)
HintNameTables.push_back(new HintNameChunk(S->getExportName()));

for (HintNameChunk *H : HintNameTables) {
LookupTables.push_back(new LookupChunk(H));
AddressTables.push_back(new LookupChunk(H));
}

for (int I = 0, E = Symbols.size(); I < E; ++I)
Symbols[I]->setLocation(AddressTables[I]);

DirTab->LookupTab = LookupTables[0];
DirTab->AddressTab = AddressTables[0];
}

} // namespace coff
} // namespace lld
Loading

0 comments on commit 411c636

Please sign in to comment.