Vendor import of lld trunk r256633:

https://llvm.org/svn/llvm-project/lld/trunk@256633
This commit is contained in:
Dimitry Andric 2015-12-30 11:57:38 +00:00
parent fb911942f1
commit 5a5c549fe9
1664 changed files with 63388 additions and 30579 deletions

View file

@ -89,10 +89,11 @@ endif()
add_subdirectory(lib)
add_subdirectory(tools)
add_subdirectory(test)
if (LLVM_INCLUDE_TESTS)
add_subdirectory(test)
add_subdirectory(unittests)
endif()
add_subdirectory(docs)
add_subdirectory(COFF)
add_subdirectory(ELF)

19
CODE_OWNERS.TXT Normal file
View file

@ -0,0 +1,19 @@
This file is a list of the people responsible for ensuring that patches for a
particular part of LLD are reviewed, either by themself or by someone else.
They are also the gatekeepers for their part of LLD, with the final word on
what goes in or not.
The list is sorted by surname and formatted to allow easy grepping and
beautification by scripts. The fields are: name (N), email (E), web-address
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
(S). Each entry should contain at least the (N), (E) and (D) fields.
N: Rui Ueyama
E: ruiu@google.com
D: COFF, ELF backends (COFF/* ELF/*)
N: Lang Hames, Nick Kledzik
E: lhames@gmail.com, kledzik@apple.com
D: Mach-O backend

33
COFF/CMakeLists.txt Normal file
View file

@ -0,0 +1,33 @@
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
DLL.cpp
Driver.cpp
DriverUtils.cpp
Error.cpp
ICF.cpp
InputFiles.cpp
MarkLive.cpp
ModuleDef.cpp
PDB.cpp
SymbolTable.cpp
Symbols.cpp
Writer.cpp
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
Core
LTO
LibDriver
Object
MC
MCDisassembler
Target
Option
Support
)
add_dependencies(lldCOFF COFFOptionsTableGen)

340
COFF/Chunks.cpp Normal file
View file

@ -0,0 +1,340 @@
//===- 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 "Error.h"
#include "InputFiles.h"
#include "Symbols.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"
#include <algorithm>
using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::COFF;
using llvm::support::ulittle32_t;
namespace lld {
namespace coff {
SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
: Chunk(SectionKind), Repl(this), File(F), Header(H),
Relocs(File->getCOFFObj()->getRelocations(Header)),
NumRelocs(std::distance(Relocs.begin(), Relocs.end())) {
// Initialize SectionName.
File->getCOFFObj()->getSectionName(Header, SectionName);
// Bit [20:24] contains section alignment. Both 0 and 1 mean alignment 1.
unsigned Shift = (Header->Characteristics >> 20) & 0xF;
if (Shift > 0)
Align = uint32_t(1) << (Shift - 1);
// Only COMDAT sections are subject of dead-stripping.
Live = !isCOMDAT();
}
static void add16(uint8_t *P, int16_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); }
static void or16(uint8_t *P, uint16_t V) { write16le(P, read16le(P) | V); }
void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym,
uint64_t P) const {
uint64_t S = Sym->getRVA();
switch (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, Sym->getSectionIndex()); break;
case IMAGE_REL_AMD64_SECREL: add32(Off, Sym->getSecrel()); break;
default:
error("Unsupported relocation type");
}
}
void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym,
uint64_t P) const {
uint64_t S = Sym->getRVA();
switch (Type) {
case IMAGE_REL_I386_ABSOLUTE: break;
case IMAGE_REL_I386_DIR32: add32(Off, S + Config->ImageBase); break;
case IMAGE_REL_I386_DIR32NB: add32(Off, S); break;
case IMAGE_REL_I386_REL32: add32(Off, S - P - 4); break;
case IMAGE_REL_I386_SECTION: add16(Off, Sym->getSectionIndex()); break;
case IMAGE_REL_I386_SECREL: add32(Off, Sym->getSecrel()); break;
default:
error("Unsupported relocation type");
}
}
static void applyMOV(uint8_t *Off, uint16_t V) {
or16(Off, ((V & 0x800) >> 1) | ((V >> 12) & 0xf));
or16(Off + 2, ((V & 0x700) << 4) | (V & 0xff));
}
static void applyMOV32T(uint8_t *Off, uint32_t V) {
applyMOV(Off, V); // set MOVW operand
applyMOV(Off + 4, V >> 16); // set MOVT operand
}
static void applyBranch20T(uint8_t *Off, int32_t V) {
uint32_t S = V < 0 ? 1 : 0;
uint32_t J1 = (V >> 19) & 1;
uint32_t J2 = (V >> 18) & 1;
or16(Off, (S << 10) | ((V >> 12) & 0x3f));
or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
}
static void applyBranch24T(uint8_t *Off, int32_t V) {
uint32_t S = V < 0 ? 1 : 0;
uint32_t J1 = ((~V >> 23) & 1) ^ S;
uint32_t J2 = ((~V >> 22) & 1) ^ S;
or16(Off, (S << 10) | ((V >> 12) & 0x3ff));
or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
}
void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym,
uint64_t P) const {
uint64_t S = Sym->getRVA();
// Pointer to thumb code must have the LSB set.
if (Sym->isExecutable())
S |= 1;
switch (Type) {
case IMAGE_REL_ARM_ADDR32: add32(Off, S + Config->ImageBase); break;
case IMAGE_REL_ARM_ADDR32NB: add32(Off, S); break;
case IMAGE_REL_ARM_MOV32T: applyMOV32T(Off, S + Config->ImageBase); break;
case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, S - P - 4); break;
case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, S - P - 4); break;
case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break;
default:
error("Unsupported relocation type");
}
}
void SectionChunk::writeTo(uint8_t *Buf) const {
if (!hasData())
return;
// Copy section contents from source object file to output file.
ArrayRef<uint8_t> A = getContents();
memcpy(Buf + OutputSectionOff, A.data(), A.size());
// Apply relocations.
for (const coff_relocation &Rel : Relocs) {
uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex)->repl();
Defined *Sym = cast<Defined>(Body);
uint64_t P = RVA + Rel.VirtualAddress;
switch (Config->Machine) {
case AMD64:
applyRelX64(Off, Rel.Type, Sym, P);
break;
case I386:
applyRelX86(Off, Rel.Type, Sym, P);
break;
case ARMNT:
applyRelARM(Off, Rel.Type, Sym, P);
break;
default:
llvm_unreachable("unknown machine type");
}
}
}
void SectionChunk::addAssociative(SectionChunk *Child) {
AssocChildren.push_back(Child);
}
static uint8_t getBaserelType(const coff_relocation &Rel) {
switch (Config->Machine) {
case AMD64:
if (Rel.Type == IMAGE_REL_AMD64_ADDR64)
return IMAGE_REL_BASED_DIR64;
return IMAGE_REL_BASED_ABSOLUTE;
case I386:
if (Rel.Type == IMAGE_REL_I386_DIR32)
return IMAGE_REL_BASED_HIGHLOW;
return IMAGE_REL_BASED_ABSOLUTE;
case ARMNT:
if (Rel.Type == IMAGE_REL_ARM_ADDR32)
return IMAGE_REL_BASED_HIGHLOW;
if (Rel.Type == IMAGE_REL_ARM_MOV32T)
return IMAGE_REL_BASED_ARM_MOV32T;
return IMAGE_REL_BASED_ABSOLUTE;
default:
llvm_unreachable("unknown machine type");
}
}
// Windows-specific.
// Collect all locations that contain absolute addresses, which need to be
// fixed by the loader if load-time relocation is needed.
// Only called when base relocation is enabled.
void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
for (const coff_relocation &Rel : Relocs) {
uint8_t Ty = getBaserelType(Rel);
if (Ty == IMAGE_REL_BASED_ABSOLUTE)
continue;
SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex)->repl();
if (isa<DefinedAbsolute>(Body))
continue;
Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
}
}
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;
}
void SectionChunk::printDiscardedMessage() const {
// Removed by dead-stripping. If it's removed by ICF, ICF already
// printed out the name, so don't repeat that here.
if (Sym && this == Repl)
llvm::outs() << "Discarded " << Sym->getName() << "\n";
}
StringRef SectionChunk::getDebugName() {
if (Sym)
return Sym->getName();
return "";
}
ArrayRef<uint8_t> SectionChunk::getContents() const {
ArrayRef<uint8_t> A;
File->getCOFFObj()->getSectionContents(Header, A);
return A;
}
void SectionChunk::replace(SectionChunk *Other) {
Other->Repl = Repl;
Other->Live = false;
}
CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
// Common symbols are aligned on natural boundaries up to 32 bytes.
// This is what MSVC link.exe does.
Align = std::min(uint64_t(32), NextPowerOf2(Sym.getValue()));
}
uint32_t CommonChunk::getPermissions() const {
return IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ |
IMAGE_SCN_MEM_WRITE;
}
void StringChunk::writeTo(uint8_t *Buf) const {
memcpy(Buf + OutputSectionOff, Str.data(), Str.size());
}
ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) {
// Intel Optimization Manual says that all branch targets
// should be 16-byte aligned. MSVC linker does this too.
Align = 16;
}
void ImportThunkChunkX64::writeTo(uint8_t *Buf) const {
memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86));
// The first two bytes is a JMP instruction. Fill its operand.
write32le(Buf + OutputSectionOff + 2, ImpSymbol->getRVA() - RVA - getSize());
}
void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *Res) {
Res->emplace_back(getRVA() + 2);
}
void ImportThunkChunkX86::writeTo(uint8_t *Buf) const {
memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86));
// The first two bytes is a JMP instruction. Fill its operand.
write32le(Buf + OutputSectionOff + 2,
ImpSymbol->getRVA() + Config->ImageBase);
}
void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *Res) {
Res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T);
}
void ImportThunkChunkARM::writeTo(uint8_t *Buf) const {
memcpy(Buf + OutputSectionOff, ImportThunkARM, sizeof(ImportThunkARM));
// Fix mov.w and mov.t operands.
applyMOV32T(Buf + OutputSectionOff, ImpSymbol->getRVA() + Config->ImageBase);
}
void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
Res->emplace_back(getRVA());
}
size_t LocalImportChunk::getSize() const {
return Config->is64() ? 8 : 4;
}
void LocalImportChunk::writeTo(uint8_t *Buf) const {
if (Config->is64()) {
write64le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase);
} else {
write32le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase);
}
}
void SEHTableChunk::writeTo(uint8_t *Buf) const {
ulittle32_t *Begin = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff);
size_t Cnt = 0;
for (Defined *D : Syms)
Begin[Cnt++] = D->getRVA();
std::sort(Begin, Begin + Cnt);
}
// Windows-specific.
// This class represents a block in .reloc section.
BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) {
// Block header consists of 4 byte page RVA and 4 byte block size.
// Each entry is 2 byte. Last entry may be padding.
Data.resize(RoundUpToAlignment((End - Begin) * 2 + 8, 4));
uint8_t *P = Data.data();
write32le(P, Page);
write32le(P + 4, Data.size());
P += 8;
for (Baserel *I = Begin; I != End; ++I) {
write16le(P, (I->Type << 12) | (I->RVA - Page));
P += 2;
}
}
void BaserelChunk::writeTo(uint8_t *Buf) const {
memcpy(Buf + OutputSectionOff, Data.data(), Data.size());
}
uint8_t Baserel::getDefaultType() {
switch (Config->Machine) {
case AMD64:
return IMAGE_REL_BASED_DIR64;
case I386:
return IMAGE_REL_BASED_HIGHLOW;
default:
llvm_unreachable("unknown machine type");
}
}
} // namespace coff
} // namespace lld

332
COFF/Chunks.h Normal file
View file

@ -0,0 +1,332 @@
//===- Chunks.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_CHUNKS_H
#define LLD_COFF_CHUNKS_H
#include "Config.h"
#include "InputFiles.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Object/COFF.h"
#include <atomic>
#include <vector>
namespace lld {
namespace coff {
using llvm::COFF::ImportDirectoryTableEntry;
using llvm::object::COFFSymbolRef;
using llvm::object::SectionRef;
using llvm::object::coff_relocation;
using llvm::object::coff_section;
using llvm::sys::fs::file_magic;
class Baserel;
class Defined;
class DefinedImportData;
class DefinedRegular;
class ObjectFile;
class OutputSection;
class SymbolBody;
// Mask for section types (code, data, bss, disacardable, etc.)
// and permissions (writable, readable or executable).
const uint32_t PermMask = 0xFF0000F0;
// 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:
enum Kind { SectionKind, OtherKind };
Kind kind() const { return ChunkKind; }
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 RVA values
// of other chunks for relocations, you need to set them properly
// before calling this function.
virtual void writeTo(uint8_t *Buf) const {}
// The writer sets and uses the addresses.
uint64_t getRVA() const { return RVA; }
uint32_t getAlign() const { return Align; }
void setRVA(uint64_t V) { RVA = V; }
void setOutputSectionOff(uint64_t V) { OutputSectionOff = V; }
// Returns true if this has non-zero data. BSS chunks return
// false. If false is returned, the space occupied by this chunk
// will be filled with zeros.
virtual bool hasData() const { return true; }
// Returns readable/writable/executable bits.
virtual uint32_t getPermissions() const { return 0; }
// 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 {
llvm_unreachable("unimplemented getSectionName");
}
// 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; }
// Windows-specific.
// Collect all locations that contain absolute addresses for base relocations.
virtual void getBaserels(std::vector<Baserel> *Res) {}
// Returns a human-readable name of this chunk. Chunks are unnamed chunks of
// bytes, so this is used only for logging or debugging.
virtual StringRef getDebugName() { return ""; }
protected:
Chunk(Kind K = OtherKind) : ChunkKind(K) {}
const Kind ChunkKind;
// The RVA of this chunk in the output. The writer sets a value.
uint64_t RVA = 0;
// The offset from beginning of the output section. The writer sets a value.
uint64_t OutputSectionOff = 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.
class SectionChunk : public Chunk {
// Identical COMDAT Folding feature accesses section internal data.
friend class ICF;
public:
class symbol_iterator : public llvm::iterator_adaptor_base<
symbol_iterator, const coff_relocation *,
std::random_access_iterator_tag, SymbolBody *> {
friend SectionChunk;
ObjectFile *File;
symbol_iterator(ObjectFile *File, const coff_relocation *I)
: symbol_iterator::iterator_adaptor_base(I), File(File) {}
public:
symbol_iterator() = default;
SymbolBody *operator*() const {
return File->getSymbolBody(I->SymbolTableIndex);
}
};
SectionChunk(ObjectFile *File, const coff_section *Header);
static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
size_t getSize() const override { return Header->SizeOfRawData; }
void writeTo(uint8_t *Buf) const override;
bool hasData() const override;
uint32_t getPermissions() const override;
StringRef getSectionName() const override { return SectionName; }
void getBaserels(std::vector<Baserel> *Res) override;
bool isCOMDAT() const;
void applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const;
void applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const;
void applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const;
// Called if the garbage collector decides to not include this chunk
// in a final output. It's supposed to print out a log message to stdout.
void printDiscardedMessage() const;
// Adds COMDAT associative sections to this COMDAT section. A chunk
// and its children are treated as a group by the garbage collector.
void addAssociative(SectionChunk *Child);
StringRef getDebugName() override;
void setSymbol(DefinedRegular *S) { if (!Sym) Sym = S; }
// Used by the garbage collector.
bool isLive() { return !Config->DoGC || Live; }
void markLive() {
assert(!isLive() && "Cannot mark an already live section!");
Live = true;
}
// Allow iteration over the bodies of this chunk's relocated symbols.
llvm::iterator_range<symbol_iterator> symbols() const {
return llvm::make_range(symbol_iterator(File, Relocs.begin()),
symbol_iterator(File, Relocs.end()));
}
// Allow iteration over the associated child chunks for this section.
ArrayRef<SectionChunk *> children() const { return AssocChildren; }
// A pointer pointing to a replacement for this chunk.
// Initially it points to "this" object. If this chunk is merged
// with other chunk by ICF, it points to another chunk,
// and this chunk is considrered as dead.
SectionChunk *Repl;
// The CRC of the contents as described in the COFF spec 4.5.5.
// Auxiliary Format 5: Section Definitions. Used for ICF.
uint32_t Checksum = 0;
private:
ArrayRef<uint8_t> getContents() const;
// A file this chunk was created from.
ObjectFile *File;
const coff_section *Header;
StringRef SectionName;
std::vector<SectionChunk *> AssocChildren;
llvm::iterator_range<const coff_relocation *> Relocs;
size_t NumRelocs;
// Used by the garbage collector.
bool Live;
// Used for ICF (Identical COMDAT Folding)
void replace(SectionChunk *Other);
std::atomic<uint64_t> GroupID = { 0 };
// Sym points to a section symbol if this is a COMDAT chunk.
DefinedRegular *Sym = nullptr;
};
// A chunk for common symbols. Common chunks don't have actual data.
class CommonChunk : public Chunk {
public:
CommonChunk(const COFFSymbolRef Sym);
size_t getSize() const override { return Sym.getValue(); }
bool hasData() const override { return false; }
uint32_t getPermissions() const override;
StringRef getSectionName() const override { return ".bss"; }
private:
const COFFSymbolRef Sym;
};
// A chunk for linker-created strings.
class StringChunk : public Chunk {
public:
explicit StringChunk(StringRef S) : Str(S) {}
size_t getSize() const override { return Str.size() + 1; }
void writeTo(uint8_t *Buf) const override;
private:
StringRef Str;
};
static const uint8_t ImportThunkX86[] = {
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0
};
static const uint8_t ImportThunkARM[] = {
0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0
0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0
0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip]
};
// Windows-specific.
// A chunk for DLL import jump table entry. In a final output, it's
// contents will be a JMP instruction to some __imp_ symbol.
class ImportThunkChunkX64 : public Chunk {
public:
explicit ImportThunkChunkX64(Defined *S);
size_t getSize() const override { return sizeof(ImportThunkX86); }
void writeTo(uint8_t *Buf) const override;
private:
Defined *ImpSymbol;
};
class ImportThunkChunkX86 : public Chunk {
public:
explicit ImportThunkChunkX86(Defined *S) : ImpSymbol(S) {}
size_t getSize() const override { return sizeof(ImportThunkX86); }
void getBaserels(std::vector<Baserel> *Res) override;
void writeTo(uint8_t *Buf) const override;
private:
Defined *ImpSymbol;
};
class ImportThunkChunkARM : public Chunk {
public:
explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {}
size_t getSize() const override { return sizeof(ImportThunkARM); }
void getBaserels(std::vector<Baserel> *Res) override;
void writeTo(uint8_t *Buf) const override;
private:
Defined *ImpSymbol;
};
// Windows-specific.
// See comments for DefinedLocalImport class.
class LocalImportChunk : public Chunk {
public:
explicit LocalImportChunk(Defined *S) : Sym(S) {}
size_t getSize() const override;
void getBaserels(std::vector<Baserel> *Res) override;
void writeTo(uint8_t *Buf) const override;
private:
Defined *Sym;
};
// Windows-specific.
// A chunk for SEH table which contains RVAs of safe exception handler
// functions. x86-only.
class SEHTableChunk : public Chunk {
public:
explicit SEHTableChunk(std::set<Defined *> S) : Syms(S) {}
size_t getSize() const override { return Syms.size() * 4; }
void writeTo(uint8_t *Buf) const override;
private:
std::set<Defined *> Syms;
};
// Windows-specific.
// This class represents a block in .reloc section.
// See the PE/COFF spec 5.6 for details.
class BaserelChunk : public Chunk {
public:
BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End);
size_t getSize() const override { return Data.size(); }
void writeTo(uint8_t *Buf) const override;
private:
std::vector<uint8_t> Data;
};
class Baserel {
public:
Baserel(uint32_t V, uint8_t Ty) : RVA(V), Type(Ty) {}
explicit Baserel(uint32_t V) : Baserel(V, getDefaultType()) {}
uint8_t getDefaultType();
uint32_t RVA;
uint8_t Type;
};
} // namespace coff
} // namespace lld
#endif

140
COFF/Config.h Normal file
View file

@ -0,0 +1,140 @@
//===- Config.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_CONFIG_H
#define LLD_COFF_CONFIG_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/COFF.h"
#include <cstdint>
#include <map>
#include <set>
#include <string>
namespace lld {
namespace coff {
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
using llvm::COFF::WindowsSubsystem;
using llvm::StringRef;
class DefinedAbsolute;
class DefinedRelative;
class Undefined;
// Short aliases.
static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
static const auto ARMNT = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT;
static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
// Represents an /export option.
struct Export {
StringRef Name; // N in /export:N or /export:E=N
StringRef ExtName; // E in /export:E=N
Undefined *Sym = nullptr;
uint16_t Ordinal = 0;
bool Noname = false;
bool Data = false;
bool Private = false;
// True if this /export option was in .drectves section.
bool Directives = false;
StringRef SymbolName;
StringRef ExportName; // Name in DLL
bool operator==(const Export &E) {
return (Name == E.Name && ExtName == E.ExtName &&
Ordinal == E.Ordinal && Noname == E.Noname &&
Data == E.Data && Private == E.Private);
}
};
// Global configuration.
struct Configuration {
enum ManifestKind { SideBySide, Embed, No };
bool is64() { return Machine == AMD64; }
llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
bool Verbose = false;
WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
Undefined *Entry = nullptr;
bool NoEntry = false;
std::string OutputFile;
bool DoGC = true;
bool DoICF = true;
bool Relocatable = true;
bool Force = false;
bool Debug = false;
bool WriteSymtab = true;
// Symbols in this set are considered as live by the garbage collector.
std::set<Undefined *> GCRoot;
std::set<StringRef> NoDefaultLibs;
bool NoDefaultLibAll = false;
// True if we are creating a DLL.
bool DLL = false;
StringRef Implib;
std::vector<Export> Exports;
std::set<std::string> DelayLoads;
std::map<std::string, int> DLLOrder;
Undefined *DelayLoadHelper = nullptr;
// Used for SafeSEH.
DefinedRelative *SEHTable = nullptr;
DefinedAbsolute *SEHCount = nullptr;
// Used for /opt:lldlto=N
unsigned LTOOptLevel = 2;
// Used for /opt:lldltojobs=N
unsigned LTOJobs = 1;
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
std::map<StringRef, StringRef> Merge;
// Options for manifest files.
ManifestKind Manifest = SideBySide;
int ManifestID = 1;
StringRef ManifestDependency;
bool ManifestUAC = true;
StringRef ManifestLevel = "'asInvoker'";
StringRef ManifestUIAccess = "'false'";
StringRef ManifestFile;
// Used for /failifmismatch.
std::map<StringRef, StringRef> MustMatch;
// Used for /alternatename.
std::map<StringRef, StringRef> AlternateNames;
uint64_t ImageBase = -1;
uint64_t StackReserve = 1024 * 1024;
uint64_t StackCommit = 4096;
uint64_t HeapReserve = 1024 * 1024;
uint64_t HeapCommit = 4096;
uint32_t MajorImageVersion = 0;
uint32_t MinorImageVersion = 0;
uint32_t MajorOSVersion = 6;
uint32_t MinorOSVersion = 0;
bool DynamicBase = true;
bool AllowBind = true;
bool NxCompat = true;
bool AllowIsolation = true;
bool TerminalServerAware = true;
bool LargeAddressAware = false;
bool HighEntropyVA = false;
};
extern Configuration *Config;
} // namespace coff
} // namespace lld
#endif

556
COFF/DLL.cpp Normal file
View file

@ -0,0 +1,556 @@
//===- DLL.cpp ------------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines various types of chunks for the DLL import or export
// descriptor tables. They are inherently Windows-specific.
// You need to read Microsoft PE/COFF spec to understand details
// about the data structures.
//
// If you are not particularly interested in linking against Windows
// DLL, you can skip this file, and you should still be able to
// understand the rest of the linker.
//
//===----------------------------------------------------------------------===//
#include "Chunks.h"
#include "DLL.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Path.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::COFF;
namespace lld {
namespace coff {
namespace {
// Import table
static int ptrSize() { return Config->is64() ? 8 : 4; }
// A chunk for the import descriptor table.
class HintNameChunk : public Chunk {
public:
HintNameChunk(StringRef N, uint16_t H) : Name(N), Hint(H) {}
size_t getSize() const override {
// Starts with 2 byte Hint field, followed by a null-terminated string,
// ends with 0 or 1 byte padding.
return RoundUpToAlignment(Name.size() + 3, 2);
}
void writeTo(uint8_t *Buf) const override {
write16le(Buf + OutputSectionOff, Hint);
memcpy(Buf + OutputSectionOff + 2, Name.data(), Name.size());
}
private:
StringRef Name;
uint16_t Hint;
};
// A chunk for the import descriptor table.
class LookupChunk : public Chunk {
public:
explicit LookupChunk(Chunk *C) : HintName(C) {}
size_t getSize() const override { return ptrSize(); }
void writeTo(uint8_t *Buf) const override {
write32le(Buf + OutputSectionOff, HintName->getRVA());
}
Chunk *HintName;
};
// A chunk for the import descriptor table.
// This chunk represent import-by-ordinal symbols.
// See Microsoft PE/COFF spec 7.1. Import Header for details.
class OrdinalOnlyChunk : public Chunk {
public:
explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {}
size_t getSize() const override { return ptrSize(); }
void writeTo(uint8_t *Buf) const override {
// An import-by-ordinal slot has MSB 1 to indicate that
// this is import-by-ordinal (and not import-by-name).
if (Config->is64()) {
write64le(Buf + OutputSectionOff, (1ULL << 63) | Ordinal);
} else {
write32le(Buf + OutputSectionOff, (1ULL << 31) | Ordinal);
}
}
uint16_t Ordinal;
};
// A chunk for the import descriptor table.
class ImportDirectoryChunk : public Chunk {
public:
explicit ImportDirectoryChunk(Chunk *N) : DLLName(N) {}
size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
void writeTo(uint8_t *Buf) const override {
auto *E = (coff_import_directory_table_entry *)(Buf + OutputSectionOff);
E->ImportLookupTableRVA = LookupTab->getRVA();
E->NameRVA = DLLName->getRVA();
E->ImportAddressTableRVA = AddressTab->getRVA();
}
Chunk *DLLName;
Chunk *LookupTab;
Chunk *AddressTab;
};
// A chunk representing null terminator in the import table.
// Contents of this chunk is always null bytes.
class NullChunk : public Chunk {
public:
explicit NullChunk(size_t N) : Size(N) {}
bool hasData() const override { return false; }
size_t getSize() const override { return Size; }
void setAlign(size_t N) { Align = N; }
private:
size_t Size;
};
static std::vector<std::vector<DefinedImportData *>>
binImports(const std::vector<DefinedImportData *> &Imports) {
// Group DLL-imported symbols by DLL name because that's how
// symbols are layed out in the import descriptor table.
auto Less = [](const std::string &A, const std::string &B) {
return Config->DLLOrder[A] < Config->DLLOrder[B];
};
std::map<std::string, std::vector<DefinedImportData *>,
bool(*)(const std::string &, const std::string &)> M(Less);
for (DefinedImportData *Sym : Imports)
M[Sym->getDLLName().lower()].push_back(Sym);
std::vector<std::vector<DefinedImportData *>> V;
for (auto &P : M) {
// Sort symbols by name for each group.
std::vector<DefinedImportData *> &Syms = P.second;
std::sort(Syms.begin(), Syms.end(),
[](DefinedImportData *A, DefinedImportData *B) {
return A->getName() < B->getName();
});
V.push_back(std::move(Syms));
}
return V;
}
// Export table
// See Microsoft PE/COFF spec 4.3 for details.
// A chunk for the delay import descriptor table etnry.
class DelayDirectoryChunk : public Chunk {
public:
explicit DelayDirectoryChunk(Chunk *N) : DLLName(N) {}
size_t getSize() const override {
return sizeof(delay_import_directory_table_entry);
}
void writeTo(uint8_t *Buf) const override {
auto *E = (delay_import_directory_table_entry *)(Buf + OutputSectionOff);
E->Attributes = 1;
E->Name = DLLName->getRVA();
E->ModuleHandle = ModuleHandle->getRVA();
E->DelayImportAddressTable = AddressTab->getRVA();
E->DelayImportNameTable = NameTab->getRVA();
}
Chunk *DLLName;
Chunk *ModuleHandle;
Chunk *AddressTab;
Chunk *NameTab;
};
// Initial contents for delay-loaded functions.
// This code calls __delayLoadHelper2 function to resolve a symbol
// and then overwrites its jump table slot with the result
// for subsequent function calls.
static const uint8_t ThunkX64[] = {
0x51, // push rcx
0x52, // push rdx
0x41, 0x50, // push r8
0x41, 0x51, // push r9
0x48, 0x83, 0xEC, 0x48, // sub rsp, 48h
0x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm0
0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1
0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2
0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3
0x48, 0x8D, 0x15, 0, 0, 0, 0, // lea rdx, [__imp_<FUNCNAME>]
0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...]
0xE8, 0, 0, 0, 0, // call __delayLoadHelper2
0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp]
0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h]
0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h]
0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h]
0x48, 0x83, 0xC4, 0x48, // add rsp, 48h
0x41, 0x59, // pop r9
0x41, 0x58, // pop r8
0x5A, // pop rdx
0x59, // pop rcx
0xFF, 0xE0, // jmp rax
};
static const uint8_t ThunkX86[] = {
0x51, // push ecx
0x52, // push edx
0x68, 0, 0, 0, 0, // push offset ___imp__<FUNCNAME>
0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll
0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8
0x5A, // pop edx
0x59, // pop ecx
0xFF, 0xE0, // jmp eax
};
// A chunk for the delay import thunk.
class ThunkChunkX64 : public Chunk {
public:
ThunkChunkX64(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
size_t getSize() const override { return sizeof(ThunkX64); }
void writeTo(uint8_t *Buf) const override {
memcpy(Buf + OutputSectionOff, ThunkX64, sizeof(ThunkX64));
write32le(Buf + OutputSectionOff + 36, Imp->getRVA() - RVA - 40);
write32le(Buf + OutputSectionOff + 43, Desc->getRVA() - RVA - 47);
write32le(Buf + OutputSectionOff + 48, Helper->getRVA() - RVA - 52);
}
Defined *Imp = nullptr;
Chunk *Desc = nullptr;
Defined *Helper = nullptr;
};
class ThunkChunkX86 : public Chunk {
public:
ThunkChunkX86(Defined *I, Chunk *D, Defined *H)
: Imp(I), Desc(D), Helper(H) {}
size_t getSize() const override { return sizeof(ThunkX86); }
void writeTo(uint8_t *Buf) const override {
memcpy(Buf + OutputSectionOff, ThunkX86, sizeof(ThunkX86));
write32le(Buf + OutputSectionOff + 3, Imp->getRVA() + Config->ImageBase);
write32le(Buf + OutputSectionOff + 8, Desc->getRVA() + Config->ImageBase);
write32le(Buf + OutputSectionOff + 13, Helper->getRVA() - RVA - 17);
}
void getBaserels(std::vector<Baserel> *Res) override {
Res->emplace_back(RVA + 3);
Res->emplace_back(RVA + 8);
}
Defined *Imp = nullptr;
Chunk *Desc = nullptr;
Defined *Helper = nullptr;
};
// A chunk for the import descriptor table.
class DelayAddressChunk : public Chunk {
public:
explicit DelayAddressChunk(Chunk *C) : Thunk(C) {}
size_t getSize() const override { return ptrSize(); }
void writeTo(uint8_t *Buf) const override {
if (Config->is64()) {
write64le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
} else {
write32le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
}
}
void getBaserels(std::vector<Baserel> *Res) override {
Res->emplace_back(RVA);
}
Chunk *Thunk;
};
// 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) const override {
auto *E = (export_directory_table_entry *)(Buf + OutputSectionOff);
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) const override {
for (Export &E : Config->Exports) {
auto *D = cast<Defined>(E.Sym->repl());
write32le(Buf + OutputSectionOff + E.Ordinal * 4, D->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) const override {
uint8_t *P = Buf + OutputSectionOff;
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) const override {
uint8_t *P = Buf + OutputSectionOff;
for (Export &E : Config->Exports) {
if (E.Noname)
continue;
write16le(P, E.Ordinal);
P += 2;
}
}
private:
size_t Size;
};
} // anonymous namespace
uint64_t IdataContents::getDirSize() {
return Dirs.size() * sizeof(ImportDirectoryTableEntry);
}
uint64_t IdataContents::getIATSize() {
return Addresses.size() * ptrSize();
}
// Returns a list of .idata contents.
// See Microsoft PE/COFF spec 5.4 for details.
std::vector<Chunk *> IdataContents::getChunks() {
create();
std::vector<Chunk *> V;
// The loader assumes a specific order of data.
// Add each type in the correct order.
for (std::unique_ptr<Chunk> &C : Dirs)
V.push_back(C.get());
for (std::unique_ptr<Chunk> &C : Lookups)
V.push_back(C.get());
for (std::unique_ptr<Chunk> &C : Addresses)
V.push_back(C.get());
for (std::unique_ptr<Chunk> &C : Hints)
V.push_back(C.get());
for (auto &P : DLLNames) {
std::unique_ptr<Chunk> &C = P.second;
V.push_back(C.get());
}
return V;
}
void IdataContents::create() {
std::vector<std::vector<DefinedImportData *>> V = binImports(Imports);
// Create .idata contents for each DLL.
for (std::vector<DefinedImportData *> &Syms : V) {
StringRef Name = Syms[0]->getDLLName();
// Create lookup and address tables. If they have external names,
// we need to create HintName chunks to store the names.
// If they don't (if they are import-by-ordinals), we store only
// ordinal values to the table.
size_t Base = Lookups.size();
for (DefinedImportData *S : Syms) {
uint16_t Ord = S->getOrdinal();
if (S->getExternalName().empty()) {
Lookups.push_back(make_unique<OrdinalOnlyChunk>(Ord));
Addresses.push_back(make_unique<OrdinalOnlyChunk>(Ord));
continue;
}
auto C = make_unique<HintNameChunk>(S->getExternalName(), Ord);
Lookups.push_back(make_unique<LookupChunk>(C.get()));
Addresses.push_back(make_unique<LookupChunk>(C.get()));
Hints.push_back(std::move(C));
}
// Terminate with null values.
Lookups.push_back(make_unique<NullChunk>(ptrSize()));
Addresses.push_back(make_unique<NullChunk>(ptrSize()));
for (int I = 0, E = Syms.size(); I < E; ++I)
Syms[I]->setLocation(Addresses[Base + I].get());
// Create the import table header.
if (!DLLNames.count(Name))
DLLNames[Name] = make_unique<StringChunk>(Name);
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>(sizeof(ImportDirectoryTableEntry)));
}
std::vector<Chunk *> DelayLoadContents::getChunks() {
std::vector<Chunk *> V;
for (std::unique_ptr<Chunk> &C : Dirs)
V.push_back(C.get());
for (std::unique_ptr<Chunk> &C : Names)
V.push_back(C.get());
for (std::unique_ptr<Chunk> &C : HintNames)
V.push_back(C.get());
for (auto &P : DLLNames) {
std::unique_ptr<Chunk> &C = P.second;
V.push_back(C.get());
}
return V;
}
std::vector<Chunk *> DelayLoadContents::getDataChunks() {
std::vector<Chunk *> V;
for (std::unique_ptr<Chunk> &C : ModuleHandles)
V.push_back(C.get());
for (std::unique_ptr<Chunk> &C : Addresses)
V.push_back(C.get());
return V;
}
uint64_t DelayLoadContents::getDirSize() {
return Dirs.size() * sizeof(delay_import_directory_table_entry);
}
void DelayLoadContents::create(Defined *H) {
Helper = H;
std::vector<std::vector<DefinedImportData *>> V = binImports(Imports);
// Create .didat contents for each DLL.
for (std::vector<DefinedImportData *> &Syms : V) {
StringRef Name = Syms[0]->getDLLName();
// Create the delay import table header.
if (!DLLNames.count(Name))
DLLNames[Name] = make_unique<StringChunk>(Name);
auto Dir = make_unique<DelayDirectoryChunk>(DLLNames[Name].get());
size_t Base = Addresses.size();
for (DefinedImportData *S : Syms) {
Chunk *T = newThunkChunk(S, Dir.get());
auto A = make_unique<DelayAddressChunk>(T);
Addresses.push_back(std::move(A));
Thunks.push_back(std::unique_ptr<Chunk>(T));
StringRef ExtName = S->getExternalName();
if (ExtName.empty()) {
Names.push_back(make_unique<OrdinalOnlyChunk>(S->getOrdinal()));
} else {
auto C = make_unique<HintNameChunk>(ExtName, 0);
Names.push_back(make_unique<LookupChunk>(C.get()));
HintNames.push_back(std::move(C));
}
}
// Terminate with null values.
Addresses.push_back(make_unique<NullChunk>(8));
Names.push_back(make_unique<NullChunk>(8));
for (int I = 0, E = Syms.size(); I < E; ++I)
Syms[I]->setLocation(Addresses[Base + I].get());
auto *MH = new NullChunk(8);
MH->setAlign(8);
ModuleHandles.push_back(std::unique_ptr<Chunk>(MH));
// Fill the delay import table header fields.
Dir->ModuleHandle = MH;
Dir->AddressTab = Addresses[Base].get();
Dir->NameTab = Names[Base].get();
Dirs.push_back(std::move(Dir));
}
// Add null terminator.
Dirs.push_back(
make_unique<NullChunk>(sizeof(delay_import_directory_table_entry)));
}
Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) {
switch (Config->Machine) {
case AMD64:
return new ThunkChunkX64(S, Dir, Helper);
case I386:
return new ThunkChunkX86(S, Dir, Helper);
default:
llvm_unreachable("unsupported machine type");
}
}
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.ExportName));
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
} // namespace lld

84
COFF/DLL.h Normal file
View file

@ -0,0 +1,84 @@
//===- DLL.h ----------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_DLL_H
#define LLD_COFF_DLL_H
#include "Chunks.h"
#include "Symbols.h"
namespace lld {
namespace coff {
// Windows-specific.
// 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 {
public:
void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
bool empty() { return Imports.empty(); }
std::vector<Chunk *> getChunks();
uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
uint64_t getDirSize();
uint64_t getIATRVA() { return Addresses[0]->getRVA(); }
uint64_t getIATSize();
private:
void create();
std::vector<DefinedImportData *> Imports;
std::vector<std::unique_ptr<Chunk>> Dirs;
std::vector<std::unique_ptr<Chunk>> Lookups;
std::vector<std::unique_ptr<Chunk>> Addresses;
std::vector<std::unique_ptr<Chunk>> Hints;
std::map<StringRef, std::unique_ptr<Chunk>> DLLNames;
};
// Windows-specific.
// DelayLoadContents creates all chunks for the delay-load DLL import table.
class DelayLoadContents {
public:
void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
bool empty() { return Imports.empty(); }
void create(Defined *Helper);
std::vector<Chunk *> getChunks();
std::vector<Chunk *> getDataChunks();
std::vector<std::unique_ptr<Chunk>> &getCodeChunks() { return Thunks; }
uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
uint64_t getDirSize();
private:
Chunk *newThunkChunk(DefinedImportData *S, Chunk *Dir);
Defined *Helper;
std::vector<DefinedImportData *> Imports;
std::vector<std::unique_ptr<Chunk>> Dirs;
std::vector<std::unique_ptr<Chunk>> ModuleHandles;
std::vector<std::unique_ptr<Chunk>> Addresses;
std::vector<std::unique_ptr<Chunk>> Names;
std::vector<std::unique_ptr<Chunk>> HintNames;
std::vector<std::unique_ptr<Chunk>> Thunks;
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
#endif

677
COFF/Driver.cpp Normal file
View file

@ -0,0 +1,677 @@
//===- Driver.cpp ---------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Config.h"
#include "Driver.h"
#include "Error.h"
#include "InputFiles.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
#include "llvm/ADT/Optional.h"
#include "llvm/LibDriver/LibDriver.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <memory>
using namespace llvm;
using namespace llvm::COFF;
using llvm::sys::Process;
using llvm::sys::fs::OpenFlags;
using llvm::sys::fs::file_magic;
using llvm::sys::fs::identify_magic;
namespace lld {
namespace coff {
Configuration *Config;
LinkerDriver *Driver;
void link(llvm::ArrayRef<const char *> Args) {
Configuration C;
LinkerDriver D;
Config = &C;
Driver = &D;
return Driver->link(Args);
}
// Drop directory components and replace extension with ".exe".
static std::string getOutputPath(StringRef Path) {
auto P = Path.find_last_of("\\/");
StringRef S = (P == StringRef::npos) ? Path : Path.substr(P + 1);
return (S.substr(0, S.rfind('.')) + ".exe").str();
}
// Opens a file. Path has to be resolved already.
// Newly created memory buffers are owned by this driver.
MemoryBufferRef LinkerDriver::openFile(StringRef Path) {
auto MBOrErr = MemoryBuffer::getFile(Path);
error(MBOrErr, Twine("Could not 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) {
// File type is detected by contents, not by file extension.
file_magic Magic = identify_magic(MB.getBuffer());
if (Magic == file_magic::archive)
return std::unique_ptr<InputFile>(new ArchiveFile(MB));
if (Magic == file_magic::bitcode)
return std::unique_ptr<InputFile>(new BitcodeFile(MB));
if (Config->OutputFile == "")
Config->OutputFile = getOutputPath(MB.getBufferIdentifier());
return std::unique_ptr<InputFile>(new ObjectFile(MB));
}
static bool isDecorated(StringRef Sym) {
return Sym.startswith("_") || Sym.startswith("@") || Sym.startswith("?");
}
// Parses .drectve section contents and returns a list of files
// specified by /defaultlib.
void LinkerDriver::parseDirectives(StringRef S) {
llvm::opt::InputArgList Args = Parser.parse(S);
for (auto *Arg : Args) {
switch (Arg->getOption().getID()) {
case OPT_alternatename:
parseAlternateName(Arg->getValue());
break;
case OPT_defaultlib:
if (Optional<StringRef> Path = findLib(Arg->getValue())) {
MemoryBufferRef MB = openFile(*Path);
Symtab.addFile(createFile(MB));
}
break;
case OPT_export: {
Export E = parseExport(Arg->getValue());
E.Directives = true;
Config->Exports.push_back(E);
break;
}
case OPT_failifmismatch:
checkFailIfMismatch(Arg->getValue());
break;
case OPT_incl:
addUndefined(Arg->getValue());
break;
case OPT_merge:
parseMerge(Arg->getValue());
break;
case OPT_nodefaultlib:
Config->NoDefaultLibs.insert(doFindLib(Arg->getValue()));
break;
case OPT_editandcontinue:
case OPT_guardsym:
case OPT_throwingnew:
break;
default:
error(Twine(Arg->getSpelling()) + " is not allowed in .drectve");
}
}
}
// Find file from search paths. You can omit ".obj", this function takes
// care of that. Note that the returned path is not guaranteed to exist.
StringRef LinkerDriver::doFindFile(StringRef Filename) {
bool hasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
if (hasPathSep)
return Filename;
bool hasExt = (Filename.find('.') != StringRef::npos);
for (StringRef Dir : SearchPaths) {
SmallString<128> Path = Dir;
llvm::sys::path::append(Path, Filename);
if (llvm::sys::fs::exists(Path.str()))
return Alloc.save(Path.str());
if (!hasExt) {
Path.append(".obj");
if (llvm::sys::fs::exists(Path.str()))
return Alloc.save(Path.str());
}
}
return Filename;
}
// Resolves a file path. This never returns the same path
// (in that case, it returns None).
Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
StringRef Path = doFindFile(Filename);
bool Seen = !VisitedFiles.insert(Path.lower()).second;
if (Seen)
return None;
return Path;
}
// Find library file from search path.
StringRef LinkerDriver::doFindLib(StringRef Filename) {
// Add ".lib" to Filename if that has no file extension.
bool hasExt = (Filename.find('.') != StringRef::npos);
if (!hasExt)
Filename = Alloc.save(Filename + ".lib");
return doFindFile(Filename);
}
// Resolves a library path. /nodefaultlib options are taken into
// consideration. This never returns the same path (in that case,
// it returns None).
Optional<StringRef> LinkerDriver::findLib(StringRef Filename) {
if (Config->NoDefaultLibAll)
return None;
StringRef Path = doFindLib(Filename);
if (Config->NoDefaultLibs.count(Path))
return None;
bool Seen = !VisitedFiles.insert(Path.lower()).second;
if (Seen)
return None;
return Path;
}
// Parses LIB environment which contains a list of search paths.
void LinkerDriver::addLibSearchPaths() {
Optional<std::string> EnvOpt = Process::GetEnv("LIB");
if (!EnvOpt.hasValue())
return;
StringRef Env = Alloc.save(*EnvOpt);
while (!Env.empty()) {
StringRef Path;
std::tie(Path, Env) = Env.split(';');
SearchPaths.push_back(Path);
}
}
Undefined *LinkerDriver::addUndefined(StringRef Name) {
Undefined *U = Symtab.addUndefined(Name);
Config->GCRoot.insert(U);
return U;
}
// Symbol names are mangled by appending "_" prefix on x86.
StringRef LinkerDriver::mangle(StringRef Sym) {
assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN);
if (Config->Machine == I386)
return Alloc.save("_" + Sym);
return Sym;
}
// Windows specific -- find default entry point name.
StringRef LinkerDriver::findDefaultEntry() {
// User-defined main functions and their corresponding entry points.
static const char *Entries[][2] = {
{"main", "mainCRTStartup"},
{"wmain", "wmainCRTStartup"},
{"WinMain", "WinMainCRTStartup"},
{"wWinMain", "wWinMainCRTStartup"},
};
for (auto E : Entries) {
StringRef Entry = Symtab.findMangle(mangle(E[0]));
if (!Entry.empty() && !isa<Undefined>(Symtab.find(Entry)->Body))
return mangle(E[1]);
}
return "";
}
WindowsSubsystem LinkerDriver::inferSubsystem() {
if (Config->DLL)
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
if (Symtab.findUnderscore("main") || Symtab.findUnderscore("wmain"))
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
if (Symtab.findUnderscore("WinMain") || Symtab.findUnderscore("wWinMain"))
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
return IMAGE_SUBSYSTEM_UNKNOWN;
}
static uint64_t getDefaultImageBase() {
if (Config->is64())
return Config->DLL ? 0x180000000 : 0x140000000;
return Config->DLL ? 0x10000000 : 0x400000;
}
void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
// If the first command line argument is "/lib", link.exe acts like lib.exe.
// We call our own implementation of lib.exe that understands bitcode files.
if (ArgsArr.size() > 1 && StringRef(ArgsArr[1]).equals_lower("/lib")) {
if (llvm::libDriverMain(ArgsArr.slice(1)) != 0)
error("lib failed");
return;
}
// Needed for LTO.
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllDisassemblers();
// Parse command line options.
llvm::opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
// Handle /help
if (Args.hasArg(OPT_help)) {
printHelp(ArgsArr[0]);
return;
}
if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end())
error("no input files.");
// Construct search path list.
SearchPaths.push_back("");
for (auto *Arg : Args.filtered(OPT_libpath))
SearchPaths.push_back(Arg->getValue());
addLibSearchPaths();
// Handle /out
if (auto *Arg = Args.getLastArg(OPT_out))
Config->OutputFile = Arg->getValue();
// Handle /verbose
if (Args.hasArg(OPT_verbose))
Config->Verbose = true;
// Handle /force or /force:unresolved
if (Args.hasArg(OPT_force) || Args.hasArg(OPT_force_unresolved))
Config->Force = true;
// Handle /debug
if (Args.hasArg(OPT_debug))
Config->Debug = true;
// Handle /noentry
if (Args.hasArg(OPT_noentry)) {
if (!Args.hasArg(OPT_dll))
error("/noentry must be specified with /dll");
Config->NoEntry = true;
}
// Handle /dll
if (Args.hasArg(OPT_dll)) {
Config->DLL = true;
Config->ManifestID = 2;
}
// Handle /fixed
if (Args.hasArg(OPT_fixed)) {
if (Args.hasArg(OPT_dynamicbase))
error("/fixed must not be specified with /dynamicbase");
Config->Relocatable = false;
Config->DynamicBase = false;
}
// Handle /machine
if (auto *Arg = Args.getLastArg(OPT_machine))
Config->Machine = getMachineType(Arg->getValue());
// Handle /nodefaultlib:<filename>
for (auto *Arg : Args.filtered(OPT_nodefaultlib))
Config->NoDefaultLibs.insert(doFindLib(Arg->getValue()));
// Handle /nodefaultlib
if (Args.hasArg(OPT_nodefaultlib_all))
Config->NoDefaultLibAll = true;
// Handle /base
if (auto *Arg = Args.getLastArg(OPT_base))
parseNumbers(Arg->getValue(), &Config->ImageBase);
// Handle /stack
if (auto *Arg = Args.getLastArg(OPT_stack))
parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit);
// Handle /heap
if (auto *Arg = Args.getLastArg(OPT_heap))
parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit);
// Handle /version
if (auto *Arg = Args.getLastArg(OPT_version))
parseVersion(Arg->getValue(), &Config->MajorImageVersion,
&Config->MinorImageVersion);
// Handle /subsystem
if (auto *Arg = Args.getLastArg(OPT_subsystem))
parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion,
&Config->MinorOSVersion);
// Handle /alternatename
for (auto *Arg : Args.filtered(OPT_alternatename))
parseAlternateName(Arg->getValue());
// Handle /include
for (auto *Arg : Args.filtered(OPT_incl))
addUndefined(Arg->getValue());
// Handle /implib
if (auto *Arg = Args.getLastArg(OPT_implib))
Config->Implib = Arg->getValue();
// Handle /opt
for (auto *Arg : Args.filtered(OPT_opt)) {
std::string Str = StringRef(Arg->getValue()).lower();
SmallVector<StringRef, 1> Vec;
StringRef(Str).split(Vec, ',');
for (StringRef S : Vec) {
if (S == "noref") {
Config->DoGC = false;
Config->DoICF = false;
continue;
}
if (S == "icf" || StringRef(S).startswith("icf=")) {
Config->DoICF = true;
continue;
}
if (S == "noicf") {
Config->DoICF = false;
continue;
}
if (StringRef(S).startswith("lldlto=")) {
StringRef OptLevel = StringRef(S).substr(7);
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
Config->LTOOptLevel > 3)
error("/opt:lldlto: invalid optimization level: " + OptLevel);
continue;
}
if (StringRef(S).startswith("lldltojobs=")) {
StringRef Jobs = StringRef(S).substr(11);
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
error("/opt:lldltojobs: invalid job count: " + Jobs);
continue;
}
if (S != "ref" && S != "lbr" && S != "nolbr")
error(Twine("/opt: unknown option: ") + S);
}
}
// Handle /failifmismatch
for (auto *Arg : Args.filtered(OPT_failifmismatch))
checkFailIfMismatch(Arg->getValue());
// Handle /merge
for (auto *Arg : Args.filtered(OPT_merge))
parseMerge(Arg->getValue());
// Handle /manifest
if (auto *Arg = Args.getLastArg(OPT_manifest_colon))
parseManifest(Arg->getValue());
// Handle /manifestuac
if (auto *Arg = Args.getLastArg(OPT_manifestuac))
parseManifestUAC(Arg->getValue());
// Handle /manifestdependency
if (auto *Arg = Args.getLastArg(OPT_manifestdependency))
Config->ManifestDependency = Arg->getValue();
// Handle /manifestfile
if (auto *Arg = Args.getLastArg(OPT_manifestfile))
Config->ManifestFile = Arg->getValue();
// Handle miscellaneous boolean flags.
if (Args.hasArg(OPT_allowbind_no))
Config->AllowBind = false;
if (Args.hasArg(OPT_allowisolation_no))
Config->AllowIsolation = false;
if (Args.hasArg(OPT_dynamicbase_no))
Config->DynamicBase = false;
if (Args.hasArg(OPT_nxcompat_no))
Config->NxCompat = false;
if (Args.hasArg(OPT_tsaware_no))
Config->TerminalServerAware = false;
if (Args.hasArg(OPT_nosymtab))
Config->WriteSymtab = false;
// Create a list of input files. Files can be given as arguments
// for /defaultlib option.
std::vector<StringRef> Paths;
std::vector<MemoryBufferRef> MBs;
for (auto *Arg : Args.filtered(OPT_INPUT))
if (Optional<StringRef> Path = findFile(Arg->getValue()))
Paths.push_back(*Path);
for (auto *Arg : Args.filtered(OPT_defaultlib))
if (Optional<StringRef> Path = findLib(Arg->getValue()))
Paths.push_back(*Path);
for (StringRef Path : Paths)
MBs.push_back(openFile(Path));
// Windows specific -- Create a resource file containing a manifest file.
if (Config->Manifest == Configuration::Embed) {
std::unique_ptr<MemoryBuffer> MB = createManifestRes();
MBs.push_back(MB->getMemBufferRef());
OwningMBs.push_back(std::move(MB)); // take ownership
}
// Windows specific -- Input files can be Windows resource files (.res files).
// We invoke cvtres.exe to convert resource files to a regular COFF file
// then link the result file normally.
std::vector<MemoryBufferRef> Resources;
auto NotResource = [](MemoryBufferRef MB) {
return identify_magic(MB.getBuffer()) != file_magic::windows_resource;
};
auto It = std::stable_partition(MBs.begin(), MBs.end(), NotResource);
if (It != MBs.end()) {
Resources.insert(Resources.end(), It, MBs.end());
MBs.erase(It, MBs.end());
}
// Read all input files given via the command line. Note that step()
// doesn't read files that are specified by directive sections.
for (MemoryBufferRef MB : MBs)
Symtab.addFile(createFile(MB));
Symtab.step();
// Determine machine type and check if all object files are
// for the same CPU type. Note that this needs to be done before
// any call to mangle().
for (std::unique_ptr<InputFile> &File : Symtab.getFiles()) {
MachineTypes MT = File->getMachineType();
if (MT == IMAGE_FILE_MACHINE_UNKNOWN)
continue;
if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
Config->Machine = MT;
continue;
}
if (Config->Machine != MT)
error(Twine(File->getShortName()) + ": machine type " + machineToStr(MT) +
" conflicts with " + machineToStr(Config->Machine));
}
if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
llvm::errs() << "warning: /machine is not specified. x64 is assumed.\n";
Config->Machine = AMD64;
}
// Windows specific -- Convert Windows resource files to a COFF file.
if (!Resources.empty()) {
std::unique_ptr<MemoryBuffer> MB = convertResToCOFF(Resources);
Symtab.addFile(createFile(MB->getMemBufferRef()));
OwningMBs.push_back(std::move(MB)); // take ownership
}
// Handle /largeaddressaware
if (Config->is64() || Args.hasArg(OPT_largeaddressaware))
Config->LargeAddressAware = true;
// Handle /highentropyva
if (Config->is64() && !Args.hasArg(OPT_highentropyva_no))
Config->HighEntropyVA = true;
// Handle /entry and /dll
if (auto *Arg = Args.getLastArg(OPT_entry)) {
Config->Entry = addUndefined(mangle(Arg->getValue()));
} else if (Args.hasArg(OPT_dll) && !Config->NoEntry) {
StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
: "_DllMainCRTStartup";
Config->Entry = addUndefined(S);
} else if (!Config->NoEntry) {
// Windows specific -- If entry point name is not given, we need to
// infer that from user-defined entry name.
StringRef S = findDefaultEntry();
if (S.empty())
error("entry point must be defined");
Config->Entry = addUndefined(S);
if (Config->Verbose)
llvm::outs() << "Entry name inferred: " << S << "\n";
}
// Handle /export
for (auto *Arg : Args.filtered(OPT_export)) {
Export E = parseExport(Arg->getValue());
if (Config->Machine == I386) {
if (!isDecorated(E.Name))
E.Name = Alloc.save("_" + E.Name);
if (!E.ExtName.empty() && !isDecorated(E.ExtName))
E.ExtName = Alloc.save("_" + E.ExtName);
}
Config->Exports.push_back(E);
}
// Handle /def
if (auto *Arg = Args.getLastArg(OPT_deffile)) {
MemoryBufferRef MB = openFile(Arg->getValue());
// parseModuleDefs mutates Config object.
parseModuleDefs(MB, &Alloc);
}
// Handle /delayload
for (auto *Arg : Args.filtered(OPT_delayload)) {
Config->DelayLoads.insert(StringRef(Arg->getValue()).lower());
if (Config->Machine == I386) {
Config->DelayLoadHelper = addUndefined("___delayLoadHelper2@8");
} else {
Config->DelayLoadHelper = addUndefined("__delayLoadHelper2");
}
}
// Set default image base if /base is not given.
if (Config->ImageBase == uint64_t(-1))
Config->ImageBase = getDefaultImageBase();
Symtab.addRelative(mangle("__ImageBase"), 0);
if (Config->Machine == I386) {
Config->SEHTable = Symtab.addRelative("___safe_se_handler_table", 0);
Config->SEHCount = Symtab.addAbsolute("___safe_se_handler_count", 0);
}
// We do not support /guard:cf (control flow protection) yet.
// Define CFG symbols anyway so that we can link MSVC 2015 CRT.
Symtab.addAbsolute(mangle("__guard_fids_table"), 0);
Symtab.addAbsolute(mangle("__guard_fids_count"), 0);
Symtab.addAbsolute(mangle("__guard_flags"), 0x100);
// Read as much files as we can from directives sections.
Symtab.run();
// Resolve auxiliary symbols until we get a convergence.
// (Trying to resolve a symbol may trigger a Lazy symbol to load a new file.
// A new file may contain a directive section to add new command line options.
// That's why we have to repeat until converge.)
for (;;) {
// Windows specific -- if entry point is not found,
// search for its mangled names.
if (Config->Entry)
Symtab.mangleMaybe(Config->Entry);
// Windows specific -- Make sure we resolve all dllexported symbols.
for (Export &E : Config->Exports) {
E.Sym = addUndefined(E.Name);
if (!E.Directives)
Symtab.mangleMaybe(E.Sym);
}
// Add weak aliases. Weak aliases is a mechanism to give remaining
// undefined symbols final chance to be resolved successfully.
for (auto Pair : Config->AlternateNames) {
StringRef From = Pair.first;
StringRef To = Pair.second;
Symbol *Sym = Symtab.find(From);
if (!Sym)
continue;
if (auto *U = dyn_cast<Undefined>(Sym->Body))
if (!U->WeakAlias)
U->WeakAlias = Symtab.addUndefined(To);
}
// Windows specific -- if __load_config_used can be resolved, resolve it.
if (Symtab.findUnderscore("_load_config_used"))
addUndefined(mangle("_load_config_used"));
if (Symtab.queueEmpty())
break;
Symtab.run();
}
// Do LTO by compiling bitcode input files to a set of native COFF files then
// link those files.
Symtab.addCombinedLTOObjects();
// Make sure we have resolved all symbols.
Symtab.reportRemainingUndefines(/*Resolve=*/true);
// Windows specific -- if no /subsystem is given, we need to infer
// that from entry point name.
if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
Config->Subsystem = inferSubsystem();
if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
error("subsystem must be defined");
}
// Handle /safeseh.
if (Args.hasArg(OPT_safeseh))
for (ObjectFile *File : Symtab.ObjectFiles)
if (!File->SEHCompat)
error("/safeseh: " + File->getName() + " is not compatible with SEH");
// Windows specific -- when we are creating a .dll file, we also
// need to create a .lib file.
if (!Config->Exports.empty() || Config->DLL) {
fixupExports();
writeImportLibrary();
assignExportOrdinals();
}
// Windows specific -- Create a side-by-side manifest file.
if (Config->Manifest == Configuration::SideBySide)
createSideBySideManifest();
// Create a dummy PDB file to satisfy build sytem rules.
if (auto *Arg = Args.getLastArg(OPT_pdb))
createPDB(Arg->getValue());
// Identify unreferenced COMDAT sections.
if (Config->DoGC)
markLive(Symtab.getChunks());
// Identify identical COMDAT sections to merge them.
if (Config->DoICF)
doICF(Symtab.getChunks());
// Write the result.
writeResult(&Symtab);
// Create a symbol map file containing symbol VAs and their names
// to help debugging.
if (auto *Arg = Args.getLastArg(OPT_lldmap)) {
std::error_code EC;
llvm::raw_fd_ostream Out(Arg->getValue(), EC, OpenFlags::F_Text);
error(EC, "Could not create the symbol map");
Symtab.printMap(Out);
}
// Call exit to avoid calling destructors.
exit(0);
}
} // namespace coff
} // namespace lld

180
COFF/Driver.h Normal file
View file

@ -0,0 +1,180 @@
//===- Driver.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_DRIVER_H
#define LLD_COFF_DRIVER_H
#include "Config.h"
#include "SymbolTable.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/COFF.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/StringSaver.h"
#include <memory>
#include <set>
#include <vector>
namespace lld {
namespace coff {
class LinkerDriver;
extern LinkerDriver *Driver;
using llvm::COFF::MachineTypes;
using llvm::COFF::WindowsSubsystem;
using llvm::Optional;
class InputFile;
// Entry point of the COFF linker.
void link(llvm::ArrayRef<const char *> Args);
// Implemented in MarkLive.cpp.
void markLive(const std::vector<Chunk *> &Chunks);
// Implemented in ICF.cpp.
void doICF(const std::vector<Chunk *> &Chunks);
class ArgParser {
public:
ArgParser() : Alloc(AllocAux) {}
// Parses command line options.
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
// Concatenate LINK environment varirable and given arguments and parse them.
llvm::opt::InputArgList parseLINK(llvm::ArrayRef<const char *> Args);
// Tokenizes a given string and then parses as command line options.
llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); }
private:
std::vector<const char *> tokenize(StringRef S);
std::vector<const char *> replaceResponseFiles(std::vector<const char *>);
llvm::BumpPtrAllocator AllocAux;
llvm::StringSaver Alloc;
};
class LinkerDriver {
public:
LinkerDriver() : Alloc(AllocAux) {}
void link(llvm::ArrayRef<const char *> Args);
// Used by the resolver to parse .drectve section contents.
void parseDirectives(StringRef S);
private:
llvm::BumpPtrAllocator AllocAux;
llvm::StringSaver Alloc;
ArgParser Parser;
SymbolTable Symtab;
// Opens a file. Path has to be resolved already.
MemoryBufferRef openFile(StringRef Path);
// Searches a file from search paths.
Optional<StringRef> findFile(StringRef Filename);
Optional<StringRef> findLib(StringRef Filename);
StringRef doFindFile(StringRef Filename);
StringRef doFindLib(StringRef Filename);
// Parses LIB environment which contains a list of search paths.
void addLibSearchPaths();
// Library search path. The first element is always "" (current directory).
std::vector<StringRef> SearchPaths;
std::set<std::string> VisitedFiles;
Undefined *addUndefined(StringRef Sym);
StringRef mangle(StringRef Sym);
// Windows specific -- "main" is not the only main function in Windows.
// You can choose one from these four -- {w,}{WinMain,main}.
// There are four different entry point functions for them,
// {w,}{WinMain,main}CRTStartup, respectively. The linker needs to
// choose the right one depending on which "main" function is defined.
// This function looks up the symbol table and resolve corresponding
// entry point name.
StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem();
// Driver is the owner of all opened files.
// InputFiles have MemoryBufferRefs to them.
std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs;
};
void parseModuleDefs(MemoryBufferRef MB, llvm::StringSaver *Alloc);
void writeImportLibrary();
// Functions below this line are defined in DriverUtils.cpp.
void printHelp(const char *Argv0);
// For /machine option.
MachineTypes getMachineType(StringRef Arg);
StringRef machineToStr(MachineTypes MT);
// Parses a string in the form of "<integer>[,<integer>]".
void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr);
// Parses a string in the form of "<integer>[.<integer>]".
// Minor's default value is 0.
void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor);
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
uint32_t *Minor);
void parseAlternateName(StringRef);
void parseMerge(StringRef);
// Parses a string in the form of "EMBED[,=<integer>]|NO".
void parseManifest(StringRef Arg);
// Parses a string in the form of "level=<string>|uiAccess=<string>"
void parseManifestUAC(StringRef Arg);
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes();
void createSideBySideManifest();
// Used for dllexported symbols.
Export parseExport(StringRef Arg);
void fixupExports();
void assignExportOrdinals();
// 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
// incompatible objects.
void checkFailIfMismatch(StringRef Arg);
// Convert Windows resource files (.res files) to a .obj file
// using cvtres.exe.
std::unique_ptr<MemoryBuffer>
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs);
void touchFile(StringRef Path);
void createPDB(StringRef Path);
// 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 coff
} // namespace lld
#endif

718
COFF/DriverUtils.cpp Normal file
View file

@ -0,0 +1,718 @@
//===- 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 "Config.h"
#include "Driver.h"
#include "Error.h"
#include "Symbols.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/COFF.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
using namespace llvm::COFF;
using namespace llvm;
using llvm::cl::ExpandResponseFiles;
using llvm::cl::TokenizeWindowsCommandLine;
using llvm::sys::Process;
namespace lld {
namespace coff {
namespace {
class Executor {
public:
explicit Executor(StringRef S) : Saver(Alloc), Prog(Saver.save(S)) {}
void add(StringRef S) { Args.push_back(Saver.save(S)); }
void add(std::string &S) { Args.push_back(Saver.save(S)); }
void add(Twine S) { Args.push_back(Saver.save(S)); }
void add(const char *S) { Args.push_back(Saver.save(S)); }
void run() {
ErrorOr<std::string> ExeOrErr = llvm::sys::findProgramByName(Prog);
error(ExeOrErr, Twine("unable to find ") + Prog + " in PATH: ");
const char *Exe = Saver.save(*ExeOrErr);
Args.insert(Args.begin(), Exe);
Args.push_back(nullptr);
if (llvm::sys::ExecuteAndWait(Args[0], Args.data()) != 0) {
for (const char *S : Args)
if (S)
llvm::errs() << S << " ";
error("failed");
}
}
private:
llvm::BumpPtrAllocator Alloc;
llvm::StringSaver Saver;
StringRef Prog;
std::vector<const char *> Args;
};
} // anonymous namespace
// Returns /machine's value.
MachineTypes getMachineType(StringRef S) {
MachineTypes MT = StringSwitch<MachineTypes>(S.lower())
.Case("x64", AMD64)
.Case("amd64", AMD64)
.Case("x86", I386)
.Case("i386", I386)
.Case("arm", ARMNT)
.Default(IMAGE_FILE_MACHINE_UNKNOWN);
if (MT != IMAGE_FILE_MACHINE_UNKNOWN)
return MT;
error(Twine("unknown /machine argument: ") + S);
}
StringRef machineToStr(MachineTypes MT) {
switch (MT) {
case ARMNT:
return "arm";
case AMD64:
return "x64";
case I386:
return "x86";
default:
llvm_unreachable("unknown machine type");
}
}
// Parses a string in the form of "<integer>[,<integer>]".
void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size) {
StringRef S1, S2;
std::tie(S1, S2) = Arg.split(',');
if (S1.getAsInteger(0, *Addr))
error(Twine("invalid number: ") + S1);
if (Size && !S2.empty() && S2.getAsInteger(0, *Size))
error(Twine("invalid number: ") + S2);
}
// Parses a string in the form of "<integer>[.<integer>]".
// If second number is not present, Minor is set to 0.
void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) {
StringRef S1, S2;
std::tie(S1, S2) = Arg.split('.');
if (S1.getAsInteger(0, *Major))
error(Twine("invalid number: ") + S1);
*Minor = 0;
if (!S2.empty() && S2.getAsInteger(0, *Minor))
error(Twine("invalid number: ") + S2);
}
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
uint32_t *Minor) {
StringRef SysStr, Ver;
std::tie(SysStr, Ver) = Arg.split(',');
*Sys = StringSwitch<WindowsSubsystem>(SysStr.lower())
.Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
.Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI)
.Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION)
.Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)
.Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM)
.Case("efi_runtime_driver", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)
.Case("native", IMAGE_SUBSYSTEM_NATIVE)
.Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI)
.Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI)
.Default(IMAGE_SUBSYSTEM_UNKNOWN);
if (*Sys == IMAGE_SUBSYSTEM_UNKNOWN)
error(Twine("unknown subsystem: ") + SysStr);
if (!Ver.empty())
parseVersion(Ver, Major, Minor);
}
// Parse a string of the form of "<from>=<to>".
// Results are directly written to Config.
void parseAlternateName(StringRef S) {
StringRef From, To;
std::tie(From, To) = S.split('=');
if (From.empty() || To.empty())
error(Twine("/alternatename: invalid argument: ") + S);
auto It = Config->AlternateNames.find(From);
if (It != Config->AlternateNames.end() && It->second != To)
error(Twine("/alternatename: conflicts: ") + S);
Config->AlternateNames.insert(It, std::make_pair(From, To));
}
// Parse a string of the form of "<from>=<to>".
// Results are directly written to Config.
void parseMerge(StringRef S) {
StringRef From, To;
std::tie(From, To) = S.split('=');
if (From.empty() || To.empty())
error(Twine("/merge: invalid argument: ") + S);
auto Pair = Config->Merge.insert(std::make_pair(From, To));
bool Inserted = Pair.second;
if (!Inserted) {
StringRef Existing = Pair.first->second;
if (Existing != To)
llvm::errs() << "warning: " << S << ": already merged into "
<< Existing << "\n";
}
}
// Parses a string in the form of "EMBED[,=<integer>]|NO".
// Results are directly written to Config.
void parseManifest(StringRef Arg) {
if (Arg.equals_lower("no")) {
Config->Manifest = Configuration::No;
return;
}
if (!Arg.startswith_lower("embed"))
error(Twine("Invalid option ") + Arg);
Config->Manifest = Configuration::Embed;
Arg = Arg.substr(strlen("embed"));
if (Arg.empty())
return;
if (!Arg.startswith_lower(",id="))
error(Twine("Invalid option ") + Arg);
Arg = Arg.substr(strlen(",id="));
if (Arg.getAsInteger(0, Config->ManifestID))
error(Twine("Invalid option ") + Arg);
}
// Parses a string in the form of "level=<string>|uiAccess=<string>|NO".
// Results are directly written to Config.
void parseManifestUAC(StringRef Arg) {
if (Arg.equals_lower("no")) {
Config->ManifestUAC = false;
return;
}
for (;;) {
Arg = Arg.ltrim();
if (Arg.empty())
return;
if (Arg.startswith_lower("level=")) {
Arg = Arg.substr(strlen("level="));
std::tie(Config->ManifestLevel, Arg) = Arg.split(" ");
continue;
}
if (Arg.startswith_lower("uiaccess=")) {
Arg = Arg.substr(strlen("uiaccess="));
std::tie(Config->ManifestUIAccess, Arg) = Arg.split(" ");
continue;
}
error(Twine("Invalid option ") + Arg);
}
}
// Quote each line with "". Existing double-quote is converted
// to two double-quotes.
static void quoteAndPrint(raw_ostream &Out, StringRef S) {
while (!S.empty()) {
StringRef Line;
std::tie(Line, S) = S.split("\n");
if (Line.empty())
continue;
Out << '\"';
for (int I = 0, E = Line.size(); I != E; ++I) {
if (Line[I] == '\"') {
Out << "\"\"";
} else {
Out << Line[I];
}
}
Out << "\"\n";
}
}
// Create a manifest file contents.
static std::string createManifestXml() {
std::string S;
llvm::raw_string_ostream OS(S);
// Emit the XML. Note that we do *not* verify that the XML attributes are
// syntactically correct. This is intentional for link.exe compatibility.
OS << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
<< "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"
<< " manifestVersion=\"1.0\">\n";
if (Config->ManifestUAC) {
OS << " <trustInfo>\n"
<< " <security>\n"
<< " <requestedPrivileges>\n"
<< " <requestedExecutionLevel level=" << Config->ManifestLevel
<< " uiAccess=" << Config->ManifestUIAccess << "/>\n"
<< " </requestedPrivileges>\n"
<< " </security>\n"
<< " </trustInfo>\n";
if (!Config->ManifestDependency.empty()) {
OS << " <dependency>\n"
<< " <dependentAssembly>\n"
<< " <assemblyIdentity " << Config->ManifestDependency << " />\n"
<< " </dependentAssembly>\n"
<< " </dependency>\n";
}
}
OS << "</assembly>\n";
OS.flush();
return S;
}
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes() {
// Create a temporary file for the resource script file.
SmallString<128> RCPath;
std::error_code EC = sys::fs::createTemporaryFile("tmp", "rc", RCPath);
error(EC, "cannot create a temporary file");
FileRemover RCRemover(RCPath);
// Open the temporary file for writing.
llvm::raw_fd_ostream Out(RCPath, EC, sys::fs::F_Text);
error(EC, Twine("failed to open ") + RCPath);
// Write resource script to the RC file.
Out << "#define LANG_ENGLISH 9\n"
<< "#define SUBLANG_DEFAULT 1\n"
<< "#define APP_MANIFEST " << Config->ManifestID << "\n"
<< "#define RT_MANIFEST 24\n"
<< "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n"
<< "APP_MANIFEST RT_MANIFEST {\n";
quoteAndPrint(Out, createManifestXml());
Out << "}\n";
Out.close();
// Create output resource file.
SmallString<128> ResPath;
EC = sys::fs::createTemporaryFile("tmp", "res", ResPath);
error(EC, "cannot create a temporary file");
Executor E("rc.exe");
E.add("/fo");
E.add(ResPath.str());
E.add("/nologo");
E.add(RCPath.str());
E.run();
ErrorOr<std::unique_ptr<MemoryBuffer>> Ret = MemoryBuffer::getFile(ResPath);
error(Ret, Twine("Could not open ") + ResPath);
return std::move(*Ret);
}
void createSideBySideManifest() {
std::string Path = Config->ManifestFile;
if (Path == "")
Path = (Twine(Config->OutputFile) + ".manifest").str();
std::error_code EC;
llvm::raw_fd_ostream Out(Path, EC, llvm::sys::fs::F_Text);
error(EC, "failed to create manifest");
Out << createManifestXml();
}
// Parse a string in the form of
// "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]".
// Used for parsing /export arguments.
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;
}
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:
error(Twine("invalid /export: ") + Arg);
}
static StringRef undecorate(StringRef Sym) {
if (Config->Machine != I386)
return Sym;
return Sym.startswith("_") ? Sym.substr(1) : Sym;
}
// Performs error checking on all /export arguments.
// It also sets ordinals.
void 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)
error("duplicate export ordinal: " + E.Name);
}
for (Export &E : Config->Exports) {
if (Undefined *U = cast_or_null<Undefined>(E.Sym->WeakAlias)) {
E.SymbolName = U->getName();
} else {
E.SymbolName = E.Sym->getName();
}
}
for (Export &E : Config->Exports)
E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName);
// Uniquefy by name.
std::map<StringRef, Export *> Map;
std::vector<Export> V;
for (Export &E : Config->Exports) {
auto Pair = Map.insert(std::make_pair(E.ExportName, &E));
bool Inserted = Pair.second;
if (Inserted) {
V.push_back(E);
continue;
}
Export *Existing = Pair.first->second;
if (E == *Existing || E.Name != Existing->Name)
continue;
llvm::errs() << "warning: duplicate /export option: " << E.Name << "\n";
}
Config->Exports = std::move(V);
// Sort by name.
std::sort(Config->Exports.begin(), Config->Exports.end(),
[](const Export &A, const Export &B) {
return A.ExportName < B.ExportName;
});
}
void assignExportOrdinals() {
// 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;
}
// Parses a string in the form of "key=value" and check
// if value matches previous values for the same key.
void checkFailIfMismatch(StringRef Arg) {
StringRef K, V;
std::tie(K, V) = Arg.split('=');
if (K.empty() || V.empty())
error(Twine("/failifmismatch: invalid argument: ") + Arg);
StringRef Existing = Config->MustMatch[K];
if (!Existing.empty() && V != Existing)
error(Twine("/failifmismatch: mismatch detected: ") + Existing + " and " +
V + " for key " + K);
Config->MustMatch[K] = V;
}
// Convert Windows resource files (.res files) to a .obj file
// using cvtres.exe.
std::unique_ptr<MemoryBuffer>
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
// Create an output file path.
SmallString<128> Path;
if (llvm::sys::fs::createTemporaryFile("resource", "obj", Path))
error("Could not create temporary file");
// Execute cvtres.exe.
Executor E("cvtres.exe");
E.add("/machine:" + machineToStr(Config->Machine));
E.add("/readonly");
E.add("/nologo");
E.add("/out:" + Path);
for (MemoryBufferRef MB : MBs)
E.add(MB.getBufferIdentifier());
E.run();
ErrorOr<std::unique_ptr<MemoryBuffer>> Ret = MemoryBuffer::getFile(Path);
error(Ret, Twine("Could not open ") + Path);
return std::move(*Ret);
}
static std::string writeToTempFile(StringRef Contents) {
SmallString<128> Path;
int FD;
if (llvm::sys::fs::createTemporaryFile("tmp", "def", FD, Path)) {
llvm::errs() << "failed to create a temporary file\n";
return "";
}
llvm::raw_fd_ostream OS(FD, /*shouldClose*/ true);
OS << Contents;
return Path.str();
}
void touchFile(StringRef Path) {
int FD;
std::error_code EC = sys::fs::openFileForWrite(Path, FD, sys::fs::F_Append);
error(EC, "failed to create a file");
sys::Process::SafelyCloseFileDescriptor(FD);
}
static std::string getImplibPath() {
if (!Config->Implib.empty())
return Config->Implib;
SmallString<128> Out = StringRef(Config->OutputFile);
sys::path::replace_extension(Out, ".lib");
return Out.str();
}
static std::unique_ptr<MemoryBuffer> createEmptyImportLibrary() {
std::string S = (Twine("LIBRARY \"") +
llvm::sys::path::filename(Config->OutputFile) + "\"\n")
.str();
std::string Path1 = writeToTempFile(S);
std::string Path2 = getImplibPath();
llvm::FileRemover Remover1(Path1);
llvm::FileRemover Remover2(Path2);
Executor E("lib.exe");
E.add("/nologo");
E.add("/machine:" + machineToStr(Config->Machine));
E.add(Twine("/def:") + Path1);
E.add(Twine("/out:") + Path2);
E.run();
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFile(Path2, -1, false);
error(BufOrErr, Twine("Failed to open ") + Path2);
return MemoryBuffer::getMemBufferCopy((*BufOrErr)->getBuffer());
}
static std::vector<NewArchiveIterator>
readMembers(const object::Archive &Archive) {
std::vector<NewArchiveIterator> V;
for (const auto &ChildOrErr : Archive.children()) {
error(ChildOrErr, "Archive::Child::getName failed");
const object::Archive::Child C(*ChildOrErr);
ErrorOr<StringRef> NameOrErr = C.getName();
error(NameOrErr, "Archive::Child::getName failed");
V.emplace_back(C, *NameOrErr);
}
return V;
}
// This class creates short import files which is described in
// PE/COFF spec 7. Import Library Format.
class ShortImportCreator {
public:
ShortImportCreator(object::Archive *A, StringRef S) : Parent(A), DLLName(S) {}
NewArchiveIterator create(StringRef Sym, uint16_t Ordinal,
ImportNameType NameType, bool isData) {
size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs
size_t Size = sizeof(object::ArchiveMemberHeader) +
sizeof(coff_import_header) + ImpSize;
char *Buf = Alloc.Allocate<char>(Size);
memset(Buf, 0, Size);
char *P = Buf;
// Write archive member header
auto *Hdr = reinterpret_cast<object::ArchiveMemberHeader *>(P);
P += sizeof(*Hdr);
sprintf(Hdr->Name, "%-12s", "dummy");
sprintf(Hdr->LastModified, "%-12d", 0);
sprintf(Hdr->UID, "%-6d", 0);
sprintf(Hdr->GID, "%-6d", 0);
sprintf(Hdr->AccessMode, "%-8d", 0644);
sprintf(Hdr->Size, "%-10d", int(sizeof(coff_import_header) + ImpSize));
// Write short import library.
auto *Imp = reinterpret_cast<coff_import_header *>(P);
P += sizeof(*Imp);
Imp->Sig2 = 0xFFFF;
Imp->Machine = Config->Machine;
Imp->SizeOfData = ImpSize;
if (Ordinal > 0)
Imp->OrdinalHint = Ordinal;
Imp->TypeInfo = (isData ? IMPORT_DATA : IMPORT_CODE);
Imp->TypeInfo |= NameType << 2;
// Write symbol name and DLL name.
memcpy(P, Sym.data(), Sym.size());
P += Sym.size() + 1;
memcpy(P, DLLName.data(), DLLName.size());
std::error_code EC;
object::Archive::Child C(Parent, Buf, &EC);
assert(!EC && "We created an invalid buffer");
return NewArchiveIterator(C, DLLName);
}
private:
BumpPtrAllocator Alloc;
object::Archive *Parent;
StringRef DLLName;
};
static ImportNameType getNameType(StringRef Sym, StringRef ExtName) {
if (Sym != ExtName)
return IMPORT_NAME_UNDECORATE;
if (Config->Machine == I386 && Sym.startswith("_"))
return IMPORT_NAME_NOPREFIX;
return IMPORT_NAME;
}
static std::string replace(StringRef S, StringRef From, StringRef To) {
size_t Pos = S.find(From);
assert(Pos != StringRef::npos);
return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str();
}
// Creates an import library for a DLL. In this function, we first
// create an empty import library using lib.exe and then adds short
// import files to that file.
void writeImportLibrary() {
std::unique_ptr<MemoryBuffer> Buf = createEmptyImportLibrary();
std::error_code EC;
object::Archive Archive(Buf->getMemBufferRef(), EC);
error(EC, "Error reading an empty import file");
std::vector<NewArchiveIterator> Members = readMembers(Archive);
std::string DLLName = llvm::sys::path::filename(Config->OutputFile);
ShortImportCreator ShortImport(&Archive, DLLName);
for (Export &E : Config->Exports) {
if (E.Private)
continue;
if (E.ExtName.empty()) {
Members.push_back(ShortImport.create(
E.SymbolName, E.Ordinal, getNameType(E.SymbolName, E.Name), E.Data));
} else {
Members.push_back(ShortImport.create(
replace(E.SymbolName, E.Name, E.ExtName), E.Ordinal,
getNameType(E.SymbolName, E.Name), E.Data));
}
}
std::string Path = getImplibPath();
std::pair<StringRef, std::error_code> Result =
writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU,
/*Deterministic*/ true, /*Thin*/ false);
error(Result.second, Twine("Failed to write ") + Path);
}
// 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 llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X6, X7, X8, X9, X10) \
{ \
X1, X2, X9, X10, OPT_##ID, llvm::opt::Option::KIND##Class, X8, X7, \
OPT_##GROUP, OPT_##ALIAS, X6 \
},
#include "Options.inc"
#undef OPTION
};
class COFFOptTable : public llvm::opt::OptTable {
public:
COFFOptTable() : OptTable(infoTable, true) {}
};
// Parses a given list of options.
llvm::opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
// First, replace respnose files (@<file>-style options).
std::vector<const char *> Argv = replaceResponseFiles(ArgsArr);
// Make InputArgList from string vectors.
COFFOptTable Table;
unsigned MissingIndex;
unsigned MissingCount;
llvm::opt::InputArgList Args =
Table.ParseArgs(Argv, MissingIndex, MissingCount);
// Print the real command line if response files are expanded.
if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) {
llvm::outs() << "Command line:";
for (const char *S : Argv)
llvm::outs() << " " << S;
llvm::outs() << "\n";
}
if (MissingCount)
error(Twine("missing arg value for \"") + Args.getArgString(MissingIndex) +
"\", expected " + Twine(MissingCount) +
(MissingCount == 1 ? " argument." : " arguments."));
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
llvm::errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n";
return Args;
}
llvm::opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) {
// Concatenate LINK env and given arguments and parse them.
Optional<std::string> Env = Process::GetEnv("LINK");
if (!Env)
return parse(Args);
std::vector<const char *> V = tokenize(*Env);
V.insert(V.end(), Args.begin(), Args.end());
return parse(V);
}
std::vector<const char *> ArgParser::tokenize(StringRef S) {
SmallVector<const char *, 16> Tokens;
StringSaver Saver(AllocAux);
llvm::cl::TokenizeWindowsCommandLine(S, Saver, Tokens);
return std::vector<const char *>(Tokens.begin(), Tokens.end());
}
// Creates a new command line by replacing options starting with '@'
// character. '@<filename>' is replaced by the file's contents.
std::vector<const char *>
ArgParser::replaceResponseFiles(std::vector<const char *> Argv) {
SmallVector<const char *, 256> Tokens(Argv.data(), Argv.data() + Argv.size());
StringSaver Saver(AllocAux);
ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens);
return std::vector<const char *>(Tokens.begin(), Tokens.end());
}
void printHelp(const char *Argv0) {
COFFOptTable Table;
Table.PrintHelp(llvm::outs(), Argv0, "LLVM Linker", false);
}
} // namespace coff
} // namespace lld

30
COFF/Error.cpp Normal file
View file

@ -0,0 +1,30 @@
//===- Error.cpp ----------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Error.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/raw_ostream.h"
namespace lld {
namespace coff {
void error(const Twine &Msg) {
llvm::errs() << Msg << "\n";
exit(1);
}
void error(std::error_code EC, const Twine &Prefix) {
if (!EC)
return;
error(Prefix + ": " + EC.message());
}
} // namespace coff
} // namespace lld

28
COFF/Error.h Normal file
View file

@ -0,0 +1,28 @@
//===- Error.h --------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_ERROR_H
#define LLD_COFF_ERROR_H
#include "lld/Core/LLVM.h"
namespace lld {
namespace coff {
LLVM_ATTRIBUTE_NORETURN void error(const Twine &Msg);
void error(std::error_code EC, const Twine &Prefix);
template <typename T> void error(const ErrorOr<T> &V, const Twine &Prefix) {
error(V.getError(), Prefix);
}
} // namespace coff
} // namespace lld
#endif

244
COFF/ICF.cpp Normal file
View file

@ -0,0 +1,244 @@
//===- ICF.cpp ------------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Identical COMDAT Folding is a feature to merge COMDAT sections not by
// name (which is regular COMDAT handling) but by contents. If two COMDAT
// sections have the same data, relocations, attributes, etc., then the two
// are considered identical and merged by the linker. This optimization
// makes outputs smaller.
//
// ICF is theoretically a problem of reducing graphs by merging as many
// identical subgraphs as possible, if we consider sections as vertices and
// relocations as edges. This may be a bit more complicated problem than you
// might think. The order of processing sections matters since merging two
// sections can make other sections, whose relocations now point to the same
// section, mergeable. Graphs may contain cycles, which is common in COFF.
// We need a sophisticated algorithm to do this properly and efficiently.
//
// What we do in this file is this. We split sections into groups. Sections
// in the same group are considered identical.
//
// First, all sections are grouped by their "constant" values. Constant
// values are values that are never changed by ICF, such as section contents,
// section name, number of relocations, type and offset of each relocation,
// etc. Because we do not care about some relocation targets in this step,
// two sections in the same group may not be identical, but at least two
// sections in different groups can never be identical.
//
// Then, we try to split each group by relocation targets. Relocations are
// considered identical if and only if the relocation targets are in the
// same group. Splitting a group may make more groups to be splittable,
// because two relocations that were previously considered identical might
// now point to different groups. We repeat this step until the convergence
// is obtained.
//
// This algorithm is so-called "optimistic" algorithm described in
// http://research.google.com/pubs/pub36912.html.
//
//===----------------------------------------------------------------------===//
#include "Chunks.h"
#include "Symbols.h"
#include "lld/Core/Parallel.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <atomic>
#include <vector>
using namespace llvm;
namespace lld {
namespace coff {
typedef std::vector<SectionChunk *>::iterator ChunkIterator;
typedef bool (*Comparator)(const SectionChunk *, const SectionChunk *);
class ICF {
public:
void run(const std::vector<Chunk *> &V);
private:
static uint64_t getHash(SectionChunk *C);
static bool equalsConstant(const SectionChunk *A, const SectionChunk *B);
static bool equalsVariable(const SectionChunk *A, const SectionChunk *B);
bool forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq);
bool partition(ChunkIterator Begin, ChunkIterator End, Comparator Eq);
std::atomic<uint64_t> NextID = { 1 };
};
// Entry point to ICF.
void doICF(const std::vector<Chunk *> &Chunks) {
ICF().run(Chunks);
}
uint64_t ICF::getHash(SectionChunk *C) {
return hash_combine(C->getPermissions(),
hash_value(C->SectionName),
C->NumRelocs,
C->getAlign(),
uint32_t(C->Header->SizeOfRawData),
C->Checksum);
}
bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
if (A->AssocChildren.size() != B->AssocChildren.size() ||
A->NumRelocs != B->NumRelocs) {
return false;
}
// Compare associative sections.
for (size_t I = 0, E = A->AssocChildren.size(); I != E; ++I)
if (A->AssocChildren[I]->GroupID != B->AssocChildren[I]->GroupID)
return false;
// Compare relocations.
auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
if (R1.Type != R2.Type ||
R1.VirtualAddress != R2.VirtualAddress) {
return false;
}
SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex)->repl();
SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex)->repl();
if (B1 == B2)
return true;
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(B2))
return D1->getValue() == D2->getValue() &&
D1->getChunk()->GroupID == D2->getChunk()->GroupID;
return false;
};
if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq))
return false;
// Compare section attributes and contents.
return A->getPermissions() == B->getPermissions() &&
A->SectionName == B->SectionName &&
A->getAlign() == B->getAlign() &&
A->Header->SizeOfRawData == B->Header->SizeOfRawData &&
A->Checksum == B->Checksum &&
A->getContents() == B->getContents();
}
bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
// Compare associative sections.
for (size_t I = 0, E = A->AssocChildren.size(); I != E; ++I)
if (A->AssocChildren[I]->GroupID != B->AssocChildren[I]->GroupID)
return false;
// Compare relocations.
auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex)->repl();
SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex)->repl();
if (B1 == B2)
return true;
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(B2))
return D1->getChunk()->GroupID == D2->getChunk()->GroupID;
return false;
};
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq);
}
bool ICF::partition(ChunkIterator Begin, ChunkIterator End, Comparator Eq) {
bool R = false;
for (auto It = Begin;;) {
SectionChunk *Head = *It;
auto Bound = std::partition(It + 1, End, [&](SectionChunk *SC) {
return Eq(Head, SC);
});
if (Bound == End)
return R;
uint64_t ID = NextID++;
std::for_each(It, Bound, [&](SectionChunk *SC) { SC->GroupID = ID; });
It = Bound;
R = true;
}
}
bool ICF::forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq) {
bool R = false;
for (auto It = Chunks.begin(), End = Chunks.end(); It != End;) {
SectionChunk *Head = *It;
auto Bound = std::find_if(It + 1, End, [&](SectionChunk *SC) {
return SC->GroupID != Head->GroupID;
});
if (partition(It, Bound, Eq))
R = true;
It = Bound;
}
return R;
}
// Merge identical COMDAT sections.
// Two sections are considered the same if their section headers,
// contents and relocations are all the same.
void ICF::run(const std::vector<Chunk *> &Vec) {
// Collect only mergeable sections and group by hash value.
parallel_for_each(Vec.begin(), Vec.end(), [&](Chunk *C) {
if (auto *SC = dyn_cast<SectionChunk>(C)) {
bool Global = SC->Sym && SC->Sym->isExternal();
bool Writable = SC->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
if (SC->isCOMDAT() && SC->isLive() && Global && !Writable)
SC->GroupID = getHash(SC) | (uint64_t(1) << 63);
}
});
std::vector<SectionChunk *> Chunks;
for (Chunk *C : Vec) {
if (auto *SC = dyn_cast<SectionChunk>(C)) {
if (SC->GroupID) {
Chunks.push_back(SC);
} else {
SC->GroupID = NextID++;
}
}
}
// From now on, sections in Chunks are ordered so that sections in
// the same group are consecutive in the vector.
std::sort(Chunks.begin(), Chunks.end(),
[](SectionChunk *A, SectionChunk *B) {
return A->GroupID < B->GroupID;
});
// Split groups until we get a convergence.
int Cnt = 1;
forEachGroup(Chunks, equalsConstant);
for (;;) {
if (!forEachGroup(Chunks, equalsVariable))
break;
++Cnt;
}
if (Config->Verbose)
llvm::outs() << "\nICF needed " << Cnt << " iterations.\n";
// Merge sections in the same group.
for (auto It = Chunks.begin(), End = Chunks.end(); It != End;) {
SectionChunk *Head = *It++;
auto Bound = std::find_if(It, End, [&](SectionChunk *SC) {
return Head->GroupID != SC->GroupID;
});
if (It == Bound)
continue;
if (Config->Verbose)
llvm::outs() << "Selected " << Head->getDebugName() << "\n";
while (It != Bound) {
SectionChunk *SC = *It++;
if (Config->Verbose)
llvm::outs() << " Removed " << SC->getDebugName() << "\n";
Head->replace(SC);
}
}
}
} // namespace coff
} // namespace lld

367
COFF/InputFiles.cpp Normal file
View file

@ -0,0 +1,367 @@
//===- InputFiles.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 "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/LTO/LTOModule.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::COFF;
using namespace llvm::object;
using namespace llvm::support::endian;
using llvm::RoundUpToAlignment;
using llvm::Triple;
using llvm::support::ulittle32_t;
using llvm::sys::fs::file_magic;
using llvm::sys::fs::identify_magic;
namespace lld {
namespace coff {
int InputFile::NextIndex = 0;
// Returns the last element of a path, which is supposed to be a filename.
static StringRef getBasename(StringRef Path) {
size_t Pos = Path.find_last_of("\\/");
if (Pos == StringRef::npos)
return Path;
return Path.substr(Pos + 1);
}
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
std::string InputFile::getShortName() {
if (ParentName == "")
return getName().lower();
std::string Res = (getBasename(ParentName) + "(" +
getBasename(getName()) + ")").str();
return StringRef(Res).lower();
}
void ArchiveFile::parse() {
// Parse a MemoryBufferRef as an archive file.
auto ArchiveOrErr = Archive::create(MB);
error(ArchiveOrErr, "Failed to parse static library");
File = std::move(*ArchiveOrErr);
// Allocate a buffer for Lazy objects.
size_t NumSyms = File->getNumberOfSymbols();
LazySymbols.reserve(NumSyms);
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols())
LazySymbols.emplace_back(this, Sym);
// Seen is a map from member files to boolean values. Initially
// all members are mapped to false, which indicates all these files
// are not read yet.
for (auto &ChildOrErr : File->children()) {
error(ChildOrErr, "Failed to parse static library");
const Archive::Child &Child = *ChildOrErr;
Seen[Child.getChildOffset()].clear();
}
}
// Returns a buffer pointing to a member file containing a given symbol.
// This function is thread-safe.
MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) {
auto COrErr = Sym->getMember();
error(COrErr, Twine("Could not get the member for symbol ") + Sym->getName());
const Archive::Child &C = *COrErr;
// Return an empty buffer if we have already returned the same buffer.
if (Seen[C.getChildOffset()].test_and_set())
return MemoryBufferRef();
ErrorOr<MemoryBufferRef> Ret = C.getMemoryBufferRef();
error(Ret, Twine("Could not get the buffer for the member defining symbol ") +
Sym->getName());
return *Ret;
}
void ObjectFile::parse() {
// Parse a memory buffer as a COFF file.
auto BinOrErr = createBinary(MB);
error(BinOrErr, "Failed to parse object file");
std::unique_ptr<Binary> Bin = std::move(*BinOrErr);
if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) {
Bin.release();
COFFObj.reset(Obj);
} else {
error(Twine(getName()) + " is not a COFF file.");
}
// Read section and symbol tables.
initializeChunks();
initializeSymbols();
initializeSEH();
}
void ObjectFile::initializeChunks() {
uint32_t NumSections = COFFObj->getNumberOfSections();
Chunks.reserve(NumSections);
SparseChunks.resize(NumSections + 1);
for (uint32_t I = 1; I < NumSections + 1; ++I) {
const coff_section *Sec;
StringRef Name;
std::error_code EC = COFFObj->getSection(I, Sec);
error(EC, Twine("getSection failed: #") + Twine(I));
EC = COFFObj->getSectionName(Sec, Name);
error(EC, Twine("getSectionName failed: #") + Twine(I));
if (Name == ".sxdata") {
SXData = Sec;
continue;
}
if (Name == ".drectve") {
ArrayRef<uint8_t> Data;
COFFObj->getSectionContents(Sec, Data);
Directives = std::string((const char *)Data.data(), Data.size());
continue;
}
// Skip non-DWARF debug info. MSVC linker converts the sections into
// a PDB file, but we don't support that.
if (Name == ".debug" || Name.startswith(".debug$"))
continue;
// We want to preserve DWARF debug sections only when /debug is on.
if (!Config->Debug && Name.startswith(".debug"))
continue;
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
continue;
auto *C = new (Alloc) SectionChunk(this, Sec);
Chunks.push_back(C);
SparseChunks[I] = C;
}
}
void ObjectFile::initializeSymbols() {
uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
SymbolBodies.reserve(NumSymbols);
SparseSymbolBodies.resize(NumSymbols);
llvm::SmallVector<Undefined *, 8> WeakAliases;
int32_t LastSectionNumber = 0;
for (uint32_t I = 0; I < NumSymbols; ++I) {
// Get a COFFSymbolRef object.
auto SymOrErr = COFFObj->getSymbol(I);
error(SymOrErr, Twine("broken object file: ") + getName());
COFFSymbolRef Sym = *SymOrErr;
const void *AuxP = nullptr;
if (Sym.getNumberOfAuxSymbols())
AuxP = COFFObj->getSymbol(I + 1)->getRawPtr();
bool IsFirst = (LastSectionNumber != Sym.getSectionNumber());
SymbolBody *Body = nullptr;
if (Sym.isUndefined()) {
Body = createUndefined(Sym);
} else if (Sym.isWeakExternal()) {
Body = createWeakExternal(Sym, AuxP);
WeakAliases.push_back((Undefined *)Body);
} else {
Body = createDefined(Sym, AuxP, IsFirst);
}
if (Body) {
SymbolBodies.push_back(Body);
SparseSymbolBodies[I] = Body;
}
I += Sym.getNumberOfAuxSymbols();
LastSectionNumber = Sym.getSectionNumber();
}
for (Undefined *U : WeakAliases)
U->WeakAlias = SparseSymbolBodies[(uintptr_t)U->WeakAlias];
}
Undefined *ObjectFile::createUndefined(COFFSymbolRef Sym) {
StringRef Name;
COFFObj->getSymbolName(Sym, Name);
return new (Alloc) Undefined(Name);
}
Undefined *ObjectFile::createWeakExternal(COFFSymbolRef Sym, const void *AuxP) {
StringRef Name;
COFFObj->getSymbolName(Sym, Name);
auto *U = new (Alloc) Undefined(Name);
auto *Aux = (const coff_aux_weak_external *)AuxP;
U->WeakAlias = (Undefined *)(uintptr_t)Aux->TagIndex;
return U;
}
Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
bool IsFirst) {
StringRef Name;
if (Sym.isCommon()) {
auto *C = new (Alloc) CommonChunk(Sym);
Chunks.push_back(C);
return new (Alloc) DefinedCommon(this, Sym, C);
}
if (Sym.isAbsolute()) {
COFFObj->getSymbolName(Sym, Name);
// Skip special symbols.
if (Name == "@comp.id")
return nullptr;
// COFF spec 5.10.1. The .sxdata section.
if (Name == "@feat.00") {
if (Sym.getValue() & 1)
SEHCompat = true;
return nullptr;
}
return new (Alloc) DefinedAbsolute(Name, Sym);
}
if (Sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_DEBUG)
return nullptr;
// Nothing else to do without a section chunk.
auto *SC = cast_or_null<SectionChunk>(SparseChunks[Sym.getSectionNumber()]);
if (!SC)
return nullptr;
// Handle section definitions
if (IsFirst && AuxP) {
auto *Aux = reinterpret_cast<const coff_aux_section_definition *>(AuxP);
if (Aux->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
if (auto *ParentSC = cast_or_null<SectionChunk>(
SparseChunks[Aux->getNumber(Sym.isBigObj())]))
ParentSC->addAssociative(SC);
SC->Checksum = Aux->CheckSum;
}
auto *B = new (Alloc) DefinedRegular(this, Sym, SC);
if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP)
SC->setSymbol(B);
return B;
}
void ObjectFile::initializeSEH() {
if (!SEHCompat || !SXData)
return;
ArrayRef<uint8_t> A;
COFFObj->getSectionContents(SXData, A);
if (A.size() % 4 != 0)
error(".sxdata must be an array of symbol table indices");
auto *I = reinterpret_cast<const ulittle32_t *>(A.data());
auto *E = reinterpret_cast<const ulittle32_t *>(A.data() + A.size());
for (; I != E; ++I)
SEHandlers.insert(SparseSymbolBodies[*I]);
}
MachineTypes ObjectFile::getMachineType() {
if (COFFObj)
return static_cast<MachineTypes>(COFFObj->getMachine());
return IMAGE_FILE_MACHINE_UNKNOWN;
}
StringRef ltrim1(StringRef S, const char *Chars) {
if (!S.empty() && strchr(Chars, S[0]))
return S.substr(1);
return S;
}
void ImportFile::parse() {
const char *Buf = MB.getBufferStart();
const char *End = MB.getBufferEnd();
const auto *Hdr = reinterpret_cast<const coff_import_header *>(Buf);
// Check if the total size is valid.
if ((size_t)(End - Buf) != (sizeof(*Hdr) + Hdr->SizeOfData))
error("broken import library");
// Read names and create an __imp_ symbol.
StringRef Name = StringAlloc.save(StringRef(Buf + sizeof(*Hdr)));
StringRef ImpName = StringAlloc.save(Twine("__imp_") + Name);
const char *NameStart = Buf + sizeof(coff_import_header) + Name.size() + 1;
DLLName = StringRef(NameStart);
StringRef ExtName;
switch (Hdr->getNameType()) {
case IMPORT_ORDINAL:
ExtName = "";
break;
case IMPORT_NAME:
ExtName = Name;
break;
case IMPORT_NAME_NOPREFIX:
ExtName = ltrim1(Name, "?@_");
break;
case IMPORT_NAME_UNDECORATE:
ExtName = ltrim1(Name, "?@_");
ExtName = ExtName.substr(0, ExtName.find('@'));
break;
}
ImpSym = new (Alloc) DefinedImportData(DLLName, ImpName, ExtName, Hdr);
SymbolBodies.push_back(ImpSym);
// If type is function, we need to create a thunk which jump to an
// address pointed by the __imp_ symbol. (This allows you to call
// DLL functions just like regular non-DLL functions.)
if (Hdr->getType() != llvm::COFF::IMPORT_CODE)
return;
ThunkSym = new (Alloc) DefinedImportThunk(Name, ImpSym, Hdr->Machine);
SymbolBodies.push_back(ThunkSym);
}
void BitcodeFile::parse() {
// Usually parse() is thread-safe, but bitcode file is an exception.
std::lock_guard<std::mutex> Lock(Mu);
ErrorOr<std::unique_ptr<LTOModule>> ModOrErr =
LTOModule::createFromBuffer(llvm::getGlobalContext(), MB.getBufferStart(),
MB.getBufferSize(), llvm::TargetOptions());
error(ModOrErr, "Could not create lto module");
M = std::move(*ModOrErr);
llvm::StringSaver Saver(Alloc);
for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) {
lto_symbol_attributes Attrs = M->getSymbolAttributes(I);
if ((Attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL)
continue;
StringRef SymName = Saver.save(M->getSymbolName(I));
int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK;
if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) {
SymbolBodies.push_back(new (Alloc) Undefined(SymName));
} else {
bool Replaceable =
(SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || // common
(Attrs & LTO_SYMBOL_COMDAT) || // comdat
(SymbolDef == LTO_SYMBOL_DEFINITION_WEAK && // weak external
(Attrs & LTO_SYMBOL_ALIAS)));
SymbolBodies.push_back(new (Alloc) DefinedBitcode(this, SymName,
Replaceable));
}
}
Directives = M->getLinkerOpts();
}
MachineTypes BitcodeFile::getMachineType() {
if (!M)
return IMAGE_FILE_MACHINE_UNKNOWN;
switch (Triple(M->getTargetTriple()).getArch()) {
case Triple::x86_64:
return AMD64;
case Triple::x86:
return I386;
case Triple::arm:
return ARMNT;
default:
return IMAGE_FILE_MACHINE_UNKNOWN;
}
}
std::mutex BitcodeFile::Mu;
} // namespace coff
} // namespace lld

222
COFF/InputFiles.h Normal file
View file

@ -0,0 +1,222 @@
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_INPUT_FILES_H
#define LLD_COFF_INPUT_FILES_H
#include "lld/Core/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/LTO/LTOModule.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/StringSaver.h"
#include <memory>
#include <mutex>
#include <set>
#include <vector>
namespace lld {
namespace coff {
using llvm::LTOModule;
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
using llvm::COFF::MachineTypes;
using llvm::object::Archive;
using llvm::object::COFFObjectFile;
using llvm::object::COFFSymbolRef;
using llvm::object::coff_section;
class Chunk;
class Defined;
class DefinedImportData;
class DefinedImportThunk;
class Lazy;
class SymbolBody;
class Undefined;
// The root class of input files.
class InputFile {
public:
enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind };
Kind kind() const { return FileKind; }
virtual ~InputFile() {}
// Returns the filename.
StringRef getName() { return MB.getBufferIdentifier(); }
// Returns symbols defined by this file.
virtual std::vector<SymbolBody *> &getSymbols() = 0;
// Reads a file (the constructor doesn't do that).
virtual void parse() = 0;
// Returns the CPU type this file was compiled to.
virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; }
// Returns a short, human-friendly filename. If this is a member of
// an archive file, a returned value includes parent's filename.
// Used for logging or debugging.
std::string getShortName();
// Sets a parent filename if this file is created from an archive.
void setParentName(StringRef N) { ParentName = N; }
// Returns .drectve section contents if exist.
StringRef getDirectives() { return StringRef(Directives).trim(); }
// Each file has a unique index. The index number is used to
// resolve ties in symbol resolution.
int Index;
static int NextIndex;
protected:
InputFile(Kind K, MemoryBufferRef M)
: Index(NextIndex++), MB(M), FileKind(K) {}
MemoryBufferRef MB;
std::string Directives;
private:
const Kind FileKind;
StringRef ParentName;
};
// .lib or .a file.
class ArchiveFile : public InputFile {
public:
explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
void parse() override;
// Returns a memory buffer for a given symbol. An empty memory buffer
// is returned if we have already returned the same memory buffer.
// (So that we don't instantiate same members more than once.)
MemoryBufferRef getMember(const Archive::Symbol *Sym);
llvm::MutableArrayRef<Lazy> getLazySymbols() { return LazySymbols; }
// All symbols returned by ArchiveFiles are of Lazy type.
std::vector<SymbolBody *> &getSymbols() override {
llvm_unreachable("internal error");
}
private:
std::unique_ptr<Archive> File;
std::string Filename;
std::vector<Lazy> LazySymbols;
std::map<uint64_t, std::atomic_flag> Seen;
};
// .obj or .o file. This may be a member of an archive file.
class ObjectFile : public InputFile {
public:
explicit ObjectFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
void parse() override;
MachineTypes getMachineType() override;
std::vector<Chunk *> &getChunks() { return Chunks; }
std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; }
// Returns a SymbolBody object for the SymbolIndex'th symbol in the
// underlying object file.
SymbolBody *getSymbolBody(uint32_t SymbolIndex) {
return SparseSymbolBodies[SymbolIndex];
}
// Returns the underying COFF file.
COFFObjectFile *getCOFFObj() { return COFFObj.get(); }
// True if this object file is compatible with SEH.
// COFF-specific and x86-only.
bool SEHCompat = false;
// The list of safe exception handlers listed in .sxdata section.
// COFF-specific and x86-only.
std::set<SymbolBody *> SEHandlers;
private:
void initializeChunks();
void initializeSymbols();
void initializeSEH();
Defined *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst);
Undefined *createUndefined(COFFSymbolRef Sym);
Undefined *createWeakExternal(COFFSymbolRef Sym, const void *Aux);
std::unique_ptr<COFFObjectFile> COFFObj;
llvm::BumpPtrAllocator Alloc;
const coff_section *SXData = nullptr;
// List of all chunks defined by this file. This includes both section
// chunks and non-section chunks for common symbols.
std::vector<Chunk *> Chunks;
// This vector contains the same chunks as Chunks, but they are
// indexed such that you can get a SectionChunk by section index.
// Nonexistent section indices are filled with null pointers.
// (Because section number is 1-based, the first slot is always a
// null pointer.)
std::vector<Chunk *> SparseChunks;
// List of all symbols referenced or defined by this file.
std::vector<SymbolBody *> SymbolBodies;
// This vector contains the same symbols as SymbolBodies, but they
// are indexed such that you can get a SymbolBody by symbol
// index. Nonexistent indices (which are occupied by auxiliary
// symbols in the real symbol table) are filled with null pointers.
std::vector<SymbolBody *> SparseSymbolBodies;
};
// This type represents import library members that contain DLL names
// and symbols exported from the DLLs. See Microsoft PE/COFF spec. 7
// for details about the format.
class ImportFile : public InputFile {
public:
explicit ImportFile(MemoryBufferRef M)
: InputFile(ImportKind, M), StringAlloc(StringAllocAux) {}
static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; }
DefinedImportData *ImpSym = nullptr;
DefinedImportThunk *ThunkSym = nullptr;
std::string DLLName;
private:
void parse() override;
std::vector<SymbolBody *> SymbolBodies;
llvm::BumpPtrAllocator Alloc;
llvm::BumpPtrAllocator StringAllocAux;
llvm::StringSaver StringAlloc;
};
// Used for LTO.
class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; }
MachineTypes getMachineType() override;
std::unique_ptr<LTOModule> takeModule() { return std::move(M); }
private:
void parse() override;
std::vector<SymbolBody *> SymbolBodies;
llvm::BumpPtrAllocator Alloc;
std::unique_ptr<LTOModule> M;
static std::mutex Mu;
};
} // namespace coff
} // namespace lld
#endif

61
COFF/MarkLive.cpp Normal file
View file

@ -0,0 +1,61 @@
//===- MarkLive.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 "Symbols.h"
#include "llvm/ADT/STLExtras.h"
#include <vector>
namespace lld {
namespace coff {
// Set live bit on for each reachable chunk. Unmarked (unreachable)
// COMDAT chunks will be ignored by Writer, so they will be excluded
// from the final output.
void markLive(const std::vector<Chunk *> &Chunks) {
// We build up a worklist of sections which have been marked as live. We only
// push into the worklist when we discover an unmarked section, and we mark
// as we push, so sections never appear twice in the list.
SmallVector<SectionChunk *, 256> Worklist;
// COMDAT section chunks are dead by default. Add non-COMDAT chunks.
for (Chunk *C : Chunks)
if (auto *SC = dyn_cast<SectionChunk>(C))
if (SC->isLive())
Worklist.push_back(SC);
auto Enqueue = [&](SectionChunk *C) {
if (C->isLive())
return;
C->markLive();
Worklist.push_back(C);
};
// Add GC root chunks.
for (Undefined *U : Config->GCRoot)
if (auto *D = dyn_cast<DefinedRegular>(U->repl()))
Enqueue(D->getChunk());
while (!Worklist.empty()) {
SectionChunk *SC = Worklist.pop_back_val();
assert(SC->isLive() && "We mark as live when pushing onto the worklist!");
// Mark all symbols listed in the relocation table for this section.
for (SymbolBody *S : SC->symbols())
if (auto *D = dyn_cast<DefinedRegular>(S->repl()))
Enqueue(D->getChunk());
// Mark associative sections if any.
for (SectionChunk *C : SC->children())
Enqueue(C);
}
}
}
}

291
COFF/ModuleDef.cpp Normal file
View file

@ -0,0 +1,291 @@
//===- COFF/ModuleDef.cpp -------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Windows-specific.
// A parser for the module-definition file (.def file).
// Parsed results are directly written to Config global variable.
//
// The format of module-definition files are described in this document:
// https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
//
//===----------------------------------------------------------------------===//
#include "Config.h"
#include "Error.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/raw_ostream.h"
#include <system_error>
using namespace llvm;
namespace lld {
namespace coff {
namespace {
enum Kind {
Unknown,
Eof,
Identifier,
Comma,
Equal,
KwBase,
KwData,
KwExports,
KwHeapsize,
KwLibrary,
KwName,
KwNoname,
KwPrivate,
KwStacksize,
KwVersion,
};
struct Token {
explicit Token(Kind T = Unknown, StringRef S = "") : K(T), Value(S) {}
Kind K;
StringRef Value;
};
static bool isDecorated(StringRef Sym) {
return Sym.startswith("_") || Sym.startswith("@") || Sym.startswith("?");
}
class Lexer {
public:
explicit Lexer(StringRef S) : Buf(S) {}
Token lex() {
Buf = Buf.trim();
if (Buf.empty())
return Token(Eof);
switch (Buf[0]) {
case '\0':
return Token(Eof);
case ';': {
size_t End = Buf.find('\n');
Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);
return lex();
}
case '=':
Buf = Buf.drop_front();
return Token(Equal, "=");
case ',':
Buf = Buf.drop_front();
return Token(Comma, ",");
case '"': {
StringRef S;
std::tie(S, Buf) = Buf.substr(1).split('"');
return Token(Identifier, S);
}
default: {
size_t End = Buf.find_first_of("=,\r\n \t\v");
StringRef Word = Buf.substr(0, End);
Kind K = llvm::StringSwitch<Kind>(Word)
.Case("BASE", KwBase)
.Case("DATA", KwData)
.Case("EXPORTS", KwExports)
.Case("HEAPSIZE", KwHeapsize)
.Case("LIBRARY", KwLibrary)
.Case("NAME", KwName)
.Case("NONAME", KwNoname)
.Case("PRIVATE", KwPrivate)
.Case("STACKSIZE", KwStacksize)
.Case("VERSION", KwVersion)
.Default(Identifier);
Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);
return Token(K, Word);
}
}
}
private:
StringRef Buf;
};
class Parser {
public:
explicit Parser(StringRef S, StringSaver *A) : Lex(S), Alloc(A) {}
void parse() {
do {
parseOne();
} while (Tok.K != Eof);
}
private:
void read() {
if (Stack.empty()) {
Tok = Lex.lex();
return;
}
Tok = Stack.back();
Stack.pop_back();
}
void readAsInt(uint64_t *I) {
read();
if (Tok.K != Identifier || Tok.Value.getAsInteger(10, *I))
error("integer expected");
}
void expect(Kind Expected, StringRef Msg) {
read();
if (Tok.K != Expected)
error(Msg);
}
void unget() { Stack.push_back(Tok); }
void parseOne() {
read();
switch (Tok.K) {
case Eof:
return;
case KwExports:
for (;;) {
read();
if (Tok.K != Identifier) {
unget();
return;
}
parseExport();
}
case KwHeapsize:
parseNumbers(&Config->HeapReserve, &Config->HeapCommit);
return;
case KwLibrary:
parseName(&Config->OutputFile, &Config->ImageBase);
if (!StringRef(Config->OutputFile).endswith_lower(".dll"))
Config->OutputFile += ".dll";
return;
case KwStacksize:
parseNumbers(&Config->StackReserve, &Config->StackCommit);
return;
case KwName:
parseName(&Config->OutputFile, &Config->ImageBase);
return;
case KwVersion:
parseVersion(&Config->MajorImageVersion, &Config->MinorImageVersion);
return;
default:
error(Twine("unknown directive: ") + Tok.Value);
}
}
void parseExport() {
Export E;
E.Name = Tok.Value;
read();
if (Tok.K == Equal) {
read();
if (Tok.K != Identifier)
error(Twine("identifier expected, but got ") + Tok.Value);
E.ExtName = E.Name;
E.Name = Tok.Value;
} else {
unget();
}
if (Config->Machine == I386) {
if (!isDecorated(E.Name))
E.Name = Alloc->save("_" + E.Name);
if (!E.ExtName.empty() && !isDecorated(E.ExtName))
E.ExtName = Alloc->save("_" + E.ExtName);
}
for (;;) {
read();
if (Tok.K == Identifier && Tok.Value[0] == '@') {
Tok.Value.drop_front().getAsInteger(10, E.Ordinal);
read();
if (Tok.K == KwNoname) {
E.Noname = true;
} else {
unget();
}
continue;
}
if (Tok.K == KwData) {
E.Data = true;
continue;
}
if (Tok.K == KwPrivate) {
E.Private = true;
continue;
}
unget();
Config->Exports.push_back(E);
return;
}
}
// HEAPSIZE/STACKSIZE reserve[,commit]
void parseNumbers(uint64_t *Reserve, uint64_t *Commit) {
readAsInt(Reserve);
read();
if (Tok.K != Comma) {
unget();
Commit = nullptr;
return;
}
readAsInt(Commit);
}
// NAME outputPath [BASE=address]
void parseName(std::string *Out, uint64_t *Baseaddr) {
read();
if (Tok.K == Identifier) {
*Out = Tok.Value;
} else {
*Out = "";
unget();
return;
}
read();
if (Tok.K == KwBase) {
expect(Equal, "'=' expected");
readAsInt(Baseaddr);
} else {
unget();
*Baseaddr = 0;
}
}
// VERSION major[.minor]
void parseVersion(uint32_t *Major, uint32_t *Minor) {
read();
if (Tok.K != Identifier)
error(Twine("identifier expected, but got ") + Tok.Value);
StringRef V1, V2;
std::tie(V1, V2) = Tok.Value.split('.');
if (V1.getAsInteger(10, *Major))
error(Twine("integer expected, but got ") + Tok.Value);
if (V2.empty())
*Minor = 0;
else if (V2.getAsInteger(10, *Minor))
error(Twine("integer expected, but got ") + Tok.Value);
}
Lexer Lex;
Token Tok;
std::vector<Token> Stack;
StringSaver *Alloc;
};
} // anonymous namespace
void parseModuleDefs(MemoryBufferRef MB, StringSaver *Alloc) {
Parser(MB.getBuffer(), Alloc).parse();
}
} // namespace coff
} // namespace lld

View file

@ -15,31 +15,32 @@ multiclass B<string name, string help> {
def _no : F<name#":no">, HelpText<help>;
}
def align : P<"align", "Section alignment">;
def alternatename : P<"alternatename", "Define weak alias">;
def base : P<"base", "Base address of the program">;
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
def nodefaultlib : P<"nodefaultlib", "Remove a default library">;
def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
def delayload : P<"delayload", "Delay loaded DLL name">;
def entry : P<"entry", "Name of entry point symbol">;
// No help text because /failifmismatch is not intended to be used by the user.
def export : P<"export", "Export a function">;
// No help text because /failifmismatch is not intended to be used by the user.
def failifmismatch : P<"failifmismatch", "">;
def heap : P<"heap", "Size of the heap">;
def align : P<"align", "Section alignment">;
def libpath : P<"libpath", "Additional library search path">;
def mllvm : P<"mllvm", "Options to pass to LLVM">;
def out : P<"out", "Path to file to write output">;
def stack : P<"stack", "Size of the stack">;
def machine : P<"machine", "Specify target platform">;
def version : P<"version", "Specify a version number in the PE header">;
def merge : P<"merge", "Combine sections">;
def section : P<"section", "Specify section attributes">;
def subsystem : P<"subsystem", "Specify subsystem">;
def stub : P<"stub", "Specify DOS stub file">;
def opt : P<"opt", "Control optimizations">;
def implib : P<"implib", "Import library name">;
def delayload : P<"delayload", "Delay loaded DLL name">;
def libpath : P<"libpath", "Additional library search path">;
def machine : P<"machine", "Specify target platform">;
def merge : P<"merge", "Combine sections">;
def mllvm : P<"mllvm", "Options to pass to LLVM">;
def nodefaultlib : P<"nodefaultlib", "Remove a default library">;
def opt : P<"opt", "Control optimizations">;
def out : P<"out", "Path to file to write output">;
def pdb : P<"pdb", "PDB file path">;
def section : P<"section", "Specify section attributes">;
def stack : P<"stack", "Size of the stack">;
def stub : P<"stub", "Specify DOS stub file">;
def subsystem : P<"subsystem", "Specify subsystem">;
def version : P<"version", "Specify a version number in the PE header">;
def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
def manifest : F<"manifest">;
def manifest_colon : P<"manifest", "Create manifest file">;
@ -58,37 +59,38 @@ def incl : Joined<["/", "-"], "include:">,
def deffile : Joined<["/", "-"], "def:">,
HelpText<"Use module-definition file">;
def debug : F<"debug">, HelpText<"Embed a symbol table in the image">;
def dll : F<"dll">, HelpText<"Create a DLL">;
def nodefaultlib_all : F<"nodefaultlib">;
def noentry : F<"noentry">;
def dll : F<"dll">;
def verbose : F<"verbose">;
def debug : F<"debug">;
def profile : F<"profile">;
def swaprun_cd : F<"swaprun:cd">;
def swaprun_net : F<"swaprun:net">;
def profile : F<"profile">;
def verbose : F<"verbose">;
def force : F<"force">,
HelpText<"Allow undefined symbols when creating executables">;
def force_unresolved : F<"force:unresolved">;
defm nxcompat : B<"nxcompat", "Disable data execution provention">;
defm largeaddressaware : B<"largeaddressaware", "Disable large addresses">;
defm allowbind: B<"allowbind", "Disable DLL binding">;
defm fixed : B<"fixed", "Enable base relocations">;
defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">;
defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">;
defm dynamicbase : B<"dynamicbase",
"Disable address space layout randomization">;
defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler">;
"Disable address space layout randomization">;
defm fixed : B<"fixed", "Enable base relocations">;
defm highentropyva : B<"highentropyva", "Set HIGH_ENTROPY_VA bit">;
defm largeaddressaware : B<"largeaddressaware", "Disable large addresses">;
defm nxcompat : B<"nxcompat", "Disable data execution provention">;
defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler">;
defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">;
def help : F<"help">;
def help_q : Flag<["/?", "-?"], "">, Alias<help>;
def DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>;
// LLD extensions
def nosymtab : F<"nosymtab">;
// Flag for debug
def lldmoduledeffile : Joined<["/", "-"], "lldmoduledeffile:">;
// Flags for debugging
def lldmap : Joined<["/", "-"], "lldmap:">;
//==============================================================================
// The flags below do nothing. They are defined only for link.exe compatibility.
@ -106,6 +108,8 @@ def ignoreidl : F<"ignoreidl">;
def incremental : F<"incremental">;
def no_incremental : F<"incremental:no">;
def nologo : F<"nologo">;
def throwingnew : F<"throwingnew">;
def editandcontinue : F<"editandcontinue">;
def delay : QF<"delay">;
def errorreport : QF<"errorreport">;
@ -116,5 +120,6 @@ def pdbaltpath : QF<"pdbaltpath">;
def tlbid : QF<"tlbid">;
def tlbout : QF<"tlbout">;
def verbose_all : QF<"verbose">;
def guardsym : QF<"guardsym">;
defm wx : QB<"wx">;

60
COFF/PDB.cpp Normal file
View file

@ -0,0 +1,60 @@
//===- PDB.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 "Error.h"
#include "Symbols.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
#include <memory>
using namespace llvm;
using namespace llvm::support;
using namespace llvm::support::endian;
const int PageSize = 4096;
const uint8_t Magic[32] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0";
namespace {
struct PDBHeader {
uint8_t Magic[32];
ulittle32_t PageSize;
ulittle32_t FpmPage;
ulittle32_t PageCount;
ulittle32_t RootSize;
ulittle32_t Reserved;
ulittle32_t RootPointer;
};
}
void lld::coff::createPDB(StringRef Path) {
// Create a file.
size_t FileSize = PageSize * 3;
ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
FileOutputBuffer::create(Path, FileSize);
error(BufferOrErr, Twine("failed to open ") + Path);
std::unique_ptr<FileOutputBuffer> Buffer = std::move(*BufferOrErr);
// Write the file header.
uint8_t *Buf = Buffer->getBufferStart();
auto *Hdr = reinterpret_cast<PDBHeader *>(Buf);
memcpy(Hdr->Magic, Magic, sizeof(Magic));
Hdr->PageSize = PageSize;
// I don't know what FpmPage field means, but it must not be 0.
Hdr->FpmPage = 1;
Hdr->PageCount = FileSize / PageSize;
// Root directory is empty, containing only the length field.
Hdr->RootSize = 4;
// Root directory is on page 1.
Hdr->RootPointer = 1;
// Write the root directory. Root stream is on page 2.
write32le(Buf + PageSize, 2);
Buffer->commit();
}

265
COFF/README.md Normal file
View file

@ -0,0 +1,265 @@
The PE/COFF Linker
==================
This directory contains a linker for Windows operating system.
Because the fundamental design of this port is different from
the other ports of LLD, this port is separated to this directory.
The linker is command-line compatible with MSVC linker and is
generally 2x faster than that. It can be used to link real-world
programs such as LLD itself or Clang, or even web browsers which
are probably the largest open-source programs for Windows.
This document is also applicable to ELF linker because the linker
shares the same design as this COFF linker.
Overall Design
--------------
This is a list of important data types in this linker.
* SymbolBody
SymbolBody is a class for symbols. They may be created for symbols
in object files or in archive file headers. The linker may create
them out of nothing.
There are mainly three types of SymbolBodies: Defined, Undefined, or
Lazy. Defined symbols are for all symbols that are considered as
"resolved", including real defined symbols, COMDAT symbols, common
symbols, absolute symbols, linker-created symbols, etc. Undefined
symbols are for undefined symbols, which need to be replaced by
Defined symbols by the resolver. Lazy symbols represent symbols we
found in archive file headers -- which can turn into Defined symbols
if we read archieve members, but we haven't done that yet.
* Symbol
Symbol is a pointer to a SymbolBody. There's only one Symbol for
each unique symbol name (this uniqueness is guaranteed by the symbol
table). Because SymbolBodies are created for each file
independently, there can be many SymbolBodies for the same
name. Thus, the relationship between Symbols and SymbolBodies is 1:N.
The resolver keeps the Symbol's pointer to always point to the "best"
SymbolBody. Pointer mutation is the resolve operation in this
linker.
SymbolBodies have pointers to their Symbols. That means you can
always find the best SymbolBody from any SymbolBody by following
pointers twice. This structure makes it very easy to find
replacements for symbols. For example, if you have an Undefined
SymbolBody, you can find a Defined SymbolBody for that symbol just
by going to its Symbol and then to SymbolBody, assuming the resolver
have successfully resolved all undefined symbols.
* Chunk
Chunk represents a chunk of data that will occupy space in an
output. Each regular section becomes a chunk.
Chunks created for common or BSS symbols are not backed by sections.
The linker may create chunks out of nothing to append additional
data to an output.
Chunks know about their size, how to copy their data to mmap'ed
outputs, and how to apply relocations to them. Specifically,
section-based chunks know how to read relocation tables and how to
apply them.
* SymbolTable
SymbolTable is basically a hash table from strings to Symbols, with
a logic to resolve symbol conflicts. It resolves conflicts by symbol
type. For example, if we add Undefined and Defined symbols, the
symbol table will keep the latter. If we add Defined and Lazy
symbols, it will keep the former. If we add Lazy and Undefined, it
will keep the former, but it will also trigger the Lazy symbol to
load the archive member to actually resolve the symbol.
* OutputSection
OutputSection is a container of Chunks. A Chunk belongs to at most
one OutputSection.
There are mainly three actors in this linker.
* InputFile
InputFile is a superclass of file readers. We have a different
subclass for each input file type, such as regular object file,
archive file, etc. They are responsible for creating and owning
SymbolBodies and Chunks.
* Writer
The writer is responsible for writing file headers and Chunks to a
file. It creates OutputSections, put all Chunks into them, assign
unique, non-overlapping addresses and file offsets to them, and then
write them down to a file.
* Driver
The linking process is drived by the driver. The driver
- processes command line options,
- creates a symbol table,
- creates an InputFile for each input file and put all symbols in it
into the symbol table,
- checks if there's no remaining undefined symbols,
- creates a writer,
- and passes the symbol table to the writer to write the result to a
file.
Performance
-----------
It's generally 2x faster than MSVC link.exe. It takes 3.5 seconds to
self-host on my Xeon 2580 machine. MSVC linker takes 7.0 seconds to
link the same executable. The resulting output is 65MB.
The old LLD is buggy that it produces 120MB executable for some reason,
and it takes 30 seconds to do that.
We believe the performance difference comes from simplification and
optimizations we made to the new port. Notable differences are listed
below.
* Reduced number of relocation table reads
In the old design, relocation tables are read from beginning to
construct graphs because they consist of graph edges. In the new
design, they are not read until we actually apply relocations.
This simplification has two benefits. One is that we don't create
additional objects for relocations but instead consume relocation
tables directly. The other is that it reduces number of relocation
entries we have to read, because we won't read relocations for
dead-stripped COMDAT sections. Large C++ programs tend to consist of
lots of COMDAT sections. In the old design, the time to process
relocation table is linear to size of input. In this new model, it's
linear to size of output.
* Reduced number of symbol table lookup
Symbol table lookup can be a heavy operation because number of
symbols can be very large and each symbol name can be very long
(think of C++ mangled symbols -- time to compute a hash value for a
string is linear to the length.)
We look up the symbol table exactly only once for each symbol in the
new design. This is I believe the minimum possible number. This is
achieved by the separation of Symbol and SymbolBody. Once you get a
pointer to a Symbol by looking up the symbol table, you can always
get the latest symbol resolution result by just dereferencing a
pointer. (I'm not sure if the idea is new to the linker. At least,
all other linkers I've investigated so far seem to look up hash
tables or sets more than once for each new symbol, but I may be
wrong.)
* Reduced number of file visits
The symbol table implements the Windows linker semantics. We treat
the symbol table as a bucket of all known symbols, including symbols
in archive file headers. We put all symbols into one bucket as we
visit new files. That means we visit each file only once.
This is different from the Unix linker semantics, in which we only
keep undefined symbols and visit each file one by one until we
resolve all undefined symbols. In the Unix model, we have to visit
archive files many times if there are circular dependencies between
archives.
* Avoiding creating additional objects or copying data
The data structures described in the previous section are all thin
wrappers for classes that LLVM libObject provides. We avoid copying
data from libObject's objects to our objects. We read much less data
than before. For example, we don't read symbol values until we apply
relocations because these values are not relevant to symbol
resolution. Again, COMDAT symbols may be discarded during symbol
resolution, so reading their attributes too early could result in a
waste. We use underlying objects directly where doing so makes
sense.
Parallelism
-----------
The abovementioned data structures are also chosen with
multi-threading in mind. It should relatively be easy to make the
symbol table a concurrent hash map, so that we let multiple workers
work on symbol table concurrently. Symbol resolution in this design is
a single pointer mutation, which allows the resolver work concurrently
in a lock-free manner using atomic pointer compare-and-swap.
It should also be easy to apply relocations and write chunks concurrently.
We created an experimental multi-threaded linker using the Microsoft
ConcRT concurrency library, and it was able to link itself in 0.5
seconds, so we think the design is promising.
Link-Time Optimization
----------------------
LTO is implemented by handling LLVM bitcode files as object files.
The linker resolves symbols in bitcode files normally. If all symbols
are successfully resolved, it then calls an LLVM libLTO function
with all bitcode files to convert them to one big regular COFF file.
Finally, the linker replaces bitcode symbols with COFF symbols,
so that we can link the input files as if they were in the native
format from the beginning.
The details are described in this document.
http://llvm.org/docs/LinkTimeOptimization.html
Glossary
--------
* RVA
Short for Relative Virtual Address.
Windows executables or DLLs are not position-independent; they are
linked against a fixed address called an image base. RVAs are
offsets from an image base.
Default image bases are 0x140000000 for executables and 0x18000000
for DLLs. For example, when we are creating an executable, we assume
that the executable will be loaded at address 0x140000000 by the
loader, so we apply relocations accordingly. Result texts and data
will contain raw absolute addresses.
* VA
Short for Virtual Address. Equivalent to RVA + image base. It is
rarely used. We almost always use RVAs instead.
* Base relocations
Relocation information for the loader. If the loader decides to map
an executable or a DLL to a different address than their image
bases, it fixes up binaries using information contained in the base
relocation table. A base relocation table consists of a list of
locations containing addresses. The loader adds a difference between
RVA and actual load address to all locations listed there.
Note that this run-time relocation mechanism is much simpler than ELF.
There's no PLT or GOT. Images are relocated as a whole just
by shifting entire images in memory by some offsets. Although doing
this breaks text sharing, I think this mechanism is not actually bad
on today's computers.
* ICF
Short for Identical COMDAT Folding.
ICF is an optimization to reduce output size by merging COMDAT sections
by not only their names but by their contents. If two COMDAT sections
happen to have the same metadata, actual contents and relocations,
they are merged by ICF. It is known as an effective technique,
and it usually reduces C++ program's size by a few percent or more.
Note that this is not entirely sound optimization. C/C++ require
different functions have different addresses. If a program depends on
that property, it would fail at runtime. However, that's not really an
issue on Windows because MSVC link.exe enabled the optimization by
default. As long as your program works with the linker's default
settings, your program should be safe with ICF.

445
COFF/SymbolTable.cpp Normal file
View file

@ -0,0 +1,445 @@
//===- SymbolTable.cpp ----------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Config.h"
#include "Driver.h"
#include "Error.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "lld/Core/Parallel.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/LTO/LTOCodeGenerator.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
using namespace llvm;
namespace lld {
namespace coff {
void SymbolTable::addFile(std::unique_ptr<InputFile> FileP) {
#if LLVM_ENABLE_THREADS
std::launch Policy = std::launch::async;
#else
std::launch Policy = std::launch::deferred;
#endif
InputFile *File = FileP.get();
Files.push_back(std::move(FileP));
if (auto *F = dyn_cast<ArchiveFile>(File)) {
ArchiveQueue.push_back(
std::async(Policy, [=]() { F->parse(); return F; }));
return;
}
ObjectQueue.push_back(
std::async(Policy, [=]() { File->parse(); return File; }));
if (auto *F = dyn_cast<ObjectFile>(File)) {
ObjectFiles.push_back(F);
} else if (auto *F = dyn_cast<BitcodeFile>(File)) {
BitcodeFiles.push_back(F);
} else {
ImportFiles.push_back(cast<ImportFile>(File));
}
}
void SymbolTable::step() {
if (queueEmpty())
return;
readObjects();
readArchives();
}
void SymbolTable::run() {
while (!queueEmpty())
step();
}
void SymbolTable::readArchives() {
if (ArchiveQueue.empty())
return;
// Add lazy symbols to the symbol table. Lazy symbols that conflict
// with existing undefined symbols are accumulated in LazySyms.
std::vector<Symbol *> LazySyms;
for (std::future<ArchiveFile *> &Future : ArchiveQueue) {
ArchiveFile *File = Future.get();
if (Config->Verbose)
llvm::outs() << "Reading " << File->getShortName() << "\n";
for (Lazy &Sym : File->getLazySymbols())
addLazy(&Sym, &LazySyms);
}
ArchiveQueue.clear();
// Add archive member files to ObjectQueue that should resolve
// existing undefined symbols.
for (Symbol *Sym : LazySyms)
addMemberFile(cast<Lazy>(Sym->Body));
}
void SymbolTable::readObjects() {
if (ObjectQueue.empty())
return;
// Add defined and undefined symbols to the symbol table.
std::vector<StringRef> Directives;
for (size_t I = 0; I < ObjectQueue.size(); ++I) {
InputFile *File = ObjectQueue[I].get();
if (Config->Verbose)
llvm::outs() << "Reading " << File->getShortName() << "\n";
// Adding symbols may add more files to ObjectQueue
// (but not to ArchiveQueue).
for (SymbolBody *Sym : File->getSymbols())
if (Sym->isExternal())
addSymbol(Sym);
StringRef S = File->getDirectives();
if (!S.empty()) {
Directives.push_back(S);
if (Config->Verbose)
llvm::outs() << "Directives: " << File->getShortName()
<< ": " << S << "\n";
}
}
ObjectQueue.clear();
// Parse directive sections. This may add files to
// ArchiveQueue and ObjectQueue.
for (StringRef S : Directives)
Driver->parseDirectives(S);
}
bool SymbolTable::queueEmpty() {
return ArchiveQueue.empty() && ObjectQueue.empty();
}
void SymbolTable::reportRemainingUndefines(bool Resolve) {
llvm::SmallPtrSet<SymbolBody *, 8> Undefs;
for (auto &I : Symtab) {
Symbol *Sym = I.second;
auto *Undef = dyn_cast<Undefined>(Sym->Body);
if (!Undef)
continue;
StringRef Name = Undef->getName();
// A weak alias may have been resolved, so check for that.
if (Defined *D = Undef->getWeakAlias()) {
if (Resolve)
Sym->Body = D;
continue;
}
// If we can resolve a symbol by removing __imp_ prefix, do that.
// This odd rule is for compatibility with MSVC linker.
if (Name.startswith("__imp_")) {
Symbol *Imp = find(Name.substr(strlen("__imp_")));
if (Imp && isa<Defined>(Imp->Body)) {
if (!Resolve)
continue;
auto *D = cast<Defined>(Imp->Body);
auto *S = new (Alloc) DefinedLocalImport(Name, D);
LocalImportChunks.push_back(S->getChunk());
Sym->Body = S;
continue;
}
}
// Remaining undefined symbols are not fatal if /force is specified.
// They are replaced with dummy defined symbols.
if (Config->Force && Resolve)
Sym->Body = new (Alloc) DefinedAbsolute(Name, 0);
Undefs.insert(Sym->Body);
}
if (Undefs.empty())
return;
for (Undefined *U : Config->GCRoot)
if (Undefs.count(U->repl()))
llvm::errs() << "<root>: undefined symbol: " << U->getName() << "\n";
for (std::unique_ptr<InputFile> &File : Files)
if (!isa<ArchiveFile>(File.get()))
for (SymbolBody *Sym : File->getSymbols())
if (Undefs.count(Sym->repl()))
llvm::errs() << File->getShortName() << ": undefined symbol: "
<< Sym->getName() << "\n";
if (!Config->Force)
error("Link failed");
}
void SymbolTable::addLazy(Lazy *New, std::vector<Symbol *> *Accum) {
Symbol *Sym = insert(New);
if (Sym->Body == New)
return;
SymbolBody *Existing = Sym->Body;
if (isa<Defined>(Existing))
return;
if (Lazy *L = dyn_cast<Lazy>(Existing))
if (L->getFileIndex() < New->getFileIndex())
return;
Sym->Body = New;
New->setBackref(Sym);
if (isa<Undefined>(Existing))
Accum->push_back(Sym);
}
void SymbolTable::addSymbol(SymbolBody *New) {
// Find an existing symbol or create and insert a new one.
assert(isa<Defined>(New) || isa<Undefined>(New));
Symbol *Sym = insert(New);
if (Sym->Body == New)
return;
SymbolBody *Existing = Sym->Body;
// If we have an undefined symbol and a lazy symbol,
// let the lazy symbol to read a member file.
if (auto *L = dyn_cast<Lazy>(Existing)) {
// Undefined symbols with weak aliases need not to be resolved,
// since they would be replaced with weak aliases if they remain
// undefined.
if (auto *U = dyn_cast<Undefined>(New)) {
if (!U->WeakAlias) {
addMemberFile(L);
return;
}
}
Sym->Body = New;
return;
}
// compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
// equivalent (conflicting), or more preferable, respectively.
int Comp = Existing->compare(New);
if (Comp == 0)
error(Twine("duplicate symbol: ") + Existing->getDebugName() + " and " +
New->getDebugName());
if (Comp < 0)
Sym->Body = New;
}
Symbol *SymbolTable::insert(SymbolBody *New) {
Symbol *&Sym = Symtab[New->getName()];
if (Sym) {
New->setBackref(Sym);
return Sym;
}
Sym = new (Alloc) Symbol(New);
New->setBackref(Sym);
return Sym;
}
// Reads an archive member file pointed by a given symbol.
void SymbolTable::addMemberFile(Lazy *Body) {
std::unique_ptr<InputFile> File = Body->getMember();
// getMember returns an empty buffer if the member was already
// read from the library.
if (!File)
return;
if (Config->Verbose)
llvm::outs() << "Loaded " << File->getShortName() << " for "
<< Body->getName() << "\n";
addFile(std::move(File));
}
std::vector<Chunk *> SymbolTable::getChunks() {
std::vector<Chunk *> Res;
for (ObjectFile *File : ObjectFiles) {
std::vector<Chunk *> &V = File->getChunks();
Res.insert(Res.end(), V.begin(), V.end());
}
return Res;
}
Symbol *SymbolTable::find(StringRef Name) {
auto It = Symtab.find(Name);
if (It == Symtab.end())
return nullptr;
return It->second;
}
Symbol *SymbolTable::findUnderscore(StringRef Name) {
if (Config->Machine == I386)
return find(("_" + Name).str());
return find(Name);
}
StringRef SymbolTable::findByPrefix(StringRef Prefix) {
for (auto Pair : Symtab) {
StringRef Name = Pair.first;
if (Name.startswith(Prefix))
return Name;
}
return "";
}
StringRef SymbolTable::findMangle(StringRef Name) {
if (Symbol *Sym = find(Name))
if (!isa<Undefined>(Sym->Body))
return Name;
if (Config->Machine != I386)
return findByPrefix(("?" + Name + "@@Y").str());
if (!Name.startswith("_"))
return "";
// Search for x86 C function.
StringRef S = findByPrefix((Name + "@").str());
if (!S.empty())
return S;
// Search for x86 C++ non-member function.
return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
}
void SymbolTable::mangleMaybe(Undefined *U) {
if (U->WeakAlias)
return;
if (!isa<Undefined>(U->repl()))
return;
StringRef Alias = findMangle(U->getName());
if (!Alias.empty())
U->WeakAlias = addUndefined(Alias);
}
Undefined *SymbolTable::addUndefined(StringRef Name) {
auto *New = new (Alloc) Undefined(Name);
addSymbol(New);
if (auto *U = dyn_cast<Undefined>(New->repl()))
return U;
return New;
}
DefinedRelative *SymbolTable::addRelative(StringRef Name, uint64_t VA) {
auto *New = new (Alloc) DefinedRelative(Name, VA);
addSymbol(New);
return New;
}
DefinedAbsolute *SymbolTable::addAbsolute(StringRef Name, uint64_t VA) {
auto *New = new (Alloc) DefinedAbsolute(Name, VA);
addSymbol(New);
return New;
}
void SymbolTable::printMap(llvm::raw_ostream &OS) {
for (ObjectFile *File : ObjectFiles) {
OS << File->getShortName() << ":\n";
for (SymbolBody *Body : File->getSymbols())
if (auto *R = dyn_cast<DefinedRegular>(Body))
if (R->getChunk()->isLive())
OS << Twine::utohexstr(Config->ImageBase + R->getRVA())
<< " " << R->getName() << "\n";
}
}
void SymbolTable::addCombinedLTOObject(ObjectFile *Obj) {
for (SymbolBody *Body : Obj->getSymbols()) {
if (!Body->isExternal())
continue;
// We should not see any new undefined symbols at this point, but we'll
// diagnose them later in reportRemainingUndefines().
StringRef Name = Body->getName();
Symbol *Sym = insert(Body);
if (isa<DefinedBitcode>(Sym->Body)) {
Sym->Body = Body;
continue;
}
if (auto *L = dyn_cast<Lazy>(Sym->Body)) {
// We may see new references to runtime library symbols such as __chkstk
// here. These symbols must be wholly defined in non-bitcode files.
addMemberFile(L);
continue;
}
SymbolBody *Existing = Sym->Body;
int Comp = Existing->compare(Body);
if (Comp == 0)
error(Twine("LTO: unexpected duplicate symbol: ") + Name);
if (Comp < 0)
Sym->Body = Body;
}
}
void SymbolTable::addCombinedLTOObjects() {
if (BitcodeFiles.empty())
return;
// Diagnose any undefined symbols early, but do not resolve weak externals,
// as resolution breaks the invariant that each Symbol points to a unique
// SymbolBody, which we rely on to replace DefinedBitcode symbols correctly.
reportRemainingUndefines(/*Resolve=*/false);
// Create an object file and add it to the symbol table by replacing any
// DefinedBitcode symbols with the definitions in the object file.
LTOCodeGenerator CG(getGlobalContext());
CG.setOptLevel(Config->LTOOptLevel);
std::vector<ObjectFile *> Objs = createLTOObjects(&CG);
for (ObjectFile *Obj : Objs)
addCombinedLTOObject(Obj);
size_t NumBitcodeFiles = BitcodeFiles.size();
run();
if (BitcodeFiles.size() != NumBitcodeFiles)
error("LTO: late loaded symbol created new bitcode reference");
}
// Combine and compile bitcode files and then return the result
// as a vector of regular COFF object files.
std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
// All symbols referenced by non-bitcode objects must be preserved.
for (ObjectFile *File : ObjectFiles)
for (SymbolBody *Body : File->getSymbols())
if (auto *S = dyn_cast<DefinedBitcode>(Body->repl()))
CG->addMustPreserveSymbol(S->getName());
// Likewise for bitcode symbols which we initially resolved to non-bitcode.
for (BitcodeFile *File : BitcodeFiles)
for (SymbolBody *Body : File->getSymbols())
if (isa<DefinedBitcode>(Body) && !isa<DefinedBitcode>(Body->repl()))
CG->addMustPreserveSymbol(Body->getName());
// Likewise for other symbols that must be preserved.
for (Undefined *U : Config->GCRoot) {
if (auto *S = dyn_cast<DefinedBitcode>(U->repl()))
CG->addMustPreserveSymbol(S->getName());
else if (auto *S = dyn_cast_or_null<DefinedBitcode>(U->getWeakAlias()))
CG->addMustPreserveSymbol(S->getName());
}
CG->setModule(BitcodeFiles[0]->takeModule());
for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I)
CG->addModule(BitcodeFiles[I]->takeModule().get());
bool DisableVerify = true;
#ifdef NDEBUG
DisableVerify = false;
#endif
if (!CG->optimize(DisableVerify, false, false, false))
error(""); // optimize() should have emitted any error message.
Objs.resize(Config->LTOJobs);
// Use std::list to avoid invalidation of pointers in OSPtrs.
std::list<raw_svector_ostream> OSs;
std::vector<raw_pwrite_stream *> OSPtrs;
for (SmallVector<char, 0> &Obj : Objs) {
OSs.emplace_back(Obj);
OSPtrs.push_back(&OSs.back());
}
if (!CG->compileOptimized(OSPtrs))
error(""); // compileOptimized() should have emitted any error message.
std::vector<ObjectFile *> ObjFiles;
for (SmallVector<char, 0> &Obj : Objs) {
auto *ObjFile = new ObjectFile(
MemoryBufferRef(StringRef(Obj.data(), Obj.size()), "<LTO object>"));
Files.emplace_back(ObjFile);
ObjectFiles.push_back(ObjFile);
ObjFile->parse();
ObjFiles.push_back(ObjFile);
}
return ObjFiles;
}
} // namespace coff
} // namespace lld

125
COFF/SymbolTable.h Normal file
View file

@ -0,0 +1,125 @@
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_SYMBOL_TABLE_H
#define LLD_COFF_SYMBOL_TABLE_H
#include "InputFiles.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/raw_ostream.h"
#ifdef _MSC_VER
// <future> depends on <eh.h> for __uncaught_exception.
#include <eh.h>
#endif
#include <future>
namespace llvm {
struct LTOCodeGenerator;
}
namespace lld {
namespace coff {
class Chunk;
class Defined;
class Lazy;
class SymbolBody;
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().
class SymbolTable {
public:
void addFile(std::unique_ptr<InputFile> File);
std::vector<std::unique_ptr<InputFile>> &getFiles() { return Files; }
void step();
void run();
bool queueEmpty();
// Print an error message on undefined symbols. If Resolve is true, try to
// resolve any undefined symbols and update the symbol table accordingly.
void reportRemainingUndefines(bool Resolve);
// Returns a list of chunks of selected symbols.
std::vector<Chunk *> getChunks();
// Returns a symbol for a given name. Returns a nullptr if not found.
Symbol *find(StringRef Name);
Symbol *findUnderscore(StringRef Name);
// Occasionally we have to resolve an undefined symbol to its
// mangled symbol. This function tries to find a mangled name
// for U from the symbol table, and if found, set the symbol as
// a weak alias for U.
void mangleMaybe(Undefined *U);
StringRef findMangle(StringRef Name);
// Print a layout map to OS.
void printMap(llvm::raw_ostream &OS);
// Build a set of COFF objects representing the combined contents of
// BitcodeFiles and add them to the symbol table. Called after all files are
// added and before the writer writes results to a file.
void addCombinedLTOObjects();
// The writer needs to handle DLL import libraries specially in
// order to create the import descriptor table.
std::vector<ImportFile *> ImportFiles;
// The writer needs to infer the machine type from the object files.
std::vector<ObjectFile *> ObjectFiles;
// Creates an Undefined symbol for a given name.
Undefined *addUndefined(StringRef Name);
DefinedRelative *addRelative(StringRef Name, uint64_t VA);
DefinedAbsolute *addAbsolute(StringRef Name, uint64_t VA);
// A list of chunks which to be added to .rdata.
std::vector<Chunk *> LocalImportChunks;
private:
void readArchives();
void readObjects();
void addSymbol(SymbolBody *New);
void addLazy(Lazy *New, std::vector<Symbol *> *Accum);
Symbol *insert(SymbolBody *New);
StringRef findByPrefix(StringRef Prefix);
void addMemberFile(Lazy *Body);
void addCombinedLTOObject(ObjectFile *Obj);
std::vector<ObjectFile *> createLTOObjects(llvm::LTOCodeGenerator *CG);
llvm::DenseMap<StringRef, Symbol *> Symtab;
std::vector<std::unique_ptr<InputFile>> Files;
std::vector<std::future<ArchiveFile *>> ArchiveQueue;
std::vector<std::future<InputFile *>> ObjectQueue;
std::vector<BitcodeFile *> BitcodeFiles;
std::vector<SmallVector<char, 0>> Objs;
llvm::BumpPtrAllocator Alloc;
};
} // namespace coff
} // namespace lld
#endif

243
COFF/Symbols.cpp Normal file
View file

@ -0,0 +1,243 @@
//===- Symbols.cpp --------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm::object;
using llvm::sys::fs::identify_magic;
using llvm::sys::fs::file_magic;
namespace lld {
namespace coff {
StringRef SymbolBody::getName() {
// DefinedCOFF names are read lazily for a performance reason.
// Non-external symbol names are never used by the linker except for logging
// or debugging. Their internal references are resolved not by name but by
// symbol index. And because they are not external, no one can refer them by
// name. Object files contain lots of non-external symbols, and creating
// StringRefs for them (which involves lots of strlen() on the string table)
// is a waste of time.
if (Name.empty()) {
auto *D = cast<DefinedCOFF>(this);
D->File->getCOFFObj()->getSymbolName(D->Sym, Name);
}
return Name;
}
// Returns 1, 0 or -1 if this symbol should take precedence
// over the Other, tie or lose, respectively.
int SymbolBody::compare(SymbolBody *Other) {
Kind LK = kind(), RK = Other->kind();
// Normalize so that the smaller kind is on the left.
if (LK > RK)
return -Other->compare(this);
// First handle comparisons between two different kinds.
if (LK != RK) {
if (RK > LastDefinedKind) {
if (LK == LazyKind && cast<Undefined>(Other)->WeakAlias)
return -1;
// The LHS is either defined or lazy and so it wins.
assert((LK <= LastDefinedKind || LK == LazyKind) && "Bad kind!");
return 1;
}
// Bitcode has special complexities.
if (RK == DefinedBitcodeKind) {
auto *RHS = cast<DefinedBitcode>(Other);
switch (LK) {
case DefinedCommonKind:
return 1;
case DefinedRegularKind:
// As an approximation, regular symbols win over bitcode symbols,
// but we definitely have a conflict if the regular symbol is not
// replaceable and neither is the bitcode symbol. We do not
// replicate the rest of the symbol resolution logic here; symbol
// resolution will be done accurately after lowering bitcode symbols
// to regular symbols in addCombinedLTOObject().
if (cast<DefinedRegular>(this)->isCOMDAT() || RHS->IsReplaceable)
return 1;
// Fallthrough to the default of a tie otherwise.
default:
return 0;
}
}
// Either of the object file kind will trump a higher kind.
if (LK <= LastDefinedCOFFKind)
return 1;
// The remaining kind pairs are ties amongst defined symbols.
return 0;
}
// Now handle the case where the kinds are the same.
switch (LK) {
case DefinedRegularKind: {
auto *LHS = cast<DefinedRegular>(this);
auto *RHS = cast<DefinedRegular>(Other);
if (LHS->isCOMDAT() && RHS->isCOMDAT())
return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1;
return 0;
}
case DefinedCommonKind: {
auto *LHS = cast<DefinedCommon>(this);
auto *RHS = cast<DefinedCommon>(Other);
if (LHS->getSize() == RHS->getSize())
return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1;
return LHS->getSize() > RHS->getSize() ? 1 : -1;
}
case DefinedBitcodeKind: {
auto *LHS = cast<DefinedBitcode>(this);
auto *RHS = cast<DefinedBitcode>(Other);
// If both are non-replaceable, we have a tie.
if (!LHS->IsReplaceable && !RHS->IsReplaceable)
return 0;
// Non-replaceable symbols win, but even two replaceable symboles don't
// tie. If both symbols are replaceable, choice is arbitrary.
if (RHS->IsReplaceable && LHS->IsReplaceable)
return uintptr_t(LHS) < uintptr_t(RHS) ? 1 : -1;
return LHS->IsReplaceable ? -1 : 1;
}
case LazyKind: {
// Don't tie, pick the earliest.
auto *LHS = cast<Lazy>(this);
auto *RHS = cast<Lazy>(Other);
return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1;
}
case UndefinedKind: {
auto *LHS = cast<Undefined>(this);
auto *RHS = cast<Undefined>(Other);
// Tie if both undefined symbols have different weak aliases.
if (LHS->WeakAlias && RHS->WeakAlias) {
if (LHS->WeakAlias->getName() != RHS->WeakAlias->getName())
return 0;
return uintptr_t(LHS) < uintptr_t(RHS) ? 1 : -1;
}
return LHS->WeakAlias ? 1 : -1;
}
case DefinedLocalImportKind:
case DefinedImportThunkKind:
case DefinedImportDataKind:
case DefinedAbsoluteKind:
case DefinedRelativeKind:
// These all simply tie.
return 0;
}
llvm_unreachable("unknown symbol kind");
}
std::string SymbolBody::getDebugName() {
std::string N = getName().str();
if (auto *D = dyn_cast<DefinedCOFF>(this)) {
N += " ";
N += D->File->getShortName();
} else if (auto *D = dyn_cast<DefinedBitcode>(this)) {
N += " ";
N += D->File->getShortName();
}
return N;
}
uint64_t Defined::getFileOff() {
switch (kind()) {
case DefinedImportDataKind:
return cast<DefinedImportData>(this)->getFileOff();
case DefinedImportThunkKind:
return cast<DefinedImportThunk>(this)->getFileOff();
case DefinedLocalImportKind:
return cast<DefinedLocalImport>(this)->getFileOff();
case DefinedCommonKind:
return cast<DefinedCommon>(this)->getFileOff();
case DefinedRegularKind:
return cast<DefinedRegular>(this)->getFileOff();
case DefinedBitcodeKind:
llvm_unreachable("There is no file offset for a bitcode symbol.");
case DefinedAbsoluteKind:
llvm_unreachable("Cannot get a file offset for an absolute symbol.");
case DefinedRelativeKind:
llvm_unreachable("Cannot get a file offset for a relative symbol.");
case LazyKind:
case UndefinedKind:
llvm_unreachable("Cannot get a file offset for an undefined symbol.");
}
llvm_unreachable("unknown symbol kind");
}
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
size_t SymSize = File->getCOFFObj()->getSymbolTableEntrySize();
if (SymSize == sizeof(coff_symbol16))
return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym));
assert(SymSize == sizeof(coff_symbol32));
return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(Sym));
}
DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S,
uint16_t Machine)
: Defined(DefinedImportThunkKind, Name) {
switch (Machine) {
case AMD64: Data.reset(new ImportThunkChunkX64(S)); return;
case I386: Data.reset(new ImportThunkChunkX86(S)); return;
case ARMNT: Data.reset(new ImportThunkChunkARM(S)); return;
default: llvm_unreachable("unknown machine type");
}
}
std::unique_ptr<InputFile> Lazy::getMember() {
MemoryBufferRef MBRef = File->getMember(&Sym);
// getMember returns an empty buffer if the member was already
// read from the library.
if (MBRef.getBuffer().empty())
return std::unique_ptr<InputFile>(nullptr);
file_magic Magic = identify_magic(MBRef.getBuffer());
if (Magic == file_magic::coff_import_library)
return std::unique_ptr<InputFile>(new ImportFile(MBRef));
std::unique_ptr<InputFile> Obj;
if (Magic == file_magic::coff_object)
Obj.reset(new ObjectFile(MBRef));
else if (Magic == file_magic::bitcode)
Obj.reset(new BitcodeFile(MBRef));
else
error(Twine(File->getName()) + ": unknown file type");
Obj->setParentName(File->getName());
return Obj;
}
Defined *Undefined::getWeakAlias() {
// A weak alias may be a weak alias to another symbol, so check recursively.
for (SymbolBody *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
if (auto *D = dyn_cast<Defined>(A->repl()))
return D;
return nullptr;
}
} // namespace coff
} // namespace lld

407
COFF/Symbols.h Normal file
View file

@ -0,0 +1,407 @@
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_SYMBOLS_H
#define LLD_COFF_SYMBOLS_H
#include "Chunks.h"
#include "Config.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include <atomic>
#include <memory>
#include <vector>
namespace lld {
namespace coff {
using llvm::object::Archive;
using llvm::object::COFFSymbolRef;
using llvm::object::coff_import_header;
using llvm::object::coff_symbol_generic;
class ArchiveFile;
class BitcodeFile;
class InputFile;
class ObjectFile;
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 {
// The order of these is significant. We start with the regular defined
// symbols as those are the most prevelant and the zero tag is the cheapest
// to set. Among the defined kinds, the lower the kind is preferred over
// the higher kind when testing wether one symbol should take precedence
// over another.
DefinedRegularKind = 0,
DefinedCommonKind,
DefinedLocalImportKind,
DefinedImportThunkKind,
DefinedImportDataKind,
DefinedAbsoluteKind,
DefinedRelativeKind,
DefinedBitcodeKind,
UndefinedKind,
LazyKind,
LastDefinedCOFFKind = DefinedCommonKind,
LastDefinedKind = DefinedBitcodeKind,
};
Kind kind() const { return static_cast<Kind>(SymbolKind); }
// Returns true if this is an external symbol.
bool isExternal() { return IsExternal; }
// Returns the symbol name.
StringRef getName();
// 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 *repl() { 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.
int compare(SymbolBody *Other);
// Returns a name of this symbol including source file name.
// Used only for debugging and logging.
std::string getDebugName();
protected:
explicit SymbolBody(Kind K, StringRef N = "")
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
IsReplaceable(false), Name(N) {}
const unsigned SymbolKind : 8;
unsigned IsExternal : 1;
// This bit is used by the \c DefinedRegular subclass.
unsigned IsCOMDAT : 1;
// This bit is used by the \c DefinedBitcode subclass.
unsigned IsReplaceable : 1;
StringRef Name;
Symbol *Backref = nullptr;
};
// The base class for any defined symbols, including absolute symbols,
// etc.
class Defined : public SymbolBody {
public:
Defined(Kind K, StringRef N = "") : SymbolBody(K, N) {}
static bool classof(const SymbolBody *S) {
return S->kind() <= LastDefinedKind;
}
// Returns the RVA (relative virtual address) of this symbol. The
// writer sets and uses RVAs.
uint64_t getRVA();
// Returns the file offset of this symbol in the final executable.
// The writer uses this information to apply relocations.
uint64_t getFileOff();
// Returns the RVA relative to the beginning of the output section.
// Used to implement SECREL relocation type.
uint64_t getSecrel();
// Returns the output section index.
// Used to implement SECTION relocation type.
uint64_t getSectionIndex();
// Returns true if this symbol points to an executable (e.g. .text) section.
// Used to implement ARM relocations.
bool isExecutable();
};
// Symbols defined via a COFF object file.
class DefinedCOFF : public Defined {
friend SymbolBody;
public:
DefinedCOFF(Kind K, ObjectFile *F, COFFSymbolRef S)
: Defined(K), File(F), Sym(S.getGeneric()) {}
static bool classof(const SymbolBody *S) {
return S->kind() <= LastDefinedCOFFKind;
}
int getFileIndex() { return File->Index; }
COFFSymbolRef getCOFFSymbol();
protected:
ObjectFile *File;
const coff_symbol_generic *Sym;
};
// Regular defined symbols read from object file symbol tables.
class DefinedRegular : public DefinedCOFF {
public:
DefinedRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C)
: DefinedCOFF(DefinedRegularKind, F, S), Data(&C->Repl) {
IsExternal = S.isExternal();
IsCOMDAT = C->isCOMDAT();
}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedRegularKind;
}
uint64_t getRVA() { return (*Data)->getRVA() + Sym->Value; }
bool isCOMDAT() { return IsCOMDAT; }
SectionChunk *getChunk() { return *Data; }
uint32_t getValue() { return Sym->Value; }
private:
SectionChunk **Data;
};
class DefinedCommon : public DefinedCOFF {
public:
DefinedCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C)
: DefinedCOFF(DefinedCommonKind, F, S), Data(C) {
IsExternal = S.isExternal();
}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedCommonKind;
}
uint64_t getRVA() { return Data->getRVA(); }
private:
friend SymbolBody;
uint64_t getSize() { return Sym->Value; }
CommonChunk *Data;
};
// Absolute symbols.
class DefinedAbsolute : public Defined {
public:
DefinedAbsolute(StringRef N, COFFSymbolRef S)
: Defined(DefinedAbsoluteKind, N), VA(S.getValue()) {
IsExternal = S.isExternal();
}
DefinedAbsolute(StringRef N, uint64_t V)
: Defined(DefinedAbsoluteKind, N), VA(V) {}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedAbsoluteKind;
}
uint64_t getRVA() { return VA - Config->ImageBase; }
void setVA(uint64_t V) { VA = V; }
private:
uint64_t VA;
};
// This is a kind of absolute symbol but relative to the image base.
// Unlike absolute symbols, relocations referring this kind of symbols
// are subject of the base relocation. This type is used rarely --
// mainly for __ImageBase.
class DefinedRelative : public Defined {
public:
explicit DefinedRelative(StringRef Name, uint64_t V = 0)
: Defined(DefinedRelativeKind, Name), RVA(V) {}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedRelativeKind;
}
uint64_t getRVA() { return RVA; }
void setRVA(uint64_t V) { RVA = V; }
private:
uint64_t RVA;
};
// This class represents a symbol defined in an archive file. It is
// created from an archive file header, and it knows how to load an
// object file from an archive to replace itself with a defined
// symbol. If the resolver finds both Undefined and Lazy for
// the same name, it will ask the Lazy to load a file.
class Lazy : public SymbolBody {
public:
Lazy(ArchiveFile *F, const Archive::Symbol S)
: SymbolBody(LazyKind, S.getName()), File(F), Sym(S) {}
static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; }
// Returns an object file for this symbol, or a nullptr if the file
// was already returned.
std::unique_ptr<InputFile> getMember();
int getFileIndex() { return File->Index; }
private:
ArchiveFile *File;
const Archive::Symbol Sym;
};
// Undefined symbols.
class Undefined : public SymbolBody {
public:
explicit Undefined(StringRef N) : SymbolBody(UndefinedKind, N) {}
static bool classof(const SymbolBody *S) {
return S->kind() == UndefinedKind;
}
// An undefined symbol can have a fallback symbol which gives an
// undefined symbol a second chance if it would remain undefined.
// If it remains undefined, it'll be replaced with whatever the
// Alias pointer points to.
SymbolBody *WeakAlias = nullptr;
// If this symbol is external weak, try to resolve it to a defined
// symbol by searching the chain of fallback symbols. Returns the symbol if
// successful, otherwise returns null.
Defined *getWeakAlias();
};
// Windows-specific classes.
// This class represents a symbol imported from a DLL. This has two
// names for internal use and external use. The former is used for
// name resolution, and the latter is used for the import descriptor
// table in an output. The former has "__imp_" prefix.
class DefinedImportData : public Defined {
public:
DefinedImportData(StringRef D, StringRef N, StringRef E,
const coff_import_header *H)
: Defined(DefinedImportDataKind, N), DLLName(D), ExternalName(E), Hdr(H) {
}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedImportDataKind;
}
uint64_t getRVA() { return Location->getRVA(); }
StringRef getDLLName() { return DLLName; }
StringRef getExternalName() { return ExternalName; }
void setLocation(Chunk *AddressTable) { Location = AddressTable; }
uint16_t getOrdinal() { return Hdr->OrdinalHint; }
private:
StringRef DLLName;
StringRef ExternalName;
const coff_import_header *Hdr;
Chunk *Location = nullptr;
};
// This class represents a symbol for a jump table entry which jumps
// to a function in a DLL. Linker are supposed to create such symbols
// without "__imp_" prefix for all function symbols exported from
// DLLs, so that you can call DLL functions as regular functions with
// a regular name. A function pointer is given as a DefinedImportData.
class DefinedImportThunk : public Defined {
public:
DefinedImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine);
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedImportThunkKind;
}
uint64_t getRVA() { return Data->getRVA(); }
Chunk *getChunk() { return Data.get(); }
private:
std::unique_ptr<Chunk> Data;
};
// If you have a symbol "__imp_foo" in your object file, a symbol name
// "foo" becomes automatically available as a pointer to "__imp_foo".
// This class is for such automatically-created symbols.
// Yes, this is an odd feature. We didn't intend to implement that.
// This is here just for compatibility with MSVC.
class DefinedLocalImport : public Defined {
public:
DefinedLocalImport(StringRef N, Defined *S)
: Defined(DefinedLocalImportKind, N), Data(S) {}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedLocalImportKind;
}
uint64_t getRVA() { return Data.getRVA(); }
Chunk *getChunk() { return &Data; }
private:
LocalImportChunk Data;
};
class DefinedBitcode : public Defined {
friend SymbolBody;
public:
DefinedBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable)
: Defined(DefinedBitcodeKind, N), File(F) {
this->IsReplaceable = IsReplaceable;
}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedBitcodeKind;
}
private:
BitcodeFile *File;
};
inline uint64_t Defined::getRVA() {
switch (kind()) {
case DefinedAbsoluteKind:
return cast<DefinedAbsolute>(this)->getRVA();
case DefinedRelativeKind:
return cast<DefinedRelative>(this)->getRVA();
case DefinedImportDataKind:
return cast<DefinedImportData>(this)->getRVA();
case DefinedImportThunkKind:
return cast<DefinedImportThunk>(this)->getRVA();
case DefinedLocalImportKind:
return cast<DefinedLocalImport>(this)->getRVA();
case DefinedCommonKind:
return cast<DefinedCommon>(this)->getRVA();
case DefinedRegularKind:
return cast<DefinedRegular>(this)->getRVA();
case DefinedBitcodeKind:
llvm_unreachable("There is no address for a bitcode symbol.");
case LazyKind:
case UndefinedKind:
llvm_unreachable("Cannot get the address for an undefined symbol.");
}
llvm_unreachable("unknown symbol kind");
}
} // namespace coff
} // namespace lld
#endif

765
COFF/Writer.cpp Normal file
View file

@ -0,0 +1,765 @@
//===- Writer.cpp ---------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Config.h"
#include "DLL.h"
#include "Error.h"
#include "InputFiles.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
#include "lld/Core/Parallel.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdio>
#include <map>
#include <memory>
#include <utility>
using namespace llvm;
using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
using namespace lld;
using namespace lld::coff;
static const int PageSize = 4096;
static const int SectorSize = 512;
static const int DOSStubSize = 64;
static const int NumberfOfDataDirectory = 16;
namespace {
// The writer writes a SymbolTable result to a file.
class Writer {
public:
Writer(SymbolTable *T) : Symtab(T) {}
void run();
private:
void createSections();
void createMiscChunks();
void createImportTables();
void createExportTable();
void assignAddresses();
void removeEmptySections();
void createSymbolAndStringTable();
void openFile(StringRef OutputPath);
template <typename PEHeaderTy> void writeHeader();
void fixSafeSEHSymbols();
void writeSections();
void sortExceptionTable();
void applyRelocations();
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
size_t addEntryToStringTable(StringRef Str);
OutputSection *findSection(StringRef Name);
OutputSection *createSection(StringRef Name);
void addBaserels(OutputSection *Dest);
void addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V);
uint32_t getSizeOfInitializedData();
std::map<StringRef, std::vector<DefinedImportData *>> binImports();
SymbolTable *Symtab;
std::unique_ptr<llvm::FileOutputBuffer> Buffer;
llvm::SpecificBumpPtrAllocator<OutputSection> CAlloc;
llvm::SpecificBumpPtrAllocator<BaserelChunk> BAlloc;
std::vector<OutputSection *> OutputSections;
std::vector<char> Strtab;
std::vector<llvm::object::coff_symbol16> OutputSymtab;
IdataContents Idata;
DelayLoadContents DelayIdata;
EdataContents Edata;
std::unique_ptr<SEHTableChunk> SEHTable;
uint64_t FileSize;
uint32_t PointerToSymbolTable = 0;
uint64_t SizeOfImage;
uint64_t SizeOfHeaders;
std::vector<std::unique_ptr<Chunk>> Chunks;
};
} // anonymous namespace
namespace lld {
namespace coff {
void writeResult(SymbolTable *T) { Writer(T).run(); }
// 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 RVAs.
class OutputSection {
public:
OutputSection(StringRef N) : Name(N), Header({}) {}
void setRVA(uint64_t);
void setFileOffset(uint64_t);
void addChunk(Chunk *C);
StringRef getName() { return Name; }
std::vector<Chunk *> &getChunks() { return Chunks; }
void addPermissions(uint32_t C);
uint32_t getPermissions() { return Header.Characteristics & PermMask; }
uint32_t getCharacteristics() { return Header.Characteristics; }
uint64_t getRVA() { return Header.VirtualAddress; }
uint64_t getFileOff() { return Header.PointerToRawData; }
void writeHeaderTo(uint8_t *Buf);
// Returns the size of this section in an executable memory image.
// This may be smaller than the raw size (the raw size is multiple
// of disk sector size, so there may be padding at end), or may be
// larger (if that's the case, the loader reserves spaces after end
// of raw data).
uint64_t getVirtualSize() { return Header.VirtualSize; }
// Returns the size of the section in the output file.
uint64_t getRawSize() { return Header.SizeOfRawData; }
// 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; }
// N.B. The section index is one based.
uint32_t SectionIndex = 0;
private:
StringRef Name;
coff_section Header;
uint32_t StringTableOff = 0;
std::vector<Chunk *> Chunks;
};
void OutputSection::setRVA(uint64_t RVA) {
Header.VirtualAddress = RVA;
for (Chunk *C : Chunks)
C->setRVA(C->getRVA() + RVA);
}
void OutputSection::setFileOffset(uint64_t Off) {
// If a section has no actual data (i.e. BSS section), we want to
// set 0 to its PointerToRawData. Otherwise the output is rejected
// by the loader.
if (Header.SizeOfRawData == 0)
return;
Header.PointerToRawData = Off;
}
void OutputSection::addChunk(Chunk *C) {
Chunks.push_back(C);
C->setOutputSection(this);
uint64_t Off = Header.VirtualSize;
Off = RoundUpToAlignment(Off, C->getAlign());
C->setRVA(Off);
C->setOutputSectionOff(Off);
Off += C->getSize();
Header.VirtualSize = Off;
if (C->hasData())
Header.SizeOfRawData = RoundUpToAlignment(Off, SectorSize);
}
void OutputSection::addPermissions(uint32_t C) {
Header.Characteristics |= C & PermMask;
}
// Write the section header to a given buffer.
void OutputSection::writeHeaderTo(uint8_t *Buf) {
auto *Hdr = reinterpret_cast<coff_section *>(Buf);
*Hdr = Header;
if (StringTableOff) {
// If name is too long, write offset into the string table as a name.
sprintf(Hdr->Name, "/%d", StringTableOff);
} else {
assert(!Config->Debug || Name.size() <= COFF::NameSize);
strncpy(Hdr->Name, Name.data(),
std::min(Name.size(), (size_t)COFF::NameSize));
}
}
uint64_t Defined::getSecrel() {
if (auto *D = dyn_cast<DefinedRegular>(this))
return getRVA() - D->getChunk()->getOutputSection()->getRVA();
error("SECREL relocation points to a non-regular symbol");
}
uint64_t Defined::getSectionIndex() {
if (auto *D = dyn_cast<DefinedRegular>(this))
return D->getChunk()->getOutputSection()->SectionIndex;
error("SECTION relocation points to a non-regular symbol");
}
bool Defined::isExecutable() {
const auto X = IMAGE_SCN_MEM_EXECUTE;
if (auto *D = dyn_cast<DefinedRegular>(this))
return D->getChunk()->getOutputSection()->getPermissions() & X;
return isa<DefinedImportThunk>(this);
}
} // namespace coff
} // namespace lld
// The main function of the writer.
void Writer::run() {
createSections();
createMiscChunks();
createImportTables();
createExportTable();
if (Config->Relocatable)
createSection(".reloc");
assignAddresses();
removeEmptySections();
createSymbolAndStringTable();
openFile(Config->OutputFile);
if (Config->is64()) {
writeHeader<pe32plus_header>();
} else {
writeHeader<pe32_header>();
}
fixSafeSEHSymbols();
writeSections();
sortExceptionTable();
error(Buffer->commit(), "Failed to write the output file");
}
static StringRef getOutputSection(StringRef Name) {
StringRef S = Name.split('$').first;
auto It = Config->Merge.find(S);
if (It == Config->Merge.end())
return S;
return It->second;
}
// Create output section objects and add them to OutputSections.
void Writer::createSections() {
// First, bin chunks by name.
std::map<StringRef, std::vector<Chunk *>> Map;
for (Chunk *C : Symtab->getChunks()) {
auto *SC = dyn_cast<SectionChunk>(C);
if (SC && !SC->isLive()) {
if (Config->Verbose)
SC->printDiscardedMessage();
continue;
}
Map[C->getSectionName()].push_back(C);
}
// Then create an OutputSection for each section.
// '$' and all following characters in input section names are
// discarded when determining output section. So, .text$foo
// contributes to .text, for example. See PE/COFF spec 3.2.
SmallDenseMap<StringRef, OutputSection *> Sections;
for (auto Pair : Map) {
StringRef Name = getOutputSection(Pair.first);
OutputSection *&Sec = Sections[Name];
if (!Sec) {
Sec = new (CAlloc.Allocate()) OutputSection(Name);
OutputSections.push_back(Sec);
}
std::vector<Chunk *> &Chunks = Pair.second;
for (Chunk *C : Chunks) {
Sec->addChunk(C);
Sec->addPermissions(C->getPermissions());
}
}
}
void Writer::createMiscChunks() {
// Create thunks for locally-dllimported symbols.
if (!Symtab->LocalImportChunks.empty()) {
OutputSection *Sec = createSection(".rdata");
for (Chunk *C : Symtab->LocalImportChunks)
Sec->addChunk(C);
}
// Create SEH table. x86-only.
if (Config->Machine != I386)
return;
std::set<Defined *> Handlers;
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
if (!File->SEHCompat)
return;
for (SymbolBody *B : File->SEHandlers)
Handlers.insert(cast<Defined>(B->repl()));
}
SEHTable.reset(new SEHTableChunk(Handlers));
createSection(".rdata")->addChunk(SEHTable.get());
}
// Create .idata section for the DLL-imported symbol table.
// The format of this section is inherently Windows-specific.
// IdataContents class abstracted away the details for us,
// so we just let it create chunks and add them to the section.
void Writer::createImportTables() {
if (Symtab->ImportFiles.empty())
return;
// Initialize DLLOrder so that import entries are ordered in
// the same order as in the command line. (That affects DLL
// initialization order, and this ordering is MSVC-compatible.)
for (ImportFile *File : Symtab->ImportFiles) {
std::string DLL = StringRef(File->DLLName).lower();
if (Config->DLLOrder.count(DLL) == 0)
Config->DLLOrder[DLL] = Config->DLLOrder.size();
}
OutputSection *Text = createSection(".text");
for (ImportFile *File : Symtab->ImportFiles) {
if (DefinedImportThunk *Thunk = File->ThunkSym)
Text->addChunk(Thunk->getChunk());
if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) {
DelayIdata.add(File->ImpSym);
} else {
Idata.add(File->ImpSym);
}
}
if (!Idata.empty()) {
OutputSection *Sec = createSection(".idata");
for (Chunk *C : Idata.getChunks())
Sec->addChunk(C);
}
if (!DelayIdata.empty()) {
Defined *Helper = cast<Defined>(Config->DelayLoadHelper->repl());
DelayIdata.create(Helper);
OutputSection *Sec = createSection(".didat");
for (Chunk *C : DelayIdata.getChunks())
Sec->addChunk(C);
Sec = createSection(".data");
for (Chunk *C : DelayIdata.getDataChunks())
Sec->addChunk(C);
Sec = createSection(".text");
for (std::unique_ptr<Chunk> &C : DelayIdata.getCodeChunks())
Sec->addChunk(C.get());
}
}
void Writer::createExportTable() {
if (Config->Exports.empty())
return;
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() {
auto IsEmpty = [](OutputSection *S) { return S->getVirtualSize() == 0; };
OutputSections.erase(
std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty),
OutputSections.end());
uint32_t Idx = 1;
for (OutputSection *Sec : OutputSections)
Sec->SectionIndex = Idx++;
}
size_t Writer::addEntryToStringTable(StringRef Str) {
assert(Str.size() > COFF::NameSize);
size_t OffsetOfEntry = Strtab.size() + 4; // +4 for the size field
Strtab.insert(Strtab.end(), Str.begin(), Str.end());
Strtab.push_back('\0');
return OffsetOfEntry;
}
Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
if (auto *D = dyn_cast<DefinedRegular>(Def))
if (!D->getChunk()->isLive())
return None;
coff_symbol16 Sym;
StringRef Name = Def->getName();
if (Name.size() > COFF::NameSize) {
Sym.Name.Offset.Zeroes = 0;
Sym.Name.Offset.Offset = addEntryToStringTable(Name);
} else {
memset(Sym.Name.ShortName, 0, COFF::NameSize);
memcpy(Sym.Name.ShortName, Name.data(), Name.size());
}
if (auto *D = dyn_cast<DefinedCOFF>(Def)) {
COFFSymbolRef Ref = D->getCOFFSymbol();
Sym.Type = Ref.getType();
Sym.StorageClass = Ref.getStorageClass();
} else {
Sym.Type = IMAGE_SYM_TYPE_NULL;
Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
}
Sym.NumberOfAuxSymbols = 0;
switch (Def->kind()) {
case SymbolBody::DefinedAbsoluteKind:
case SymbolBody::DefinedRelativeKind:
Sym.Value = Def->getRVA();
Sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
break;
default: {
uint64_t RVA = Def->getRVA();
OutputSection *Sec = nullptr;
for (OutputSection *S : OutputSections) {
if (S->getRVA() > RVA)
break;
Sec = S;
}
Sym.Value = RVA - Sec->getRVA();
Sym.SectionNumber = Sec->SectionIndex;
break;
}
}
return Sym;
}
void Writer::createSymbolAndStringTable() {
if (!Config->Debug || !Config->WriteSymtab)
return;
// Name field in the section table is 8 byte long. Longer names need
// to be written to the string table. First, construct string table.
for (OutputSection *Sec : OutputSections) {
StringRef Name = Sec->getName();
if (Name.size() <= COFF::NameSize)
continue;
Sec->setStringTableOff(addEntryToStringTable(Name));
}
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles)
for (SymbolBody *B : File->getSymbols())
if (auto *D = dyn_cast<Defined>(B))
if (Optional<coff_symbol16> Sym = createSymbol(D))
OutputSymtab.push_back(*Sym);
for (ImportFile *File : Symtab->ImportFiles)
for (SymbolBody *B : File->getSymbols())
if (Optional<coff_symbol16> Sym = createSymbol(cast<Defined>(B)))
OutputSymtab.push_back(*Sym);
OutputSection *LastSection = OutputSections.back();
// We position the symbol table to be adjacent to the end of the last section.
uint64_t FileOff =
LastSection->getFileOff() +
RoundUpToAlignment(LastSection->getRawSize(), SectorSize);
if (!OutputSymtab.empty()) {
PointerToSymbolTable = FileOff;
FileOff += OutputSymtab.size() * sizeof(coff_symbol16);
}
if (!Strtab.empty())
FileOff += Strtab.size() + 4;
FileSize = RoundUpToAlignment(FileOff, SectorSize);
}
// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses() {
SizeOfHeaders = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
sizeof(data_directory) * NumberfOfDataDirectory +
sizeof(coff_section) * OutputSections.size();
SizeOfHeaders +=
Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
SizeOfHeaders = RoundUpToAlignment(SizeOfHeaders, SectorSize);
uint64_t RVA = 0x1000; // The first page is kept unmapped.
FileSize = SizeOfHeaders;
// Move DISCARDABLE (or non-memory-mapped) sections to the end of file because
// the loader cannot handle holes.
std::stable_partition(
OutputSections.begin(), OutputSections.end(), [](OutputSection *S) {
return (S->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0;
});
for (OutputSection *Sec : OutputSections) {
if (Sec->getName() == ".reloc")
addBaserels(Sec);
Sec->setRVA(RVA);
Sec->setFileOffset(FileSize);
RVA += RoundUpToAlignment(Sec->getVirtualSize(), PageSize);
FileSize += RoundUpToAlignment(Sec->getRawSize(), SectorSize);
}
SizeOfImage = SizeOfHeaders + RoundUpToAlignment(RVA - 0x1000, PageSize);
}
template <typename PEHeaderTy> void Writer::writeHeader() {
// Write DOS stub
uint8_t *Buf = Buffer->getBufferStart();
auto *DOS = reinterpret_cast<dos_header *>(Buf);
Buf += DOSStubSize;
DOS->Magic[0] = 'M';
DOS->Magic[1] = 'Z';
DOS->AddressOfRelocationTable = sizeof(dos_header);
DOS->AddressOfNewExeHeader = DOSStubSize;
// Write PE magic
memcpy(Buf, PEMagic, sizeof(PEMagic));
Buf += sizeof(PEMagic);
// Write COFF header
auto *COFF = reinterpret_cast<coff_file_header *>(Buf);
Buf += sizeof(*COFF);
COFF->Machine = Config->Machine;
COFF->NumberOfSections = OutputSections.size();
COFF->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
if (Config->LargeAddressAware)
COFF->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
if (!Config->is64())
COFF->Characteristics |= IMAGE_FILE_32BIT_MACHINE;
if (Config->DLL)
COFF->Characteristics |= IMAGE_FILE_DLL;
if (!Config->Relocatable)
COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
COFF->SizeOfOptionalHeader =
sizeof(PEHeaderTy) + sizeof(data_directory) * NumberfOfDataDirectory;
// Write PE header
auto *PE = reinterpret_cast<PEHeaderTy *>(Buf);
Buf += sizeof(*PE);
PE->Magic = Config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32;
PE->ImageBase = Config->ImageBase;
PE->SectionAlignment = PageSize;
PE->FileAlignment = SectorSize;
PE->MajorImageVersion = Config->MajorImageVersion;
PE->MinorImageVersion = Config->MinorImageVersion;
PE->MajorOperatingSystemVersion = Config->MajorOSVersion;
PE->MinorOperatingSystemVersion = Config->MinorOSVersion;
PE->MajorSubsystemVersion = Config->MajorOSVersion;
PE->MinorSubsystemVersion = Config->MinorOSVersion;
PE->Subsystem = Config->Subsystem;
PE->SizeOfImage = SizeOfImage;
PE->SizeOfHeaders = SizeOfHeaders;
if (!Config->NoEntry) {
Defined *Entry = cast<Defined>(Config->Entry->repl());
PE->AddressOfEntryPoint = Entry->getRVA();
// Pointer to thumb code must have the LSB set, so adjust it.
if (Config->Machine == ARMNT)
PE->AddressOfEntryPoint |= 1;
}
PE->SizeOfStackReserve = Config->StackReserve;
PE->SizeOfStackCommit = Config->StackCommit;
PE->SizeOfHeapReserve = Config->HeapReserve;
PE->SizeOfHeapCommit = Config->HeapCommit;
if (Config->DynamicBase)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
if (Config->HighEntropyVA)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;
if (!Config->AllowBind)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND;
if (Config->NxCompat)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
if (!Config->AllowIsolation)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
if (Config->TerminalServerAware)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
PE->NumberOfRvaAndSize = NumberfOfDataDirectory;
if (OutputSection *Text = findSection(".text")) {
PE->BaseOfCode = Text->getRVA();
PE->SizeOfCode = Text->getRawSize();
}
PE->SizeOfInitializedData = getSizeOfInitializedData();
// Write data directory
auto *Dir = reinterpret_cast<data_directory *>(Buf);
Buf += sizeof(*Dir) * NumberfOfDataDirectory;
if (OutputSection *Sec = findSection(".edata")) {
Dir[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[EXPORT_TABLE].Size = Sec->getVirtualSize();
}
if (!Idata.empty()) {
Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA();
Dir[IMPORT_TABLE].Size = Idata.getDirSize();
Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA();
Dir[IAT].Size = Idata.getIATSize();
}
if (!DelayIdata.empty()) {
Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
DelayIdata.getDirRVA();
Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
}
if (OutputSection *Sec = findSection(".rsrc")) {
Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize();
}
if (OutputSection *Sec = findSection(".reloc")) {
Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
}
if (OutputSection *Sec = findSection(".pdata")) {
Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize();
}
if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) {
if (Defined *B = dyn_cast<Defined>(Sym->Body)) {
Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA();
Dir[TLS_TABLE].Size = 40;
}
}
if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) {
if (Defined *B = dyn_cast<Defined>(Sym->Body)) {
Dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = B->getRVA();
Dir[LOAD_CONFIG_TABLE].Size = Config->is64() ? 112 : 64;
}
}
// Write section table
for (OutputSection *Sec : OutputSections) {
Sec->writeHeaderTo(Buf);
Buf += sizeof(coff_section);
}
if (OutputSymtab.empty())
return;
COFF->PointerToSymbolTable = PointerToSymbolTable;
uint32_t NumberOfSymbols = OutputSymtab.size();
COFF->NumberOfSymbols = NumberOfSymbols;
auto *SymbolTable = reinterpret_cast<coff_symbol16 *>(
Buffer->getBufferStart() + COFF->PointerToSymbolTable);
for (size_t I = 0; I != NumberOfSymbols; ++I)
SymbolTable[I] = OutputSymtab[I];
// Create the string table, it follows immediately after the symbol table.
// The first 4 bytes is length including itself.
Buf = reinterpret_cast<uint8_t *>(&SymbolTable[NumberOfSymbols]);
write32le(Buf, Strtab.size() + 4);
memcpy(Buf + 4, Strtab.data(), Strtab.size());
}
void Writer::openFile(StringRef Path) {
ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable);
error(BufferOrErr, Twine("failed to open ") + Path);
Buffer = std::move(*BufferOrErr);
}
void Writer::fixSafeSEHSymbols() {
if (!SEHTable)
return;
Config->SEHTable->setRVA(SEHTable->getRVA());
Config->SEHCount->setVA(SEHTable->getSize() / 4);
}
// Write section contents to a mmap'ed file.
void Writer::writeSections() {
uint8_t *Buf = Buffer->getBufferStart();
for (OutputSection *Sec : OutputSections) {
uint8_t *SecBuf = Buf + Sec->getFileOff();
// Fill gaps between functions in .text with INT3 instructions
// instead of leaving as NUL bytes (which can be interpreted as
// ADD instructions).
if (Sec->getPermissions() & IMAGE_SCN_CNT_CODE)
memset(SecBuf, 0xCC, Sec->getRawSize());
parallel_for_each(Sec->getChunks().begin(), Sec->getChunks().end(),
[&](Chunk *C) { C->writeTo(SecBuf); });
}
}
// Sort .pdata section contents according to PE/COFF spec 5.5.
void Writer::sortExceptionTable() {
OutputSection *Sec = findSection(".pdata");
if (!Sec)
return;
// We assume .pdata contains function table entries only.
uint8_t *Begin = Buffer->getBufferStart() + Sec->getFileOff();
uint8_t *End = Begin + Sec->getVirtualSize();
if (Config->Machine == AMD64) {
struct Entry { ulittle32_t Begin, End, Unwind; };
parallel_sort(
(Entry *)Begin, (Entry *)End,
[](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
return;
}
if (Config->Machine == ARMNT) {
struct Entry { ulittle32_t Begin, Unwind; };
parallel_sort(
(Entry *)Begin, (Entry *)End,
[](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
return;
}
errs() << "warning: don't know how to handle .pdata.\n";
}
OutputSection *Writer::findSection(StringRef Name) {
for (OutputSection *Sec : OutputSections)
if (Sec->getName() == Name)
return Sec;
return nullptr;
}
uint32_t Writer::getSizeOfInitializedData() {
uint32_t Res = 0;
for (OutputSection *S : OutputSections)
if (S->getPermissions() & IMAGE_SCN_CNT_INITIALIZED_DATA)
Res += S->getRawSize();
return Res;
}
// Returns an existing section or create a new one if not found.
OutputSection *Writer::createSection(StringRef Name) {
if (auto *Sec = findSection(Name))
return Sec;
const auto DATA = IMAGE_SCN_CNT_INITIALIZED_DATA;
const auto BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA;
const auto CODE = IMAGE_SCN_CNT_CODE;
const auto DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE;
const auto R = IMAGE_SCN_MEM_READ;
const auto W = IMAGE_SCN_MEM_WRITE;
const auto X = IMAGE_SCN_MEM_EXECUTE;
uint32_t Perms = StringSwitch<uint32_t>(Name)
.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)
.Case(".text", CODE | R | X)
.Default(0);
if (!Perms)
llvm_unreachable("unknown section name");
auto Sec = new (CAlloc.Allocate()) OutputSection(Name);
Sec->addPermissions(Perms);
OutputSections.push_back(Sec);
return Sec;
}
// Dest is .reloc section. Add contents to that section.
void Writer::addBaserels(OutputSection *Dest) {
std::vector<Baserel> V;
for (OutputSection *Sec : OutputSections) {
if (Sec == Dest)
continue;
// Collect all locations for base relocations.
for (Chunk *C : Sec->getChunks())
C->getBaserels(&V);
// Add the addresses to .reloc section.
if (!V.empty())
addBaserelBlocks(Dest, V);
V.clear();
}
}
// Add addresses to .reloc section. Note that addresses are grouped by page.
void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) {
const uint32_t Mask = ~uint32_t(PageSize - 1);
uint32_t Page = V[0].RVA & Mask;
size_t I = 0, J = 1;
for (size_t E = V.size(); J < E; ++J) {
uint32_t P = V[J].RVA & Mask;
if (P == Page)
continue;
BaserelChunk *Buf = BAlloc.Allocate();
Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J));
I = J;
Page = P;
}
if (I == J)
return;
BaserelChunk *Buf = BAlloc.Allocate();
Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J));
}

26
COFF/Writer.h Normal file
View file

@ -0,0 +1,26 @@
//===- Writer.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_WRITER_H
#define LLD_COFF_WRITER_H
#include <vector>
namespace lld {
namespace coff {
class Chunk;
class OutputSection;
void writeResult(SymbolTable *T);
}
}
#endif

26
ELF/CMakeLists.txt Normal file
View file

@ -0,0 +1,26 @@
set(LLVM_TARGET_DEFINITIONS Options.td)
tablegen(LLVM Options.inc -gen-opt-parser-defs)
add_public_tablegen_target(ELFOptionsTableGen)
add_llvm_library(lldELF2
Driver.cpp
DriverUtils.cpp
Error.cpp
InputFiles.cpp
InputSection.cpp
LinkerScript.cpp
MarkLive.cpp
OutputSections.cpp
SymbolTable.cpp
Symbols.cpp
Target.cpp
Writer.cpp
LINK_COMPONENTS
Object
Option
MC
Support
)
add_dependencies(lldELF2 ELFOptionsTableGen)

84
ELF/Config.h Normal file
View file

@ -0,0 +1,84 @@
//===- Config.h -------------------------------------------------*- C++ -*-===//
//
// 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/MapVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ELF.h"
#include <vector>
namespace lld {
namespace elf2 {
class InputFile;
class SymbolBody;
enum ELFKind {
ELFNoneKind,
ELF32LEKind,
ELF32BEKind,
ELF64LEKind,
ELF64BEKind
};
struct Configuration {
SymbolBody *EntrySym = nullptr;
SymbolBody *MipsGpDisp = nullptr;
InputFile *FirstElf = nullptr;
llvm::StringRef DynamicLinker;
llvm::StringRef Entry;
llvm::StringRef Emulation;
llvm::StringRef Fini;
llvm::StringRef Init;
llvm::StringRef OutputFile;
llvm::StringRef SoName;
llvm::StringRef Sysroot;
std::string RPath;
llvm::MapVector<llvm::StringRef, std::vector<llvm::StringRef>> OutputSections;
std::vector<llvm::StringRef> SearchPaths;
std::vector<llvm::StringRef> Undefined;
bool AllowMultipleDefinition;
bool AsNeeded = false;
bool Bsymbolic;
bool DiscardAll;
bool DiscardLocals;
bool DiscardNone;
bool EnableNewDtags;
bool ExportDynamic;
bool GcSections;
bool GnuHash = false;
bool Mips64EL = false;
bool NoInhibitExec;
bool NoUndefined;
bool PrintGcSections;
bool Shared;
bool Static = false;
bool StripAll;
bool SysvHash = true;
bool Verbose;
bool ZExecStack;
bool ZNodelete;
bool ZNow;
bool ZOrigin;
bool ZRelro;
ELFKind EKind = ELFNoneKind;
uint16_t EMachine = llvm::ELF::EM_NONE;
uint64_t EntryAddr = -1;
unsigned Optimize = 0;
};
extern Configuration *Config;
} // namespace elf2
} // namespace lld
#endif

299
ELF/Driver.cpp Normal file
View file

@ -0,0 +1,299 @@
//===- 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 "Error.h"
#include "InputFiles.h"
#include "SymbolTable.h"
#include "Target.h"
#include "Writer.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace lld;
using namespace lld::elf2;
Configuration *lld::elf2::Config;
LinkerDriver *lld::elf2::Driver;
void lld::elf2::link(ArrayRef<const char *> Args) {
Configuration C;
LinkerDriver D;
Config = &C;
Driver = &D;
Driver->main(Args.slice(1));
}
static std::pair<ELFKind, uint16_t> parseEmulation(StringRef S) {
if (S == "elf32btsmip")
return {ELF32BEKind, EM_MIPS};
if (S == "elf32ltsmip")
return {ELF32LEKind, EM_MIPS};
if (S == "elf32ppc")
return {ELF32BEKind, EM_PPC};
if (S == "elf64ppc")
return {ELF64BEKind, EM_PPC64};
if (S == "elf_i386")
return {ELF32LEKind, EM_386};
if (S == "elf_x86_64")
return {ELF64LEKind, EM_X86_64};
if (S == "aarch64linux")
return {ELF64LEKind, EM_AARCH64};
if (S == "i386pe" || S == "i386pep" || S == "thumb2pe")
error("Windows targets are not supported on the ELF frontend: " + S);
error("Unknown emulation: " + S);
}
// Opens and parses a file. Path has to be resolved already.
// Newly created memory buffers are owned by this driver.
void LinkerDriver::addFile(StringRef Path) {
using namespace llvm::sys::fs;
if (Config->Verbose)
llvm::outs() << Path << "\n";
auto MBOrErr = MemoryBuffer::getFile(Path);
error(MBOrErr, "cannot open " + Path);
std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
MemoryBufferRef MBRef = MB->getMemBufferRef();
OwningMBs.push_back(std::move(MB)); // take MB ownership
switch (identify_magic(MBRef.getBuffer())) {
case file_magic::unknown:
readLinkerScript(&Alloc, MBRef);
return;
case file_magic::archive:
if (WholeArchive) {
auto File = make_unique<ArchiveFile>(MBRef);
for (MemoryBufferRef &MB : File->getMembers())
Files.push_back(createELFFile<ObjectFile>(MB));
OwningArchives.emplace_back(std::move(File));
return;
}
Files.push_back(make_unique<ArchiveFile>(MBRef));
return;
case file_magic::elf_shared_object:
Files.push_back(createELFFile<SharedFile>(MBRef));
return;
default:
Files.push_back(createELFFile<ObjectFile>(MBRef));
}
}
static StringRef
getString(opt::InputArgList &Args, unsigned Key, StringRef Default = "") {
if (auto *Arg = Args.getLastArg(Key))
return Arg->getValue();
return Default;
}
static bool hasZOption(opt::InputArgList &Args, StringRef Key) {
for (auto *Arg : Args.filtered(OPT_z))
if (Key == Arg->getValue())
return true;
return false;
}
void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
initSymbols();
opt::InputArgList Args = parseArgs(&Alloc, ArgsArr);
createFiles(Args);
// Traditional linkers can generate re-linkable object files instead
// of executables or DSOs. We don't support that since the feature
// does not seem to provide more value than the static archiver.
if (Args.hasArg(OPT_relocatable))
error("-r option is not supported. Use 'ar' command instead.");
switch (Config->EKind) {
case ELF32LEKind:
link<ELF32LE>(Args);
return;
case ELF32BEKind:
link<ELF32BE>(Args);
return;
case ELF64LEKind:
link<ELF64LE>(Args);
return;
case ELF64BEKind:
link<ELF64BE>(Args);
return;
default:
error("-m or at least a .o file required");
}
}
void LinkerDriver::createFiles(opt::InputArgList &Args) {
for (auto *Arg : Args.filtered(OPT_L))
Config->SearchPaths.push_back(Arg->getValue());
std::vector<StringRef> RPaths;
for (auto *Arg : Args.filtered(OPT_rpath))
RPaths.push_back(Arg->getValue());
if (!RPaths.empty())
Config->RPath = llvm::join(RPaths.begin(), RPaths.end(), ":");
if (auto *Arg = Args.getLastArg(OPT_m)) {
StringRef S = Arg->getValue();
std::pair<ELFKind, uint16_t> P = parseEmulation(S);
Config->EKind = P.first;
Config->EMachine = P.second;
Config->Emulation = S;
}
Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition);
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
Config->DiscardAll = Args.hasArg(OPT_discard_all);
Config->DiscardLocals = Args.hasArg(OPT_discard_locals);
Config->DiscardNone = Args.hasArg(OPT_discard_none);
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
Config->ExportDynamic = Args.hasArg(OPT_export_dynamic);
Config->GcSections = Args.hasArg(OPT_gc_sections);
Config->NoInhibitExec = Args.hasArg(OPT_noinhibit_exec);
Config->NoUndefined = Args.hasArg(OPT_no_undefined);
Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
Config->Shared = Args.hasArg(OPT_shared);
Config->StripAll = Args.hasArg(OPT_strip_all);
Config->Verbose = Args.hasArg(OPT_verbose);
Config->DynamicLinker = getString(Args, OPT_dynamic_linker);
Config->Entry = getString(Args, OPT_entry);
Config->Fini = getString(Args, OPT_fini, "_fini");
Config->Init = getString(Args, OPT_init, "_init");
Config->OutputFile = getString(Args, OPT_o);
Config->SoName = getString(Args, OPT_soname);
Config->Sysroot = getString(Args, OPT_sysroot);
Config->ZExecStack = hasZOption(Args, "execstack");
Config->ZNodelete = hasZOption(Args, "nodelete");
Config->ZNow = hasZOption(Args, "now");
Config->ZOrigin = hasZOption(Args, "origin");
Config->ZRelro = !hasZOption(Args, "norelro");
if (auto *Arg = Args.getLastArg(OPT_O)) {
StringRef Val = Arg->getValue();
if (Val.getAsInteger(10, Config->Optimize))
error("Invalid optimization level");
}
if (auto *Arg = Args.getLastArg(OPT_hash_style)) {
StringRef S = Arg->getValue();
if (S == "gnu") {
Config->GnuHash = true;
Config->SysvHash = false;
} else if (S == "both") {
Config->GnuHash = true;
} else if (S != "sysv")
error("Unknown hash style: " + S);
}
for (auto *Arg : Args.filtered(OPT_undefined))
Config->Undefined.push_back(Arg->getValue());
for (auto *Arg : Args) {
switch (Arg->getOption().getID()) {
case OPT_l:
addFile(searchLibrary(Arg->getValue()));
break;
case OPT_INPUT:
case OPT_script:
addFile(Arg->getValue());
break;
case OPT_as_needed:
Config->AsNeeded = true;
break;
case OPT_no_as_needed:
Config->AsNeeded = false;
break;
case OPT_Bstatic:
Config->Static = true;
break;
case OPT_Bdynamic:
Config->Static = false;
break;
case OPT_whole_archive:
WholeArchive = true;
break;
case OPT_no_whole_archive:
WholeArchive = false;
break;
}
}
if (Files.empty())
error("no input files.");
if (Config->GnuHash && Config->EMachine == EM_MIPS)
error("The .gnu.hash section is not compatible with the MIPS target.");
}
template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
SymbolTable<ELFT> Symtab;
Target.reset(createTarget());
if (!Config->Shared) {
// Add entry symbol.
if (Config->Entry.empty())
Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start";
// In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol
// is magical and is used to produce a R_386_GOTPC relocation.
// The R_386_GOTPC relocation value doesn't actually depend on the
// symbol value, so it could use an index of STN_UNDEF which, according
// to the spec, means the symbol value is 0.
// Unfortunately both gas and MC keep the _GLOBAL_OFFSET_TABLE_ symbol in
// the object file.
// The situation is even stranger on x86_64 where the assembly doesn't
// need the magical symbol, but gas still puts _GLOBAL_OFFSET_TABLE_ as
// an undefined symbol in the .o files.
// Given that the symbol is effectively unused, we just create a dummy
// hidden one to avoid the undefined symbol error.
Symtab.addIgnored("_GLOBAL_OFFSET_TABLE_");
}
if (!Config->Entry.empty()) {
// Set either EntryAddr (if S is a number) or EntrySym (otherwise).
StringRef S = Config->Entry;
if (S.getAsInteger(0, Config->EntryAddr))
Config->EntrySym = Symtab.addUndefined(S);
}
if (Config->EMachine == EM_MIPS) {
// On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between
// start of function and gp pointer into GOT.
Config->MipsGpDisp = Symtab.addIgnored("_gp_disp");
// Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
// so that it points to an absolute address which is relative to GOT.
// See "Global Data Symbols" in Chapter 6 in the following document:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
Symtab.addAbsolute("_gp", ElfSym<ELFT>::MipsGp);
}
for (std::unique_ptr<InputFile> &F : Files)
Symtab.addFile(std::move(F));
for (StringRef S : Config->Undefined)
Symtab.addUndefinedOpt(S);
if (Config->OutputFile.empty())
Config->OutputFile = "a.out";
// Write the result to the file.
Symtab.scanShlibUndefined();
if (Config->GcSections)
markLive<ELFT>(&Symtab);
writeResult<ELFT>(&Symtab);
}

67
ELF/Driver.h Normal file
View file

@ -0,0 +1,67 @@
//===- Driver.h -------------------------------------------------*- C++ -*-===//
//
// 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 "SymbolTable.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Option/ArgList.h"
namespace lld {
namespace elf2 {
extern class LinkerDriver *Driver;
// Entry point of the ELF linker.
void link(ArrayRef<const char *> Args);
class LinkerDriver {
public:
void main(ArrayRef<const char *> Args);
void createFiles(llvm::opt::InputArgList &Args);
template <class ELFT> void link(llvm::opt::InputArgList &Args);
void addFile(StringRef Path);
private:
template <template <class> class T>
std::unique_ptr<InputFile> createELFInputFile(MemoryBufferRef MB);
llvm::BumpPtrAllocator Alloc;
bool WholeArchive = false;
std::vector<std::unique_ptr<InputFile>> Files;
std::vector<std::unique_ptr<ArchiveFile>> OwningArchives;
std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs;
};
// Parses command line options.
llvm::opt::InputArgList parseArgs(llvm::BumpPtrAllocator *A,
ArrayRef<const char *> Args);
// 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
};
// Parses a linker script. Calling this function updates the Symtab and Config.
void readLinkerScript(llvm::BumpPtrAllocator *A, MemoryBufferRef MB);
std::string findFromSearchPaths(StringRef Path);
std::string searchLibrary(StringRef Path);
std::string buildSysrootedPath(llvm::StringRef Dir, llvm::StringRef File);
} // namespace elf2
} // namespace lld
#endif

120
ELF/DriverUtils.cpp Normal file
View file

@ -0,0 +1,120 @@
//===- 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 "Error.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/StringSaver.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) {}
};
// Parses a given list of options.
opt::InputArgList lld::elf2::parseArgs(llvm::BumpPtrAllocator *A,
ArrayRef<const char *> Argv) {
// Make InputArgList from string vectors.
ELFOptTable Table;
unsigned MissingIndex;
unsigned MissingCount;
// Expand response files. '@<filename>' is replaced by the file's contents.
SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
StringSaver Saver(*A);
llvm::cl::ExpandResponseFiles(Saver, llvm::cl::TokenizeGNUCommandLine, Vec);
// Parse options and then do error checking.
opt::InputArgList Args = Table.ParseArgs(Vec, MissingIndex, MissingCount);
if (MissingCount)
error(Twine("missing arg value for \"") + Args.getArgString(MissingIndex) +
"\", expected " + Twine(MissingCount) +
(MissingCount == 1 ? " argument.\n" : " arguments"));
iterator_range<opt::arg_iterator> Unknowns = Args.filtered(OPT_UNKNOWN);
for (auto *Arg : Unknowns)
warning("warning: unknown argument: " + Arg->getSpelling());
if (Unknowns.begin() != Unknowns.end())
error("unknown argument(s) found");
return Args;
}
std::string lld::elf2::findFromSearchPaths(StringRef Path) {
for (StringRef Dir : Config->SearchPaths) {
std::string FullPath = buildSysrootedPath(Dir, Path);
if (sys::fs::exists(FullPath))
return FullPath;
}
return "";
}
// Searches a given library from input search paths, which are filled
// from -L command line switches. Returns a path to an existent library file.
std::string lld::elf2::searchLibrary(StringRef Path) {
std::vector<std::string> Names;
if (Path[0] == ':') {
Names.push_back(Path.drop_front());
} else {
if (!Config->Static)
Names.push_back(("lib" + Path + ".so").str());
Names.push_back(("lib" + Path + ".a").str());
}
for (const std::string &Name : Names) {
std::string S = findFromSearchPaths(Name);
if (!S.empty())
return S;
}
error("Unable to find library -l" + Path);
}
// Makes a path by concatenating Dir and File.
// If Dir starts with '=' the result will be preceded by Sysroot,
// which can be set with --sysroot command line switch.
std::string lld::elf2::buildSysrootedPath(StringRef Dir, StringRef File) {
SmallString<128> Path;
if (Dir.startswith("="))
sys::path::append(Path, Config->Sysroot, Dir.substr(1), File);
else
sys::path::append(Path, Dir, File);
return Path.str();
}

38
ELF/Error.cpp Normal file
View file

@ -0,0 +1,38 @@
//===- Error.cpp ----------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Error.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/raw_ostream.h"
namespace lld {
namespace elf2 {
void warning(const Twine &Msg) { llvm::errs() << Msg << "\n"; }
void error(const Twine &Msg) {
llvm::errs() << Msg << "\n";
exit(1);
}
void error(std::error_code EC, const Twine &Prefix) {
if (!EC)
return;
error(Prefix + ": " + EC.message());
}
void error(std::error_code EC) {
if (!EC)
return;
error(EC.message());
}
} // namespace elf2
} // namespace lld

32
ELF/Error.h Normal file
View file

@ -0,0 +1,32 @@
//===- Error.h --------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_ERROR_H
#define LLD_COFF_ERROR_H
#include "lld/Core/LLVM.h"
namespace lld {
namespace elf2 {
void warning(const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void error(const Twine &Msg);
void error(std::error_code EC, const Twine &Prefix);
void error(std::error_code EC);
template <typename T> void error(const ErrorOr<T> &V, const Twine &Prefix) {
error(V.getError(), Prefix);
}
template <typename T> void error(const ErrorOr<T> &V) { error(V.getError()); }
} // namespace elf2
} // namespace lld
#endif

496
ELF/InputFiles.cpp Normal file
View file

@ -0,0 +1,496 @@
//===- 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 "InputSection.h"
#include "Error.h"
#include "Symbols.h"
#include "llvm/ADT/STLExtras.h"
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::sys::fs;
using namespace lld;
using namespace lld::elf2;
namespace {
class ECRAII {
std::error_code EC;
public:
std::error_code &getEC() { return EC; }
~ECRAII() { error(EC); }
};
}
template <class ELFT>
ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef M)
: InputFile(K, M), ELFObj(MB.getBuffer(), ECRAII().getEC()) {}
template <class ELFT>
ELFKind ELFFileBase<ELFT>::getELFKind() {
using llvm::support::little;
if (ELFT::Is64Bits)
return ELFT::TargetEndianness == little ? ELF64LEKind : ELF64BEKind;
return ELFT::TargetEndianness == little ? ELF32LEKind : ELF32BEKind;
}
template <class ELFT>
typename ELFFileBase<ELFT>::Elf_Sym_Range
ELFFileBase<ELFT>::getSymbolsHelper(bool Local) {
if (!Symtab)
return Elf_Sym_Range(nullptr, nullptr);
Elf_Sym_Range Syms = ELFObj.symbols(Symtab);
uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
uint32_t FirstNonLocal = Symtab->sh_info;
if (FirstNonLocal > NumSymbols)
error("Invalid sh_info in symbol table");
if (!Local)
return make_range(Syms.begin() + FirstNonLocal, Syms.end());
// +1 to skip over dummy symbol.
return make_range(Syms.begin() + 1, Syms.begin() + FirstNonLocal);
}
template <class ELFT>
uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
uint32_t I = Sym.st_shndx;
if (I == ELF::SHN_XINDEX)
return this->ELFObj.getExtendedSymbolTableIndex(&Sym, this->Symtab,
SymtabSHNDX);
if (I >= ELF::SHN_LORESERVE || I == ELF::SHN_ABS)
return 0;
return I;
}
template <class ELFT> void ELFFileBase<ELFT>::initStringTable() {
if (!Symtab)
return;
ErrorOr<StringRef> StringTableOrErr = ELFObj.getStringTableForSymtab(*Symtab);
error(StringTableOrErr.getError());
StringTable = *StringTableOrErr;
}
template <class ELFT>
typename ELFFileBase<ELFT>::Elf_Sym_Range
ELFFileBase<ELFT>::getNonLocalSymbols() {
return getSymbolsHelper(false);
}
template <class ELFT>
ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M)
: ELFFileBase<ELFT>(Base::ObjectKind, M) {}
template <class ELFT>
typename ObjectFile<ELFT>::Elf_Sym_Range ObjectFile<ELFT>::getLocalSymbols() {
return this->getSymbolsHelper(true);
}
template <class ELFT> uint32_t ObjectFile<ELFT>::getMipsGp0() const {
return MipsReginfo ? MipsReginfo->getGp0() : 0;
}
template <class ELFT>
const typename ObjectFile<ELFT>::Elf_Sym *
ObjectFile<ELFT>::getLocalSymbol(uintX_t SymIndex) {
uint32_t FirstNonLocal = this->Symtab->sh_info;
if (SymIndex >= FirstNonLocal)
return nullptr;
Elf_Sym_Range Syms = this->ELFObj.symbols(this->Symtab);
return Syms.begin() + SymIndex;
}
template <class ELFT>
void elf2::ObjectFile<ELFT>::parse(DenseSet<StringRef> &Comdats) {
// Read section and symbol tables.
initializeSections(Comdats);
initializeSymbols();
}
// Sections with SHT_GROUP and comdat bits define comdat section groups.
// They are identified and deduplicated by group name. This function
// returns a group name.
template <class ELFT>
StringRef ObjectFile<ELFT>::getShtGroupSignature(const Elf_Shdr &Sec) {
const ELFFile<ELFT> &Obj = this->ELFObj;
uint32_t SymtabdSectionIndex = Sec.sh_link;
ErrorOr<const Elf_Shdr *> SecOrErr = Obj.getSection(SymtabdSectionIndex);
error(SecOrErr);
const Elf_Shdr *SymtabSec = *SecOrErr;
uint32_t SymIndex = Sec.sh_info;
const Elf_Sym *Sym = Obj.getSymbol(SymtabSec, SymIndex);
ErrorOr<StringRef> StringTableOrErr = Obj.getStringTableForSymtab(*SymtabSec);
error(StringTableOrErr);
ErrorOr<StringRef> SignatureOrErr = Sym->getName(*StringTableOrErr);
error(SignatureOrErr);
return *SignatureOrErr;
}
template <class ELFT>
ArrayRef<typename ObjectFile<ELFT>::GroupEntryType>
ObjectFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
const ELFFile<ELFT> &Obj = this->ELFObj;
ErrorOr<ArrayRef<GroupEntryType>> EntriesOrErr =
Obj.template getSectionContentsAsArray<GroupEntryType>(&Sec);
error(EntriesOrErr.getError());
ArrayRef<GroupEntryType> Entries = *EntriesOrErr;
if (Entries.empty() || Entries[0] != GRP_COMDAT)
error("Unsupported SHT_GROUP format");
return Entries.slice(1);
}
template <class ELFT>
static bool shouldMerge(const typename ELFFile<ELFT>::Elf_Shdr &Sec) {
typedef typename ELFFile<ELFT>::uintX_t uintX_t;
uintX_t Flags = Sec.sh_flags;
if (!(Flags & SHF_MERGE))
return false;
if (Flags & SHF_WRITE)
error("Writable SHF_MERGE sections are not supported");
uintX_t EntSize = Sec.sh_entsize;
if (!EntSize || Sec.sh_size % EntSize)
error("SHF_MERGE section size must be a multiple of sh_entsize");
// Don't try to merge if the aligment is larger than the sh_entsize.
//
// If this is not a SHF_STRINGS, we would need to pad after every entity. It
// would be equivalent for the producer of the .o to just set a larger
// sh_entsize.
//
// If this is a SHF_STRINGS, the larger alignment makes sense. Unfortunately
// it would complicate tail merging. This doesn't seem that common to
// justify the effort.
if (Sec.sh_addralign > EntSize)
return false;
return true;
}
template <class ELFT>
void elf2::ObjectFile<ELFT>::initializeSections(DenseSet<StringRef> &Comdats) {
uint64_t Size = this->ELFObj.getNumSections();
Sections.resize(Size);
unsigned I = -1;
const ELFFile<ELFT> &Obj = this->ELFObj;
for (const Elf_Shdr &Sec : Obj.sections()) {
++I;
if (Sections[I] == &InputSection<ELFT>::Discarded)
continue;
switch (Sec.sh_type) {
case SHT_GROUP:
Sections[I] = &InputSection<ELFT>::Discarded;
if (Comdats.insert(getShtGroupSignature(Sec)).second)
continue;
for (GroupEntryType E : getShtGroupEntries(Sec)) {
uint32_t SecIndex = E;
if (SecIndex >= Size)
error("Invalid section index in group");
Sections[SecIndex] = &InputSection<ELFT>::Discarded;
}
break;
case SHT_SYMTAB:
this->Symtab = &Sec;
break;
case SHT_SYMTAB_SHNDX: {
ErrorOr<ArrayRef<Elf_Word>> ErrorOrTable = Obj.getSHNDXTable(Sec);
error(ErrorOrTable);
this->SymtabSHNDX = *ErrorOrTable;
break;
}
case SHT_STRTAB:
case SHT_NULL:
break;
case SHT_RELA:
case SHT_REL: {
uint32_t RelocatedSectionIndex = Sec.sh_info;
if (RelocatedSectionIndex >= Size)
error("Invalid relocated section index");
InputSectionBase<ELFT> *RelocatedSection =
Sections[RelocatedSectionIndex];
if (!RelocatedSection)
error("Unsupported relocation reference");
if (auto *S = dyn_cast<InputSection<ELFT>>(RelocatedSection)) {
S->RelocSections.push_back(&Sec);
} else if (auto *S = dyn_cast<EHInputSection<ELFT>>(RelocatedSection)) {
if (S->RelocSection)
error("Multiple relocation sections to .eh_frame are not supported");
S->RelocSection = &Sec;
} else {
error("Relocations pointing to SHF_MERGE are not supported");
}
break;
}
default:
Sections[I] = createInputSection(Sec);
}
}
}
template <class ELFT> InputSectionBase<ELFT> *
elf2::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
ErrorOr<StringRef> NameOrErr = this->ELFObj.getSectionName(&Sec);
error(NameOrErr);
StringRef Name = *NameOrErr;
// .note.GNU-stack is a marker section to control the presence of
// PT_GNU_STACK segment in outputs. Since the presence of the segment
// is controlled only by the command line option (-z execstack) in LLD,
// .note.GNU-stack is ignored.
if (Name == ".note.GNU-stack")
return &InputSection<ELFT>::Discarded;
// A MIPS object file has a special section that contains register
// usage info, which needs to be handled by the linker specially.
if (Config->EMachine == EM_MIPS && Name == ".reginfo") {
MipsReginfo = new (this->Alloc) MipsReginfoInputSection<ELFT>(this, &Sec);
return MipsReginfo;
}
if (Name == ".eh_frame")
return new (this->EHAlloc.Allocate()) EHInputSection<ELFT>(this, &Sec);
if (shouldMerge<ELFT>(Sec))
return new (this->MAlloc.Allocate()) MergeInputSection<ELFT>(this, &Sec);
return new (this->Alloc) InputSection<ELFT>(this, &Sec);
}
template <class ELFT> void elf2::ObjectFile<ELFT>::initializeSymbols() {
this->initStringTable();
Elf_Sym_Range Syms = this->getNonLocalSymbols();
uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
this->SymbolBodies.reserve(NumSymbols);
for (const Elf_Sym &Sym : Syms)
this->SymbolBodies.push_back(createSymbolBody(this->StringTable, &Sym));
}
template <class ELFT>
InputSectionBase<ELFT> *
elf2::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
uint32_t Index = this->getSectionIndex(Sym);
if (Index == 0)
return nullptr;
if (Index >= Sections.size() || !Sections[Index])
error("Invalid section index");
return Sections[Index];
}
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;
switch (Sym->st_shndx) {
case SHN_UNDEF:
return new (this->Alloc) UndefinedElf<ELFT>(Name, *Sym);
case SHN_COMMON:
return new (this->Alloc) DefinedCommon(
Name, Sym->st_size, Sym->st_value,
Sym->getBinding() == llvm::ELF::STB_WEAK, Sym->getVisibility());
}
switch (Sym->getBinding()) {
default:
error("unexpected binding");
case STB_GLOBAL:
case STB_WEAK:
case STB_GNU_UNIQUE: {
InputSectionBase<ELFT> *Sec = getSection(*Sym);
if (Sec == &InputSection<ELFT>::Discarded)
return new (this->Alloc) UndefinedElf<ELFT>(Name, *Sym);
return new (this->Alloc) DefinedRegular<ELFT>(Name, *Sym, Sec);
}
}
}
static std::unique_ptr<Archive> openArchive(MemoryBufferRef MB) {
ErrorOr<std::unique_ptr<Archive>> ArchiveOrErr = Archive::create(MB);
error(ArchiveOrErr, "Failed to parse archive");
return std::move(*ArchiveOrErr);
}
void ArchiveFile::parse() {
File = openArchive(MB);
// Allocate a buffer for Lazy objects.
size_t NumSyms = File->getNumberOfSymbols();
LazySymbols.reserve(NumSyms);
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols())
LazySymbols.emplace_back(this, Sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) {
ErrorOr<Archive::Child> COrErr = Sym->getMember();
error(COrErr, "Could not get the member for symbol " + Sym->getName());
const Archive::Child &C = *COrErr;
if (!Seen.insert(C.getChildOffset()).second)
return MemoryBufferRef();
ErrorOr<MemoryBufferRef> RefOrErr = C.getMemoryBufferRef();
if (!RefOrErr)
error(RefOrErr, "Could not get the buffer for the member defining symbol " +
Sym->getName());
return *RefOrErr;
}
std::vector<MemoryBufferRef> ArchiveFile::getMembers() {
File = openArchive(MB);
std::vector<MemoryBufferRef> Result;
for (auto &ChildOrErr : File->children()) {
error(ChildOrErr,
"Could not get the child of the archive " + File->getFileName());
const Archive::Child Child(*ChildOrErr);
ErrorOr<MemoryBufferRef> MbOrErr = Child.getMemoryBufferRef();
if (!MbOrErr)
error(MbOrErr, "Could not get the buffer for a child of the archive " +
File->getFileName());
Result.push_back(MbOrErr.get());
}
return Result;
}
template <class ELFT>
SharedFile<ELFT>::SharedFile(MemoryBufferRef M)
: ELFFileBase<ELFT>(Base::SharedKind, M) {
AsNeeded = Config->AsNeeded;
}
template <class ELFT>
const typename ELFFile<ELFT>::Elf_Shdr *
SharedFile<ELFT>::getSection(const Elf_Sym &Sym) const {
uint32_t Index = this->getSectionIndex(Sym);
if (Index == 0)
return nullptr;
ErrorOr<const Elf_Shdr *> Ret = this->ELFObj.getSection(Index);
error(Ret);
return *Ret;
}
template <class ELFT> void SharedFile<ELFT>::parseSoName() {
typedef typename ELFFile<ELFT>::Elf_Dyn Elf_Dyn;
typedef typename ELFFile<ELFT>::uintX_t uintX_t;
const Elf_Shdr *DynamicSec = nullptr;
const ELFFile<ELFT> Obj = this->ELFObj;
for (const Elf_Shdr &Sec : Obj.sections()) {
switch (Sec.sh_type) {
default:
continue;
case SHT_DYNSYM:
this->Symtab = &Sec;
break;
case SHT_DYNAMIC:
DynamicSec = &Sec;
break;
case SHT_SYMTAB_SHNDX: {
ErrorOr<ArrayRef<Elf_Word>> ErrorOrTable = Obj.getSHNDXTable(Sec);
error(ErrorOrTable);
this->SymtabSHNDX = *ErrorOrTable;
break;
}
}
}
this->initStringTable();
this->SoName = this->getName();
if (!DynamicSec)
return;
auto *Begin =
reinterpret_cast<const Elf_Dyn *>(Obj.base() + DynamicSec->sh_offset);
const Elf_Dyn *End = Begin + DynamicSec->sh_size / sizeof(Elf_Dyn);
for (const Elf_Dyn &Dyn : make_range(Begin, End)) {
if (Dyn.d_tag == DT_SONAME) {
uintX_t Val = Dyn.getVal();
if (Val >= this->StringTable.size())
error("Invalid DT_SONAME entry");
this->SoName = StringRef(this->StringTable.data() + Val);
return;
}
}
}
template <class ELFT> void SharedFile<ELFT>::parse() {
Elf_Sym_Range Syms = this->getNonLocalSymbols();
uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
SymbolBodies.reserve(NumSymbols);
for (const Elf_Sym &Sym : Syms) {
ErrorOr<StringRef> NameOrErr = Sym.getName(this->StringTable);
error(NameOrErr.getError());
StringRef Name = *NameOrErr;
if (Sym.isUndefined())
Undefs.push_back(Name);
else
SymbolBodies.emplace_back(this, Name, Sym);
}
}
template <typename T>
static std::unique_ptr<InputFile> createELFFileAux(MemoryBufferRef MB) {
std::unique_ptr<T> Ret = llvm::make_unique<T>(MB);
if (!Config->FirstElf)
Config->FirstElf = Ret.get();
if (Config->EKind == ELFNoneKind) {
Config->EKind = Ret->getELFKind();
Config->EMachine = Ret->getEMachine();
}
return std::move(Ret);
}
template <template <class> class T>
std::unique_ptr<InputFile> lld::elf2::createELFFile(MemoryBufferRef MB) {
std::pair<unsigned char, unsigned char> Type = getElfArchType(MB.getBuffer());
if (Type.second != ELF::ELFDATA2LSB && Type.second != ELF::ELFDATA2MSB)
error("Invalid data encoding: " + MB.getBufferIdentifier());
if (Type.first == ELF::ELFCLASS32) {
if (Type.second == ELF::ELFDATA2LSB)
return createELFFileAux<T<ELF32LE>>(MB);
return createELFFileAux<T<ELF32BE>>(MB);
}
if (Type.first == ELF::ELFCLASS64) {
if (Type.second == ELF::ELFDATA2LSB)
return createELFFileAux<T<ELF64LE>>(MB);
return createELFFileAux<T<ELF64BE>>(MB);
}
error("Invalid file class: " + MB.getBufferIdentifier());
}
template class elf2::ELFFileBase<ELF32LE>;
template class elf2::ELFFileBase<ELF32BE>;
template class elf2::ELFFileBase<ELF64LE>;
template class elf2::ELFFileBase<ELF64BE>;
template class elf2::ObjectFile<ELF32LE>;
template class elf2::ObjectFile<ELF32BE>;
template class elf2::ObjectFile<ELF64LE>;
template class elf2::ObjectFile<ELF64BE>;
template class elf2::SharedFile<ELF32LE>;
template class elf2::SharedFile<ELF32BE>;
template class elf2::SharedFile<ELF64LE>;
template class elf2::SharedFile<ELF64BE>;
template std::unique_ptr<InputFile>
elf2::createELFFile<ObjectFile>(MemoryBufferRef);
template std::unique_ptr<InputFile>
elf2::createELFFile<SharedFile>(MemoryBufferRef);

211
ELF/InputFiles.h Normal file
View file

@ -0,0 +1,211 @@
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
//
// 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 "Config.h"
#include "InputSection.h"
#include "Error.h"
#include "Symbols.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
namespace lld {
namespace elf2 {
using llvm::object::Archive;
class InputFile;
class Lazy;
class SymbolBody;
// The root class of input files.
class InputFile {
public:
enum Kind { ObjectKind, SharedKind, ArchiveKind };
Kind kind() const { return FileKind; }
StringRef getName() const { return MB.getBufferIdentifier(); }
protected:
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
MemoryBufferRef MB;
private:
const Kind FileKind;
};
template <typename ELFT> class ELFFileBase : public InputFile {
public:
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym_Range Elf_Sym_Range;
ELFFileBase(Kind K, MemoryBufferRef M);
static bool classof(const InputFile *F) {
Kind K = F->kind();
return K == ObjectKind || K == SharedKind;
}
static ELFKind getELFKind();
const llvm::object::ELFFile<ELFT> &getObj() const { return ELFObj; }
llvm::object::ELFFile<ELFT> &getObj() { return ELFObj; }
uint16_t getEMachine() const { return getObj().getHeader()->e_machine; }
uint8_t getOSABI() const {
return getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI];
}
StringRef getStringTable() const { return StringTable; }
uint32_t getSectionIndex(const Elf_Sym &Sym) const;
protected:
llvm::object::ELFFile<ELFT> ELFObj;
const Elf_Shdr *Symtab = nullptr;
ArrayRef<Elf_Word> SymtabSHNDX;
StringRef StringTable;
void initStringTable();
Elf_Sym_Range getNonLocalSymbols();
Elf_Sym_Range getSymbolsHelper(bool);
};
// .o file.
template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
typedef ELFFileBase<ELFT> Base;
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;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
typedef llvm::support::detail::packed_endian_specific_integral<
uint32_t, ELFT::TargetEndianness, 2> GroupEntryType;
StringRef getShtGroupSignature(const Elf_Shdr &Sec);
ArrayRef<GroupEntryType> getShtGroupEntries(const Elf_Shdr &Sec);
public:
static bool classof(const InputFile *F) {
return F->kind() == Base::ObjectKind;
}
ArrayRef<SymbolBody *> getSymbols() { return this->SymbolBodies; }
explicit ObjectFile(MemoryBufferRef M);
void parse(llvm::DenseSet<StringRef> &Comdats);
ArrayRef<InputSectionBase<ELFT> *> getSections() const { return Sections; }
InputSectionBase<ELFT> *getSection(const Elf_Sym &Sym) const;
SymbolBody *getSymbolBody(uint32_t SymbolIndex) const {
uint32_t FirstNonLocal = this->Symtab->sh_info;
if (SymbolIndex < FirstNonLocal)
return nullptr;
return this->SymbolBodies[SymbolIndex - FirstNonLocal];
}
Elf_Sym_Range getLocalSymbols();
const Elf_Sym *getLocalSymbol(uintX_t SymIndex);
const Elf_Shdr *getSymbolTable() const { return this->Symtab; };
// Get MIPS GP0 value defined by this file. This value represents the gp value
// used to create the relocatable object and required to support
// R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
uint32_t getMipsGp0() const;
private:
void initializeSections(llvm::DenseSet<StringRef> &Comdats);
void initializeSymbols();
InputSectionBase<ELFT> *createInputSection(const Elf_Shdr &Sec);
SymbolBody *createSymbolBody(StringRef StringTable, const Elf_Sym *Sym);
// List of all sections defined by this file.
std::vector<InputSectionBase<ELFT> *> Sections;
// List of all symbols referenced or defined by this file.
std::vector<SymbolBody *> SymbolBodies;
// MIPS .reginfo section defined by this file.
MipsReginfoInputSection<ELFT> *MipsReginfo = nullptr;
llvm::BumpPtrAllocator Alloc;
llvm::SpecificBumpPtrAllocator<MergeInputSection<ELFT>> MAlloc;
llvm::SpecificBumpPtrAllocator<EHInputSection<ELFT>> EHAlloc;
};
class ArchiveFile : public InputFile {
public:
explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
void parse();
// Returns a memory buffer for a given symbol. An empty memory buffer
// is returned if we have already returned the same memory buffer.
// (So that we don't instantiate same members more than once.)
MemoryBufferRef getMember(const Archive::Symbol *Sym);
llvm::MutableArrayRef<Lazy> getLazySymbols() { return LazySymbols; }
std::vector<MemoryBufferRef> getMembers();
private:
std::unique_ptr<Archive> File;
std::vector<Lazy> LazySymbols;
llvm::DenseSet<uint64_t> Seen;
};
// .so file.
template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
typedef ELFFileBase<ELFT> Base;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym_Range Elf_Sym_Range;
std::vector<SharedSymbol<ELFT>> SymbolBodies;
std::vector<StringRef> Undefs;
StringRef SoName;
public:
StringRef getSoName() const { return SoName; }
llvm::MutableArrayRef<SharedSymbol<ELFT>> getSharedSymbols() {
return SymbolBodies;
}
const Elf_Shdr *getSection(const Elf_Sym &Sym) const;
llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }
static bool classof(const InputFile *F) {
return F->kind() == Base::SharedKind;
}
explicit SharedFile(MemoryBufferRef M);
void parseSoName();
void parse();
// Used for --as-needed
bool AsNeeded = false;
bool IsUsed = false;
bool isNeeded() const { return !AsNeeded || IsUsed; }
};
template <template <class> class T>
std::unique_ptr<InputFile> createELFFile(MemoryBufferRef MB);
} // namespace elf2
} // namespace lld
#endif

399
ELF/InputSection.cpp Normal file
View file

@ -0,0 +1,399 @@
//===- InputSection.cpp ---------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "InputSection.h"
#include "Config.h"
#include "Error.h"
#include "InputFiles.h"
#include "OutputSections.h"
#include "Target.h"
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace lld;
using namespace lld::elf2;
template <class ELFT>
InputSectionBase<ELFT>::InputSectionBase(ObjectFile<ELFT> *File,
const Elf_Shdr *Header,
Kind SectionKind)
: Header(Header), File(File), SectionKind(SectionKind) {}
template <class ELFT> StringRef InputSectionBase<ELFT>::getSectionName() const {
ErrorOr<StringRef> Name = File->getObj().getSectionName(this->Header);
error(Name);
return *Name;
}
template <class ELFT>
ArrayRef<uint8_t> InputSectionBase<ELFT>::getSectionData() const {
ErrorOr<ArrayRef<uint8_t>> Ret =
this->File->getObj().getSectionContents(this->Header);
error(Ret);
return *Ret;
}
template <class ELFT>
typename ELFFile<ELFT>::uintX_t
InputSectionBase<ELFT>::getOffset(uintX_t Offset) {
switch (SectionKind) {
case Regular:
return cast<InputSection<ELFT>>(this)->OutSecOff + Offset;
case EHFrame:
return cast<EHInputSection<ELFT>>(this)->getOffset(Offset);
case Merge:
return cast<MergeInputSection<ELFT>>(this)->getOffset(Offset);
case MipsReginfo:
return cast<MipsReginfoInputSection<ELFT>>(this)->getOffset(Offset);
}
llvm_unreachable("Invalid section kind");
}
template <class ELFT>
typename ELFFile<ELFT>::uintX_t
InputSectionBase<ELFT>::getOffset(const Elf_Sym &Sym) {
return getOffset(Sym.st_value);
}
// Returns a section that Rel relocation is pointing to.
template <class ELFT>
InputSectionBase<ELFT> *
InputSectionBase<ELFT>::getRelocTarget(const Elf_Rel &Rel) {
// Global symbol
uint32_t SymIndex = Rel.getSymbol(Config->Mips64EL);
if (SymbolBody *B = File->getSymbolBody(SymIndex))
if (auto *D = dyn_cast<DefinedRegular<ELFT>>(B->repl()))
return D->Section;
// Local symbol
if (const Elf_Sym *Sym = File->getLocalSymbol(SymIndex))
if (InputSectionBase<ELFT> *Sec = File->getSection(*Sym))
return Sec;
return nullptr;
}
template <class ELFT>
InputSectionBase<ELFT> *
InputSectionBase<ELFT>::getRelocTarget(const Elf_Rela &Rel) {
return getRelocTarget(reinterpret_cast<const Elf_Rel &>(Rel));
}
template <class ELFT>
InputSection<ELFT>::InputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header)
: InputSectionBase<ELFT>(F, Header, Base::Regular) {}
template <class ELFT>
bool InputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
return S->SectionKind == Base::Regular;
}
template <class ELFT>
template <bool isRela>
uint8_t *
InputSectionBase<ELFT>::findMipsPairedReloc(uint8_t *Buf, uint32_t SymIndex,
uint32_t Type,
RelIteratorRange<isRela> Rels) {
// Some MIPS relocations use addend calculated from addend of the relocation
// itself and addend of paired relocation. ABI requires to compute such
// combined addend in case of REL relocation record format only.
// See p. 4-17 at ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
if (isRela || Config->EMachine != EM_MIPS)
return nullptr;
if (Type == R_MIPS_HI16)
Type = R_MIPS_LO16;
else if (Type == R_MIPS_PCHI16)
Type = R_MIPS_PCLO16;
else if (Type == R_MICROMIPS_HI16)
Type = R_MICROMIPS_LO16;
else
return nullptr;
for (const auto &RI : Rels) {
if (RI.getType(Config->Mips64EL) != Type)
continue;
if (RI.getSymbol(Config->Mips64EL) != SymIndex)
continue;
uintX_t Offset = getOffset(RI.r_offset);
if (Offset == (uintX_t)-1)
return nullptr;
return Buf + Offset;
}
return nullptr;
}
template <class ELFT>
static typename llvm::object::ELFFile<ELFT>::uintX_t
getSymSize(SymbolBody &Body) {
if (auto *SS = dyn_cast<DefinedElf<ELFT>>(&Body))
return SS->Sym.st_size;
return 0;
}
template <class ELFT>
template <bool isRela>
void InputSectionBase<ELFT>::relocate(uint8_t *Buf, uint8_t *BufEnd,
RelIteratorRange<isRela> Rels) {
typedef Elf_Rel_Impl<ELFT, isRela> RelType;
size_t Num = Rels.end() - Rels.begin();
for (size_t I = 0; I < Num; ++I) {
const RelType &RI = *(Rels.begin() + I);
uint32_t SymIndex = RI.getSymbol(Config->Mips64EL);
uint32_t Type = RI.getType(Config->Mips64EL);
uintX_t Offset = getOffset(RI.r_offset);
if (Offset == (uintX_t)-1)
continue;
uint8_t *BufLoc = Buf + Offset;
uintX_t AddrLoc = OutSec->getVA() + Offset;
auto NextRelocs = llvm::make_range(&RI, Rels.end());
if (Target->isTlsLocalDynamicReloc(Type) &&
!Target->isTlsOptimized(Type, nullptr)) {
Target->relocateOne(BufLoc, BufEnd, Type, AddrLoc,
Out<ELFT>::Got->getLocalTlsIndexVA() +
getAddend<ELFT>(RI));
continue;
}
const Elf_Shdr *SymTab = File->getSymbolTable();
SymbolBody *Body = nullptr;
if (SymIndex >= SymTab->sh_info)
Body = File->getSymbolBody(SymIndex)->repl();
if (Target->isTlsOptimized(Type, Body)) {
uintX_t SymVA;
if (!Body)
SymVA = getLocalRelTarget(*File, RI, 0);
else if (Target->relocNeedsGot(Type, *Body))
SymVA = Out<ELFT>::Got->getEntryAddr(*Body);
else
SymVA = getSymVA<ELFT>(*Body);
// By optimizing TLS relocations, it is sometimes needed to skip
// relocations that immediately follow TLS relocations. This function
// knows how many slots we need to skip.
I += Target->relocateTlsOptimize(BufLoc, BufEnd, Type, AddrLoc, SymVA,
*Body);
continue;
}
// Handle relocations for local symbols -- they never get
// resolved so we don't allocate a SymbolBody.
uintX_t A = getAddend<ELFT>(RI);
if (!Body) {
uintX_t SymVA = getLocalRelTarget(*File, RI, A);
// We need to adjust SymVA value in case of R_MIPS_GPREL16/32 relocations
// because they use the following expression to calculate the relocation's
// result for local symbol: S + A + GP0 - G.
if (Config->EMachine == EM_MIPS &&
(Type == R_MIPS_GPREL16 || Type == R_MIPS_GPREL32))
SymVA += File->getMipsGp0();
Target->relocateOne(BufLoc, BufEnd, Type, AddrLoc, SymVA, 0,
findMipsPairedReloc(Buf, SymIndex, Type, NextRelocs));
continue;
}
if (Target->isTlsGlobalDynamicReloc(Type) &&
!Target->isTlsOptimized(Type, Body)) {
Target->relocateOne(BufLoc, BufEnd, Type, AddrLoc,
Out<ELFT>::Got->getGlobalDynAddr(*Body) +
getAddend<ELFT>(RI));
continue;
}
uintX_t SymVA = getSymVA<ELFT>(*Body);
if (Target->relocNeedsPlt(Type, *Body)) {
SymVA = Out<ELFT>::Plt->getEntryAddr(*Body);
Type = Target->getPltRefReloc(Type);
} else if (Target->relocNeedsGot(Type, *Body)) {
SymVA = Out<ELFT>::Got->getEntryAddr(*Body);
if (Body->isTls())
Type = Target->getTlsGotReloc(Type);
} else if (!Target->needsCopyRel(Type, *Body) &&
isa<SharedSymbol<ELFT>>(*Body)) {
continue;
} else if (Target->isTlsDynReloc(Type, *Body) ||
Target->isSizeDynReloc(Type, *Body)) {
continue;
} else if (Config->EMachine == EM_MIPS) {
if (Type == R_MIPS_HI16 && Body == Config->MipsGpDisp)
SymVA = getMipsGpAddr<ELFT>() - AddrLoc;
else if (Type == R_MIPS_LO16 && Body == Config->MipsGpDisp)
SymVA = getMipsGpAddr<ELFT>() - AddrLoc + 4;
}
uintX_t Size = getSymSize<ELFT>(*Body);
Target->relocateOne(BufLoc, BufEnd, Type, AddrLoc, SymVA + A, Size + A,
findMipsPairedReloc(Buf, SymIndex, Type, NextRelocs));
}
}
template <class ELFT> void InputSection<ELFT>::writeTo(uint8_t *Buf) {
if (this->Header->sh_type == SHT_NOBITS)
return;
// Copy section contents from source object file to output file.
ArrayRef<uint8_t> Data = this->getSectionData();
memcpy(Buf + OutSecOff, Data.data(), Data.size());
ELFFile<ELFT> &EObj = this->File->getObj();
uint8_t *BufEnd = Buf + OutSecOff + Data.size();
// Iterate over all relocation sections that apply to this section.
for (const Elf_Shdr *RelSec : this->RelocSections) {
if (RelSec->sh_type == SHT_RELA)
this->relocate(Buf, BufEnd, EObj.relas(RelSec));
else
this->relocate(Buf, BufEnd, EObj.rels(RelSec));
}
}
template <class ELFT>
SplitInputSection<ELFT>::SplitInputSection(
ObjectFile<ELFT> *File, const Elf_Shdr *Header,
typename InputSectionBase<ELFT>::Kind SectionKind)
: InputSectionBase<ELFT>(File, Header, SectionKind) {}
template <class ELFT>
EHInputSection<ELFT>::EHInputSection(ObjectFile<ELFT> *F,
const Elf_Shdr *Header)
: SplitInputSection<ELFT>(F, Header, InputSectionBase<ELFT>::EHFrame) {
// Mark .eh_frame sections as live by default because there are
// usually no relocations that point to .eh_frames. Otherwise,
// the garbage collector would drop all .eh_frame sections.
this->Live = true;
}
template <class ELFT>
bool EHInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
return S->SectionKind == InputSectionBase<ELFT>::EHFrame;
}
template <class ELFT>
typename EHInputSection<ELFT>::uintX_t
EHInputSection<ELFT>::getOffset(uintX_t Offset) {
// The file crtbeginT.o has relocations pointing to the start of an empty
// .eh_frame that is known to be the first in the link. It does that to
// identify the start of the output .eh_frame. Handle this special case.
if (this->getSectionHdr()->sh_size == 0)
return Offset;
std::pair<uintX_t, uintX_t> *I = this->getRangeAndSize(Offset).first;
uintX_t Base = I->second;
if (Base == uintX_t(-1))
return -1; // Not in the output
uintX_t Addend = Offset - I->first;
return Base + Addend;
}
template <class ELFT>
MergeInputSection<ELFT>::MergeInputSection(ObjectFile<ELFT> *F,
const Elf_Shdr *Header)
: SplitInputSection<ELFT>(F, Header, InputSectionBase<ELFT>::Merge) {}
template <class ELFT>
bool MergeInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
return S->SectionKind == InputSectionBase<ELFT>::Merge;
}
template <class ELFT>
std::pair<std::pair<typename ELFFile<ELFT>::uintX_t,
typename ELFFile<ELFT>::uintX_t> *,
typename ELFFile<ELFT>::uintX_t>
SplitInputSection<ELFT>::getRangeAndSize(uintX_t Offset) {
ArrayRef<uint8_t> D = this->getSectionData();
StringRef Data((const char *)D.data(), D.size());
uintX_t Size = Data.size();
if (Offset >= Size)
error("Entry is past the end of the section");
// Find the element this offset points to.
auto I = std::upper_bound(
Offsets.begin(), Offsets.end(), Offset,
[](const uintX_t &A, const std::pair<uintX_t, uintX_t> &B) {
return A < B.first;
});
uintX_t End = I == Offsets.end() ? Data.size() : I->first;
--I;
return std::make_pair(&*I, End);
}
template <class ELFT>
typename MergeInputSection<ELFT>::uintX_t
MergeInputSection<ELFT>::getOffset(uintX_t Offset) {
std::pair<std::pair<uintX_t, uintX_t> *, uintX_t> T =
this->getRangeAndSize(Offset);
std::pair<uintX_t, uintX_t> *I = T.first;
uintX_t End = T.second;
uintX_t Start = I->first;
// Compute the Addend and if the Base is cached, return.
uintX_t Addend = Offset - Start;
uintX_t &Base = I->second;
if (Base != uintX_t(-1))
return Base + Addend;
// Map the base to the offset in the output section and cache it.
ArrayRef<uint8_t> D = this->getSectionData();
StringRef Data((const char *)D.data(), D.size());
StringRef Entry = Data.substr(Start, End - Start);
Base =
static_cast<MergeOutputSection<ELFT> *>(this->OutSec)->getOffset(Entry);
return Base + Addend;
}
template <class ELFT>
MipsReginfoInputSection<ELFT>::MipsReginfoInputSection(ObjectFile<ELFT> *F,
const Elf_Shdr *Header)
: InputSectionBase<ELFT>(F, Header, InputSectionBase<ELFT>::MipsReginfo) {}
template <class ELFT>
uint32_t MipsReginfoInputSection<ELFT>::getGeneralMask() const {
ArrayRef<uint8_t> D = this->getSectionData();
if (D.size() != sizeof(Elf_Mips_RegInfo))
error("Invalid size of .reginfo section");
return reinterpret_cast<const Elf_Mips_RegInfo *>(D.data())->ri_gprmask;
}
template <class ELFT> uint32_t MipsReginfoInputSection<ELFT>::getGp0() const {
ArrayRef<uint8_t> D = this->getSectionData();
if (D.size() != sizeof(Elf_Mips_RegInfo))
error("Invalid size of .reginfo section");
return reinterpret_cast<const Elf_Mips_RegInfo *>(D.data())->ri_gp_value;
}
template <class ELFT>
bool MipsReginfoInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
return S->SectionKind == InputSectionBase<ELFT>::MipsReginfo;
}
namespace lld {
namespace elf2 {
template class InputSectionBase<object::ELF32LE>;
template class InputSectionBase<object::ELF32BE>;
template class InputSectionBase<object::ELF64LE>;
template class InputSectionBase<object::ELF64BE>;
template class InputSection<object::ELF32LE>;
template class InputSection<object::ELF32BE>;
template class InputSection<object::ELF64LE>;
template class InputSection<object::ELF64BE>;
template class EHInputSection<object::ELF32LE>;
template class EHInputSection<object::ELF32BE>;
template class EHInputSection<object::ELF64LE>;
template class EHInputSection<object::ELF64BE>;
template class MergeInputSection<object::ELF32LE>;
template class MergeInputSection<object::ELF32BE>;
template class MergeInputSection<object::ELF64LE>;
template class MergeInputSection<object::ELF64BE>;
template class MipsReginfoInputSection<object::ELF32LE>;
template class MipsReginfoInputSection<object::ELF32BE>;
template class MipsReginfoInputSection<object::ELF64LE>;
template class MipsReginfoInputSection<object::ELF64BE>;
}
}

187
ELF/InputSection.h Normal file
View file

@ -0,0 +1,187 @@
//===- InputSection.h -------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_INPUT_SECTION_H
#define LLD_ELF_INPUT_SECTION_H
#include "Config.h"
#include "lld/Core/LLVM.h"
#include "llvm/Object/ELF.h"
namespace lld {
namespace elf2 {
template <class ELFT> class ObjectFile;
template <class ELFT> class OutputSection;
template <class ELFT> class OutputSectionBase;
// This corresponds to a section of an input file.
template <class ELFT> class InputSectionBase {
protected:
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
const Elf_Shdr *Header;
// The file this section is from.
ObjectFile<ELFT> *File;
public:
enum Kind { Regular, EHFrame, Merge, MipsReginfo };
Kind SectionKind;
InputSectionBase(ObjectFile<ELFT> *File, const Elf_Shdr *Header,
Kind SectionKind);
OutputSectionBase<ELFT> *OutSec = nullptr;
// Used for garbage collection.
// Live bit makes sense only when Config->GcSections is true.
bool isLive() const { return !Config->GcSections || Live; }
bool Live = false;
// Returns the size of this section (even if this is a common or BSS.)
size_t getSize() const { return Header->sh_size; }
static InputSectionBase<ELFT> Discarded;
StringRef getSectionName() const;
const Elf_Shdr *getSectionHdr() const { return Header; }
ObjectFile<ELFT> *getFile() const { return File; }
// The writer sets and uses the addresses.
uintX_t getAlign() {
// The ELF spec states that a value of 0 means the section has no alignment
// constraits.
return std::max<uintX_t>(Header->sh_addralign, 1);
}
uintX_t getOffset(const Elf_Sym &Sym);
// Translate an offset in the input section to an offset in the output
// section.
uintX_t getOffset(uintX_t Offset);
ArrayRef<uint8_t> getSectionData() const;
// Returns a section that Rel is pointing to. Used by the garbage collector.
InputSectionBase<ELFT> *getRelocTarget(const Elf_Rel &Rel);
InputSectionBase<ELFT> *getRelocTarget(const Elf_Rela &Rel);
template <bool isRela>
using RelIteratorRange =
llvm::iterator_range<const llvm::object::Elf_Rel_Impl<ELFT, isRela> *>;
template <bool isRela>
void relocate(uint8_t *Buf, uint8_t *BufEnd, RelIteratorRange<isRela> Rels);
private:
template <bool isRela>
uint8_t *findMipsPairedReloc(uint8_t *Buf, uint32_t SymIndex, uint32_t Type,
RelIteratorRange<isRela> Rels);
};
template <class ELFT>
InputSectionBase<ELFT>
InputSectionBase<ELFT>::Discarded(nullptr, nullptr,
InputSectionBase<ELFT>::Regular);
template <class ELFT> class SplitInputSection : public InputSectionBase<ELFT> {
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
public:
SplitInputSection(ObjectFile<ELFT> *File, const Elf_Shdr *Header,
typename InputSectionBase<ELFT>::Kind SectionKind);
std::vector<std::pair<uintX_t, uintX_t>> Offsets;
std::pair<std::pair<uintX_t, uintX_t> *, uintX_t>
getRangeAndSize(uintX_t Offset);
};
// This corresponds to a SHF_MERGE section of an input file.
template <class ELFT> class MergeInputSection : public SplitInputSection<ELFT> {
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
public:
MergeInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header);
static bool classof(const InputSectionBase<ELFT> *S);
// Translate an offset in the input section to an offset in the output
// section.
uintX_t getOffset(uintX_t Offset);
};
// This corresponds to a .eh_frame section of an input file.
template <class ELFT> class EHInputSection : public SplitInputSection<ELFT> {
public:
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
EHInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header);
static bool classof(const InputSectionBase<ELFT> *S);
// Translate an offset in the input section to an offset in the output
// section.
uintX_t getOffset(uintX_t Offset);
// Relocation section that refer to this one.
const Elf_Shdr *RelocSection = nullptr;
};
// This corresponds to a non SHF_MERGE section of an input file.
template <class ELFT> class InputSection : public InputSectionBase<ELFT> {
typedef InputSectionBase<ELFT> Base;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
public:
InputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header);
// Write this section to a mmap'ed file, assuming Buf is pointing to
// beginning of the output section.
void writeTo(uint8_t *Buf);
// Relocation sections that refer to this one.
SmallVector<const Elf_Shdr *, 1> RelocSections;
// The offset from beginning of the output sections this section was assigned
// to. The writer sets a value.
uint64_t OutSecOff = 0;
static bool classof(const InputSectionBase<ELFT> *S);
};
// MIPS .reginfo section provides information on the registers used by the code
// in the object file. Linker should collect this information and write a single
// .reginfo section in the output file. The output section contains a union of
// used registers masks taken from input .reginfo sections and final value
// of the `_gp` symbol. For details: Chapter 4 / "Register Information" at
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
template <class ELFT>
class MipsReginfoInputSection : public InputSectionBase<ELFT> {
typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
public:
MipsReginfoInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header);
uint32_t getGeneralMask() const;
uint32_t getGp0() const;
static bool classof(const InputSectionBase<ELFT> *S);
};
} // namespace elf2
} // namespace lld
#endif

318
ELF/LinkerScript.cpp Normal file
View file

@ -0,0 +1,318 @@
//===- LinkerScript.cpp ---------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the parser/evaluator of the linker script.
// It does not construct an AST but consume linker script directives directly.
// Results are written to Driver or Config object.
//
//===----------------------------------------------------------------------===//
#include "Config.h"
#include "Driver.h"
#include "SymbolTable.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/StringSaver.h"
using namespace llvm;
using namespace lld;
using namespace lld::elf2;
namespace {
class LinkerScript {
public:
LinkerScript(BumpPtrAllocator *A, StringRef S, bool B)
: Saver(*A), Tokens(tokenize(S)), IsUnderSysroot(B) {}
void run();
private:
static std::vector<StringRef> tokenize(StringRef S);
static StringRef skipSpace(StringRef S);
StringRef next();
bool skip(StringRef Tok);
bool atEOF() { return Tokens.size() == Pos; }
void expect(StringRef Expect);
void addFile(StringRef Path);
void readAsNeeded();
void readEntry();
void readExtern();
void readGroup();
void readInclude();
void readOutput();
void readOutputArch();
void readOutputFormat();
void readSearchDir();
void readSections();
void readOutputSectionDescription();
StringSaver Saver;
std::vector<StringRef> Tokens;
size_t Pos = 0;
bool IsUnderSysroot;
};
}
void LinkerScript::run() {
while (!atEOF()) {
StringRef Tok = next();
if (Tok == ";")
continue;
if (Tok == "ENTRY") {
readEntry();
} else if (Tok == "EXTERN") {
readExtern();
} else if (Tok == "GROUP" || Tok == "INPUT") {
readGroup();
} else if (Tok == "INCLUDE") {
readInclude();
} else if (Tok == "OUTPUT") {
readOutput();
} else if (Tok == "OUTPUT_ARCH") {
readOutputArch();
} else if (Tok == "OUTPUT_FORMAT") {
readOutputFormat();
} else if (Tok == "SEARCH_DIR") {
readSearchDir();
} else if (Tok == "SECTIONS") {
readSections();
} else {
error("unknown directive: " + Tok);
}
}
}
// Split S into linker script tokens.
std::vector<StringRef> LinkerScript::tokenize(StringRef S) {
std::vector<StringRef> Ret;
for (;;) {
S = skipSpace(S);
if (S.empty())
return Ret;
// Quoted token
if (S.startswith("\"")) {
size_t E = S.find("\"", 1);
if (E == StringRef::npos)
error("unclosed quote");
Ret.push_back(S.substr(1, E));
S = S.substr(E + 1);
continue;
}
// Unquoted token
size_t Pos = S.find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789_.$/\\~=+[]*?-:");
// A character that cannot start a word (which is usually a
// punctuation) forms a single character token.
if (Pos == 0)
Pos = 1;
Ret.push_back(S.substr(0, Pos));
S = S.substr(Pos);
}
}
// Skip leading whitespace characters or /**/-style comments.
StringRef LinkerScript::skipSpace(StringRef S) {
for (;;) {
if (S.startswith("/*")) {
size_t E = S.find("*/", 2);
if (E == StringRef::npos)
error("unclosed comment in a linker script");
S = S.substr(E + 2);
continue;
}
size_t Size = S.size();
S = S.ltrim();
if (S.size() == Size)
return S;
}
}
StringRef LinkerScript::next() {
if (atEOF())
error("unexpected EOF");
return Tokens[Pos++];
}
bool LinkerScript::skip(StringRef Tok) {
if (atEOF())
error("unexpected EOF");
if (Tok != Tokens[Pos])
return false;
++Pos;
return true;
}
void LinkerScript::expect(StringRef Expect) {
StringRef Tok = next();
if (Tok != Expect)
error(Expect + " expected, but got " + Tok);
}
void LinkerScript::addFile(StringRef S) {
if (IsUnderSysroot && S.startswith("/")) {
SmallString<128> Path;
(Config->Sysroot + S).toStringRef(Path);
if (sys::fs::exists(Path)) {
Driver->addFile(Saver.save(Path.str()));
return;
}
}
if (sys::path::is_absolute(S)) {
Driver->addFile(S);
} else if (S.startswith("=")) {
if (Config->Sysroot.empty())
Driver->addFile(S.substr(1));
else
Driver->addFile(Saver.save(Config->Sysroot + "/" + S.substr(1)));
} else if (S.startswith("-l")) {
Driver->addFile(searchLibrary(S.substr(2)));
} else if (sys::fs::exists(S)) {
Driver->addFile(S);
} else {
std::string Path = findFromSearchPaths(S);
if (Path.empty())
error("Unable to find " + S);
Driver->addFile(Saver.save(Path));
}
}
void LinkerScript::readAsNeeded() {
expect("(");
bool Orig = Config->AsNeeded;
Config->AsNeeded = true;
for (;;) {
StringRef Tok = next();
if (Tok == ")")
break;
addFile(Tok);
}
Config->AsNeeded = Orig;
}
void LinkerScript::readEntry() {
// -e <symbol> takes predecence over ENTRY(<symbol>).
expect("(");
StringRef Tok = next();
if (Config->Entry.empty())
Config->Entry = Tok;
expect(")");
}
void LinkerScript::readExtern() {
expect("(");
for (;;) {
StringRef Tok = next();
if (Tok == ")")
return;
Config->Undefined.push_back(Tok);
}
}
void LinkerScript::readGroup() {
expect("(");
for (;;) {
StringRef Tok = next();
if (Tok == ")")
return;
if (Tok == "AS_NEEDED") {
readAsNeeded();
continue;
}
addFile(Tok);
}
}
void LinkerScript::readInclude() {
StringRef Tok = next();
auto MBOrErr = MemoryBuffer::getFile(Tok);
error(MBOrErr, "cannot open " + Tok);
std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
StringRef S = Saver.save(MB->getMemBufferRef().getBuffer());
std::vector<StringRef> V = tokenize(S);
Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end());
}
void LinkerScript::readOutput() {
// -o <file> takes predecence over OUTPUT(<file>).
expect("(");
StringRef Tok = next();
if (Config->OutputFile.empty())
Config->OutputFile = Tok;
expect(")");
}
void LinkerScript::readOutputArch() {
// Error checking only for now.
expect("(");
next();
expect(")");
}
void LinkerScript::readOutputFormat() {
// Error checking only for now.
expect("(");
next();
StringRef Tok = next();
if (Tok == ")")
return;
if (Tok != ",")
error("unexpected token: " + Tok);
next();
expect(",");
next();
expect(")");
}
void LinkerScript::readSearchDir() {
expect("(");
Config->SearchPaths.push_back(next());
expect(")");
}
void LinkerScript::readSections() {
expect("{");
while (!skip("}"))
readOutputSectionDescription();
}
void LinkerScript::readOutputSectionDescription() {
StringRef Name = next();
std::vector<StringRef> &InputSections = Config->OutputSections[Name];
expect(":");
expect("{");
while (!skip("}")) {
next(); // Skip input file name.
expect("(");
while (!skip(")"))
InputSections.push_back(next());
}
}
static bool isUnderSysroot(StringRef Path) {
if (Config->Sysroot == "")
return false;
for (; !Path.empty(); Path = sys::path::parent_path(Path))
if (sys::fs::equivalent(Config->Sysroot, Path))
return true;
return false;
}
// Entry point. The other functions or classes are private to this file.
void lld::elf2::readLinkerScript(BumpPtrAllocator *A, MemoryBufferRef MB) {
StringRef Path = MB.getBufferIdentifier();
LinkerScript(A, MB.getBuffer(), isUnderSysroot(Path)).run();
}

131
ELF/MarkLive.cpp Normal file
View file

@ -0,0 +1,131 @@
//===- MarkLive.cpp -------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements --gc-sections, which is a feature to remove unused
// sections from output. Unused sections are sections that are not reachable
// from known GC-root symbols or sections. Naturally the feature is
// implemented as a mark-sweep garbage collector.
//
// Here's how it works. Each InputSectionBase has a "Live" bit. The bit is off
// by default. Starting with GC-root symbols or sections, markLive function
// defined in this file visits all reachable sections to set their Live
// bits. Writer will then ignore sections whose Live bits are off, so that
// such sections are removed from output.
//
//===----------------------------------------------------------------------===//
#include "InputSection.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/ELF.h"
#include <functional>
#include <vector>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace lld;
using namespace lld::elf2;
// Calls Fn for each section that Sec refers to.
template <class ELFT>
static void forEachSuccessor(InputSection<ELFT> *Sec,
std::function<void(InputSectionBase<ELFT> *)> Fn) {
typedef typename ELFFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela;
typedef typename ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
ELFFile<ELFT> &Obj = Sec->getFile()->getObj();
for (const Elf_Shdr *RelSec : Sec->RelocSections) {
if (RelSec->sh_type == SHT_RELA) {
for (const Elf_Rela &RI : Obj.relas(RelSec))
if (InputSectionBase<ELFT> *Succ = Sec->getRelocTarget(RI))
Fn(Succ);
} else {
for (const Elf_Rel &RI : Obj.rels(RelSec))
if (InputSectionBase<ELFT> *Succ = Sec->getRelocTarget(RI))
Fn(Succ);
}
}
}
// Sections listed below are special because they are used by the loader
// just by being in an ELF file. They should not be garbage-collected.
template <class ELFT> static bool isReserved(InputSectionBase<ELFT> *Sec) {
switch (Sec->getSectionHdr()->sh_type) {
case SHT_FINI_ARRAY:
case SHT_INIT_ARRAY:
case SHT_NOTE:
case SHT_PREINIT_ARRAY:
return true;
default:
StringRef S = Sec->getSectionName();
return S.startswith(".ctors") || S.startswith(".dtors") ||
S.startswith(".init") || S.startswith(".fini") ||
S.startswith(".jcr");
}
}
// This is the main function of the garbage collector.
// Starting from GC-root sections, this function visits all reachable
// sections to set their "Live" bits.
template <class ELFT> void lld::elf2::markLive(SymbolTable<ELFT> *Symtab) {
SmallVector<InputSection<ELFT> *, 256> Q;
auto Enqueue = [&](InputSectionBase<ELFT> *Sec) {
if (!Sec || Sec->Live)
return;
Sec->Live = true;
if (InputSection<ELFT> *S = dyn_cast<InputSection<ELFT>>(Sec))
Q.push_back(S);
};
auto MarkSymbol = [&](SymbolBody *Sym) {
if (Sym)
if (auto *D = dyn_cast<DefinedRegular<ELFT>>(Sym->repl()))
Enqueue(D->Section);
};
// Add GC root symbols.
MarkSymbol(Config->EntrySym);
MarkSymbol(Symtab->find(Config->Init));
MarkSymbol(Symtab->find(Config->Fini));
for (StringRef S : Config->Undefined)
MarkSymbol(Symtab->find(S));
// Preserve externally-visible symbols if the symbols defined by this
// file could override other ELF file's symbols at runtime.
if (Config->Shared || Config->ExportDynamic) {
for (const std::pair<StringRef, Symbol *> &P : Symtab->getSymbols()) {
SymbolBody *B = P.second->Body;
if (B->getVisibility() == STV_DEFAULT)
MarkSymbol(B);
}
}
// Preserve special sections.
for (const std::unique_ptr<ObjectFile<ELFT>> &F : Symtab->getObjectFiles())
for (InputSectionBase<ELFT> *Sec : F->getSections())
if (Sec && Sec != &InputSection<ELFT>::Discarded)
if (isReserved(Sec))
Enqueue(Sec);
// Mark all reachable sections.
while (!Q.empty())
forEachSuccessor<ELFT>(Q.pop_back_val(), Enqueue);
}
template void lld::elf2::markLive<ELF32LE>(SymbolTable<ELF32LE> *);
template void lld::elf2::markLive<ELF32BE>(SymbolTable<ELF32BE> *);
template void lld::elf2::markLive<ELF64LE>(SymbolTable<ELF64LE> *);
template void lld::elf2::markLive<ELF64BE>(SymbolTable<ELF64BE> *);

161
ELF/Options.td Normal file
View file

@ -0,0 +1,161 @@
include "llvm/Option/OptParser.td"
def Bsymbolic: Flag<["-"], "Bsymbolic">,
HelpText<"Bind defined symbols locally">;
def Bdynamic: Flag<["-"], "Bdynamic">,
HelpText<"Link against shared libraries">;
def Bstatic: Flag<["-"], "Bstatic">,
HelpText<"Do not link against shared libraries">;
def L : JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
HelpText<"Directory to search for libraries">;
def O : Joined<["-"], "O">, HelpText<"Optimize">;
def allow_multiple_definition: Flag<["--"], "allow-multiple-definition">,
HelpText<"Allow multiple definitions">;
def allow_shlib_undefined : Flag<["--", "-"], "allow-shlib-undefined">;
def as_needed : Flag<["--"], "as-needed">;
def disable_new_dtags : Flag<["--"], "disable-new-dtags">,
HelpText<"Disable new dynamic tags">;
def discard_all : Flag<["-"], "discard-all">,
HelpText<"Delete all local symbols">;
def discard_locals : Flag<["-"], "discard-locals">,
HelpText<"Delete temporary local symbols">;
def discard_none : Flag<["-"], "discard-none">,
HelpText<"Keep all symbols in the symbol table">;
def dynamic_linker : Separate<["--", "-"], "dynamic-linker">,
HelpText<"Which dynamic linker to use">;
def enable_new_dtags : Flag<["--"], "enable-new-dtags">,
HelpText<"Enable new dynamic tags">;
def entry : Separate<["--", "-"], "entry">, MetaVarName<"<entry>">,
HelpText<"Name of entry point symbol">;
def export_dynamic : Flag<["--", "-"], "export-dynamic">,
HelpText<"Put symbols in the dynamic symbol table">;
def fini : Separate<["-"], "fini">, MetaVarName<"<symbol>">,
HelpText<"Specify a finalizer function">;
def hash_style : Separate<["--", "-"], "hash-style">,
HelpText<"Specify hash style (sysv, gnu or both)">;
def gc_sections : Flag<["--"], "gc-sections">,
HelpText<"Enable garbage collection of unused sections">;
def init : Separate<["-"], "init">, MetaVarName<"<symbol>">,
HelpText<"Specify an initializer function">;
def l : JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
HelpText<"Root name of library to use">;
def m : JoinedOrSeparate<["-"], "m">,
HelpText<"Set target emulation">;
def no_allow_shlib_undefined : Flag<["--"], "no-allow-shlib-undefined">;
def no_as_needed : Flag<["--"], "no-as-needed">;
def no_whole_archive : Flag<["--", "-"], "no-whole-archive">,
HelpText<"Restores the default behavior of loading archive members">;
def noinhibit_exec : Flag<["--"], "noinhibit-exec">,
HelpText<"Retain the executable output file whenever it is still usable">;
def no_undefined : Flag<["--"], "no-undefined">,
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
def o : Separate<["-"], "o">, MetaVarName<"<path>">,
HelpText<"Path to file to write output">;
def print_gc_sections: Flag<["--"], "print-gc-sections">,
HelpText<"List removed unused sections">;
def rpath : Separate<["-"], "rpath">,
HelpText<"Add a DT_RUNPATH to the output">;
def relocatable : Flag<["--"], "relocatable">;
def script : Separate<["--"], "script">, HelpText<"Read linker script">;
def shared : Flag<["-"], "shared">,
HelpText<"Build a shared object">;
def soname : Joined<["-"], "soname=">,
HelpText<"Set DT_SONAME">;
def strip_all : Flag<["--"], "strip-all">,
HelpText<"Strip all symbols">;
def sysroot : Joined<["--"], "sysroot=">,
HelpText<"Set the system root">;
def undefined : Joined<["--"], "undefined=">,
HelpText<"Force undefined symbol during linking">;
def verbose : Flag<["--"], "verbose">;
def whole_archive : Flag<["--", "-"], "whole-archive">,
HelpText<"Force load of all members in a static library">;
def z : JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
HelpText<"Linker option extensions">;
// Aliases
def alias_Bdynamic_call_shared: Flag<["-"], "call_shared">, Alias<Bdynamic>;
def alias_Bdynamic_dy: Flag<["-"], "dy">, Alias<Bdynamic>;
def alias_Bstatic_dn: Flag<["-"], "dn">, Alias<Bstatic>;
def alias_Bstatic_non_shared: Flag<["-"], "non_shared">, Alias<Bstatic>;
def alias_Bstatic_static: Flag<["-"], "static">, Alias<Bstatic>;
def alias_L__library_path : Joined<["--"], "library-path=">, Alias<L>;
def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
def alias_entry_e : Separate<["-"], "e">, Alias<entry>;
def alias_export_dynamic_E: Flag<["-"], "E">, Alias<export_dynamic>;
def alias_fini_fini : Joined<["-"], "fini=">, Alias<fini>;
def alias_hash_style_hash_style : Joined<["--", "-"], "hash-style=">, Alias<hash_style>;
def alias_init_init : Joined<["-"], "init=">, Alias<init>;
def alias_l__library : Joined<["--"], "library=">, Alias<l>;
def alias_o_output : Joined<["--"], "output=">, Alias<o>;
def alias_rpath_rpath : Joined<["-"], "rpath=">, Alias<rpath>;
def alias_relocatable_r : Flag<["-"], "r">, Alias<relocatable>;
def alias_shared_Bshareable : Flag<["-"], "Bshareable">, Alias<shared>;
def alias_soname_h : Separate<["-"], "h">, Alias<soname>;
def alias_soname_soname : Separate<["-"], "soname">, Alias<soname>;
def alias_script_T : Separate<["-"], "T">, Alias<script>;
def alias_strip_all: Flag<["-"], "s">, Alias<strip_all>;
def alias_undefined_u : Separate<["-"], "u">, Alias<undefined>;
// Our symbol resolution algorithm handles symbols in archive files differently
// than traditional linkers, so we don't need --start-group and --end-group.
// These options are recongized for compatibility but ignored.
def end_group : Flag<["--"], "end-group">;
def end_group_paren: Flag<["-"], ")">;
def start_group : Flag<["--"], "start-group">;
def start_group_paren: Flag<["-"], "(">;
// Options listed below are silently ignored for now for compatibility.
def build_id : Flag<["--"], "build-id">;
def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">;
def fatal_warnings : Flag<["--"], "fatal-warnings">;
def no_add_needed : Flag<["--"], "no-add-needed">;
def no_fatal_warnings : Flag<["--"], "no-fatal-warnings">;
def no_warn_mismatch : Flag<["--"], "no-warn-mismatch">;
def version_script : Separate<["--"], "version-script">;
def warn_common : Flag<["--"], "warn-common">;
def warn_shared_textrel : Flag<["--"], "warn-shared-textrel">;
def G : Separate<["-"], "G">;
// Aliases for ignored options
def alias_version_script_version_script : Joined<["--"], "version-script=">, Alias<version_script>;

1534
ELF/OutputSections.cpp Normal file

File diff suppressed because it is too large Load diff

485
ELF/OutputSections.h Normal file
View file

@ -0,0 +1,485 @@
//===- OutputSections.h -----------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_OUTPUT_SECTIONS_H
#define LLD_ELF_OUTPUT_SECTIONS_H
#include "lld/Core/LLVM.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELF.h"
#include "Config.h"
#include <type_traits>
namespace lld {
namespace elf2 {
class SymbolBody;
template <class ELFT> class SymbolTable;
template <class ELFT> class SymbolTableSection;
template <class ELFT> class StringTableSection;
template <class ELFT> class EHInputSection;
template <class ELFT> class InputSection;
template <class ELFT> class InputSectionBase;
template <class ELFT> class MergeInputSection;
template <class ELFT> class MipsReginfoInputSection;
template <class ELFT> class OutputSection;
template <class ELFT> class ObjectFile;
template <class ELFT> class DefinedRegular;
// Flag to force GOT to be in output if we have relocations
// that relies on its address.
extern bool HasGotOffRel;
template <class ELFT>
static inline typename llvm::object::ELFFile<ELFT>::uintX_t
getAddend(const typename llvm::object::ELFFile<ELFT>::Elf_Rel &Rel) {
return 0;
}
template <class ELFT>
static inline typename llvm::object::ELFFile<ELFT>::uintX_t
getAddend(const typename llvm::object::ELFFile<ELFT>::Elf_Rela &Rel) {
return Rel.r_addend;
}
template <class ELFT>
typename llvm::object::ELFFile<ELFT>::uintX_t getSymVA(const SymbolBody &S);
template <class ELFT, bool IsRela>
typename llvm::object::ELFFile<ELFT>::uintX_t
getLocalRelTarget(const ObjectFile<ELFT> &File,
const llvm::object::Elf_Rel_Impl<ELFT, IsRela> &Rel,
typename llvm::object::ELFFile<ELFT>::uintX_t Addend);
bool canBePreempted(const SymbolBody *Body, bool NeedsGot);
template <class ELFT>
bool shouldKeepInSymtab(
const ObjectFile<ELFT> &File, StringRef Name,
const typename llvm::object::ELFFile<ELFT>::Elf_Sym &Sym);
// This represents a section in an output file.
// Different sub classes represent different types of sections. Some contain
// input sections, others are created by the linker.
// The writer creates multiple OutputSections and assign them unique,
// non-overlapping file offsets and VAs.
template <class ELFT> class OutputSectionBase {
public:
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
OutputSectionBase(StringRef Name, uint32_t sh_type, uintX_t sh_flags);
void setVA(uintX_t VA) { Header.sh_addr = VA; }
uintX_t getVA() const { return Header.sh_addr; }
void setFileOffset(uintX_t Off) { Header.sh_offset = Off; }
void writeHeaderTo(Elf_Shdr *SHdr);
StringRef getName() { return Name; }
virtual void addSection(InputSectionBase<ELFT> *C) {}
unsigned SectionIndex;
// Returns the size of the section in the output file.
uintX_t getSize() const { return Header.sh_size; }
void setSize(uintX_t Val) { Header.sh_size = Val; }
uintX_t getFlags() { return Header.sh_flags; }
uintX_t getFileOff() { return Header.sh_offset; }
uintX_t getAlign() {
// The ELF spec states that a value of 0 means the section has no alignment
// constraits.
return std::max<uintX_t>(Header.sh_addralign, 1);
}
uint32_t getType() { return Header.sh_type; }
void updateAlign(uintX_t Align) {
if (Align > Header.sh_addralign)
Header.sh_addralign = Align;
}
virtual void finalize() {}
virtual void writeTo(uint8_t *Buf) = 0;
virtual ~OutputSectionBase() = default;
protected:
StringRef Name;
Elf_Shdr Header;
};
template <class ELFT> class GotSection final : public OutputSectionBase<ELFT> {
typedef OutputSectionBase<ELFT> Base;
typedef typename Base::uintX_t uintX_t;
public:
GotSection();
void finalize() override;
void writeTo(uint8_t *Buf) override;
void addEntry(SymbolBody *Sym);
bool addDynTlsEntry(SymbolBody *Sym);
bool addCurrentModuleTlsIndex();
bool empty() const { return Entries.empty(); }
uintX_t getEntryAddr(const SymbolBody &B) const;
uintX_t getGlobalDynAddr(const SymbolBody &B) const;
uintX_t getNumEntries() const { return Entries.size(); }
// Returns the symbol which corresponds to the first entry of the global part
// of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic
// table properties.
// Returns nullptr if the global part is empty.
const SymbolBody *getMipsFirstGlobalEntry() const;
// Returns the number of entries in the local part of GOT including
// the number of reserved entries. This method is MIPS-specific.
unsigned getMipsLocalEntriesNum() const;
uint32_t getLocalTlsIndexVA() { return Base::getVA() + LocalTlsIndexOff; }
private:
std::vector<const SymbolBody *> Entries;
uint32_t LocalTlsIndexOff = -1;
};
template <class ELFT>
class GotPltSection final : public OutputSectionBase<ELFT> {
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
public:
GotPltSection();
void finalize() override;
void writeTo(uint8_t *Buf) override;
void addEntry(SymbolBody *Sym);
bool empty() const;
uintX_t getEntryAddr(const SymbolBody &B) const;
private:
std::vector<const SymbolBody *> Entries;
};
template <class ELFT> class PltSection final : public OutputSectionBase<ELFT> {
typedef OutputSectionBase<ELFT> Base;
typedef typename Base::uintX_t uintX_t;
public:
PltSection();
void finalize() override;
void writeTo(uint8_t *Buf) override;
void addEntry(SymbolBody *Sym);
bool empty() const { return Entries.empty(); }
uintX_t getEntryAddr(const SymbolBody &B) const;
private:
std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
};
template <class ELFT> struct DynamicReloc {
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel;
InputSectionBase<ELFT> *C;
const Elf_Rel *RI;
};
template <class ELFT>
class SymbolTableSection final : public OutputSectionBase<ELFT> {
public:
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym_Range Elf_Sym_Range;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
SymbolTableSection(SymbolTable<ELFT> &Table,
StringTableSection<ELFT> &StrTabSec);
void finalize() override;
void writeTo(uint8_t *Buf) override;
void addLocalSymbol(StringRef Name);
void addSymbol(SymbolBody *Body);
StringTableSection<ELFT> &getStrTabSec() const { return StrTabSec; }
unsigned getNumSymbols() const { return NumVisible + 1; }
ArrayRef<SymbolBody *> getSymbols() const { return Symbols; }
private:
void writeLocalSymbols(uint8_t *&Buf);
void writeGlobalSymbols(uint8_t *Buf);
static uint8_t getSymbolBinding(SymbolBody *Body);
SymbolTable<ELFT> &Table;
StringTableSection<ELFT> &StrTabSec;
std::vector<SymbolBody *> Symbols;
unsigned NumVisible = 0;
unsigned NumLocals = 0;
};
template <class ELFT>
class RelocationSection final : public OutputSectionBase<ELFT> {
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
public:
RelocationSection(StringRef Name, bool IsRela);
void addReloc(const DynamicReloc<ELFT> &Reloc) { Relocs.push_back(Reloc); }
unsigned getRelocOffset();
void finalize() override;
void writeTo(uint8_t *Buf) override;
bool hasRelocs() const { return !Relocs.empty(); }
bool isRela() const { return IsRela; }
bool Static = false;
private:
bool applyTlsDynamicReloc(SymbolBody *Body, uint32_t Type, Elf_Rel *P,
Elf_Rel *N);
std::vector<DynamicReloc<ELFT>> Relocs;
const bool IsRela;
};
template <class ELFT>
class OutputSection final : public OutputSectionBase<ELFT> {
public:
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
OutputSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags);
void addSection(InputSectionBase<ELFT> *C) override;
void writeTo(uint8_t *Buf) override;
private:
std::vector<InputSection<ELFT> *> Sections;
};
template <class ELFT>
class MergeOutputSection final : public OutputSectionBase<ELFT> {
typedef typename OutputSectionBase<ELFT>::uintX_t uintX_t;
bool shouldTailMerge() const;
public:
MergeOutputSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags);
void addSection(InputSectionBase<ELFT> *S) override;
void writeTo(uint8_t *Buf) override;
unsigned getOffset(StringRef Val);
void finalize() override;
private:
llvm::StringTableBuilder Builder{llvm::StringTableBuilder::RAW};
};
// FDE or CIE
template <class ELFT> struct EHRegion {
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
EHRegion(EHInputSection<ELFT> *S, unsigned Index);
StringRef data() const;
EHInputSection<ELFT> *S;
unsigned Index;
};
template <class ELFT> struct Cie : public EHRegion<ELFT> {
Cie(EHInputSection<ELFT> *S, unsigned Index);
std::vector<EHRegion<ELFT>> Fdes;
};
template <class ELFT>
class EHOutputSection final : public OutputSectionBase<ELFT> {
public:
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela;
EHOutputSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags);
void writeTo(uint8_t *Buf) override;
template <bool IsRela>
void addSectionAux(
EHInputSection<ELFT> *S,
llvm::iterator_range<const llvm::object::Elf_Rel_Impl<ELFT, IsRela> *>
Rels);
void addSection(InputSectionBase<ELFT> *S) override;
private:
uintX_t readEntryLength(ArrayRef<uint8_t> D);
std::vector<EHInputSection<ELFT> *> Sections;
std::vector<Cie<ELFT>> Cies;
// Maps CIE content + personality to a index in Cies.
llvm::DenseMap<std::pair<StringRef, StringRef>, unsigned> CieMap;
};
template <class ELFT>
class InterpSection final : public OutputSectionBase<ELFT> {
public:
InterpSection();
void writeTo(uint8_t *Buf) override;
};
template <class ELFT>
class StringTableSection final : public OutputSectionBase<ELFT> {
public:
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
StringTableSection(StringRef Name, bool Dynamic);
void add(StringRef S) { StrTabBuilder.add(S); }
size_t getOffset(StringRef S) const { return StrTabBuilder.getOffset(S); }
StringRef data() const { return StrTabBuilder.data(); }
void writeTo(uint8_t *Buf) override;
void finalize() override {
StrTabBuilder.finalize();
this->Header.sh_size = StrTabBuilder.data().size();
}
bool isDynamic() const { return Dynamic; }
private:
const bool Dynamic;
llvm::StringTableBuilder StrTabBuilder{llvm::StringTableBuilder::ELF};
};
template <class ELFT>
class HashTableSection final : public OutputSectionBase<ELFT> {
typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word;
public:
HashTableSection();
void finalize() override;
void writeTo(uint8_t *Buf) override;
};
// Outputs GNU Hash section. For detailed explanation see:
// https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections
template <class ELFT>
class GnuHashTableSection final : public OutputSectionBase<ELFT> {
typedef typename llvm::object::ELFFile<ELFT>::Elf_Off Elf_Off;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
public:
GnuHashTableSection();
void finalize() override;
void writeTo(uint8_t *Buf) override;
// Adds symbols to the hash table.
// Sorts the input to satisfy GNU hash section requirements.
void addSymbols(std::vector<SymbolBody *> &Symbols);
private:
static unsigned calcNBuckets(unsigned NumHashed);
static unsigned calcMaskWords(unsigned NumHashed);
void writeHeader(uint8_t *&Buf);
void writeBloomFilter(uint8_t *&Buf);
void writeHashTable(uint8_t *Buf);
struct HashedSymbolData {
SymbolBody *Body;
uint32_t Hash;
};
std::vector<HashedSymbolData> HashedSymbols;
unsigned MaskWords;
unsigned NBuckets;
unsigned Shift2;
};
template <class ELFT>
class DynamicSection final : public OutputSectionBase<ELFT> {
typedef OutputSectionBase<ELFT> Base;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Dyn Elf_Dyn;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel Elf_Rel;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela Elf_Rela;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
public:
DynamicSection(SymbolTable<ELFT> &SymTab);
void finalize() override;
void writeTo(uint8_t *Buf) override;
OutputSectionBase<ELFT> *PreInitArraySec = nullptr;
OutputSectionBase<ELFT> *InitArraySec = nullptr;
OutputSectionBase<ELFT> *FiniArraySec = nullptr;
private:
SymbolTable<ELFT> &SymTab;
const SymbolBody *InitSym = nullptr;
const SymbolBody *FiniSym = nullptr;
uint32_t DtFlags = 0;
uint32_t DtFlags1 = 0;
};
template <class ELFT>
class MipsReginfoOutputSection final : public OutputSectionBase<ELFT> {
typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
public:
MipsReginfoOutputSection();
void writeTo(uint8_t *Buf) override;
void addSection(InputSectionBase<ELFT> *S) override;
private:
uint32_t GeneralMask = 0;
};
// All output sections that are hadnled by the linker specially are
// globally accessible. Writer initializes them, so don't use them
// until Writer is initialized.
template <class ELFT> struct Out {
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
typedef typename llvm::object::ELFFile<ELFT>::Elf_Phdr Elf_Phdr;
static DynamicSection<ELFT> *Dynamic;
static GnuHashTableSection<ELFT> *GnuHashTab;
static GotPltSection<ELFT> *GotPlt;
static GotSection<ELFT> *Got;
static HashTableSection<ELFT> *HashTab;
static InterpSection<ELFT> *Interp;
static OutputSection<ELFT> *Bss;
static OutputSection<ELFT> *MipsRldMap;
static OutputSectionBase<ELFT> *Opd;
static uint8_t *OpdBuf;
static PltSection<ELFT> *Plt;
static RelocationSection<ELFT> *RelaDyn;
static RelocationSection<ELFT> *RelaPlt;
static StringTableSection<ELFT> *DynStrTab;
static StringTableSection<ELFT> *ShStrTab;
static StringTableSection<ELFT> *StrTab;
static SymbolTableSection<ELFT> *DynSymTab;
static SymbolTableSection<ELFT> *SymTab;
static Elf_Phdr *TlsPhdr;
};
template <class ELFT> DynamicSection<ELFT> *Out<ELFT>::Dynamic;
template <class ELFT> GnuHashTableSection<ELFT> *Out<ELFT>::GnuHashTab;
template <class ELFT> GotPltSection<ELFT> *Out<ELFT>::GotPlt;
template <class ELFT> GotSection<ELFT> *Out<ELFT>::Got;
template <class ELFT> HashTableSection<ELFT> *Out<ELFT>::HashTab;
template <class ELFT> InterpSection<ELFT> *Out<ELFT>::Interp;
template <class ELFT> OutputSection<ELFT> *Out<ELFT>::Bss;
template <class ELFT> OutputSection<ELFT> *Out<ELFT>::MipsRldMap;
template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::Opd;
template <class ELFT> uint8_t *Out<ELFT>::OpdBuf;
template <class ELFT> PltSection<ELFT> *Out<ELFT>::Plt;
template <class ELFT> RelocationSection<ELFT> *Out<ELFT>::RelaDyn;
template <class ELFT> RelocationSection<ELFT> *Out<ELFT>::RelaPlt;
template <class ELFT> StringTableSection<ELFT> *Out<ELFT>::DynStrTab;
template <class ELFT> StringTableSection<ELFT> *Out<ELFT>::ShStrTab;
template <class ELFT> StringTableSection<ELFT> *Out<ELFT>::StrTab;
template <class ELFT> SymbolTableSection<ELFT> *Out<ELFT>::DynSymTab;
template <class ELFT> SymbolTableSection<ELFT> *Out<ELFT>::SymTab;
template <class ELFT> typename Out<ELFT>::Elf_Phdr *Out<ELFT>::TlsPhdr;
} // namespace elf2
} // namespace lld
#endif // LLD_ELF_OUTPUT_SECTIONS_H

21
ELF/README.md Normal file
View file

@ -0,0 +1,21 @@
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. Note that unlike COFF, we do not
distinguish chunks from input sections; they are merged together.
Capabilities
------------
This linker can link LLVM and Clang on Linux/x86-64 or FreeBSD/x86-64
"Hello world" can be linked on Linux/PPC64 and on Linux/AArch64 or
FreeBSD/AArch64.
Performance
-----------
Achieving good performance is one of our goals. It's too early to reach a
conclusion, but we are optimistic about that as it currently seems to be faster
than GNU gold. It will be interesting to compare when we are close to feature
parity.

267
ELF/SymbolTable.cpp Normal file
View file

@ -0,0 +1,267 @@
//===- SymbolTable.cpp ----------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Symbol table is a bag of all known symbols. We put all symbols of
// all input files to the symbol table. The symbol Table is basically
// a hash table with the logic to resolve symbol name conflicts using
// the symbol types.
//
//===----------------------------------------------------------------------===//
#include "SymbolTable.h"
#include "Config.h"
#include "Error.h"
#include "Symbols.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf2;
template <class ELFT> SymbolTable<ELFT>::SymbolTable() {}
template <class ELFT>
static void checkCompatibility(InputFile *FileP) {
auto *F = dyn_cast<ELFFileBase<ELFT>>(FileP);
if (!F)
return;
if (F->getELFKind() == Config->EKind && F->getEMachine() == Config->EMachine)
return;
StringRef A = F->getName();
StringRef B = Config->Emulation;
if (B.empty())
B = Config->FirstElf->getName();
error(A + " is incompatible with " + B);
}
template <class ELFT>
void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) {
InputFile *FileP = File.get();
checkCompatibility<ELFT>(FileP);
// .a file
if (auto *F = dyn_cast<ArchiveFile>(FileP)) {
ArchiveFiles.emplace_back(cast<ArchiveFile>(File.release()));
F->parse();
for (Lazy &Sym : F->getLazySymbols())
addLazy(&Sym);
return;
}
// .so file
if (auto *F = dyn_cast<SharedFile<ELFT>>(FileP)) {
// DSOs are uniquified not by filename but by soname.
F->parseSoName();
if (!IncludedSoNames.insert(F->getSoName()).second)
return;
SharedFiles.emplace_back(cast<SharedFile<ELFT>>(File.release()));
F->parse();
for (SharedSymbol<ELFT> &B : F->getSharedSymbols())
resolve(&B);
return;
}
// .o file
auto *F = cast<ObjectFile<ELFT>>(FileP);
ObjectFiles.emplace_back(cast<ObjectFile<ELFT>>(File.release()));
F->parse(Comdats);
for (SymbolBody *B : F->getSymbols())
resolve(B);
}
// Add an undefined symbol.
template <class ELFT>
SymbolBody *SymbolTable<ELFT>::addUndefined(StringRef Name) {
auto *Sym = new (Alloc) Undefined(Name, false, STV_DEFAULT, false);
resolve(Sym);
return Sym;
}
// Add an undefined symbol. Unlike addUndefined, that symbol
// doesn't have to be resolved, thus "opt" (optional).
template <class ELFT>
SymbolBody *SymbolTable<ELFT>::addUndefinedOpt(StringRef Name) {
auto *Sym = new (Alloc) Undefined(Name, false, STV_HIDDEN, true);
resolve(Sym);
return Sym;
}
template <class ELFT>
void SymbolTable<ELFT>::addAbsolute(StringRef Name,
typename ELFFile<ELFT>::Elf_Sym &ESym) {
resolve(new (Alloc) DefinedRegular<ELFT>(Name, ESym, nullptr));
}
template <class ELFT>
void SymbolTable<ELFT>::addSynthetic(StringRef Name,
OutputSectionBase<ELFT> &Section,
typename ELFFile<ELFT>::uintX_t Value) {
auto *Sym = new (Alloc) DefinedSynthetic<ELFT>(Name, Value, Section);
resolve(Sym);
}
template <class ELFT>
SymbolBody *SymbolTable<ELFT>::addIgnored(StringRef Name) {
auto *Sym = new (Alloc)
DefinedRegular<ELFT>(Name, ElfSym<ELFT>::IgnoreUndef, nullptr);
resolve(Sym);
return Sym;
}
template <class ELFT> bool SymbolTable<ELFT>::isUndefined(StringRef Name) {
if (SymbolBody *Sym = find(Name))
return Sym->isUndefined();
return false;
}
// Returns a file from which symbol B was created.
// If B does not belong to any file in ObjectFiles, returns a nullptr.
template <class ELFT>
ELFFileBase<ELFT> *
elf2::findFile(ArrayRef<std::unique_ptr<ObjectFile<ELFT>>> ObjectFiles,
const SymbolBody *B) {
for (const std::unique_ptr<ObjectFile<ELFT>> &F : ObjectFiles) {
ArrayRef<SymbolBody *> Syms = F->getSymbols();
if (std::find(Syms.begin(), Syms.end(), B) != Syms.end())
return F.get();
}
return nullptr;
}
template <class ELFT>
std::string SymbolTable<ELFT>::conflictMsg(SymbolBody *Old, SymbolBody *New) {
ELFFileBase<ELFT> *OldFile = findFile<ELFT>(ObjectFiles, Old);
ELFFileBase<ELFT> *NewFile = findFile<ELFT>(ObjectFiles, New);
StringRef Sym = Old->getName();
StringRef F1 = OldFile ? OldFile->getName() : "(internal)";
StringRef F2 = NewFile ? NewFile->getName() : "(internal)";
return (Sym + " in " + F1 + " and " + F2).str();
}
// 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) {
Symbol *Sym = insert(New);
if (Sym->Body == New)
return;
SymbolBody *Existing = Sym->Body;
if (Lazy *L = dyn_cast<Lazy>(Existing)) {
if (auto *Undef = dyn_cast<Undefined>(New)) {
addMemberFile(Undef, L);
return;
}
// Found a definition for something also in an archive.
// Ignore the archive definition.
Sym->Body = New;
return;
}
if (New->isTls() != Existing->isTls())
error("TLS attribute mismatch for symbol: " + conflictMsg(Existing, New));
// compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
// equivalent (conflicting), or more preferable, respectively.
int comp = Existing->compare<ELFT>(New);
if (comp == 0) {
std::string S = "duplicate symbol: " + conflictMsg(Existing, New);
if (!Config->AllowMultipleDefinition)
error(S);
warning(S);
return;
}
if (comp < 0)
Sym->Body = New;
}
template <class ELFT> Symbol *SymbolTable<ELFT>::insert(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 Sym;
}
template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
auto It = Symtab.find(Name);
if (It == Symtab.end())
return nullptr;
return It->second->Body;
}
template <class ELFT> void SymbolTable<ELFT>::addLazy(Lazy *L) {
Symbol *Sym = insert(L);
if (Sym->Body == L)
return;
if (auto *Undef = dyn_cast<Undefined>(Sym->Body)) {
Sym->Body = L;
addMemberFile(Undef, L);
}
}
template <class ELFT>
void SymbolTable<ELFT>::addMemberFile(Undefined *Undef, Lazy *L) {
// Weak undefined symbols should not fetch members from archives.
// If we were to keep old symbol we would not know that an archive member was
// available if a strong undefined symbol shows up afterwards in the link.
// If a strong undefined symbol never shows up, this lazy symbol will
// get to the end of the link and must be treated as the weak undefined one.
// We set UsedInRegularObj in a similar way to what is done with shared
// symbols and mark it as weak to reduce how many special cases are needed.
if (Undef->isWeak()) {
L->setUsedInRegularObj();
L->setWeak();
return;
}
// Fetch a member file that has the definition for L.
// getMember returns nullptr if the member was already read from the library.
if (std::unique_ptr<InputFile> File = L->getMember())
addFile(std::move(File));
}
// This function takes care of the case in which shared libraries depend on
// the user program (not the other way, which is usual). Shared libraries
// may have undefined symbols, expecting that the user program provides
// the definitions for them. An example is BSD's __progname symbol.
// We need to put such symbols to the main program's .dynsym so that
// shared libraries can find them.
// Except this, we ignore undefined symbols in DSOs.
template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
for (std::unique_ptr<SharedFile<ELFT>> &File : SharedFiles)
for (StringRef U : File->getUndefinedSymbols())
if (SymbolBody *Sym = find(U))
if (Sym->isDefined())
Sym->setUsedInDynamicReloc();
}
template class lld::elf2::SymbolTable<ELF32LE>;
template class lld::elf2::SymbolTable<ELF32BE>;
template class lld::elf2::SymbolTable<ELF64LE>;
template class lld::elf2::SymbolTable<ELF64BE>;
template ELFFileBase<ELF32LE> *
lld::elf2::findFile(ArrayRef<std::unique_ptr<ObjectFile<ELF32LE>>>,
const SymbolBody *);
template ELFFileBase<ELF32BE> *
lld::elf2::findFile(ArrayRef<std::unique_ptr<ObjectFile<ELF32BE>>>,
const SymbolBody *);
template ELFFileBase<ELF64LE> *
lld::elf2::findFile(ArrayRef<std::unique_ptr<ObjectFile<ELF64LE>>>,
const SymbolBody *);
template ELFFileBase<ELF64BE> *
lld::elf2::findFile(ArrayRef<std::unique_ptr<ObjectFile<ELF64BE>>>,
const SymbolBody *);

98
ELF/SymbolTable.h Normal file
View file

@ -0,0 +1,98 @@
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
//
// 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 "llvm/ADT/MapVector.h"
namespace lld {
namespace elf2 {
class Lazy;
template <class ELFT> class OutputSectionBase;
struct Symbol;
class Undefined;
// 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);
const llvm::MapVector<StringRef, Symbol *> &getSymbols() const {
return Symtab;
}
const std::vector<std::unique_ptr<ObjectFile<ELFT>>> &getObjectFiles() const {
return ObjectFiles;
}
const std::vector<std::unique_ptr<SharedFile<ELFT>>> &getSharedFiles() const {
return SharedFiles;
}
SymbolBody *addUndefined(StringRef Name);
SymbolBody *addUndefinedOpt(StringRef Name);
void addAbsolute(StringRef Name,
typename llvm::object::ELFFile<ELFT>::Elf_Sym &ESym);
void addSynthetic(StringRef Name, OutputSectionBase<ELFT> &Section,
typename llvm::object::ELFFile<ELFT>::uintX_t Value);
SymbolBody *addIgnored(StringRef Name);
bool isUndefined(StringRef Name);
void scanShlibUndefined();
SymbolBody *find(StringRef Name);
private:
Symbol *insert(SymbolBody *New);
void addLazy(Lazy *New);
void addMemberFile(Undefined *Undef, Lazy *L);
void resolve(SymbolBody *Body);
std::string conflictMsg(SymbolBody *Old, SymbolBody *New);
std::vector<std::unique_ptr<ArchiveFile>> ArchiveFiles;
// The order the global symbols are in is not defined. We can use an arbitrary
// order, but it has to be reproducible. That is true even when cross linking.
// The default hashing of StringRef produces different results on 32 and 64
// bit systems so we use a MapVector. That is arbitrary, deterministic but
// a bit inefficient.
// FIXME: Experiment with passing in a custom hashing or sorting the symbols
// once symbol resolution is finished.
llvm::MapVector<StringRef, Symbol *> Symtab;
llvm::BumpPtrAllocator Alloc;
llvm::DenseSet<StringRef> Comdats;
// The writer needs to infer the machine type from the object files.
std::vector<std::unique_ptr<ObjectFile<ELFT>>> ObjectFiles;
std::vector<std::unique_ptr<SharedFile<ELFT>>> SharedFiles;
llvm::DenseSet<StringRef> IncludedSoNames;
};
template <class ELFT>
ELFFileBase<ELFT> *
findFile(ArrayRef<std::unique_ptr<ObjectFile<ELFT>>> ObjectFiles,
const SymbolBody *B);
} // namespace elf2
} // namespace lld
#endif

148
ELF/Symbols.cpp Normal file
View file

@ -0,0 +1,148 @@
//===- 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 "InputSection.h"
#include "Error.h"
#include "InputFiles.h"
#include "llvm/ADT/STLExtras.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf2;
static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
if (VA == STV_DEFAULT)
return VB;
if (VB == STV_DEFAULT)
return VA;
return std::min(VA, VB);
}
// Returns 1, 0 or -1 if this symbol should take precedence
// over the Other, tie or lose, respectively.
template <class ELFT> int SymbolBody::compare(SymbolBody *Other) {
typedef typename ELFFile<ELFT>::uintX_t uintX_t;
assert(!isLazy() && !Other->isLazy());
std::pair<bool, bool> L(isDefined(), !isWeak());
std::pair<bool, bool> R(Other->isDefined(), !Other->isWeak());
// Normalize
if (L > R)
return -Other->compare<ELFT>(this);
Visibility = Other->Visibility =
getMinVisibility(Visibility, Other->Visibility);
if (IsUsedInRegularObj || Other->IsUsedInRegularObj)
IsUsedInRegularObj = Other->IsUsedInRegularObj = true;
if (L != R)
return -1;
if (!L.first || !L.second)
return 1;
if (isShared())
return -1;
if (Other->isShared())
return 1;
if (isCommon()) {
if (!Other->isCommon())
return -1;
auto *ThisC = cast<DefinedCommon>(this);
auto *OtherC = cast<DefinedCommon>(Other);
uintX_t Align = std::max(ThisC->MaxAlignment, OtherC->MaxAlignment);
if (ThisC->Size >= OtherC->Size) {
ThisC->MaxAlignment = Align;
return 1;
}
OtherC->MaxAlignment = Align;
return -1;
}
if (Other->isCommon())
return 1;
return 0;
}
Defined::Defined(Kind K, StringRef Name, bool IsWeak, uint8_t Visibility,
bool IsTls)
: SymbolBody(K, Name, IsWeak, Visibility, IsTls) {}
Undefined::Undefined(SymbolBody::Kind K, StringRef N, bool IsWeak,
uint8_t Visibility, bool IsTls)
: SymbolBody(K, N, IsWeak, Visibility, IsTls), CanKeepUndefined(false) {}
Undefined::Undefined(StringRef N, bool IsWeak, uint8_t Visibility,
bool CanKeepUndefined)
: Undefined(SymbolBody::UndefinedKind, N, IsWeak, Visibility,
/*IsTls*/ false) {
this->CanKeepUndefined = CanKeepUndefined;
}
template <typename ELFT>
UndefinedElf<ELFT>::UndefinedElf(StringRef N, const Elf_Sym &Sym)
: Undefined(SymbolBody::UndefinedElfKind, N,
Sym.getBinding() == llvm::ELF::STB_WEAK, Sym.getVisibility(),
Sym.getType() == llvm::ELF::STT_TLS),
Sym(Sym) {}
template <typename ELFT>
DefinedSynthetic<ELFT>::DefinedSynthetic(StringRef N, uintX_t Value,
OutputSectionBase<ELFT> &Section)
: Defined(SymbolBody::DefinedSyntheticKind, N, false, STV_DEFAULT, false),
Value(Value), Section(Section) {}
DefinedCommon::DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment,
bool IsWeak, uint8_t Visibility)
: Defined(SymbolBody::DefinedCommonKind, N, IsWeak, Visibility, false) {
MaxAlignment = Alignment;
this->Size = Size;
}
std::unique_ptr<InputFile> Lazy::getMember() {
MemoryBufferRef MBRef = File->getMember(&Sym);
// getMember returns an empty buffer if the member was already
// read from the library.
if (MBRef.getBuffer().empty())
return std::unique_ptr<InputFile>(nullptr);
return createELFFile<ObjectFile>(MBRef);
}
template <class ELFT> static void doInitSymbols() {
ElfSym<ELFT>::End.setBinding(STB_GLOBAL);
ElfSym<ELFT>::IgnoreUndef.setBinding(STB_WEAK);
ElfSym<ELFT>::IgnoreUndef.setVisibility(STV_HIDDEN);
}
void lld::elf2::initSymbols() {
doInitSymbols<ELF32LE>();
doInitSymbols<ELF32BE>();
doInitSymbols<ELF64LE>();
doInitSymbols<ELF64BE>();
}
template int SymbolBody::compare<ELF32LE>(SymbolBody *Other);
template int SymbolBody::compare<ELF32BE>(SymbolBody *Other);
template int SymbolBody::compare<ELF64LE>(SymbolBody *Other);
template int SymbolBody::compare<ELF64BE>(SymbolBody *Other);
template class lld::elf2::UndefinedElf<ELF32LE>;
template class lld::elf2::UndefinedElf<ELF32BE>;
template class lld::elf2::UndefinedElf<ELF64LE>;
template class lld::elf2::UndefinedElf<ELF64BE>;
template class lld::elf2::DefinedSynthetic<ELF32LE>;
template class lld::elf2::DefinedSynthetic<ELF32BE>;
template class lld::elf2::DefinedSynthetic<ELF64LE>;
template class lld::elf2::DefinedSynthetic<ELF64BE>;

327
ELF/Symbols.h Normal file
View file

@ -0,0 +1,327 @@
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// All symbols are handled as SymbolBodies regardless of their types.
// This file defines various types of SymbolBodies.
//
// File-scope symbols in ELF objects are the only exception of SymbolBody
// instantiation. We will never create SymbolBodies for them for performance
// reason. They are often represented as nullptrs. This is fine for symbol
// resolution because the symbol table naturally cares only about
// externally-visible symbols. For relocations, you have to deal with both
// local and non-local functions, and we have two different functions
// where we need them.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_SYMBOLS_H
#define LLD_ELF_SYMBOLS_H
#include "InputSection.h"
#include "lld/Core/LLVM.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
namespace lld {
namespace elf2 {
class ArchiveFile;
class InputFile;
class SymbolBody;
template <class ELFT> class ObjectFile;
template <class ELFT> class OutputSection;
template <class ELFT> class OutputSectionBase;
template <class ELFT> class SharedFile;
// Initializes global objects defined in this file.
// Called at the beginning of main().
void initSymbols();
// 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 {
SymbolBody *Body;
};
// The base class for real symbol classes.
class SymbolBody {
public:
enum Kind {
DefinedFirst,
DefinedRegularKind = DefinedFirst,
SharedKind,
DefinedElfLast = SharedKind,
DefinedCommonKind,
DefinedSyntheticKind,
DefinedLast = DefinedSyntheticKind,
UndefinedElfKind,
UndefinedKind,
LazyKind
};
Kind kind() const { return static_cast<Kind>(SymbolKind); }
bool isWeak() const { return IsWeak; }
bool isUndefined() const {
return SymbolKind == UndefinedKind || SymbolKind == UndefinedElfKind;
}
bool isDefined() const { return SymbolKind <= DefinedLast; }
bool isCommon() const { return SymbolKind == DefinedCommonKind; }
bool isLazy() const { return SymbolKind == LazyKind; }
bool isShared() const { return SymbolKind == SharedKind; }
bool isUsedInRegularObj() const { return IsUsedInRegularObj; }
bool isUsedInDynamicReloc() const { return IsUsedInDynamicReloc; }
void setUsedInDynamicReloc() { IsUsedInDynamicReloc = true; }
bool isTls() const { return IsTls; }
// Returns the symbol name.
StringRef getName() const { return Name; }
uint8_t getVisibility() const { return Visibility; }
unsigned DynamicSymbolTableIndex = 0;
uint32_t GlobalDynIndex = -1;
uint32_t GotIndex = -1;
uint32_t GotPltIndex = -1;
uint32_t PltIndex = -1;
bool hasGlobalDynIndex() { return GlobalDynIndex != uint32_t(-1); }
bool isInGot() const { return GotIndex != -1U; }
bool isInGotPlt() const { return GotPltIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
// 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 *repl() { 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.
template <class ELFT> int compare(SymbolBody *Other);
protected:
SymbolBody(Kind K, StringRef Name, bool IsWeak, uint8_t Visibility,
bool IsTls)
: SymbolKind(K), IsWeak(IsWeak), Visibility(Visibility), IsTls(IsTls),
Name(Name) {
IsUsedInRegularObj = K != SharedKind && K != LazyKind;
IsUsedInDynamicReloc = 0;
}
const unsigned SymbolKind : 8;
unsigned IsWeak : 1;
unsigned Visibility : 2;
// True if the symbol was used for linking and thus need to be
// added to the output file's symbol table. It is usually true,
// but if it is a shared symbol that were not referenced by anyone,
// it can be false.
unsigned IsUsedInRegularObj : 1;
// If true, the symbol is added to .dynsym symbol table.
unsigned IsUsedInDynamicReloc : 1;
unsigned IsTls : 1;
StringRef Name;
Symbol *Backref = nullptr;
};
// The base class for any defined symbols.
class Defined : public SymbolBody {
public:
Defined(Kind K, StringRef Name, bool IsWeak, uint8_t Visibility, bool IsTls);
static bool classof(const SymbolBody *S) { return S->isDefined(); }
};
// Any defined symbol from an ELF file.
template <class ELFT> class DefinedElf : public Defined {
protected:
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
public:
DefinedElf(Kind K, StringRef N, const Elf_Sym &Sym)
: Defined(K, N, Sym.getBinding() == llvm::ELF::STB_WEAK,
Sym.getVisibility(), Sym.getType() == llvm::ELF::STT_TLS),
Sym(Sym) {}
const Elf_Sym &Sym;
static bool classof(const SymbolBody *S) {
return S->kind() <= DefinedElfLast;
}
};
class DefinedCommon : public Defined {
public:
DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, bool IsWeak,
uint8_t Visibility);
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedCommonKind;
}
// The output offset of this common symbol in the output bss. Computed by the
// writer.
uint64_t OffsetInBSS;
// The maximum alignment we have seen for this symbol.
uint64_t MaxAlignment;
uint64_t Size;
};
// Regular defined symbols read from object file symbol tables.
template <class ELFT> class DefinedRegular : public DefinedElf<ELFT> {
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
public:
DefinedRegular(StringRef N, const Elf_Sym &Sym,
InputSectionBase<ELFT> *Section)
: DefinedElf<ELFT>(SymbolBody::DefinedRegularKind, N, Sym),
Section(Section) {}
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedRegularKind;
}
// If this is null, the symbol is absolute.
InputSectionBase<ELFT> *Section;
};
// DefinedSynthetic is a class to represent linker-generated ELF symbols.
// The difference from the regular symbol is that DefinedSynthetic symbols
// don't belong to any input files or sections. Thus, its constructor
// takes an output section to calculate output VA, etc.
template <class ELFT> class DefinedSynthetic : public Defined {
public:
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
DefinedSynthetic(StringRef N, uintX_t Value,
OutputSectionBase<ELFT> &Section);
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedSyntheticKind;
}
uintX_t Value;
const OutputSectionBase<ELFT> &Section;
};
// Undefined symbol.
class Undefined : public SymbolBody {
typedef SymbolBody::Kind Kind;
bool CanKeepUndefined;
protected:
Undefined(Kind K, StringRef N, bool IsWeak, uint8_t Visibility, bool IsTls);
public:
Undefined(StringRef N, bool IsWeak, uint8_t Visibility,
bool CanKeepUndefined);
static bool classof(const SymbolBody *S) { return S->isUndefined(); }
bool canKeepUndefined() const { return CanKeepUndefined; }
};
template <class ELFT> class UndefinedElf : public Undefined {
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
public:
UndefinedElf(StringRef N, const Elf_Sym &Sym);
const Elf_Sym &Sym;
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::UndefinedElfKind;
}
};
template <class ELFT> class SharedSymbol : public DefinedElf<ELFT> {
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
typedef typename llvm::object::ELFFile<ELFT>::uintX_t uintX_t;
public:
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::SharedKind;
}
SharedSymbol(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym)
: DefinedElf<ELFT>(SymbolBody::SharedKind, Name, Sym), File(F) {}
SharedFile<ELFT> *File;
// True if the linker has to generate a copy relocation for this shared
// symbol. OffsetInBSS is significant only when NeedsCopy is true.
bool NeedsCopy = false;
uintX_t OffsetInBSS = 0;
};
// This class represents a symbol defined in an archive file. It is
// created from an archive file header, and it knows how to load an
// object file from an archive to replace itself with a defined
// symbol. If the resolver finds both Undefined and Lazy for
// the same name, it will ask the Lazy to load a file.
class Lazy : public SymbolBody {
public:
Lazy(ArchiveFile *F, const llvm::object::Archive::Symbol S)
: SymbolBody(LazyKind, S.getName(), false, llvm::ELF::STV_DEFAULT, false),
File(F), Sym(S) {}
static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; }
// Returns an object file for this symbol, or a nullptr if the file
// was already returned.
std::unique_ptr<InputFile> getMember();
void setWeak() { IsWeak = true; }
void setUsedInRegularObj() { IsUsedInRegularObj = true; }
private:
ArchiveFile *File;
const llvm::object::Archive::Symbol Sym;
};
// Some linker-generated symbols need to be created as
// DefinedRegular symbols, so they need Elf_Sym symbols.
// Here we allocate such Elf_Sym symbols statically.
template <class ELFT> struct ElfSym {
typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym Elf_Sym;
// Used to represent an undefined symbol which we don't want
// to add to the output file's symbol table.
static Elf_Sym IgnoreUndef;
// The content for _end and end symbols.
static Elf_Sym End;
// The content for _gp symbol for MIPS target.
static Elf_Sym MipsGp;
// __rel_iplt_start/__rel_iplt_end for signaling
// where R_[*]_IRELATIVE relocations do live.
static Elf_Sym RelaIpltStart;
static Elf_Sym RelaIpltEnd;
};
template <class ELFT> typename ElfSym<ELFT>::Elf_Sym ElfSym<ELFT>::IgnoreUndef;
template <class ELFT> typename ElfSym<ELFT>::Elf_Sym ElfSym<ELFT>::End;
template <class ELFT> typename ElfSym<ELFT>::Elf_Sym ElfSym<ELFT>::MipsGp;
template <class ELFT>
typename ElfSym<ELFT>::Elf_Sym ElfSym<ELFT>::RelaIpltStart;
template <class ELFT> typename ElfSym<ELFT>::Elf_Sym ElfSym<ELFT>::RelaIpltEnd;
} // namespace elf2
} // namespace lld
#endif

1481
ELF/Target.cpp Normal file

File diff suppressed because it is too large Load diff

117
ELF/Target.h Normal file
View file

@ -0,0 +1,117 @@
//===- Target.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_TARGET_H
#define LLD_ELF_TARGET_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/ELF.h"
#include <memory>
namespace lld {
namespace elf2 {
class SymbolBody;
class TargetInfo {
public:
unsigned getPageSize() const { return PageSize; }
uint64_t getVAStart() const;
unsigned getCopyReloc() const { return CopyReloc; }
unsigned getGotReloc() const { return GotReloc; }
unsigned getPltReloc() const { return PltReloc; }
unsigned getRelativeReloc() const { return RelativeReloc; }
unsigned getIRelativeReloc() const { return IRelativeReloc; }
bool isTlsLocalDynamicReloc(unsigned Type) const {
return Type == TlsLocalDynamicReloc;
}
bool isTlsGlobalDynamicReloc(unsigned Type) const {
return Type == TlsGlobalDynamicReloc;
}
unsigned getTlsModuleIndexReloc() const { return TlsModuleIndexReloc; }
unsigned getTlsOffsetReloc() const { return TlsOffsetReloc; }
unsigned getPltZeroEntrySize() const { return PltZeroEntrySize; }
unsigned getPltEntrySize() const { return PltEntrySize; }
bool supportsLazyRelocations() const { return LazyRelocations; }
unsigned getGotHeaderEntriesNum() const { return GotHeaderEntriesNum; }
unsigned getGotPltHeaderEntriesNum() const { return GotPltHeaderEntriesNum; }
virtual unsigned getDynReloc(unsigned Type) const { return Type; }
virtual bool isTlsDynReloc(unsigned Type, const SymbolBody &S) const {
return false;
}
virtual unsigned getPltRefReloc(unsigned Type) const;
virtual unsigned getTlsGotReloc(unsigned Type = -1) const {
return TlsGotReloc;
}
virtual void writeGotHeaderEntries(uint8_t *Buf) const;
virtual void writeGotPltHeaderEntries(uint8_t *Buf) const;
virtual void writeGotPltEntry(uint8_t *Buf, uint64_t Plt) const = 0;
virtual void writePltZeroEntry(uint8_t *Buf, uint64_t GotEntryAddr,
uint64_t PltEntryAddr) const = 0;
virtual void writePltEntry(uint8_t *Buf, uint64_t GotAddr,
uint64_t GotEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const = 0;
virtual bool isRelRelative(uint32_t Type) const;
virtual bool isSizeDynReloc(uint32_t Type, const SymbolBody &S) const;
virtual bool relocNeedsDynRelative(unsigned Type) const { return false; }
virtual bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const = 0;
virtual bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const = 0;
virtual void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA, uint64_t ZA = 0,
uint8_t *PairedLoc = nullptr) const = 0;
virtual bool isGotRelative(uint32_t Type) const;
virtual bool isTlsOptimized(unsigned Type, const SymbolBody *S) const;
virtual bool needsCopyRel(uint32_t Type, const SymbolBody &S) const;
virtual unsigned relocateTlsOptimize(uint8_t *Loc, uint8_t *BufEnd,
uint32_t Type, uint64_t P, uint64_t SA,
const SymbolBody &S) const;
virtual ~TargetInfo();
protected:
unsigned PageSize = 4096;
// On freebsd x86_64 the first page cannot be mmaped.
// On linux that is controled by vm.mmap_min_addr. At least on some x86_64
// installs that is 65536, so the first 15 pages cannot be used.
// Given that, the smallest value that can be used in here is 0x10000.
// If using 2MB pages, the smallest page aligned address that works is
// 0x200000, but it looks like every OS uses 4k pages for executables.
uint64_t VAStart = 0x10000;
unsigned CopyReloc;
unsigned PCRelReloc;
unsigned GotReloc;
unsigned PltReloc;
unsigned RelativeReloc;
unsigned IRelativeReloc;
unsigned TlsGotReloc = 0;
unsigned TlsLocalDynamicReloc = 0;
unsigned TlsGlobalDynamicReloc = 0;
unsigned TlsModuleIndexReloc;
unsigned TlsOffsetReloc;
unsigned PltEntrySize = 8;
unsigned PltZeroEntrySize = 0;
unsigned GotHeaderEntriesNum = 0;
unsigned GotPltHeaderEntriesNum = 3;
bool LazyRelocations = false;
};
uint64_t getPPC64TocBase();
template <class ELFT>
typename llvm::object::ELFFile<ELFT>::uintX_t getMipsGpAddr();
template <class ELFT> bool isGnuIFunc(const SymbolBody &S);
extern std::unique_ptr<TargetInfo> Target;
TargetInfo *createTarget();
}
}
#endif

1282
ELF/Writer.cpp Normal file

File diff suppressed because it is too large Load diff

24
ELF/Writer.h Normal file
View file

@ -0,0 +1,24 @@
//===- Writer.h -------------------------------------------------*- C++ -*-===//
//
// 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
namespace lld {
namespace elf2 {
template <class ELFT> class SymbolTable;
template <class ELFT> void writeResult(SymbolTable<ELFT> *Symtab);
template <class ELFT> void markLive(SymbolTable<ELFT> *Symtab);
}
}
#endif

View file

@ -1,86 +0,0 @@
##===- Makefile --------------------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
# If LLD_LEVEL is not set, then we are the top-level Makefile. Otherwise, we
# are being included from a subdirectory makefile.
ifndef LLD_LEVEL
IS_TOP_LEVEL := 1
LLD_LEVEL := .
DIRS := include lib tools unittests
PARALLEL_DIRS :=
endif
ifeq ($(MAKECMDGOALS),libs-only)
DIRS := $(filter-out tools docs, $(DIRS))
OPTIONAL_DIRS :=
endif
ifeq ($(BUILD_LLD_ONLY),YES)
DIRS := $(filter-out docs unittests, $(DIRS))
OPTIONAL_DIRS :=
endif
###
# Common Makefile code, shared by all lld Makefiles.
# Set LLVM source root level.
LEVEL := $(LLD_LEVEL)/../..
# Include LLVM common makefile.
include $(LEVEL)/Makefile.common
ifneq ($(ENABLE_DOCS),1)
DIRS := $(filter-out docs, $(DIRS))
endif
CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/include
CPP.Flags += -I$(PROJ_OBJ_DIR)/$(LLD_LEVEL)/include
###
# lld Top Level specific stuff.
ifeq ($(IS_TOP_LEVEL),1)
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
$(RecursiveTargets)::
$(Verb) for dir in test unittests; do \
if [ -f $(PROJ_SRC_DIR)/$${dir}/Makefile ] && [ ! -f $${dir}/Makefile ]; then \
$(MKDIR) $${dir}; \
$(CP) $(PROJ_SRC_DIR)/$${dir}/Makefile $${dir}/Makefile; \
fi \
done
endif
test::
@ $(MAKE) -C test
report::
@ $(MAKE) -C test report
clean::
@ $(MAKE) -C test clean
libs-only: all
tags::
$(Verb) etags `find . -type f -name '*.h' -or -name '*.cpp' | \
grep -v /lib/Headers | grep -v /test/`
cscope.files:
find tools lib include -name '*.cpp' \
-or -name '*.def' \
-or -name '*.td' \
-or -name '*.h' > cscope.files
.PHONY: test report clean cscope.files
endif

View file

@ -1,155 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
all: html
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/lld.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/lld.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/lld"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/lld"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

View file

@ -52,10 +52,9 @@ Where to start
--------------
The lld project already has a skeleton of source code for Readers for
``ELF``, ``PECOFF``, ``MachO``, and lld's native Atom graph format
(both binary ``Native`` and ``YAML`` representations). If your file format
is a variant of one of those, you should modify the existing Reader to
support your variant. This is done by customizing the Options
``ELF``, ``PECOFF``, ``MachO``, and lld's native ``YAML`` graph format.
If your file format is a variant of one of those, you should modify the
existing Reader to support your variant. This is done by customizing the Options
class for the Reader and making appropriate changes to the ``.cpp`` file to
interpret those options and act accordingly.

View file

@ -300,60 +300,13 @@ and the ivars programmatically set.
lld::File representations
-------------------------
Just as LLVM has three representations of its IR model, lld has three
Just as LLVM has three representations of its IR model, lld has two
representations of its File/Atom/Reference model:
* In memory, abstract C++ classes (lld::Atom, lld::Reference, and lld::File).
* textual (in YAML)
* binary format ("native")
Binary File Format
~~~~~~~~~~~~~~~~~~
In theory, lld::File objects could be written to disk in an existing Object File
format standard (e.g. ELF). Instead we choose to define a new binary file
format. There are two main reasons for this: fidelity and performance. In order
for lld to work as a linker on all platforms, its internal model must be rich
enough to model all CPU and OS linking features. But if we choose an existing
Object File format as the lld binary format, that means an on going need to
retrofit each platform specific feature needed from alternate platforms into the
existing Object File format. Having our own "native" binary format side steps
that issue. We still need to be able to binary encode all the features, but
once the in-memory model can represent the feature, it is straight forward to
binary encode it.
The reason to use a binary file format at all, instead of a textual file format,
is speed. You want the binary format to be as fast as possible to read into the
in-memory model. Given that we control the in-memory model and the binary
format, the obvious way to make reading super fast it to make the file format be
basically just an array of atoms. The reader just mmaps in the file and looks
at the header to see how many atoms there are and instantiate that many atom
objects with the atom attribute information coming from that array. The trick
is designing this in a way that can be extended as the Atom mode evolves and new
attributes are added.
The native object file format starts with a header that lists how many "chunks"
are in the file. A chunk is an array of "ivar data". The native file reader
instantiates an array of Atom objects (with one large malloc call). Each atom
contains just a pointer to its vtable and a pointer to its ivar data. All
methods on lld::Atom are virtual, so all the method implementations return
values based on the ivar data to which it has a pointer. If a new linking
features is added which requires a change to the lld::Atom model, a new native
reader class (e.g. version 2) is defined which knows how to read the new feature
information from the new ivar data. The old reader class (e.g. version 1) is
updated to do its best to model (the lack of the new feature) given the old ivar
data in existing native object files.
With this model for the native file format, files can be read and turned
into the in-memory graph of lld::Atoms with just a few memory allocations.
And the format can easily adapt over time to new features.
The binary file format follows the ReaderWriter patterns used in lld. The lld
library comes with the classes: ReaderNative and WriterNative. So, switching
between file formats is as easy as switching which Reader subclass is used.
Textual representations in YAML
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -406,11 +359,7 @@ test is a text file with comments telling lit how to run the test and check the
result To facilitate testing, the lld project builds a tool called lld-core.
This tool reads a YAML file (default from stdin), parses it into one or more
lld::File objects in memory and then feeds those lld::File objects to the
resolver phase. The output of the resolver is written as a native object file.
It is then read back in using the native object file reader and then pass to the
YAML writer. This round-about path means that all three representations
(in-memory, binary, and text) are exercised, and any new feature has to work in
all the representations to pass the test.
resolver phase.
Resolver testing

View file

@ -56,7 +56,7 @@ On Unix-like Systems
5. Test::
$ make lld-test
$ make check-lld
Using Visual Studio
~~~~~~~~~~~~~~~~~~~
@ -64,7 +64,7 @@ Using Visual Studio
#. Get the required tools.
* `CMake 2.8`_\+.
* `Visual Studio 11 (2012) or later`_ (required for C++11 support)
* `Visual Studio 12 (2013) or later`_ (required for C++11 support)
* `Python 2.4`_\+ (not 3.x) for running tests.
.. _CMake 2.8: http://www.cmake.org/cmake/resources/software.html

View file

@ -3,15 +3,18 @@
lld - The LLVM Linker
=====================
lld is a new set of modular code for creating linker tools.
lld contains two linkers whose architectures are different from each other.
One is a linker that implements native features directly.
They are in `COFF` or `ELF` directories. Other directories contains the other
implementation that is designed to be a set of modular code for creating
linker tools. This document covers mainly the latter.
For the former, please read README.md in `COFF` directory.
* End-User Features:
* Compatible with existing linker options
* Reads standard Object Files (e.g. ELF, Mach-O, PE/COFF)
* Writes standard Executable Files (e.g. ELF, Mach-O, PE)
* Fast link times
* Minimal memory use
* Remove clang's reliance on "the system linker"
* Uses the LLVM `"UIUC" BSD-Style license`__.
@ -26,8 +29,6 @@ lld is a new set of modular code for creating linker tools.
* Extensive unit tests
* Internal linker model can be dumped/read to textual format
* Internal linker model can be dumped/read to a new native format
* Native format designed to be fast to read and write
* Additional linking features can be plugged in as "passes"
* OS specific and CPU specific code factored out

View file

@ -4,7 +4,6 @@ Open Projects
=============
.. include:: ../include/lld/Core/TODO.txt
.. include:: ../lib/Core/TODO.txt
.. include:: ../lib/Driver/TODO.rst
.. include:: ../lib/ReaderWriter/ELF/X86_64/TODO.rst
.. include:: ../lib/ReaderWriter/ELF/AArch64/TODO.rst

View file

@ -14,14 +14,14 @@
Windows support
===============
LLD has some experimental Windows support. When invoked as ``link.exe`` or with
LLD supports Windows operating system. When invoked as ``lld-link.exe`` or with
``-flavor link``, the driver for Windows operating system is used to parse
command line options, and it drives further linking processes. LLD accepts
almost all command line options that the linker shipped with Microsoft Visual
C++ (link.exe) supports.
The current status is that LLD can link itself on Windows x86 using Visual C++
2012 or 2013 as the compiler.
The current status is that LLD can link itself on Windows x86/x64
using Visual C++ 2013 as the compiler.
Development status
==================
@ -55,17 +55,12 @@ Windows resource files support
COFF object file section. Both tools are shipped with MSVC.
Safe Structured Exception Handler (SEH)
:good:`Done` for x86. :partial:`Work in progress` for x64.
:good:`Done` for both x86 and x64.
Module-definition file
:partial:`Partially done`. LLD currently recognizes these directives:
``EXPORTS``, ``HEAPSIZE``, ``STACKSIZE``, ``NAME``, and ``VERSION``.
x64 (x86-64)
:partial:`Work in progress`. LLD can create PE32+ executable but the generated
file does not work unless source object files are very simple because of the
lack of SEH handler table.
Debug info
:none:`No progress has been made`. Microsoft linker can interpret the CodeGen
debug info (old-style debug info) and PDB to emit an .pdb file. LLD doesn't
@ -86,7 +81,7 @@ Using Visual Studio IDE/MSBuild
Alternatively, you can use msbuild if you don't like to work in an IDE::
msbuild LLVM.sln /m /target:"lld executables\lld"
MSBuild.exe had been shipped as a component of the .NET framework, but since
2013 it's part of Visual Studio. You can find it at "C:\\Program Files
(x86)\\msbuild".
@ -100,19 +95,3 @@ Using Ninja
1. Check out LLVM and LLD from the LLVM SVN repository (or Git mirror),
#. run ``cmake -G ninja <llvm-source-dir>`` from VS command prompt,
#. run ``ninja lld``
Known issues
============
Note that LLD is still in early stage in development, so there are still many
bugs. Here is a list of notable bugs.
* Symbol name resolution from library files sometimes fails. On Windows, the
order of library files in command line does not matter, but LLD sometimes
fails to simulate the semantics. A workaround for it is to explicitly add
library files to command line with ``/DEFAULTLIB``.
* Subsystem inference is not very reliable. Linker is supposed to set
``subsystem`` field in the PE/COFF header according to entry function name,
but LLD sometimes ended up with ``unknown`` subsystem type. You need to give
``/SUBSYSTEM`` option if it fails to infer it.

View file

@ -1,4 +0,0 @@
LLD_LEVEL := ..
DIRS := lld
include $(LLD_LEVEL)/Makefile

View file

@ -1,32 +0,0 @@
LLD_LEVEL := ../../..
BUILT_SOURCES = Version.inc
TABLEGEN_INC_FILES_COMMON = 1
include $(LLD_LEVEL)/Makefile
# Compute the lld version from the LLVM version, unless specified explicitly.
ifndef LLD_VERSION
LLD_VERSION := $(subst svn,,$(LLVMVersion))
LLD_VERSION := $(subst rc,,$(LLD_VERSION))
endif
LLD_VERSION_COMPONENTS := $(subst ., ,$(LLD_VERSION))
LLD_VERSION_MAJOR := $(word 1,$(LLD_VERSION_COMPONENTS))
LLD_VERSION_MINOR := $(word 2,$(LLD_VERSION_COMPONENTS))
LLD_REVISION := $(strip \
$(shell $(LLVM_SRC_ROOT)/utils/GetSourceVersion $(LLVM_SRC_ROOT)/tools/lld))
LLD_REPOSITORY := $(strip \
$(shell $(LLVM_SRC_ROOT)/utils/GetRepositoryPath $(LLVM_SRC_ROOT)/tools/lld))
$(ObjDir)/Version.inc.tmp : Version.inc.in Makefile $(LLVM_OBJ_ROOT)/Makefile.config $(ObjDir)/.dir
$(Echo) "Updating LLD version info."
$(Verb)sed -e "s#@LLD_VERSION@#$(LLD_VERSION)#g" \
-e "s#@LLD_VERSION_MAJOR@#$(LLD_VERSION_MAJOR)#g" \
-e "s#@LLD_VERSION_MINOR@#$(LLD_VERSION_MINOR)#g" \
-e "s#@LLD_REVISION@#$(LLD_REVISION)#g" \
-e "s#@LLD_REPOSITORY@#$(LLD_REPOSITORY)#g" \
$< > $@

View file

@ -33,9 +33,7 @@ namespace lld {
class AliasAtom : public SimpleDefinedAtom {
public:
AliasAtom(const File &file, StringRef name)
: SimpleDefinedAtom(file), _target(nullptr), _name(name),
_merge(DefinedAtom::mergeNo), _deadStrip(DefinedAtom::deadStripNormal) {
}
: SimpleDefinedAtom(file), _name(name) {}
StringRef name() const override { return _name; }
uint64_t size() const override { return 0; }
@ -91,10 +89,10 @@ private:
}
}
mutable const DefinedAtom *_target;
std::string _name;
llvm::Optional<Merge> _merge;
DeadStripKind _deadStrip;
mutable const DefinedAtom *_target = nullptr;
llvm::Optional<Merge> _merge = DefinedAtom::mergeNo;
DeadStripKind _deadStrip = DefinedAtom::deadStripNormal;
};
} // end namespace lld

View file

@ -151,6 +151,7 @@ public:
typeNoAlloc, // Identifies non allocatable sections [ELF]
typeGroupComdat, // Identifies a section group [ELF, COFF]
typeGnuLinkOnce, // Identifies a gnu.linkonce section [ELF]
typeSectCreate, // Created via the -sectcreate option [Darwin]
};
// Permission bits for atoms and segments. The order of these values are
@ -190,23 +191,26 @@ public:
// Attributes describe a code model used by the atom.
enum CodeModel {
codeNA, // no specific code model
// MIPS code models
codeMipsPIC, // PIC function in a PIC / non-PIC mixed file
codeMipsMicro, // microMIPS instruction encoding
codeMipsMicroPIC, // microMIPS instruction encoding + PIC
codeMips16, // MIPS-16 instruction encoding
// ARM code models
codeARMThumb, // ARM Thumb instruction set
codeARM_a, // $a-like mapping symbol (for ARM code)
codeARM_d, // $d-like mapping symbol (for data)
codeARM_t, // $t-like mapping symbol (for Thumb code)
};
struct Alignment {
Alignment(int p2, int m = 0)
: powerOf2(p2)
, modulus(m) {}
Alignment(int v, int m = 0) : value(v), modulus(m) {}
uint16_t powerOf2;
uint16_t value;
uint16_t modulus;
bool operator==(const Alignment &rhs) const {
return (powerOf2 == rhs.powerOf2) && (modulus == rhs.modulus);
return (value == rhs.value) && (modulus == rhs.modulus);
}
};

View file

@ -19,26 +19,9 @@
namespace lld {
const std::error_category &native_reader_category();
enum class NativeReaderError {
success = 0,
unknown_file_format,
file_too_short,
file_malformed,
unknown_chunk_type,
memory_error,
conflicting_target_machine,
};
inline std::error_code make_error_code(NativeReaderError e) {
return std::error_code(static_cast<int>(e), native_reader_category());
}
const std::error_category &YamlReaderCategory();
enum class YamlReaderError {
success = 0,
unknown_keyword,
illegal_value
};
@ -53,7 +36,11 @@ enum class LinkerScriptReaderError {
success = 0,
parse_error,
unknown_symbol_in_expr,
unrecognized_function_in_expr
unrecognized_function_in_expr,
unknown_phdr_ids,
extra_program_phdr,
misplaced_program_phdr,
program_phdr_wrong_phdrs,
};
inline std::error_code make_error_code(LinkerScriptReaderError e) {
@ -66,14 +53,13 @@ inline std::error_code make_error_code(LinkerScriptReaderError e) {
/// supplied error string.
/// Note: Once ErrorOr<> is updated to work with errors other than error_code,
/// this can be updated to return some other kind of error.
std::error_code make_dynamic_error_code(const char *msg);
std::error_code make_dynamic_error_code(StringRef msg);
std::error_code make_dynamic_error_code(const Twine &msg);
} // end namespace lld
namespace std {
template <>
struct is_error_code_enum<lld::NativeReaderError> : std::true_type {};
template <> struct is_error_code_enum<lld::YamlReaderError> : std::true_type {};
template <>
struct is_error_code_enum<lld::LinkerScriptReaderError> : std::true_type {};

View file

@ -86,90 +86,48 @@ public:
/// Sets the command line order of the file.
void setOrdinal(uint64_t ordinal) const { _ordinal = ordinal; }
template <typename T> class atom_iterator; // forward reference
/// Returns the ordinal for the next atom to be defined in this file.
uint64_t getNextAtomOrdinalAndIncrement() const {
return _nextAtomOrdinal++;
}
/// For allocating any objects owned by this File.
llvm::BumpPtrAllocator &allocator() const {
return _allocator;
}
/// \brief For use interating over DefinedAtoms in this File.
typedef atom_iterator<DefinedAtom> defined_iterator;
/// The type of atom mutable container.
template <typename T> using AtomVector = std::vector<const T *>;
/// \brief For use interating over UndefinedAtoms in this File.
typedef atom_iterator<UndefinedAtom> undefined_iterator;
/// \brief For use interating over SharedLibraryAtoms in this File.
typedef atom_iterator<SharedLibraryAtom> shared_library_iterator;
/// \brief For use interating over AbsoluteAtoms in this File.
typedef atom_iterator<AbsoluteAtom> absolute_iterator;
/// \brief Different object file readers may instantiate and manage atoms with
/// different data structures. This class is a collection abstraction.
/// Each concrete File instance must implement these atom_collection
/// methods to enable clients to interate the File's atoms.
template <typename T>
class atom_collection {
/// The range type for the atoms. It's backed by a std::vector, but hides
/// its member functions so that you can only call begin or end.
template <typename T> class AtomRange {
public:
virtual ~atom_collection() { }
virtual atom_iterator<T> begin() const = 0;
virtual atom_iterator<T> end() const = 0;
virtual const T *deref(const void *it) const = 0;
virtual void next(const void *&it) const = 0;
virtual uint64_t size() const = 0;
bool empty() const { return size() == 0; }
};
AtomRange(AtomVector<T> v) : _v(v) {}
typename AtomVector<T>::const_iterator begin() const { return _v.begin(); }
typename AtomVector<T>::const_iterator end() const { return _v.end(); }
typename AtomVector<T>::iterator begin() { return _v.begin(); }
typename AtomVector<T>::iterator end() { return _v.end(); }
/// \brief The class is the iterator type used to iterate through a File's
/// Atoms. This iterator delegates the work to the associated atom_collection
/// object. There are four kinds of Atoms, so this iterator is templated on
/// the four base Atom kinds.
template <typename T>
class atom_iterator : public std::iterator<std::forward_iterator_tag, T> {
public:
atom_iterator(const atom_collection<T> &c, const void *it)
: _collection(&c), _it(it) { }
const T *operator*() const {
return _collection->deref(_it);
}
const T *operator->() const {
return _collection->deref(_it);
}
friend bool operator==(const atom_iterator<T> &lhs, const atom_iterator<T> &rhs) {
return lhs._it == rhs._it;
}
friend bool operator!=(const atom_iterator<T> &lhs, const atom_iterator<T> &rhs) {
return !(lhs == rhs);
}
atom_iterator<T> &operator++() {
_collection->next(_it);
return *this;
}
private:
const atom_collection<T> *_collection;
const void *_it;
AtomVector<T> &_v;
};
/// \brief Must be implemented to return the atom_collection object for
/// \brief Must be implemented to return the AtomVector object for
/// all DefinedAtoms in this File.
virtual const atom_collection<DefinedAtom> &defined() const = 0;
virtual const AtomVector<DefinedAtom> &defined() const = 0;
/// \brief Must be implemented to return the atom_collection object for
/// \brief Must be implemented to return the AtomVector object for
/// all UndefinedAtomw in this File.
virtual const atom_collection<UndefinedAtom> &undefined() const = 0;
virtual const AtomVector<UndefinedAtom> &undefined() const = 0;
/// \brief Must be implemented to return the atom_collection object for
/// \brief Must be implemented to return the AtomVector object for
/// all SharedLibraryAtoms in this File.
virtual const atom_collection<SharedLibraryAtom> &sharedLibrary() const = 0;
virtual const AtomVector<SharedLibraryAtom> &sharedLibrary() const = 0;
/// \brief Must be implemented to return the atom_collection object for
/// \brief Must be implemented to return the AtomVector object for
/// all AbsoluteAtoms in this File.
virtual const atom_collection<AbsoluteAtom> &absolute() const = 0;
virtual const AtomVector<AbsoluteAtom> &absolute() const = 0;
/// \brief If a file is parsed using a different method than doParse(),
/// one must use this method to set the last error status, so that
@ -199,67 +157,18 @@ public:
protected:
/// \brief only subclasses of File can be instantiated
File(StringRef p, Kind kind)
: _path(p), _kind(kind), _ordinal(UINT64_MAX) {}
: _path(p), _kind(kind), _ordinal(UINT64_MAX),
_nextAtomOrdinal(0) {}
/// \brief Subclasses should override this method to parse the
/// memory buffer passed to this file's constructor.
virtual std::error_code doParse() { return std::error_code(); }
/// \brief This is a convenience class for File subclasses which manage their
/// atoms as a simple std::vector<>.
template <typename T>
class atom_collection_vector : public atom_collection<T> {
public:
atom_iterator<T> begin() const override {
auto *it = _atoms.empty() ? nullptr
: reinterpret_cast<const void *>(_atoms.data());
return atom_iterator<T>(*this, it);
}
atom_iterator<T> end() const override {
auto *it = _atoms.empty() ? nullptr : reinterpret_cast<const void *>(
_atoms.data() + _atoms.size());
return atom_iterator<T>(*this, it);
}
const T *deref(const void *it) const override {
return *reinterpret_cast<const T *const *>(it);
}
void next(const void *&it) const override {
const T *const *p = reinterpret_cast<const T *const *>(it);
++p;
it = reinterpret_cast<const void*>(p);
}
uint64_t size() const override { return _atoms.size(); }
std::vector<const T *> _atoms;
};
/// \brief This is a convenience class for File subclasses which need to
/// return an empty collection.
template <typename T>
class atom_collection_empty : public atom_collection<T> {
public:
atom_iterator<T> begin() const override {
return atom_iterator<T>(*this, nullptr);
}
atom_iterator<T> end() const override {
return atom_iterator<T>(*this, nullptr);
}
const T *deref(const void *it) const override {
llvm_unreachable("empty collection should never be accessed");
}
void next(const void *&it) const override {}
uint64_t size() const override { return 0; }
};
static atom_collection_empty<DefinedAtom> _noDefinedAtoms;
static atom_collection_empty<UndefinedAtom> _noUndefinedAtoms;
static atom_collection_empty<SharedLibraryAtom> _noSharedLibraryAtoms;
static atom_collection_empty<AbsoluteAtom> _noAbsoluteAtoms;
mutable llvm::BumpPtrAllocator _allocator;
static AtomVector<DefinedAtom> _noDefinedAtoms;
static AtomVector<UndefinedAtom> _noUndefinedAtoms;
static AtomVector<SharedLibraryAtom> _noSharedLibraryAtoms;
static AtomVector<AbsoluteAtom> _noAbsoluteAtoms;
mutable llvm::BumpPtrAllocator _allocator;
private:
StringRef _path;
@ -267,29 +176,12 @@ private:
mutable std::string _archiveMemberPath;
Kind _kind;
mutable uint64_t _ordinal;
mutable uint64_t _nextAtomOrdinal;
std::shared_ptr<MemoryBuffer> _sharedMemoryBuffer;
llvm::Optional<std::error_code> _lastError;
std::mutex _parseMutex;
};
/// \brief A mutable File.
class MutableFile : public File {
public:
/// \brief Add an atom to the file. Invalidates iterators for all returned
/// containters.
virtual void addAtom(const Atom&) = 0;
typedef range<std::vector<const DefinedAtom *>::iterator> DefinedAtomRange;
virtual DefinedAtomRange definedAtoms() = 0;
virtual void
removeDefinedAtomsIf(std::function<bool(const DefinedAtom *)> pred) = 0;
protected:
/// \brief only subclasses of MutableFile can be instantiated
MutableFile(StringRef p) : File(p, kindObject) {}
};
/// An ErrorFile represents a file that doesn't exist.
/// If you try to parse a file which doesn't exist, an instance of this
/// class will be returned. That's parse method always returns an error.
@ -302,16 +194,16 @@ public:
std::error_code doParse() override { return _ec; }
const atom_collection<DefinedAtom> &defined() const override {
const AtomVector<DefinedAtom> &defined() const override {
llvm_unreachable("internal error");
}
const atom_collection<UndefinedAtom> &undefined() const override {
const AtomVector<UndefinedAtom> &undefined() const override {
llvm_unreachable("internal error");
}
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
const AtomVector<SharedLibraryAtom> &sharedLibrary() const override {
llvm_unreachable("internal error");
}
const atom_collection<AbsoluteAtom> &absolute() const override {
const AtomVector<AbsoluteAtom> &absolute() const override {
llvm_unreachable("internal error");
}

View file

@ -26,6 +26,7 @@ namespace llvm {
class StringRef;
class Twine;
class MemoryBuffer;
class MemoryBufferRef;
template<typename T> class ArrayRef;
template<unsigned InternalLen> class SmallString;
template<typename T, unsigned N> class SmallVector;
@ -53,6 +54,7 @@ namespace lld {
using llvm::StringRef;
using llvm::Twine;
using llvm::MemoryBuffer;
using llvm::MemoryBufferRef;
using llvm::ArrayRef;
using llvm::SmallString;
using llvm::SmallVector;

View file

@ -41,7 +41,6 @@ public:
enum class OutputFileType : uint8_t {
Default, // The default output type for this target
YAML, // The output type is set to YAML
Native // The output file format is Native (Atoms)
};
virtual ~LinkingContext();
@ -62,7 +61,7 @@ public:
/// should be marked live (along with all Atoms they reference). Usually
/// this method returns false for main executables, but true for dynamic
/// shared libraries.
bool globalsAreDeadStripRoots() const { return _globalsAreDeadStripRoots; };
bool globalsAreDeadStripRoots() const { return _globalsAreDeadStripRoots; }
/// Only used if deadStrip() returns true. This method returns the names
/// of DefinedAtoms that should be marked live (along with all Atoms they
@ -273,13 +272,11 @@ public:
/// Set the various output file types that the linker would
/// create
bool setOutputFileType(StringRef outputFileType) {
if (outputFileType.equals_lower("yaml"))
if (outputFileType.equals_lower("yaml")) {
_outputFileType = OutputFileType::YAML;
else if (outputFileType.equals_lower("native"))
_outputFileType = OutputFileType::YAML;
else
return false;
return true;
return true;
}
return false;
}
/// Returns the output file type that that the linker needs to create.
@ -292,7 +289,7 @@ public:
/// This method is called by core linking to give the Writer a chance
/// to add file format specific "files" to set of files to be linked. This is
/// how file format specific atoms can be added to the link.
virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &);
virtual void createImplicitFiles(std::vector<std::unique_ptr<File>> &);
/// This method is called by core linking to build the list of Passes to be
/// run on the merged/linked graph of all input files.

View file

@ -14,21 +14,15 @@
#include "lld/Core/LLVM.h"
#include "lld/Core/range.h"
#include "llvm/Support/MathExtras.h"
#ifdef _MSC_VER
// concrt.h depends on eh.h for __uncaught_exception declaration
// even if we disable exceptions.
#include <eh.h>
#endif
#include "llvm/Support/thread.h"
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <stack>
#ifdef _MSC_VER
#if defined(_MSC_VER) && LLVM_ENABLE_THREADS
#include <concrt.h>
#include <ppl.h>
#endif
@ -104,13 +98,55 @@ private:
std::condition_variable _cond;
};
// Classes in this namespace are implementation details of this header.
namespace internal {
/// \brief An abstract class that takes closures and runs them asynchronously.
class Executor {
public:
virtual ~Executor() {}
virtual ~Executor() = default;
virtual void add(std::function<void()> func) = 0;
};
#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0
class SyncExecutor : public Executor {
public:
virtual void add(std::function<void()> func) {
func();
}
};
inline Executor *getDefaultExecutor() {
static SyncExecutor exec;
return &exec;
}
#elif defined(_MSC_VER)
/// \brief An Executor that runs tasks via ConcRT.
class ConcRTExecutor : public Executor {
struct Taskish {
Taskish(std::function<void()> task) : _task(task) {}
std::function<void()> _task;
static void run(void *p) {
Taskish *self = static_cast<Taskish *>(p);
self->_task();
concurrency::Free(self);
}
};
public:
virtual void add(std::function<void()> func) {
Concurrency::CurrentScheduler::ScheduleTask(Taskish::run,
new (concurrency::Alloc(sizeof(Taskish))) Taskish(func));
}
};
inline Executor *getDefaultExecutor() {
static ConcRTExecutor exec;
return &exec;
}
#else
/// \brief An implementation of an Executor that runs closures on a thread pool
/// in filo order.
class ThreadPoolExecutor : public Executor {
@ -130,7 +166,7 @@ public:
}).detach();
}
~ThreadPoolExecutor() {
~ThreadPoolExecutor() override {
std::unique_lock<std::mutex> lock(_mutex);
_stop = true;
lock.unlock();
@ -169,39 +205,14 @@ private:
Latch _done;
};
#ifdef _MSC_VER
/// \brief An Executor that runs tasks via ConcRT.
class ConcRTExecutor : public Executor {
struct Taskish {
Taskish(std::function<void()> task) : _task(task) {}
std::function<void()> _task;
static void run(void *p) {
Taskish *self = static_cast<Taskish *>(p);
self->_task();
concurrency::Free(self);
}
};
public:
virtual void add(std::function<void()> func) {
Concurrency::CurrentScheduler::ScheduleTask(Taskish::run,
new (concurrency::Alloc(sizeof(Taskish))) Taskish(func));
}
};
inline Executor *getDefaultExecutor() {
static ConcRTExecutor exec;
return &exec;
}
#else
inline Executor *getDefaultExecutor() {
static ThreadPoolExecutor exec;
return &exec;
}
#endif
} // namespace internal
/// \brief Allows launching a number of tasks and waiting for them to finish
/// either explicitly via sync() or implicitly on destruction.
class TaskGroup {
@ -210,7 +221,7 @@ class TaskGroup {
public:
void spawn(std::function<void()> f) {
_latch.inc();
getDefaultExecutor()->add([&, f] {
internal::getDefaultExecutor()->add([&, f] {
f();
_latch.dec();
});
@ -219,7 +230,15 @@ public:
void sync() const { _latch.sync(); }
};
#ifdef _MSC_VER
#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0
template <class RandomAccessIterator, class Comp>
void parallel_sort(
RandomAccessIterator start, RandomAccessIterator end,
const Comp &comp = std::less<
typename std::iterator_traits<RandomAccessIterator>::value_type>()) {
std::sort(start, end, comp);
}
#elif defined(_MSC_VER)
// Use ppl parallel_sort on Windows.
template <class RandomAccessIterator, class Comp>
void parallel_sort(
@ -286,7 +305,12 @@ template <class T> void parallel_sort(T *start, T *end) {
parallel_sort(start, end, std::less<T>());
}
#ifdef _MSC_VER
#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0
template <class Iterator, class Func>
void parallel_for_each(Iterator begin, Iterator end, Func func) {
std::for_each(begin, end, func);
}
#elif defined(_MSC_VER)
// Use ppl parallel_for_each on Windows.
template <class Iterator, class Func>
void parallel_for_each(Iterator begin, Iterator end, Func func) {
@ -306,4 +330,4 @@ void parallel_for_each(Iterator begin, Iterator end, Func func) {
#endif
} // end namespace lld
#endif
#endif // LLD_CORE_PARALLEL_H

View file

@ -17,7 +17,7 @@
#include <vector>
namespace lld {
class MutableFile;
class SimpleFile;
/// Once the core linking is done (which resolves references, coalesces atoms
/// and produces a complete Atom graph), the linker runs a series of passes
@ -34,7 +34,7 @@ public:
virtual ~Pass() { }
/// Do the actual work of the Pass.
virtual void perform(std::unique_ptr<MutableFile> &mergedFile) = 0;
virtual std::error_code perform(SimpleFile &mergedFile) = 0;
protected:
// Only subclassess can be instantiated.

View file

@ -16,7 +16,7 @@
#include <vector>
namespace lld {
class MutableFile;
class SimpleFile;
class Pass;
/// \brief Owns and runs a collection of passes.
@ -31,9 +31,10 @@ public:
_passes.push_back(std::move(pass));
}
std::error_code runOnFile(std::unique_ptr<MutableFile> &file) {
std::error_code runOnFile(SimpleFile &file) {
for (std::unique_ptr<Pass> &pass : _passes)
pass->perform(file);
if (std::error_code EC = pass->perform(file))
return EC;
return std::error_code();
}

View file

@ -31,13 +31,12 @@ class ELFLinkingContext;
class File;
class LinkingContext;
class PECOFFLinkingContext;
class TargetHandlerBase;
class MachOLinkingContext;
/// \brief An abstract class for reading object files, library files, and
/// executable files.
///
/// Each file format (e.g. ELF, mach-o, PECOFF, native, etc) have a concrete
/// Each file format (e.g. ELF, mach-o, PECOFF, etc) have a concrete
/// subclass of Reader.
class Reader {
public:
@ -46,17 +45,14 @@ public:
/// Sniffs the file to determine if this Reader can parse it.
/// The method is called with:
/// 1) the file_magic enumeration returned by identify_magic()
/// 2) the file extension (e.g. ".obj")
/// 3) the whole file content buffer if the above is not enough.
virtual bool canParse(file_magic magic, StringRef fileExtension,
const MemoryBuffer &mb) const = 0;
/// 2) the whole file content buffer if the above is not enough.
virtual bool canParse(file_magic magic, MemoryBufferRef mb) const = 0;
/// \brief Parse a supplied buffer (already filled with the contents of a
/// file) and create a File object.
/// The resulting File object takes ownership of the MemoryBuffer.
virtual std::error_code
loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &,
std::vector<std::unique_ptr<File>> &result) const = 0;
virtual ErrorOr<std::unique_ptr<File>>
loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &) const = 0;
};
@ -93,8 +89,8 @@ public:
/// Walk the list of registered Readers and find one that can parse the
/// supplied file and parse it.
std::error_code loadFile(std::unique_ptr<MemoryBuffer> mb,
std::vector<std::unique_ptr<File>> &result) const;
ErrorOr<std::unique_ptr<File>>
loadFile(std::unique_ptr<MemoryBuffer> mb) const;
/// Walk the list of registered kind tables to convert a Reference Kind
/// name to a value.
@ -118,7 +114,6 @@ public:
// as parameters to the addSupport*() method.
void addSupportArchives(bool logLoading);
void addSupportYamlFiles();
void addSupportNativeObjects();
void addSupportCOFFObjects(PECOFFLinkingContext &);
void addSupportCOFFImportLibraries(PECOFFLinkingContext &);
void addSupportMachOObjects(MachOLinkingContext &);

View file

@ -56,7 +56,7 @@ public:
void setKindNamespace(KindNamespace ns) { _kindNamespace = (uint8_t)ns; }
// Which architecture the kind value is for.
enum class KindArch { all, AArch64, ARM, Hexagon, Mips, x86, x86_64 };
enum class KindArch { all, AArch64, AMDGPU, ARM, Hexagon, Mips, x86, x86_64 };
KindArch kindArch() const { return (KindArch)_kindArch; }
void setKindArch(KindArch a) { _kindArch = (uint8_t)a; }

View file

@ -54,7 +54,7 @@ public:
/// @brief do work of merging and resolving and return list
bool resolve();
std::unique_ptr<MutableFile> resultFile() { return std::move(_result); }
std::unique_ptr<SimpleFile> resultFile() { return std::move(_result); }
private:
typedef std::function<void(StringRef, bool)> UndefCallback;

View file

@ -34,19 +34,19 @@ public:
// the import name (Windows).
virtual StringRef getDSOName() const = 0;
const atom_collection<DefinedAtom> &defined() const override {
const AtomVector<DefinedAtom> &defined() const override {
return _definedAtoms;
}
const atom_collection<UndefinedAtom> &undefined() const override {
const AtomVector<UndefinedAtom> &undefined() const override {
return _undefinedAtoms;
}
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
const AtomVector<SharedLibraryAtom> &sharedLibrary() const override {
return _sharedLibraryAtoms;
}
const atom_collection<AbsoluteAtom> &absolute() const override {
const AtomVector<AbsoluteAtom> &absolute() const override {
return _absoluteAtoms;
}
@ -54,10 +54,10 @@ protected:
/// only subclasses of SharedLibraryFile can be instantiated
explicit SharedLibraryFile(StringRef path) : File(path, kindSharedLibrary) {}
atom_collection_vector<DefinedAtom> _definedAtoms;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
AtomVector<DefinedAtom> _definedAtoms;
AtomVector<UndefinedAtom> _undefinedAtoms;
AtomVector<SharedLibraryAtom> _sharedLibraryAtoms;
AtomVector<AbsoluteAtom> _absoluteAtoms;
};
} // namespace lld

View file

@ -23,59 +23,61 @@
#include "lld/Core/UndefinedAtom.h"
#include "llvm/ADT/ilist.h"
#include "llvm/ADT/ilist_node.h"
#include <atomic>
namespace lld {
class SimpleFile : public MutableFile {
class SimpleFile : public File {
public:
SimpleFile(StringRef path) : MutableFile(path) {}
SimpleFile(StringRef path) : File(path, kindObject) {}
void addAtom(const Atom &atom) override {
if (auto *defAtom = dyn_cast<DefinedAtom>(&atom)) {
_definedAtoms._atoms.push_back(defAtom);
} else if (auto *undefAtom = dyn_cast<UndefinedAtom>(&atom)) {
_undefinedAtoms._atoms.push_back(undefAtom);
} else if (auto *shlibAtom = dyn_cast<SharedLibraryAtom>(&atom)) {
_sharedLibraryAtoms._atoms.push_back(shlibAtom);
} else if (auto *absAtom = dyn_cast<AbsoluteAtom>(&atom)) {
_absoluteAtoms._atoms.push_back(absAtom);
void addAtom(const DefinedAtom &a) { _defined.push_back(&a); }
void addAtom(const UndefinedAtom &a) { _undefined.push_back(&a); }
void addAtom(const SharedLibraryAtom &a) { _shared.push_back(&a); }
void addAtom(const AbsoluteAtom &a) { _absolute.push_back(&a); }
void addAtom(const Atom &atom) {
if (auto *p = dyn_cast<DefinedAtom>(&atom)) {
_defined.push_back(p);
} else if (auto *p = dyn_cast<UndefinedAtom>(&atom)) {
_undefined.push_back(p);
} else if (auto *p = dyn_cast<SharedLibraryAtom>(&atom)) {
_shared.push_back(p);
} else if (auto *p = dyn_cast<AbsoluteAtom>(&atom)) {
_absolute.push_back(p);
} else {
llvm_unreachable("atom has unknown definition kind");
}
}
void
removeDefinedAtomsIf(std::function<bool(const DefinedAtom *)> pred) override {
auto &atoms = _definedAtoms._atoms;
void removeDefinedAtomsIf(std::function<bool(const DefinedAtom *)> pred) {
auto &atoms = _defined;
auto newEnd = std::remove_if(atoms.begin(), atoms.end(), pred);
atoms.erase(newEnd, atoms.end());
}
const atom_collection<DefinedAtom> &defined() const override {
return _definedAtoms;
const AtomVector<DefinedAtom> &defined() const override { return _defined; }
const AtomVector<UndefinedAtom> &undefined() const override {
return _undefined;
}
const atom_collection<UndefinedAtom> &undefined() const override {
return _undefinedAtoms;
const AtomVector<SharedLibraryAtom> &sharedLibrary() const override {
return _shared;
}
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
return _sharedLibraryAtoms;
const AtomVector<AbsoluteAtom> &absolute() const override {
return _absolute;
}
const atom_collection<AbsoluteAtom> &absolute() const override {
return _absoluteAtoms;
}
DefinedAtomRange definedAtoms() override {
return make_range(_definedAtoms._atoms);
}
typedef range<std::vector<const DefinedAtom *>::iterator> DefinedAtomRange;
DefinedAtomRange definedAtoms() { return make_range(_defined); }
private:
atom_collection_vector<DefinedAtom> _definedAtoms;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
AtomVector<DefinedAtom> _defined;
AtomVector<UndefinedAtom> _undefined;
AtomVector<SharedLibraryAtom> _shared;
AtomVector<AbsoluteAtom> _absolute;
};
/// \brief Archive library file that may be used as a virtual container
@ -86,19 +88,19 @@ public:
SimpleArchiveLibraryFile(StringRef filename)
: ArchiveLibraryFile(filename) {}
const atom_collection<DefinedAtom> &defined() const override {
const AtomVector<DefinedAtom> &defined() const override {
return _definedAtoms;
}
const atom_collection<UndefinedAtom> &undefined() const override {
const AtomVector<UndefinedAtom> &undefined() const override {
return _undefinedAtoms;
}
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
const AtomVector<SharedLibraryAtom> &sharedLibrary() const override {
return _sharedLibraryAtoms;
}
const atom_collection<AbsoluteAtom> &absolute() const override {
const AtomVector<AbsoluteAtom> &absolute() const override {
return _absoluteAtoms;
}
@ -114,10 +116,10 @@ public:
}
private:
atom_collection_vector<DefinedAtom> _definedAtoms;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
AtomVector<DefinedAtom> _definedAtoms;
AtomVector<UndefinedAtom> _undefinedAtoms;
AtomVector<SharedLibraryAtom> _sharedLibraryAtoms;
AtomVector<AbsoluteAtom> _absoluteAtoms;
};
class SimpleReference : public Reference {
@ -204,9 +206,8 @@ namespace lld {
class SimpleDefinedAtom : public DefinedAtom {
public:
explicit SimpleDefinedAtom(const File &f) : _file(f) {
static uint32_t lastOrdinal = 0;
_ordinal = lastOrdinal++;
explicit SimpleDefinedAtom(const File &f)
: _file(f), _ordinal(f.getNextAtomOrdinalAndIncrement()) {
_references.setAllocator(&f.allocator());
}
@ -224,7 +225,7 @@ public:
Merge merge() const override { return DefinedAtom::mergeNo; }
Alignment alignment() const override { return Alignment(0, 0); }
Alignment alignment() const override { return 1; }
SectionChoice sectionChoice() const override {
return DefinedAtom::sectionBasedOnContent;

View file

@ -105,7 +105,7 @@ private:
bool addByName(const Atom &);
bool addByContent(const DefinedAtom &);
LinkingContext &_context;
LinkingContext &_ctx;
AtomToAtom _replacedAtoms;
NameToAtom _nameTable;
NameToAtom _groupTable;

View file

@ -1,7 +1,7 @@
include/lld/Core
~~~~~~~~~~~~~~~~
* The native/yaml reader/writer interfaces should be changed to return
* The yaml reader/writer interfaces should be changed to return
an explanatory string if there is an error. The existing error_code
abstraction only works for returning low level OS errors. It does not
work for describing formatting issues.

View file

@ -15,16 +15,15 @@
#include <vector>
namespace lld {
class File;
class ELFLinkingContext;
class File;
class LinkingContext;
class MachOLinkingContext;
class PECOFFLinkingContext;
class LinkingContext;
class TargetHandlerBase;
/// \brief The Writer is an abstract class for writing object files, shared
/// library files, and executable files. Each file format (e.g. ELF, mach-o,
/// PECOFF, native, etc) have a concrete subclass of Writer.
/// PECOFF, etc) have a concrete subclass of Writer.
class Writer {
public:
virtual ~Writer();
@ -35,17 +34,16 @@ public:
/// \brief This method is called by Core Linking to give the Writer a chance
/// to add file format specific "files" to set of files to be linked. This is
/// how file format specific atoms can be added to the link.
virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &);
virtual void createImplicitFiles(std::vector<std::unique_ptr<File>> &) {}
protected:
// only concrete subclasses can be instantiated
Writer();
};
std::unique_ptr<Writer> createWriterELF(TargetHandlerBase *handler);
std::unique_ptr<Writer> createWriterELF(const ELFLinkingContext &);
std::unique_ptr<Writer> createWriterMachO(const MachOLinkingContext &);
std::unique_ptr<Writer> createWriterPECOFF(const PECOFFLinkingContext &);
std::unique_ptr<Writer> createWriterNative();
std::unique_ptr<Writer> createWriterYAML(const LinkingContext &);
} // end namespace lld

View file

@ -35,7 +35,7 @@ class ELFLinkingContext;
typedef std::vector<std::unique_ptr<File>> FileVector;
FileVector makeErrorFile(StringRef path, std::error_code ec);
FileVector parseMemberFiles(FileVector &files);
FileVector parseMemberFiles(std::unique_ptr<File> File);
FileVector loadFile(LinkingContext &ctx, StringRef path, bool wholeArchive);
/// Base class for all Drivers.
@ -46,6 +46,9 @@ protected:
static bool link(LinkingContext &context,
raw_ostream &diag = llvm::errs());
/// Parses the LLVM options from the context.
static void parseLLVMOptions(const LinkingContext &context);
private:
Driver() = delete;
};
@ -55,7 +58,7 @@ private:
class UniversalDriver : public Driver {
public:
/// Determine flavor and pass control to Driver for that flavor.
static bool link(int argc, const char *argv[],
static bool link(llvm::MutableArrayRef<const char *> args,
raw_ostream &diag = llvm::errs());
private:
@ -67,12 +70,12 @@ 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(int argc, const char *argv[],
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(int argc, const char *argv[],
static bool parse(llvm::ArrayRef<const char *> args,
std::unique_ptr<ELFLinkingContext> &context,
raw_ostream &diag = llvm::errs());
@ -103,12 +106,13 @@ 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(int argc, const char *argv[],
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(int argc, const char *argv[], MachOLinkingContext &info,
static bool parse(llvm::ArrayRef<const char *> args,
MachOLinkingContext &info,
raw_ostream &diag = llvm::errs());
private:
@ -116,41 +120,25 @@ private:
};
/// 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(int argc, const char *argv[],
raw_ostream &diag = llvm::errs());
namespace coff {
void link(llvm::ArrayRef<const char *> args);
}
/// Uses Windows style link command line options to fill in options struct.
/// Returns true iff there was an error.
static bool parse(int argc, const char *argv[], 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(argc, argv, info, diag, true);
}
private:
WinLinkDriver() = delete;
};
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(int argc, const char *argv[],
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(int argc, const char *argv[], CoreLinkingContext &info,
static bool parse(llvm::ArrayRef<const char *> args, CoreLinkingContext &info,
raw_ostream &diag = llvm::errs());
private:

View file

@ -1,200 +0,0 @@
//===- lld/Driver/WinLinkModuleDef.h --------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Windows module definition file parser.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_DRIVER_WIN_LINK_MODULE_DEF_H
#define LLD_DRIVER_WIN_LINK_MODULE_DEF_H
#include "lld/Core/LLVM.h"
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/Allocator.h"
#include <vector>
namespace lld {
namespace moduledef {
enum class Kind {
unknown,
eof,
identifier,
comma,
equal,
kw_base,
kw_data,
kw_exports,
kw_heapsize,
kw_library,
kw_name,
kw_noname,
kw_private,
kw_stacksize,
kw_version,
};
class Token {
public:
Token() : _kind(Kind::unknown) {}
Token(Kind kind, StringRef range) : _kind(kind), _range(range) {}
Kind _kind;
StringRef _range;
};
class Lexer {
public:
explicit Lexer(std::unique_ptr<MemoryBuffer> mb) : _buffer(mb->getBuffer()) {
_sourceManager.AddNewSourceBuffer(std::move(mb), llvm::SMLoc());
}
Token lex();
const llvm::SourceMgr &getSourceMgr() const { return _sourceManager; }
private:
StringRef _buffer;
llvm::SourceMgr _sourceManager;
};
class Directive {
public:
enum class Kind { exports, heapsize, library, name, stacksize, version };
Kind getKind() const { return _kind; }
virtual ~Directive() {}
protected:
explicit Directive(Kind k) : _kind(k) {}
private:
Kind _kind;
};
class Exports : public Directive {
public:
explicit Exports(const std::vector<PECOFFLinkingContext::ExportDesc> &exports)
: Directive(Kind::exports), _exports(exports) {}
static bool classof(const Directive *dir) {
return dir->getKind() == Kind::exports;
}
const std::vector<PECOFFLinkingContext::ExportDesc> &getExports() const {
return _exports;
}
private:
const std::vector<PECOFFLinkingContext::ExportDesc> _exports;
};
template <Directive::Kind kind>
class MemorySize : public Directive {
public:
MemorySize(uint64_t reserve, uint64_t commit)
: Directive(kind), _reserve(reserve), _commit(commit) {}
static bool classof(const Directive *dir) {
return dir->getKind() == kind;
}
uint64_t getReserve() const { return _reserve; }
uint64_t getCommit() const { return _commit; }
private:
const uint64_t _reserve;
const uint64_t _commit;
};
typedef MemorySize<Directive::Kind::heapsize> Heapsize;
typedef MemorySize<Directive::Kind::stacksize> Stacksize;
class Name : public Directive {
public:
Name(StringRef outputPath, uint64_t baseaddr)
: Directive(Kind::name), _outputPath(outputPath), _baseaddr(baseaddr) {}
static bool classof(const Directive *dir) {
return dir->getKind() == Kind::name;
}
StringRef getOutputPath() const { return _outputPath; }
uint64_t getBaseAddress() const { return _baseaddr; }
private:
const std::string _outputPath;
const uint64_t _baseaddr;
};
class Library : public Directive {
public:
Library(StringRef name, uint64_t baseaddr)
: Directive(Kind::library), _name(name), _baseaddr(baseaddr) {}
static bool classof(const Directive *dir) {
return dir->getKind() == Kind::library;
}
StringRef getName() const { return _name; }
uint64_t getBaseAddress() const { return _baseaddr; }
private:
const std::string _name;
const uint64_t _baseaddr;
};
class Version : public Directive {
public:
Version(int major, int minor)
: Directive(Kind::version), _major(major), _minor(minor) {}
static bool classof(const Directive *dir) {
return dir->getKind() == Kind::version;
}
int getMajorVersion() const { return _major; }
int getMinorVersion() const { return _minor; }
private:
const int _major;
const int _minor;
};
class Parser {
public:
Parser(Lexer &lex, llvm::BumpPtrAllocator &alloc)
: _lex(lex), _alloc(alloc) {}
bool parse(std::vector<Directive *> &ret);
private:
void consumeToken();
bool consumeTokenAsInt(uint64_t &result);
bool expectAndConsume(Kind kind, Twine msg);
void ungetToken();
void error(const Token &tok, Twine msg);
bool parseOne(Directive *&dir);
bool parseExport(PECOFFLinkingContext::ExportDesc &result);
bool parseMemorySize(uint64_t &reserve, uint64_t &commit);
bool parseName(std::string &outfile, uint64_t &baseaddr);
bool parseVersion(int &major, int &minor);
Lexer &_lex;
llvm::BumpPtrAllocator &_alloc;
Token _tok;
std::vector<Token> _tokBuf;
};
}
}
#endif

View file

@ -1,44 +0,0 @@
LLD_LEVEL := ../..
DIRS := Config
include $(LLD_LEVEL)/Makefile
install-local::
$(Echo) Installing lld include files
$(Verb) $(MKDIR) $(DESTDIR)$(PROJ_includedir)
$(Verb) if test -d "$(PROJ_SRC_DIR)" ; then \
cd $(PROJ_SRC_DIR)/.. && \
for hdr in `find lld -type f \
'(' -name LICENSE.TXT \
-o -name '*.def' \
-o -name '*.h' \
-o -name '*.inc' \
')' -print \
| grep -v CVS | grep -v .svn | grep -v .dir` ; do \
instdir=$(DESTDIR)`dirname "$(PROJ_includedir)/$$hdr"` ; \
if test \! -d "$$instdir" ; then \
$(EchoCmd) Making install directory $$instdir ; \
$(MKDIR) $$instdir ;\
fi ; \
$(DataInstall) $$hdr $(DESTDIR)$(PROJ_includedir)/$$hdr ; \
done ; \
fi
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
$(Verb) if test -d "$(PROJ_OBJ_ROOT)/tools/lld/include/lld" ; then \
cd $(PROJ_OBJ_ROOT)/tools/lld/include && \
for hdr in `find lld -type f \
'(' -name LICENSE.TXT \
-o -name '*.def' \
-o -name '*.h' \
-o -name '*.inc' \
')' -print \
| grep -v CVS | grep -v .tmp | grep -v .dir` ; do \
instdir=$(DESTDIR)`dirname "$(PROJ_includedir)/$$hdr"` ; \
if test \! -d "$$instdir" ; then \
$(EchoCmd) Making install directory $$instdir ; \
$(MKDIR) $$instdir ;\
fi ; \
$(DataInstall) $$hdr $(DESTDIR)$(PROJ_includedir)/$$hdr ; \
done ; \
fi
endif

View file

@ -26,24 +26,60 @@
#include <memory>
#include <set>
namespace lld {
class DefinedAtom;
class Reference;
class File;
namespace elf {
template <typename ELFT> class TargetHandler;
namespace llvm {
class FileOutputBuffer;
}
class TargetHandlerBase {
public:
virtual ~TargetHandlerBase() {}
virtual void registerRelocationNames(Registry &) = 0;
namespace lld {
struct AtomLayout;
class File;
class Reference;
namespace elf {
using llvm::object::ELF32LE;
using llvm::object::ELF32BE;
using llvm::object::ELF64LE;
using llvm::object::ELF64BE;
class ELFWriter;
std::unique_ptr<ELFLinkingContext> createAArch64LinkingContext(llvm::Triple);
std::unique_ptr<ELFLinkingContext> createAMDGPULinkingContext(llvm::Triple);
std::unique_ptr<ELFLinkingContext> createARMLinkingContext(llvm::Triple);
std::unique_ptr<ELFLinkingContext> createExampleLinkingContext(llvm::Triple);
std::unique_ptr<ELFLinkingContext> createHexagonLinkingContext(llvm::Triple);
std::unique_ptr<ELFLinkingContext> createMipsLinkingContext(llvm::Triple);
std::unique_ptr<ELFLinkingContext> createX86LinkingContext(llvm::Triple);
std::unique_ptr<ELFLinkingContext> createX86_64LinkingContext(llvm::Triple);
class TargetRelocationHandler {
public:
virtual ~TargetRelocationHandler() {}
virtual std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &,
const lld::AtomLayout &,
const Reference &) const = 0;
};
} // namespace elf
/// \brief TargetHandler contains all the information responsible to handle a
/// a particular target on ELF. A target might wish to override implementation
/// of creating atoms and how the atoms are written to the output file.
class TargetHandler {
public:
virtual ~TargetHandler() {}
/// Determines how relocations need to be applied.
virtual const elf::TargetRelocationHandler &getRelocationHandler() const = 0;
/// Returns a reader for object files.
virtual std::unique_ptr<Reader> getObjReader() = 0;
/// Returns a reader for .so files.
virtual std::unique_ptr<Reader> getDSOReader() = 0;
/// Returns a writer to write an ELF file.
virtual std::unique_ptr<Writer> getWriter() = 0;
};
@ -52,28 +88,32 @@ public:
/// \brief The type of ELF executable that the linker
/// creates.
enum class OutputMagic : uint8_t {
DEFAULT, // The default mode, no specific magic set
NMAGIC, // Disallow shared libraries and don't align sections
// PageAlign Data, Mark Text Segment/Data segment RW
OMAGIC // Disallow shared libraries and don't align sections,
// Mark Text Segment/Data segment RW
// The default mode, no specific magic set
DEFAULT,
// Disallow shared libraries and don't align sections
// PageAlign Data, Mark Text Segment/Data segment RW
NMAGIC,
// Disallow shared libraries and don't align sections,
// Mark Text Segment/Data segment RW
OMAGIC,
};
/// \brief ELF DT_FLAGS.
enum DTFlag : uint32_t {
DT_NOW = 1 << 1,
DT_ORIGIN = 1 << 2,
};
llvm::Triple getTriple() const { return _triple; }
// Page size.
virtual uint64_t getPageSize() const {
if (_maxPageSize)
return *_maxPageSize;
return 0x1000;
}
virtual void setMaxPageSize(uint64_t pagesize) {
_maxPageSize = pagesize;
}
uint64_t getPageSize() const { return _maxPageSize; }
void setMaxPageSize(uint64_t v) { _maxPageSize = v; }
OutputMagic getOutputMagic() const { return _outputMagic; }
uint16_t getOutputELFType() const { return _outputELFType; }
uint16_t getOutputMachine() const;
bool mergeCommonStrings() const { return _mergeCommonStrings; }
virtual int getMachineType() const = 0;
virtual uint64_t getBaseAddress() const { return _baseAddress; }
virtual void setBaseAddress(uint64_t address) { _baseAddress = address; }
@ -85,9 +125,9 @@ public:
/// created for every undefined symbol that are present in the dynamic table
/// in the shared library
bool useShlibUndefines() const { return _useShlibUndefines; }
/// @}
/// \brief Does this relocation belong in the dynamic relocation table?
/// \brief Returns true if a given relocation should be added to the
/// dynamic relocation table.
///
/// This table is evaluated at loadtime by the dynamic loader and is
/// referenced by the DT_RELA{,ENT,SZ} entries in the dynamic table.
@ -95,20 +135,20 @@ public:
/// table.
virtual bool isDynamicRelocation(const Reference &) const { return false; }
/// \brief Is this a copy relocation?
/// \brief Returns true if a given reference is a copy relocation.
///
/// If this is a copy relocation, its target must be an ObjectAtom. We must
/// include in DT_NEEDED the name of the library where this object came from.
virtual bool isCopyRelocation(const Reference &) const {
return false;
}
virtual bool isCopyRelocation(const Reference &) const { return false; }
bool validateImpl(raw_ostream &diagnostics) override;
/// \brief Does the linker allow dynamic libraries to be linked with?
/// \brief Returns true if the linker allows dynamic libraries to be
/// linked with.
///
/// This is true when the output mode of the executable is set to be
/// having NMAGIC/OMAGIC
virtual bool allowLinkWithDynamicLibraries() const {
bool allowLinkWithDynamicLibraries() const {
if (_outputMagic == OutputMagic::NMAGIC ||
_outputMagic == OutputMagic::OMAGIC || _noAllowDynamicLibraries)
return false;
@ -118,7 +158,7 @@ public:
/// \brief Use Elf_Rela format to output relocation tables.
virtual bool isRelaOutputFormat() const { return true; }
/// \brief Does this relocation belong in the dynamic plt relocation table?
/// \brief Returns true if a given relocation should be added to PLT.
///
/// This table holds all of the relocations used for delayed symbol binding.
/// It will be evaluated at load time if LD_BIND_NOW is set. It is referenced
@ -133,30 +173,28 @@ public:
}
/// \brief The dynamic linker path set by the --dynamic-linker option
virtual StringRef getInterpreter() const {
if (_dynamicLinkerArg)
return _dynamicLinkerPath;
StringRef getInterpreter() const {
if (_dynamicLinkerPath.hasValue())
return _dynamicLinkerPath.getValue();
return getDefaultInterpreter();
}
/// \brief Does the output have dynamic sections.
virtual bool isDynamic() const;
/// \brief Returns true if the output have dynamic sections.
bool isDynamic() const;
/// \brief Are we creating a shared library?
virtual bool isDynamicLibrary() const {
return _outputELFType == llvm::ELF::ET_DYN;
}
/// \brief Returns true if we are creating a shared library.
bool isDynamicLibrary() const { return _outputELFType == llvm::ELF::ET_DYN; }
/// \brief Is the relocation a relative relocation
/// \brief Returns true if a given relocation is a relative relocation.
virtual bool isRelativeReloc(const Reference &r) const;
template <typename ELFT>
lld::elf::TargetHandler<ELFT> &getTargetHandler() const {
TargetHandler &getTargetHandler() const {
assert(_targetHandler && "Got null TargetHandler!");
return static_cast<lld::elf::TargetHandler<ELFT> &>(*_targetHandler.get());
return *_targetHandler;
}
TargetHandlerBase *targetHandler() const { return _targetHandler.get(); }
virtual void registerRelocationNames(Registry &) = 0;
void addPasses(PassManager &pm) override;
void setTriple(llvm::Triple trip) { _triple = trip; }
@ -174,19 +212,16 @@ public:
void finalizeInputFiles() override;
/// \brief Set the dynamic linker path
void setInterpreter(StringRef dynamicLinker) {
_dynamicLinkerArg = true;
_dynamicLinkerPath = dynamicLinker;
}
void setInterpreter(StringRef s) { _dynamicLinkerPath = s; }
/// \brief Set NMAGIC output kind when the linker specifies --nmagic
/// or -n in the command line
/// Set OMAGIC output kind when the linker specifies --omagic
/// or -N in the command line
virtual void setOutputMagic(OutputMagic magic) { _outputMagic = magic; }
void setOutputMagic(OutputMagic magic) { _outputMagic = magic; }
/// \brief Disallow dynamic libraries during linking
virtual void setNoAllowDynamicLibraries() { _noAllowDynamicLibraries = true; }
void setNoAllowDynamicLibraries() { _noAllowDynamicLibraries = true; }
/// Searches directories for a match on the input File
ErrorOr<StringRef> searchLibrary(StringRef libName) const;
@ -220,34 +255,17 @@ public:
_absoluteSymbols[name] = addr;
}
void setSharedObjectName(StringRef soname) {
_soname = soname;
}
StringRef sharedObjectName() const { return _soname; }
void setSharedObjectName(StringRef soname) { _soname = soname; }
StringRef getSysroot() const { return _sysrootPath; }
void setSysroot(StringRef path) { _sysrootPath = path; }
/// \brief Set path to the system root
void setSysroot(StringRef path) {
_sysrootPath = path;
}
void addRpath(StringRef path) { _rpathList.push_back(path); }
range<const StringRef *> getRpathList() const { return _rpathList; }
void addRpath(StringRef path) {
_rpathList.push_back(path);
}
range<const StringRef *> getRpathList() const {
return _rpathList;
}
void addRpathLink(StringRef path) {
_rpathLinkList.push_back(path);
}
range<const StringRef *> getRpathLinkList() const {
return _rpathLinkList;
}
void addRpathLink(StringRef path) { _rpathLinkList.push_back(path); }
range<const StringRef *> getRpathLinkList() const { return _rpathLinkList; }
const std::map<std::string, uint64_t> &getAbsoluteSymbols() const {
return _absoluteSymbols;
@ -262,13 +280,10 @@ public:
}
// add search path to list.
virtual bool addSearchPath(StringRef ref) {
_inputSearchPaths.push_back(ref);
return true;
}
void addSearchPath(StringRef ref) { _inputSearchPaths.push_back(ref); }
// Retrieve search path list.
StringRefVector getSearchPaths() { return _inputSearchPaths; };
StringRefVector getSearchPaths() { return _inputSearchPaths; }
// By default, the linker would merge sections that are read only with
// segments that have read and execute permissions. When the user specifies a
@ -290,6 +305,26 @@ public:
bool alignSegments() const { return _alignSegments; }
void setAlignSegments(bool align) { _alignSegments = align; }
/// \brief Enable new dtags.
/// If this flag is set lld emits DT_RUNPATH instead of
/// DT_RPATH. They are functionally equivalent except for
/// the following two differences:
/// - DT_RUNPATH is searched after LD_LIBRARY_PATH, while
/// DT_RPATH is searched before.
/// - DT_RUNPATH is used only to search for direct dependencies
/// of the object it's contained in, while DT_RPATH is used
/// for indirect dependencies as well.
bool getEnableNewDtags() const { return _enableNewDtags; }
void setEnableNewDtags(bool e) { _enableNewDtags = e; }
/// \brief Discard local symbols.
bool discardLocals() const { return _discardLocals; }
void setDiscardLocals(bool d) { _discardLocals = d; }
/// \brief Discard temprorary local symbols.
bool discardTempLocals() const { return _discardTempLocals; }
void setDiscardTempLocals(bool d) { _discardTempLocals = d; }
/// \brief Strip symbols.
bool stripSymbols() const { return _stripSymbols; }
void setStripSymbols(bool strip) { _stripSymbols = strip; }
@ -301,6 +336,10 @@ public:
// --wrap option.
void addWrapForSymbol(StringRef sym) { _wrapCalls.insert(sym); }
// \brief Set DT_FLAGS flag.
void setDTFlag(DTFlag f) { _dtFlags |= f; }
bool getDTFlag(DTFlag f) { return (_dtFlags & f); }
const llvm::StringSet<> &wrapCalls() const { return _wrapCalls; }
void setUndefinesResolver(std::unique_ptr<File> resolver);
@ -308,43 +347,61 @@ public:
script::Sema &linkerScriptSema() { return _linkerScriptSema; }
const script::Sema &linkerScriptSema() const { return _linkerScriptSema; }
private:
ELFLinkingContext() = delete;
/// Notify the ELFLinkingContext when the new ELF section is read.
void notifyInputSectionName(StringRef name);
/// Encountered C-ident input section names.
const llvm::StringSet<> &cidentSectionNames() const {
return _cidentSections;
}
// Set R_ARM_TARGET1 relocation behaviour
bool armTarget1Rel() const { return _armTarget1Rel; }
void setArmTarget1Rel(bool value) { _armTarget1Rel = value; }
// Set R_MIPS_EH relocation behaviour.
bool mipsPcRelEhRel() const { return _mipsPcRelEhRel; }
void setMipsPcRelEhRel(bool value) { _mipsPcRelEhRel = value; }
protected:
ELFLinkingContext(llvm::Triple, std::unique_ptr<TargetHandlerBase>);
ELFLinkingContext(llvm::Triple triple, std::unique_ptr<TargetHandler> handler)
: _triple(triple), _targetHandler(std::move(handler)) {}
Writer &writer() const override;
/// Method to create a internal file for an undefined symbol
std::unique_ptr<File> createUndefinedSymbolFile() const override;
uint16_t _outputELFType; // e.g ET_EXEC
uint16_t _outputELFType = llvm::ELF::ET_EXEC;
llvm::Triple _triple;
std::unique_ptr<TargetHandlerBase> _targetHandler;
uint64_t _baseAddress;
bool _isStaticExecutable;
bool _noInhibitExec;
bool _exportDynamic;
bool _mergeCommonStrings;
bool _useShlibUndefines;
bool _dynamicLinkerArg;
bool _noAllowDynamicLibraries;
bool _mergeRODataToTextSegment;
bool _demangle;
bool _stripSymbols;
bool _alignSegments;
bool _nostdlib;
bool _collectStats;
llvm::Optional<uint64_t> _maxPageSize;
std::unique_ptr<TargetHandler> _targetHandler;
uint64_t _baseAddress = 0;
bool _isStaticExecutable = false;
bool _noInhibitExec = false;
bool _exportDynamic = false;
bool _mergeCommonStrings = false;
bool _useShlibUndefines = true;
bool _dynamicLinkerArg = false;
bool _noAllowDynamicLibraries = false;
bool _mergeRODataToTextSegment = true;
bool _demangle = true;
bool _discardTempLocals = false;
bool _discardLocals = false;
bool _stripSymbols = false;
bool _alignSegments = true;
bool _enableNewDtags = false;
bool _collectStats = false;
bool _armTarget1Rel = false;
bool _mipsPcRelEhRel = false;
uint64_t _maxPageSize = 0x1000;
uint32_t _dtFlags = 0;
OutputMagic _outputMagic;
OutputMagic _outputMagic = OutputMagic::DEFAULT;
StringRefVector _inputSearchPaths;
std::unique_ptr<Writer> _writer;
StringRef _dynamicLinkerPath;
StringRef _initFunction;
StringRef _finiFunction;
StringRef _sysrootPath;
llvm::Optional<StringRef> _dynamicLinkerPath;
StringRef _initFunction = "_init";
StringRef _finiFunction = "_fini";
StringRef _sysrootPath = "";
StringRef _soname;
StringRefVector _rpathList;
StringRefVector _rpathLinkList;
@ -352,11 +409,14 @@ protected:
std::map<std::string, uint64_t> _absoluteSymbols;
llvm::StringSet<> _dynamicallyExportedSymbols;
std::unique_ptr<File> _resolver;
std::mutex _cidentMutex;
llvm::StringSet<> _cidentSections;
// The linker script semantic object, which owns all script ASTs, is stored
// in the current linking context via _linkerScriptSema.
script::Sema _linkerScriptSema;
};
} // end namespace lld
#endif

View file

@ -1,38 +0,0 @@
//===- lld/ReaderWriter/ELFTargets.h --------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_ELF_TARGETS_H
#define LLD_READER_WRITER_ELF_TARGETS_H
#include "ELFLinkingContext.h"
namespace lld {
namespace elf {
#define LLVM_TARGET(TargetName) \
class TargetName##LinkingContext final : public ELFLinkingContext { \
public: \
static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); \
};
// FIXME: #include "llvm/Config/Targets.def"
LLVM_TARGET(AArch64)
LLVM_TARGET(ARM)
LLVM_TARGET(Hexagon)
LLVM_TARGET(Mips)
LLVM_TARGET(X86)
LLVM_TARGET(Example)
LLVM_TARGET(X86_64)
#undef LLVM_TARGET
} // end namespace elf
} // end namespace lld
#endif

View file

@ -20,6 +20,7 @@
#include "lld/Core/range.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
@ -78,6 +79,9 @@ public:
kw_entry,
kw_exclude_file,
kw_extern,
kw_filehdr,
kw_fill,
kw_flags,
kw_group,
kw_hidden,
kw_input,
@ -85,6 +89,7 @@ public:
kw_length,
kw_memory,
kw_origin,
kw_phdrs,
kw_provide,
kw_provide_hidden,
kw_only_if_ro,
@ -160,6 +165,7 @@ public:
enum class Kind {
Entry,
Extern,
Fill,
Group,
Input,
InputSectionsCmd,
@ -170,6 +176,7 @@ public:
OutputFormat,
OutputSectionDescription,
Overlay,
PHDRS,
SearchDir,
Sections,
SortedGroup,
@ -191,6 +198,14 @@ private:
Kind _kind;
};
template <class T>
ArrayRef<T> save_array(llvm::BumpPtrAllocator &alloc, ArrayRef<T> array) {
size_t num = array.size();
T *start = alloc.Allocate<T>(num);
std::uninitialized_copy(std::begin(array), std::end(array), start);
return llvm::makeArrayRef(start, num);
}
class Output : public Command {
public:
Output(Parser &ctx, StringRef outputFileName)
@ -212,10 +227,7 @@ class OutputFormat : public Command {
public:
OutputFormat(Parser &ctx, const SmallVectorImpl<StringRef> &formats)
: Command(ctx, Kind::OutputFormat) {
size_t numFormats = formats.size();
StringRef *formatsStart = getAllocator().Allocate<StringRef>(numFormats);
std::copy(std::begin(formats), std::end(formats), formatsStart);
_formats = llvm::makeArrayRef(formatsStart, numFormats);
_formats = save_array<StringRef>(getAllocator(), formats);
}
static bool classof(const Command *c) {
@ -274,10 +286,7 @@ class PathList : public Command {
public:
PathList(Parser &ctx, StringRef name, const SmallVectorImpl<Path> &paths)
: Command(ctx, K), _name(name) {
size_t numPaths = paths.size();
Path *pathsStart = getAllocator().template Allocate<Path>(numPaths);
std::copy(std::begin(paths), std::end(paths), pathsStart);
_paths = llvm::makeArrayRef(pathsStart, numPaths);
_paths = save_array<Path>(getAllocator(), paths);
}
static bool classof(const Command *c) { return c->getKind() == K; }
@ -382,7 +391,8 @@ public:
Kind getKind() const { return _kind; }
inline llvm::BumpPtrAllocator &getAllocator() const;
virtual void dump(raw_ostream &os) const = 0;
virtual ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const = 0;
virtual ErrorOr<int64_t>
evalExpr(const SymbolTableTy &symbolTable = SymbolTableTy()) const = 0;
virtual ~Expression() {}
protected:
@ -406,7 +416,7 @@ public:
return c->getKind() == Kind::Constant;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
ErrorOr<int64_t> evalExpr(const SymbolTableTy &symbolTable) const override;
private:
uint64_t _num;
@ -422,7 +432,7 @@ public:
return c->getKind() == Kind::Symbol;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
ErrorOr<int64_t> evalExpr(const SymbolTableTy &symbolTable) const override;
private:
StringRef _name;
@ -433,11 +443,7 @@ public:
FunctionCall(Parser &ctx, StringRef name,
const SmallVectorImpl<const Expression *> &args)
: Expression(ctx, Kind::FunctionCall), _name(name) {
size_t numArgs = args.size();
const Expression **argsStart =
getAllocator().Allocate<const Expression *>(numArgs);
std::copy(std::begin(args), std::end(args), argsStart);
_args = llvm::makeArrayRef(argsStart, numArgs);
_args = save_array<const Expression *>(getAllocator(), args);
}
void dump(raw_ostream &os) const override;
@ -446,7 +452,7 @@ public:
return c->getKind() == Kind::FunctionCall;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
ErrorOr<int64_t> evalExpr(const SymbolTableTy &symbolTable) const override;
private:
StringRef _name;
@ -468,7 +474,7 @@ public:
return c->getKind() == Kind::Unary;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
ErrorOr<int64_t> evalExpr(const SymbolTableTy &symbolTable) const override;
private:
Operation _op;
@ -503,7 +509,7 @@ public:
return c->getKind() == Kind::BinOp;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
ErrorOr<int64_t> evalExpr(const SymbolTableTy &symbolTable) const override;
private:
Operation _op;
@ -538,7 +544,7 @@ public:
return c->getKind() == Kind::TernaryConditional;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
ErrorOr<int64_t> evalExpr(const SymbolTableTy &symbolTable) const override;
private:
const Expression *_conditional;
@ -647,11 +653,7 @@ public:
InputSectionSortedGroup(Parser &ctx, WildcardSortMode sort,
const SmallVectorImpl<const InputSection *> &sections)
: InputSection(ctx, Kind::SortedGroup), _sortMode(sort) {
size_t numSections = sections.size();
const InputSection **sectionsStart =
getAllocator().Allocate<const InputSection *>(numSections);
std::copy(std::begin(sections), std::end(sections), sectionsStart);
_sections = llvm::makeArrayRef(sectionsStart, numSections);
_sections = save_array<const InputSection *>(getAllocator(), sections);
}
void dump(raw_ostream &os) const override;
@ -691,11 +693,7 @@ public:
: Command(ctx, Kind::InputSectionsCmd), _memberName(memberName),
_archiveName(archiveName), _keep(keep), _fileSortMode(fileSortMode),
_archiveSortMode(archiveSortMode) {
size_t numSections = sections.size();
const InputSection **sectionsStart =
getAllocator().Allocate<const InputSection *>(numSections);
std::copy(std::begin(sections), std::end(sections), sectionsStart);
_sections = llvm::makeArrayRef(sectionsStart, numSections);
_sections = save_array<const InputSection *>(getAllocator(), sections);
}
void dump(raw_ostream &os) const override;
@ -720,6 +718,24 @@ private:
llvm::ArrayRef<const InputSection *> _sections;
};
class FillCmd : public Command {
public:
FillCmd(Parser &ctx, ArrayRef<uint8_t> bytes) : Command(ctx, Kind::Fill) {
_bytes = save_array<uint8_t>(getAllocator(), bytes);
}
void dump(raw_ostream &os) const override;
static bool classof(const Command *c) {
return c->getKind() == Kind::Fill;
}
ArrayRef<uint8_t> bytes() { return _bytes; }
private:
ArrayRef<uint8_t> _bytes;
};
/// A sections-command to specify which input sections and symbols compose a
/// given output section.
/// Example:
@ -743,18 +759,16 @@ public:
const Expression *align, const Expression *subAlign, const Expression *at,
const Expression *fillExpr, StringRef fillStream, bool alignWithInput,
bool discard, Constraint constraint,
const SmallVectorImpl<const Command *> &outputSectionCommands)
const SmallVectorImpl<const Command *> &outputSectionCommands,
ArrayRef<StringRef> phdrs)
: Command(ctx, Kind::OutputSectionDescription), _sectionName(sectionName),
_address(address), _align(align), _subAlign(subAlign), _at(at),
_fillExpr(fillExpr), _fillStream(fillStream),
_alignWithInput(alignWithInput), _discard(discard),
_constraint(constraint) {
size_t numCommands = outputSectionCommands.size();
const Command **commandsStart =
getAllocator().Allocate<const Command *>(numCommands);
std::copy(std::begin(outputSectionCommands),
std::end(outputSectionCommands), commandsStart);
_outputSectionCommands = llvm::makeArrayRef(commandsStart, numCommands);
_outputSectionCommands =
save_array<const Command *>(getAllocator(), outputSectionCommands);
_phdrs = save_array<StringRef>(getAllocator(), phdrs);
}
static bool classof(const Command *c) {
@ -766,6 +780,8 @@ public:
const_iterator begin() const { return _outputSectionCommands.begin(); }
const_iterator end() const { return _outputSectionCommands.end(); }
StringRef name() const { return _sectionName; }
bool isDiscarded() const { return _discard; }
ArrayRef<StringRef> PHDRs() const { return _phdrs; }
private:
StringRef _sectionName;
@ -779,6 +795,7 @@ private:
bool _discard;
Constraint _constraint;
llvm::ArrayRef<const Command *> _outputSectionCommands;
ArrayRef<StringRef> _phdrs;
};
/// Represents an Overlay structure as documented in
@ -794,6 +811,52 @@ public:
void dump(raw_ostream &os) const override { os << "Overlay description\n"; }
};
class PHDR {
public:
PHDR(StringRef name, uint64_t type, bool includeFileHdr, bool includePHDRs,
const Expression *at, uint64_t flags)
: _name(name), _type(type), _includeFileHdr(includeFileHdr),
_includePHDRs(includePHDRs), _at(at), _flags(flags) {}
StringRef name() const { return _name; }
uint64_t type() const { return _type; }
bool hasFileHdr() const { return _includeFileHdr; }
bool hasPHDRs() const { return _includePHDRs; }
uint64_t flags() const { return _flags; }
bool isNone() const;
void dump(raw_ostream &os) const;
private:
StringRef _name;
uint64_t _type;
bool _includeFileHdr;
bool _includePHDRs;
const Expression *_at;
uint64_t _flags;
};
class PHDRS : public Command {
public:
typedef ArrayRef<const PHDR *>::const_iterator const_iterator;
PHDRS(Parser &ctx, const SmallVectorImpl<const PHDR *> &phdrs)
: Command(ctx, Kind::PHDRS) {
_phdrs = save_array<const PHDR *>(getAllocator(), phdrs);
}
static bool classof(const Command *c) {
return c->getKind() == Kind::PHDRS;
}
void dump(raw_ostream &os) const override;
const_iterator begin() const { return _phdrs.begin(); }
const_iterator end() const { return _phdrs.end(); }
private:
ArrayRef<const PHDR *> _phdrs;
};
/// Represents all the contents of the SECTIONS {} construct.
class Sections : public Command {
public:
@ -802,12 +865,8 @@ public:
Sections(Parser &ctx,
const SmallVectorImpl<const Command *> &sectionsCommands)
: Command(ctx, Kind::Sections) {
size_t numCommands = sectionsCommands.size();
const Command **commandsStart =
getAllocator().Allocate<const Command *>(numCommands);
std::copy(std::begin(sectionsCommands), std::end(sectionsCommands),
commandsStart);
_sectionsCommands = llvm::makeArrayRef(commandsStart, numCommands);
_sectionsCommands =
save_array<const Command *>(getAllocator(), sectionsCommands);
}
static bool classof(const Command *c) {
@ -844,11 +903,7 @@ public:
Memory(Parser &ctx,
const SmallVectorImpl<const MemoryBlock *> &blocks)
: Command(ctx, Kind::Memory) {
size_t numBlocks = blocks.size();
const MemoryBlock **blocksStart =
getAllocator().Allocate<const MemoryBlock *>(numBlocks);
std::copy(std::begin(blocks), std::end(blocks), blocksStart);
_blocks = llvm::makeArrayRef(blocksStart, numBlocks);
_blocks = save_array<const MemoryBlock *>(getAllocator(), blocks);
}
static bool classof(const Command *c) {
@ -869,11 +924,7 @@ public:
Extern(Parser &ctx,
const SmallVectorImpl<StringRef> &symbols)
: Command(ctx, Kind::Extern) {
size_t numSymbols = symbols.size();
StringRef *symbolsStart =
getAllocator().Allocate<StringRef>(numSymbols);
std::copy(std::begin(symbols), std::end(symbols), symbolsStart);
_symbols = llvm::makeArrayRef(symbolsStart, numSymbols);
_symbols = save_array<StringRef>(getAllocator(), symbols);
}
static bool classof(const Command *c) {
@ -1133,6 +1184,8 @@ private:
/// }
const InputSectionsCmd *parseInputSectionsCmd();
const FillCmd *parseFillCmd();
/// Parse output section description statements.
/// Example:
///
@ -1145,6 +1198,10 @@ private:
/// Stub for parsing overlay commands. Currently unimplemented.
const Overlay *parseOverlay();
const PHDR *parsePHDR();
PHDRS *parsePHDRS();
/// Parse the SECTIONS linker script command.
/// Example:
///
@ -1231,7 +1288,7 @@ public:
/// Prepare our data structures according to the linker scripts currently in
/// our control (control given via addLinkerScript()). Called once all linker
/// scripts have been parsed.
void perform();
std::error_code perform();
/// Answer if we have layout commands (section mapping rules). If we don't,
/// the output file writer can assume there is no linker script special rule
@ -1273,6 +1330,15 @@ public:
/// has been performed (by calling evalExpr() for all expressions).
uint64_t getLinkerScriptExprValue(StringRef name) const;
/// Check if there are custom headers available.
bool hasPHDRs() const;
/// Retrieve all the headers the given output section is assigned to.
std::vector<const PHDR *> getPHDRsForOutputSection(StringRef name) const;
/// Retrieve program header if available.
const PHDR *getProgramPHDR() const;
void dump() const;
private:
@ -1314,6 +1380,14 @@ private:
bool localCompare(int order, const SectionKey &lhs,
const SectionKey &rhs) const;
/// Convert the PHDRS command into map of names to headers.
/// Determine program header during processing.
std::error_code collectPHDRs(const PHDRS *ph,
llvm::StringMap<const PHDR *> &phdrs);
/// Build map that matches output section names to segments they should be
/// put into.
std::error_code buildSectionToPHDR(llvm::StringMap<const PHDR *> &phdrs);
/// Our goal with all linearizeAST overloaded functions is to
/// traverse the linker script AST while putting nodes in a vector and
@ -1370,8 +1444,6 @@ private:
void linearizeAST(const InputSectionsCmd *inputSections);
void linearizeAST(const InputSection *inputSection);
void perform(const LinkerScript *ls);
std::vector<std::unique_ptr<Parser>> _scripts;
std::vector<const Command *> _layoutCommands;
std::unordered_multimap<std::string, int> _memberToLayoutOrder;
@ -1381,6 +1453,9 @@ private:
llvm::DenseSet<int> _deliveredExprs;
mutable llvm::StringSet<> _definedSymbols;
llvm::StringMap<llvm::SmallVector<const PHDR *, 2>> _sectionToPHDR;
const PHDR *_programPHDR;
Expression::SymbolTableTy _symbolTable;
};

View file

@ -27,12 +27,13 @@ namespace mach_o {
class ArchHandler;
class MachODylibFile;
class MachOFile;
class SectCreateFile;
}
class MachOLinkingContext : public LinkingContext {
public:
MachOLinkingContext();
~MachOLinkingContext();
~MachOLinkingContext() override;
enum Arch {
arch_unknown,
@ -63,6 +64,13 @@ public:
noDebugMap // -S option
};
enum class UndefinedMode {
error,
warning,
suppress,
dynamicLookup
};
/// Initializes the context to sane default values given the specified output
/// file type, arch, os, and minimum os version. This should be called before
/// other setXXX() methods.
@ -72,7 +80,7 @@ public:
bool validateImpl(raw_ostream &diagnostics) override;
std::string demangle(StringRef symbolName) const override;
bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override;
void createImplicitFiles(std::vector<std::unique_ptr<File>> &) override;
uint32_t getCPUType() const;
uint32_t getCPUSubType() const;
@ -129,6 +137,9 @@ public:
bool PIE() const { return _pie; }
void setPIE(bool pie) { _pie = pie; }
uint64_t stackSize() const { return _stackSize; }
void setStackSize(uint64_t stackSize) { _stackSize = stackSize; }
uint64_t baseAddress() const { return _baseAddress; }
void setBaseAddress(uint64_t baseAddress) { _baseAddress = baseAddress; }
@ -201,6 +212,30 @@ public:
/// when linking a binary that does not use any of its symbols.
bool deadStrippableDylib() const { return _deadStrippableDylib; }
/// \brief Whether or not to use flat namespace.
///
/// MachO usually uses a two-level namespace, where each external symbol
/// referenced by the target is associated with the dylib that will provide
/// the symbol's definition at runtime. Using flat namespace overrides this
/// behavior: the linker searches all dylibs on the command line and all
/// dylibs those original dylibs depend on, but does not record which dylib
/// an external symbol came from. At runtime dyld again searches all images
/// and uses the first definition it finds. In addition, any undefines in
/// loaded flat_namespace dylibs must be resolvable at build time.
bool useFlatNamespace() const { return _flatNamespace; }
/// \brief How to handle undefined symbols.
///
/// Options are:
/// * error: Report an error and terminate linking.
/// * warning: Report a warning, but continue linking.
/// * suppress: Ignore and continue linking.
/// * dynamic_lookup: For use with -twolevel namespace: Records source dylibs
/// for symbols that are defined in a linked dylib at static link time.
/// Undefined symbols are handled by searching all loaded images at
/// runtime.
UndefinedMode undefinedMode() const { return _undefinedMode; }
/// \brief The path to the executable that will load the bundle at runtime.
///
/// When building a Mach-O bundle, this executable will be examined if there
@ -215,6 +250,14 @@ public:
void setDeadStrippableDylib(bool deadStrippable) {
_deadStrippableDylib = deadStrippable;
}
void setUseFlatNamespace(bool flatNamespace) {
_flatNamespace = flatNamespace;
}
void setUndefinedMode(UndefinedMode undefinedMode) {
_undefinedMode = undefinedMode;
}
void setBundleLoader(StringRef loader) { _bundleLoader = loader; }
void setPrintAtoms(bool value=true) { _printAtoms = value; }
void setTestingFileUsage(bool value = true) {
@ -228,10 +271,14 @@ public:
const StringRefVector &rpaths() const { return _rpaths; }
/// Add section alignment constraint on final layout.
void addSectionAlignment(StringRef seg, StringRef sect, uint8_t align2);
void addSectionAlignment(StringRef seg, StringRef sect, uint16_t align);
/// \brief Add a section based on a command-line sectcreate option.
void addSectCreateSection(StringRef seg, StringRef sect,
std::unique_ptr<MemoryBuffer> content);
/// Returns true if specified section had alignment constraints.
bool sectionAligned(StringRef seg, StringRef sect, uint8_t &align2) const;
bool sectionAligned(StringRef seg, StringRef sect, uint16_t &align) const;
StringRef dyldPath() const { return "/usr/lib/dyld"; }
@ -241,6 +288,9 @@ public:
// GOT creation Pass should be run.
bool needsGOTPass() const;
/// Pass to add TLV sections.
bool needsTLVPass() const;
/// Pass to transform __compact_unwind into __unwind_info should be run.
bool needsCompactUnwindPass() const;
@ -271,8 +321,7 @@ public:
/// If the memoryBuffer is a fat file with a slice for the current arch,
/// this method will return the offset and size of that slice.
bool sliceFromFatFile(const MemoryBuffer &mb, uint32_t &offset,
uint32_t &size);
bool sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset, uint32_t &size);
/// Returns if a command line option specified dylib is an upward link.
bool isUpwardDylib(StringRef installName) const;
@ -296,6 +345,11 @@ public:
bool customAtomOrderer(const DefinedAtom *left, const DefinedAtom *right,
bool &leftBeforeRight) const;
/// Return the 'flat namespace' file. This is the file that supplies
/// atoms for otherwise undefined symbols when the -flat_namespace or
/// -undefined dynamic_lookup options are used.
File* flatNamespaceFile() const { return _flatNamespaceFile; }
private:
Writer &writer() const override;
mach_o::MachODylibFile* loadIndirectDylib(StringRef path);
@ -312,7 +366,7 @@ private:
struct SectionAlign {
StringRef segmentName;
StringRef sectionName;
uint8_t align2;
uint16_t align;
};
struct OrderFileNode {
@ -339,10 +393,13 @@ private:
uint64_t _pageZeroSize;
uint64_t _pageSize;
uint64_t _baseAddress;
uint64_t _stackSize;
uint32_t _compatibilityVersion;
uint32_t _currentVersion;
StringRef _installName;
StringRefVector _rpaths;
bool _flatNamespace;
UndefinedMode _undefinedMode;
bool _deadStrippableDylib;
bool _printAtoms;
bool _testingFileUsage;
@ -356,14 +413,17 @@ private:
mutable std::set<mach_o::MachODylibFile*> _allDylibs;
mutable std::set<mach_o::MachODylibFile*> _upwardDylibs;
mutable std::vector<std::unique_ptr<File>> _indirectDylibs;
mutable std::mutex _dylibsMutex;
ExportMode _exportMode;
llvm::StringSet<> _exportedSymbols;
DebugInfoMode _debugInfoMode;
std::unique_ptr<llvm::raw_fd_ostream> _dependencyInfo;
llvm::StringMap<std::vector<OrderFileNode>> _orderFiles;
unsigned _orderFileEntries;
File *_flatNamespaceFile;
mach_o::SectCreateFile *_sectCreateFile = nullptr;
};
} // end namespace lld
#endif
#endif // LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H

View file

@ -1,463 +0,0 @@
//===- lld/ReaderWriter/PECOFFLinkingContext.h ----------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_PECOFF_LINKING_CONTEXT_H
#define LLD_READER_WRITER_PECOFF_LINKING_CONTEXT_H
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Reader.h"
#include "lld/Core/Writer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/COFF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileUtilities.h"
#include <map>
#include <mutex>
#include <set>
#include <vector>
using llvm::COFF::MachineTypes;
using llvm::COFF::WindowsSubsystem;
static const uint8_t DEFAULT_DOS_STUB[128] = {'M', 'Z'};
namespace lld {
class PECOFFLinkingContext : public LinkingContext {
public:
PECOFFLinkingContext()
: _mutex(), _allocMutex(), _hasEntry(true),
_baseAddress(invalidBaseAddress), _stackReserve(1024 * 1024),
_stackCommit(4096), _heapReserve(1024 * 1024), _heapCommit(4096),
_noDefaultLibAll(false), _sectionDefaultAlignment(4096),
_subsystem(llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN),
_machineType(llvm::COFF::IMAGE_FILE_MACHINE_I386), _imageVersion(0, 0),
_minOSVersion(6, 0), _nxCompat(true), _largeAddressAware(false),
_allowBind(true), _allowIsolation(true), _swapRunFromCD(false),
_swapRunFromNet(false), _baseRelocationEnabled(true),
_terminalServerAware(true), _dynamicBaseEnabled(true),
_createManifest(true), _embedManifest(false), _manifestId(1),
_manifestUAC(true), _manifestLevel("'asInvoker'"),
_manifestUiAccess("'false'"), _isDll(false), _highEntropyVA(true),
_requireSEH(false), _noSEH(false), _implib(""), _debug(false),
_pdbFilePath(""), _dosStub(llvm::makeArrayRef(DEFAULT_DOS_STUB)),
_parseDirectives(nullptr) {
setDeadStripping(true);
}
struct Version {
Version(int v1, int v2) : majorVersion(v1), minorVersion(v2) {}
int majorVersion;
int minorVersion;
};
struct ExportDesc {
ExportDesc()
: ordinal(-1), noname(false), isData(false), isPrivate(false) {}
bool operator<(const ExportDesc &other) const {
return getExternalName().compare(other.getExternalName()) < 0;
}
StringRef getRealName() const {
return mangledName.empty() ? name : mangledName;
}
StringRef getExternalName() const {
return externalName.empty() ? name : externalName;
}
std::string name;
std::string externalName;
std::string mangledName;
int ordinal;
bool noname;
bool isData;
bool isPrivate;
};
typedef bool (*ParseDirectives)(int, const char **, PECOFFLinkingContext &,
raw_ostream &);
/// \brief Casting support
static bool classof(const LinkingContext *info) { return true; }
Writer &writer() const override;
bool validateImpl(raw_ostream &diagnostics) override;
void addPasses(PassManager &pm) override;
bool createImplicitFiles(
std::vector<std::unique_ptr<File> > &result) override;
bool is64Bit() const {
return _machineType == llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
}
// Returns a set of all defined symbols in input files.
const std::set<std::string> &definedSymbols();
/// Page size of x86 processor. Some data needs to be aligned at page boundary
/// when loaded into memory.
uint64_t getPageSize() const {
return 0x1000;
}
void appendInputSearchPath(StringRef dirPath) {
_inputSearchPaths.push_back(dirPath);
}
const std::vector<StringRef> getInputSearchPaths() {
return _inputSearchPaths;
}
void registerTemporaryFile(StringRef path) {
std::unique_ptr<llvm::FileRemover> fileRemover(
new llvm::FileRemover(Twine(allocate(path))));
_tempFiles.push_back(std::move(fileRemover));
}
StringRef searchLibraryFile(StringRef path) const;
StringRef decorateSymbol(StringRef name) const;
StringRef undecorateSymbol(StringRef name) const;
void setEntrySymbolName(StringRef name) { _entry = name; }
StringRef getEntrySymbolName() const { return _entry; }
void setHasEntry(bool val) { _hasEntry = val; }
bool hasEntry() const { return _hasEntry; }
void setBaseAddress(uint64_t addr) { _baseAddress = addr; }
uint64_t getBaseAddress() const;
void setStackReserve(uint64_t size) { _stackReserve = size; }
void setStackCommit(uint64_t size) { _stackCommit = size; }
uint64_t getStackReserve() const { return _stackReserve; }
uint64_t getStackCommit() const { return _stackCommit; }
void setHeapReserve(uint64_t size) { _heapReserve = size; }
void setHeapCommit(uint64_t size) { _heapCommit = size; }
uint64_t getHeapReserve() const { return _heapReserve; }
uint64_t getHeapCommit() const { return _heapCommit; }
void setSectionDefaultAlignment(uint32_t val) {
_sectionDefaultAlignment = val;
}
uint32_t getSectionDefaultAlignment() const {
return _sectionDefaultAlignment;
}
void setSubsystem(WindowsSubsystem ss) { _subsystem = ss; }
WindowsSubsystem getSubsystem() const { return _subsystem; }
void setMachineType(MachineTypes type) { _machineType = type; }
MachineTypes getMachineType() const { return _machineType; }
void setImageVersion(const Version &version) { _imageVersion = version; }
Version getImageVersion() const { return _imageVersion; }
void setMinOSVersion(const Version &version) { _minOSVersion = version; }
Version getMinOSVersion() const { return _minOSVersion; }
void setNxCompat(bool nxCompat) { _nxCompat = nxCompat; }
bool isNxCompat() const { return _nxCompat; }
void setLargeAddressAware(bool val) { _largeAddressAware = val; }
bool getLargeAddressAware() const { return _largeAddressAware; }
void setAllowBind(bool val) { _allowBind = val; }
bool getAllowBind() const { return _allowBind; }
void setAllowIsolation(bool val) { _allowIsolation = val; }
bool getAllowIsolation() const { return _allowIsolation; }
void setSwapRunFromCD(bool val) { _swapRunFromCD = val; }
bool getSwapRunFromCD() const { return _swapRunFromCD; }
void setSwapRunFromNet(bool val) { _swapRunFromNet = val; }
bool getSwapRunFromNet() const { return _swapRunFromNet; }
void setBaseRelocationEnabled(bool val) { _baseRelocationEnabled = val; }
bool getBaseRelocationEnabled() const { return _baseRelocationEnabled; }
void setTerminalServerAware(bool val) { _terminalServerAware = val; }
bool isTerminalServerAware() const { return _terminalServerAware; }
void setDynamicBaseEnabled(bool val) { _dynamicBaseEnabled = val; }
bool getDynamicBaseEnabled() const { return _dynamicBaseEnabled; }
void setCreateManifest(bool val) { _createManifest = val; }
bool getCreateManifest() const { return _createManifest; }
void setManifestOutputPath(std::string val) { _manifestOutputPath = val; }
const std::string &getManifestOutputPath() const {
return _manifestOutputPath;
}
void setEmbedManifest(bool val) { _embedManifest = val; }
bool getEmbedManifest() const { return _embedManifest; }
void setManifestId(int val) { _manifestId = val; }
int getManifestId() const { return _manifestId; }
void setManifestUAC(bool val) { _manifestUAC = val; }
bool getManifestUAC() const { return _manifestUAC; }
void setManifestLevel(std::string val) { _manifestLevel = std::move(val); }
const std::string &getManifestLevel() const { return _manifestLevel; }
void setManifestUiAccess(std::string val) { _manifestUiAccess = val; }
const std::string &getManifestUiAccess() const { return _manifestUiAccess; }
void setManifestDependency(std::string val) { _manifestDependency = val; }
const std::string &getManifestDependency() const {
return _manifestDependency;
}
void setIsDll(bool val) { _isDll = val; }
bool isDll() const { return _isDll; }
void setSafeSEH(bool val) {
if (val)
_requireSEH = true;
else
_noSEH = true;
}
bool requireSEH() const { return _requireSEH; }
bool noSEH() const { return _noSEH; }
void setHighEntropyVA(bool val) { _highEntropyVA = val; }
bool getHighEntropyVA() const { return _highEntropyVA; }
void setOutputImportLibraryPath(const std::string &val) { _implib = val; }
std::string getOutputImportLibraryPath() const;
void setDebug(bool val) { _debug = val; }
bool getDebug() { return _debug; }
void setPDBFilePath(StringRef str) { _pdbFilePath = str; }
std::string getPDBFilePath() const;
void addDelayLoadDLL(StringRef dll) {
_delayLoadDLLs.insert(dll.lower());
}
bool isDelayLoadDLL(StringRef dll) const {
return _delayLoadDLLs.count(dll.lower()) == 1;
}
StringRef getOutputSectionName(StringRef sectionName) const;
bool addSectionRenaming(raw_ostream &diagnostics,
StringRef from, StringRef to);
const std::set<std::string> &getAlternateNames(StringRef name) {
return _alternateNames[name];
}
void addAlternateName(StringRef weak, StringRef def) {
_alternateNames[def].insert(weak);
}
void addNoDefaultLib(StringRef path) {
if (path.endswith_lower(".lib"))
_noDefaultLibs.insert(path.drop_back(4).lower());
else
_noDefaultLibs.insert(path.lower());
}
bool hasNoDefaultLib(StringRef path) const {
if (path.endswith_lower(".lib"))
return _noDefaultLibs.count(path.drop_back(4).lower()) > 0;
return _noDefaultLibs.count(path.lower()) > 0;
}
void setNoDefaultLibAll(bool val) { _noDefaultLibAll = val; }
bool getNoDefaultLibAll() const { return _noDefaultLibAll; }
void setSectionSetMask(StringRef sectionName, uint32_t flags);
void setSectionClearMask(StringRef sectionName, uint32_t flags);
uint32_t getSectionAttributes(StringRef sectionName, uint32_t flags) const;
void setDosStub(ArrayRef<uint8_t> data) { _dosStub = data; }
ArrayRef<uint8_t> getDosStub() const { return _dosStub; }
void addDllExport(ExportDesc &desc);
std::vector<ExportDesc> &getDllExports() { return _dllExports; }
const std::vector<ExportDesc> &getDllExports() const { return _dllExports; }
StringRef getDelayLoadHelperName() const {
return is64Bit() ? "__delayLoadHelper2" : "___delayLoadHelper2@8";
}
StringRef allocate(StringRef ref) const {
_allocMutex.lock();
char *x = _allocator.Allocate<char>(ref.size() + 1);
_allocMutex.unlock();
memcpy(x, ref.data(), ref.size());
x[ref.size()] = '\0';
return x;
}
ArrayRef<uint8_t> allocate(ArrayRef<uint8_t> array) const {
size_t size = array.size();
_allocMutex.lock();
uint8_t *p = _allocator.Allocate<uint8_t>(size);
_allocMutex.unlock();
memcpy(p, array.data(), size);
return ArrayRef<uint8_t>(p, p + array.size());
}
template <typename T> T &allocateCopy(const T &x) const {
_allocMutex.lock();
T *r = new (_allocator) T(x);
_allocMutex.unlock();
return *r;
}
void addLibraryFile(std::unique_ptr<FileNode> file);
void setModuleDefinitionFile(const std::string val) {
_moduleDefinitionFile = val;
}
std::string getModuleDefinitionFile() const {
return _moduleDefinitionFile;
}
std::recursive_mutex &getMutex() { return _mutex; }
void setParseDirectives(ParseDirectives parseDirectives) {
_parseDirectives = parseDirectives;
}
ParseDirectives getParseDirectives() {
return _parseDirectives;
}
protected:
/// Method to create a internal file for the entry symbol
std::unique_ptr<File> createEntrySymbolFile() const override;
/// Method to create a internal file for an undefined symbol
std::unique_ptr<File> createUndefinedSymbolFile() const override;
private:
enum : uint64_t {
invalidBaseAddress = UINT64_MAX,
pe32DefaultBaseAddress = 0x400000U,
pe32PlusDefaultBaseAddress = 0x140000000U
};
std::recursive_mutex _mutex;
mutable std::mutex _allocMutex;
std::string _entry;
// False if /noentry option is given.
bool _hasEntry;
// The start address for the program. The default value for the executable is
// 0x400000, but can be altered using /base command line option.
uint64_t _baseAddress;
uint64_t _stackReserve;
uint64_t _stackCommit;
uint64_t _heapReserve;
uint64_t _heapCommit;
bool _noDefaultLibAll;
uint32_t _sectionDefaultAlignment;
WindowsSubsystem _subsystem;
MachineTypes _machineType;
Version _imageVersion;
Version _minOSVersion;
bool _nxCompat;
bool _largeAddressAware;
bool _allowBind;
bool _allowIsolation;
bool _swapRunFromCD;
bool _swapRunFromNet;
bool _baseRelocationEnabled;
bool _terminalServerAware;
bool _dynamicBaseEnabled;
bool _createManifest;
std::string _manifestOutputPath;
bool _embedManifest;
int _manifestId;
bool _manifestUAC;
std::string _manifestLevel;
std::string _manifestUiAccess;
std::string _manifestDependency;
bool _isDll;
bool _highEntropyVA;
// True if /SAFESEH option is specified. Valid only for x86. If true, LLD will
// produce an image with SEH table. If any modules were not compatible with
// SEH, LLD will exit with an error.
bool _requireSEH;
// True if /SAFESEH:no option is specified. Valid only for x86. If true, LLD
// will not produce an image with SEH table even if all input object files are
// compatible with SEH.
bool _noSEH;
// /IMPLIB command line option.
std::string _implib;
// True if /DEBUG is given.
bool _debug;
// PDB file output path. NB: this is dummy -- LLD just creates the empty file.
std::string _pdbFilePath;
// /DELAYLOAD option.
std::set<std::string> _delayLoadDLLs;
// The set to store /nodefaultlib arguments.
std::set<std::string> _noDefaultLibs;
std::vector<StringRef> _inputSearchPaths;
std::unique_ptr<Writer> _writer;
// A map for weak aliases.
std::map<std::string, std::set<std::string>> _alternateNames;
// A map for section renaming. For example, if there is an entry in the map
// whose value is .rdata -> .text, the section contens of .rdata will be
// merged to .text in the resulting executable.
std::map<std::string, std::string> _renamedSections;
// Section attributes specified by /section option.
std::map<std::string, uint32_t> _sectionSetMask;
std::map<std::string, uint32_t> _sectionClearMask;
// DLLExport'ed symbols.
std::vector<ExportDesc> _dllExports;
// List of files that will be removed on destruction.
std::vector<std::unique_ptr<llvm::FileRemover> > _tempFiles;
// DOS Stub. DOS stub is data located at the beginning of PE/COFF file.
// Windows loader do not really care about DOS stub contents, but it's usually
// a small DOS program that prints out a message "This program requires
// Microsoft Windows." This feature was somewhat useful before Windows 95.
ArrayRef<uint8_t> _dosStub;
// Name of the temporary file for lib.exe subcommand. For debugging
// only.
std::string _moduleDefinitionFile;
std::set<std::string> _definedSyms;
std::set<Node *> _seen;
ParseDirectives _parseDirectives;
};
} // end namespace lld
#endif

View file

@ -1,57 +0,0 @@
//===- lld/ReaderWriter/RelocationHelperFunctions.h------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_RELOCATION_HELPER_FUNCTIONS_H
#define LLD_READER_WRITER_RELOCATION_HELPER_FUNCTIONS_H
namespace lld {
/// Gather val's bits as specified by the mask. Example:
///
/// Val: 0bABCDEFGHIJKLMN
/// Mask: 0b10111100001011
/// Output: 0b000000ACDEFKMN
template <typename T> T gatherBits(T val, T mask) {
T result = 0;
size_t off = 0;
for (size_t bit = 0; bit < sizeof(T) * 8; ++bit) {
bool maskBit = (mask >> bit) & 1;
if (maskBit) {
bool valBit = (val >> bit) & 1;
result |= static_cast<T>(valBit) << off;
++off;
}
}
return result;
}
/// Scatter val's bits as specified by the mask. Example:
///
/// Val: 0bABCDEFG
/// Mask: 0b10111100001011
/// Output: 0b00ABCD0000E0FG
template <typename T> T scatterBits(T val, T mask) {
T result = 0;
size_t off = 0;
for (size_t bit = 0; bit < sizeof(T) * 8; ++bit) {
bool maskBit = (mask >> bit) & 1;
if (maskBit) {
bool valBit = (val >> off) & 1;
result |= static_cast<T>(valBit) << bit;
++off;
}
}
return result;
}
} // namespace lld
#endif // LLD_READER_WRITER_RELOCATION_HELPER_FUNCTIONS_H

View file

@ -30,14 +30,10 @@ using lld::mach_o::normalized::NormalizedFile;
/// object. We need to support hetergenous yaml documents which each require
/// different context info. This struct supports all clients.
struct YamlContext {
YamlContext()
: _linkingContext(nullptr), _registry(nullptr), _file(nullptr),
_normalizeMachOFile(nullptr) {}
const LinkingContext *_linkingContext;
const Registry *_registry;
File *_file;
NormalizedFile *_normalizeMachOFile;
const LinkingContext *_ctx = nullptr;
const Registry *_registry = nullptr;
File *_file = nullptr;
NormalizedFile *_normalizeMachOFile = nullptr;
StringRef _path;
};

View file

@ -1,13 +0,0 @@
##===- lib/Config/Makefile ---------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
LLD_LEVEL := ../..
LIBRARYNAME := lldConfig
include $(LLD_LEVEL)/Makefile

View file

@ -76,6 +76,7 @@ DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) {
case typeGnuLinkOnce:
case typeUnknown:
case typeTempLTO:
case typeSectCreate:
return permUnknown;
}
llvm_unreachable("unknown content type");

View file

@ -16,39 +16,6 @@
using namespace lld;
class _NativeReaderErrorCategory : public std::error_category {
public:
const char* name() const LLVM_NOEXCEPT override {
return "lld.native.reader";
}
std::string message(int ev) const override {
switch (static_cast<NativeReaderError>(ev)) {
case NativeReaderError::success:
return "Success";
case NativeReaderError::unknown_file_format:
return "Unknown file format";
case NativeReaderError::file_too_short:
return "file truncated";
case NativeReaderError::file_malformed:
return "file malformed";
case NativeReaderError::memory_error:
return "out of memory";
case NativeReaderError::unknown_chunk_type:
return "unknown chunk type";
case NativeReaderError::conflicting_target_machine:
return "conflicting target machine";
}
llvm_unreachable("An enumerator of NativeReaderError does not have a "
"message defined.");
}
};
const std::error_category &lld::native_reader_category() {
static _NativeReaderErrorCategory o;
return o;
}
class _YamlReaderErrorCategory : public std::error_category {
public:
const char* name() const LLVM_NOEXCEPT override {
@ -57,8 +24,6 @@ public:
std::string message(int ev) const override {
switch (static_cast<YamlReaderError>(ev)) {
case YamlReaderError::success:
return "Success";
case YamlReaderError::unknown_keyword:
return "Unknown keyword found in yaml file";
case YamlReaderError::illegal_value:
@ -91,6 +56,14 @@ public:
case LinkerScriptReaderError::unrecognized_function_in_expr:
return "Unrecognized function call when evaluating linker script "
"expression";
case LinkerScriptReaderError::unknown_phdr_ids:
return "Unknown header identifiers (missing in PHDRS command) are used";
case LinkerScriptReaderError::extra_program_phdr:
return "Extra program header is found";
case LinkerScriptReaderError::misplaced_program_phdr:
return "Program header must precede load segments";
case LinkerScriptReaderError::program_phdr_wrong_phdrs:
return "Program header has invalid PHDRS attribute";
}
llvm_unreachable("An enumerator of LinkerScriptReaderError does not have a "
"message defined.");
@ -102,7 +75,6 @@ const std::error_category &lld::LinkerScriptReaderCategory() {
return o;
}
namespace lld {
/// Temporary class to enable make_dynamic_error_code() until
@ -110,7 +82,7 @@ namespace lld {
/// other than error_code.
class dynamic_error_category : public std::error_category {
public:
~dynamic_error_category() LLVM_NOEXCEPT {}
~dynamic_error_category() override = default;
const char *name() const LLVM_NOEXCEPT override {
return "lld.dynamic_error";
@ -140,6 +112,10 @@ private:
static dynamic_error_category categorySingleton;
std::error_code make_dynamic_error_code(const char *msg) {
return make_dynamic_error_code(StringRef(msg));
}
std::error_code make_dynamic_error_code(StringRef msg) {
return std::error_code(categorySingleton.add(msg), categorySingleton);
}
@ -148,4 +124,4 @@ std::error_code make_dynamic_error_code(const Twine &msg) {
return std::error_code(categorySingleton.add(msg.str()), categorySingleton);
}
}
} // namespace lld

View file

@ -15,10 +15,10 @@ namespace lld {
File::~File() {}
File::atom_collection_empty<DefinedAtom> File::_noDefinedAtoms;
File::atom_collection_empty<UndefinedAtom> File::_noUndefinedAtoms;
File::atom_collection_empty<SharedLibraryAtom> File::_noSharedLibraryAtoms;
File::atom_collection_empty<AbsoluteAtom> File::_noAbsoluteAtoms;
File::AtomVector<DefinedAtom> File::_noDefinedAtoms;
File::AtomVector<UndefinedAtom> File::_noUndefinedAtoms;
File::AtomVector<SharedLibraryAtom> File::_noSharedLibraryAtoms;
File::AtomVector<AbsoluteAtom> File::_noAbsoluteAtoms;
std::error_code File::parse() {
std::lock_guard<std::mutex> lock(_parseMutex);

View file

@ -24,7 +24,7 @@ LinkingContext::LinkingContext()
_warnIfCoalesableAtomsHaveDifferentCanBeNull(false),
_warnIfCoalesableAtomsHaveDifferentLoadName(false),
_printRemainingUndefines(true), _allowRemainingUndefines(false),
_logInputFiles(false), _allowShlibUndefines(false),
_logInputFiles(false), _allowShlibUndefines(true),
_outputFileType(OutputFileType::Default), _nextOrdinal(0) {}
LinkingContext::~LinkingContext() {}
@ -37,9 +37,9 @@ std::error_code LinkingContext::writeFile(const File &linkedFile) const {
return this->writer().writeFile(linkedFile, _outputPath);
}
bool LinkingContext::createImplicitFiles(
std::vector<std::unique_ptr<File> > &result) {
return this->writer().createImplicitFiles(result);
void LinkingContext::createImplicitFiles(
std::vector<std::unique_ptr<File>> &result) {
this->writer().createImplicitFiles(result);
}
std::unique_ptr<File> LinkingContext::createEntrySymbolFile() const {

View file

@ -1,13 +0,0 @@
##===- lld/lib/Core/Makefile ---------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
LLD_LEVEL := ../..
LIBRARYNAME := lldCore
include $(LLD_LEVEL)/Makefile

View file

@ -13,7 +13,6 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include <memory>
#include <system_error>
@ -29,22 +28,17 @@ void Registry::add(std::unique_ptr<YamlIOTaggedDocumentHandler> handler) {
_yamlHandlers.push_back(std::move(handler));
}
std::error_code
Registry::loadFile(std::unique_ptr<MemoryBuffer> mb,
std::vector<std::unique_ptr<File>> &result) const {
// Get file type.
ErrorOr<std::unique_ptr<File>>
Registry::loadFile(std::unique_ptr<MemoryBuffer> mb) const {
// Get file magic.
StringRef content(mb->getBufferStart(), mb->getBufferSize());
llvm::sys::fs::file_magic fileType = llvm::sys::fs::identify_magic(content);
// Get file extension.
StringRef extension = llvm::sys::path::extension(mb->getBufferIdentifier());
// Ask each registered reader if it can handle this file type or extension.
for (const std::unique_ptr<Reader> &reader : _readers) {
if (!reader->canParse(fileType, extension, *mb))
if (!reader->canParse(fileType, mb->getMemBufferRef()))
continue;
if (std::error_code ec = reader->loadFile(std::move(mb), *this, result))
return ec;
return std::error_code();
return reader->loadFile(std::move(mb), *this);
}
// No Reader could parse this file.

View file

@ -153,7 +153,6 @@ void Resolver::maybeAddSectionGroupOrGnuLinkOnce(const DefinedAtom &atom) {
llvm::errs() << "SymbolTable: error while merging " << atom.name()
<< "\n";
llvm::report_fatal_error("duplicate symbol error");
return;
}
for (const Reference *r : atom) {
@ -180,6 +179,8 @@ void Resolver::doDefinedAtom(const DefinedAtom &atom) {
<< atom.ordinal()
<< ", name="
<< atom.name()
<< ", type="
<< atom.contentType()
<< "\n");
// add to list of known atoms
@ -295,11 +296,15 @@ void Resolver::updatePreloadArchiveMap() {
// Keep adding atoms until _ctx.getNextFile() returns an error. This
// function is where undefined atoms are resolved.
bool Resolver::resolveUndefines() {
DEBUG_WITH_TYPE("resolver",
llvm::dbgs() << "******** Resolving undefines:\n");
ScopedTask task(getDefaultDomain(), "resolveUndefines");
int index = 0;
std::set<File *> seen;
for (;;) {
bool undefAdded = false;
DEBUG_WITH_TYPE("resolver",
llvm::dbgs() << "Loading file #" << index << "\n");
File *file = getFile(index);
if (!file)
return true;
@ -308,6 +313,8 @@ bool Resolver::resolveUndefines() {
<< ": " << ec.message() << "\n";
return false;
}
DEBUG_WITH_TYPE("resolver",
llvm::dbgs() << "Loaded file: " << file->path() << "\n");
file->beforeLink();
updatePreloadArchiveMap();
switch (file->kind()) {
@ -340,6 +347,8 @@ bool Resolver::resolveUndefines() {
// switch all references to undefined or coalesced away atoms
// to the new defined atom
void Resolver::updateReferences() {
DEBUG_WITH_TYPE("resolver",
llvm::dbgs() << "******** Updating references:\n");
ScopedTask task(getDefaultDomain(), "updateReferences");
for (const Atom *atom : _atoms) {
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
@ -388,6 +397,8 @@ static bool isBackref(const Reference *ref) {
// remove all atoms not actually used
void Resolver::deadStripOptimize() {
DEBUG_WITH_TYPE("resolver",
llvm::dbgs() << "******** Dead stripping unused atoms:\n");
ScopedTask task(getDefaultDomain(), "deadStripOptimize");
// only do this optimization with -dead_strip
if (!_ctx.deadStrip())
@ -433,6 +444,9 @@ void Resolver::deadStripOptimize() {
// error out if some undefines remain
bool Resolver::checkUndefines() {
DEBUG_WITH_TYPE("resolver",
llvm::dbgs() << "******** Checking for undefines:\n");
// build vector of remaining undefined symbols
std::vector<const UndefinedAtom *> undefinedAtoms = _symbolTable.undefines();
if (_ctx.deadStrip()) {
@ -479,6 +493,8 @@ bool Resolver::checkUndefines() {
// remove from _atoms all coaleseced away atoms
void Resolver::removeCoalescedAwayAtoms() {
DEBUG_WITH_TYPE("resolver",
llvm::dbgs() << "******** Removing coalesced away atoms:\n");
ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms");
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), [&](const Atom *a) {
return _symbolTable.isCoalescedAway(a) || _deadAtoms.count(a);
@ -487,28 +503,53 @@ void Resolver::removeCoalescedAwayAtoms() {
}
bool Resolver::resolve() {
DEBUG_WITH_TYPE("resolver",
llvm::dbgs() << "******** Resolving atom references:\n");
updatePreloadArchiveMap();
if (!resolveUndefines())
return false;
updateReferences();
deadStripOptimize();
if (checkUndefines())
if (!_ctx.allowRemainingUndefines())
if (checkUndefines()) {
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Found undefines... ");
if (!_ctx.allowRemainingUndefines()) {
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we don't allow\n");
return false;
}
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "which we are ok with\n");
}
removeCoalescedAwayAtoms();
_result->addAtoms(_atoms);
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "******** Finished resolver\n");
return true;
}
void Resolver::MergedFile::addAtoms(std::vector<const Atom *> &all) {
ScopedTask task(getDefaultDomain(), "addAtoms");
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n");
for (const Atom *atom : all) {
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
<< llvm::format(" 0x%09lX", atom)
<< ", name="
<< atom->name()
<< "\n");
#ifndef NDEBUG
if (auto *definedAtom = dyn_cast<DefinedAtom>(atom)) {
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
<< llvm::format(" 0x%09lX", atom)
<< ", file=#"
<< definedAtom->file().ordinal()
<< ", atom=#"
<< definedAtom->ordinal()
<< ", name="
<< definedAtom->name()
<< ", type="
<< definedAtom->contentType()
<< "\n");
} else {
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
<< llvm::format(" 0x%09lX", atom)
<< ", name="
<< atom->name()
<< "\n");
}
#endif
addAtom(*atom);
}
}

View file

@ -28,7 +28,7 @@
#include <vector>
namespace lld {
SymbolTable::SymbolTable(LinkingContext &context) : _context(context) {}
SymbolTable::SymbolTable(LinkingContext &context) : _ctx(context) {}
bool SymbolTable::add(const UndefinedAtom &atom) { return addByName(atom); }
@ -185,7 +185,7 @@ bool SymbolTable::addByName(const Atom &newAtom) {
// fallthrough
}
case MCR_Error:
if (!_context.getAllowDuplicates()) {
if (!_ctx.getAllowDuplicates()) {
llvm::errs() << "Duplicate symbols: "
<< existing->name()
<< ":"
@ -207,8 +207,7 @@ bool SymbolTable::addByName(const Atom &newAtom) {
const UndefinedAtom* newUndef = cast<UndefinedAtom>(&newAtom);
bool sameCanBeNull = (existingUndef->canBeNull() == newUndef->canBeNull());
if (!sameCanBeNull &&
_context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
if (!sameCanBeNull && _ctx.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
llvm::errs() << "lld warning: undefined symbol "
<< existingUndef->name()
<< " has different weakness in "
@ -244,14 +243,14 @@ bool SymbolTable::addByName(const Atom &newAtom) {
(curShLib->canBeNullAtRuntime() == newShLib->canBeNullAtRuntime());
bool sameName = curShLib->loadName().equals(newShLib->loadName());
if (sameName && !sameNullness &&
_context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
_ctx.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
// FIXME: need diagonstics interface for writing warning messages
llvm::errs() << "lld warning: shared library symbol "
<< curShLib->name() << " has different weakness in "
<< curShLib->file().path() << " and in "
<< newShLib->file().path();
}
if (!sameName && _context.warnIfCoalesableAtomsHaveDifferentLoadName()) {
if (!sameName && _ctx.warnIfCoalesableAtomsHaveDifferentLoadName()) {
// FIXME: need diagonstics interface for writing warning messages
llvm::errs() << "lld warning: shared library symbol "
<< curShLib->name() << " has different load path in "
@ -268,7 +267,7 @@ bool SymbolTable::addByName(const Atom &newAtom) {
}
// Give context a chance to change which is kept.
_context.notifySymbolTableCoalesce(existing, &newAtom, useNew);
_ctx.notifySymbolTableCoalesce(existing, &newAtom, useNew);
if (useNew) {
// Update name table to use new atom.

View file

@ -1,18 +0,0 @@
lib/Core
~~~~~~~~
* Add endianness support to the native reader and writer.
* The NativeReader has lots of similar code for converting arrays of ivar
data in mapped memory into arrays of objects. The commonality can be
factored out, maybe templatized.
* The NativeFileFormat.h is old school C structs and constants. We scope
things better by defining constants used with a struct inside the struct
declaration.
* The native reader and writer currently just blast in memory enumeration
values (e.g. DefinedAtom::Scope) into a byte in the disk format. To support
future changes to the enumerations, there should be a translation layer
to map disk values to in-memory values.

View file

@ -16,8 +16,4 @@ Writer::Writer() {
Writer::~Writer() {
}
bool Writer::createImplicitFiles(std::vector<std::unique_ptr<File> > &) {
return true;
}
} // end namespace lld

Some files were not shown because too many files have changed in this diff Show more