mirror of
https://github.com/opnsense/src.git
synced 2026-06-04 22:32:43 -04:00
Vendor import of lld trunk r256633:
https://llvm.org/svn/llvm-project/lld/trunk@256633
This commit is contained in:
parent
fb911942f1
commit
5a5c549fe9
1664 changed files with 63388 additions and 30579 deletions
|
|
@ -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
19
CODE_OWNERS.TXT
Normal 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
33
COFF/CMakeLists.txt
Normal 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
340
COFF/Chunks.cpp
Normal 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
332
COFF/Chunks.h
Normal 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
140
COFF/Config.h
Normal 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
556
COFF/DLL.cpp
Normal 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
84
COFF/DLL.h
Normal 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
677
COFF/Driver.cpp
Normal 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
180
COFF/Driver.h
Normal 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
718
COFF/DriverUtils.cpp
Normal 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
30
COFF/Error.cpp
Normal 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
28
COFF/Error.h
Normal 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
244
COFF/ICF.cpp
Normal 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
367
COFF/InputFiles.cpp
Normal 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
222
COFF/InputFiles.h
Normal 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
61
COFF/MarkLive.cpp
Normal 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
291
COFF/ModuleDef.cpp
Normal 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
|
||||
|
|
@ -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
60
COFF/PDB.cpp
Normal 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
265
COFF/README.md
Normal 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
445
COFF/SymbolTable.cpp
Normal 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
125
COFF/SymbolTable.h
Normal 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
243
COFF/Symbols.cpp
Normal 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
407
COFF/Symbols.h
Normal 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
765
COFF/Writer.cpp
Normal 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
26
COFF/Writer.h
Normal 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
26
ELF/CMakeLists.txt
Normal 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
84
ELF/Config.h
Normal 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
299
ELF/Driver.cpp
Normal 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
67
ELF/Driver.h
Normal 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
120
ELF/DriverUtils.cpp
Normal 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
38
ELF/Error.cpp
Normal 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
32
ELF/Error.h
Normal 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
496
ELF/InputFiles.cpp
Normal 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
211
ELF/InputFiles.h
Normal 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
399
ELF/InputSection.cpp
Normal 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
187
ELF/InputSection.h
Normal 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
318
ELF/LinkerScript.cpp
Normal 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
131
ELF/MarkLive.cpp
Normal 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
161
ELF/Options.td
Normal 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
1534
ELF/OutputSections.cpp
Normal file
File diff suppressed because it is too large
Load diff
485
ELF/OutputSections.h
Normal file
485
ELF/OutputSections.h
Normal 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
21
ELF/README.md
Normal 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
267
ELF/SymbolTable.cpp
Normal 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
98
ELF/SymbolTable.h
Normal 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
148
ELF/Symbols.cpp
Normal 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
327
ELF/Symbols.h
Normal 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
1481
ELF/Target.cpp
Normal file
File diff suppressed because it is too large
Load diff
117
ELF/Target.h
Normal file
117
ELF/Target.h
Normal 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
1282
ELF/Writer.cpp
Normal file
File diff suppressed because it is too large
Load diff
24
ELF/Writer.h
Normal file
24
ELF/Writer.h
Normal 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
|
||||
86
Makefile
86
Makefile
|
|
@ -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
|
||||
155
docs/Makefile
155
docs/Makefile
|
|
@ -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."
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
LLD_LEVEL := ..
|
||||
DIRS := lld
|
||||
|
||||
include $(LLD_LEVEL)/Makefile
|
||||
|
|
@ -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" \
|
||||
$< > $@
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 &);
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ private:
|
|||
bool addByName(const Atom &);
|
||||
bool addByContent(const DefinedAtom &);
|
||||
|
||||
LinkingContext &_context;
|
||||
LinkingContext &_ctx;
|
||||
AtomToAtom _replacedAtoms;
|
||||
NameToAtom _nameTable;
|
||||
NameToAtom _groupTable;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 *> §ions)
|
||||
: 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 *> §ionsCommands)
|
||||
: 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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
@ -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
Loading…
Reference in a new issue