Vendor import of lld trunk r338150:

https://llvm.org/svn/llvm-project/lld/trunk@338150
This commit is contained in:
Dimitry Andric 2018-07-28 11:08:33 +00:00
parent ae1a339de3
commit 20d35e67e6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/lld/dist/; revision=336821
svn path=/vendor/lld/lld-trunk-r338150/; revision=336822; tag=vendor/lld/lld-trunk-r338150
1593 changed files with 41054 additions and 11669 deletions

View File

@ -18,7 +18,6 @@ add_lld_library(lldCOFF
MarkLive.cpp
MinGW.cpp
PDB.cpp
Strings.cpp
SymbolTable.cpp
Symbols.cpp
Writer.cpp

View File

@ -31,8 +31,7 @@ namespace coff {
SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
: Chunk(SectionKind), Repl(this), Header(H), File(F),
Relocs(File->getCOFFObj()->getRelocations(Header)),
NumRelocs(std::distance(Relocs.begin(), Relocs.end())) {
Relocs(File->getCOFFObj()->getRelocations(Header)) {
// Initialize SectionName.
File->getCOFFObj()->getSectionName(Header, SectionName);
@ -51,13 +50,21 @@ 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); }
static void or32(uint8_t *P, uint32_t V) { write32le(P, read32le(P) | V); }
// Verify that given sections are appropriate targets for SECREL
// relocations. This check is relaxed because unfortunately debug
// sections have section-relative relocations against absolute symbols.
static bool checkSecRel(const SectionChunk *Sec, OutputSection *OS) {
if (OS)
return true;
if (Sec->isCodeView())
return false;
fatal("SECREL relocation cannot be applied to absolute symbols");
}
static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
OutputSection *OS, uint64_t S) {
if (!OS) {
if (Sec->isCodeView())
return;
fatal("SECREL relocation cannot be applied to absolute symbols");
}
if (!checkSecRel(Sec, OS))
return;
uint64_t SecRel = S - OS->getRVA();
if (SecRel > UINT32_MAX) {
error("overflow in SECREL relocation in section: " + Sec->getSectionName());
@ -67,10 +74,13 @@ static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
}
static void applySecIdx(uint8_t *Off, OutputSection *OS) {
// If we have no output section, this must be an absolute symbol. Use the
// sentinel absolute symbol section index.
uint16_t SecIdx = OS ? OS->SectionIndex : DefinedAbsolute::OutputSectionIndex;
add16(Off, SecIdx);
// Absolute symbol doesn't have section index, but section index relocation
// against absolute symbol should be resolved to one plus the last output
// section index. This is required for compatibility with MSVC.
if (OS)
add16(Off, OS->SectionIndex);
else
add16(Off, DefinedAbsolute::NumOutputSections + 1);
}
void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS,
@ -88,7 +98,8 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS,
case IMAGE_REL_AMD64_SECTION: applySecIdx(Off, OS); break;
case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, OS, S); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
toString(File));
}
}
@ -102,7 +113,8 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS,
case IMAGE_REL_I386_SECTION: applySecIdx(Off, OS); break;
case IMAGE_REL_I386_SECREL: applySecRel(this, Off, OS, S); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
toString(File));
}
}
@ -112,11 +124,10 @@ static void applyMOV(uint8_t *Off, uint16_t V) {
}
static uint16_t readMOV(uint8_t *Off) {
uint16_t Opcode1 = read16le(Off);
uint16_t Opcode2 = read16le(Off + 2);
uint16_t Imm = (Opcode2 & 0x00ff) | ((Opcode2 >> 4) & 0x0700);
Imm |= ((Opcode1 << 1) & 0x0800) | ((Opcode1 & 0x000f) << 12);
return Imm;
uint16_t Op1 = read16le(Off);
uint16_t Op2 = read16le(Off + 2);
return (Op2 & 0x00ff) | ((Op2 >> 4) & 0x0700) | ((Op1 << 1) & 0x0800) |
((Op1 & 0x000f) << 12);
}
void applyMOV32T(uint8_t *Off, uint32_t V) {
@ -153,7 +164,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
uint64_t S, uint64_t P) const {
// Pointer to thumb code must have the LSB set.
uint64_t SX = S;
if (OS && (OS->getPermissions() & IMAGE_SCN_MEM_EXECUTE))
if (OS && (OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE))
SX |= 1;
switch (Type) {
case IMAGE_REL_ARM_ADDR32: add32(Off, SX + Config->ImageBase); break;
@ -165,18 +176,19 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
case IMAGE_REL_ARM_SECTION: applySecIdx(Off, OS); break;
case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, OS, S); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
toString(File));
}
}
// Interpret the existing immediate value as a byte offset to the
// target symbol, then update the instruction with the immediate as
// the page offset from the current instruction to the target.
static void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P) {
static void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) {
uint32_t Orig = read32le(Off);
uint64_t Imm = ((Orig >> 29) & 0x3) | ((Orig >> 3) & 0x1FFFFC);
S += Imm;
Imm = (S >> 12) - (P >> 12);
Imm = (S >> Shift) - (P >> Shift);
uint32_t ImmLo = (Imm & 0x3) << 29;
uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
@ -213,19 +225,70 @@ static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) {
applyArm64Imm(Off, Imm >> Size, Size);
}
static void applySecRelLow12A(const SectionChunk *Sec, uint8_t *Off,
OutputSection *OS, uint64_t S) {
if (checkSecRel(Sec, OS))
applyArm64Imm(Off, (S - OS->getRVA()) & 0xfff, 0);
}
static void applySecRelHigh12A(const SectionChunk *Sec, uint8_t *Off,
OutputSection *OS, uint64_t S) {
if (!checkSecRel(Sec, OS))
return;
uint64_t SecRel = (S - OS->getRVA()) >> 12;
if (0xfff < SecRel) {
error("overflow in SECREL_HIGH12A relocation in section: " +
Sec->getSectionName());
return;
}
applyArm64Imm(Off, SecRel & 0xfff, 0);
}
static void applySecRelLdr(const SectionChunk *Sec, uint8_t *Off,
OutputSection *OS, uint64_t S) {
if (checkSecRel(Sec, OS))
applyArm64Ldr(Off, (S - OS->getRVA()) & 0xfff);
}
static void applyArm64Branch26(uint8_t *Off, int64_t V) {
if (!isInt<28>(V))
fatal("relocation out of range");
or32(Off, (V & 0x0FFFFFFC) >> 2);
}
static void applyArm64Branch19(uint8_t *Off, int64_t V) {
if (!isInt<21>(V))
fatal("relocation out of range");
or32(Off, (V & 0x001FFFFC) << 3);
}
static void applyArm64Branch14(uint8_t *Off, int64_t V) {
if (!isInt<16>(V))
fatal("relocation out of range");
or32(Off, (V & 0x0000FFFC) << 3);
}
void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS,
uint64_t S, uint64_t P) const {
switch (Type) {
case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, S, P); break;
case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, S, P, 12); break;
case IMAGE_REL_ARM64_REL21: applyArm64Addr(Off, S, P, 0); break;
case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff, 0); break;
case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(Off, S & 0xfff); break;
case IMAGE_REL_ARM64_BRANCH26: or32(Off, ((S - P) & 0x0FFFFFFC) >> 2); break;
case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(Off, S - P); break;
case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(Off, S - P); break;
case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(Off, S - P); break;
case IMAGE_REL_ARM64_ADDR32: add32(Off, S + Config->ImageBase); break;
case IMAGE_REL_ARM64_ADDR32NB: add32(Off, S); break;
case IMAGE_REL_ARM64_ADDR64: add64(Off, S + Config->ImageBase); break;
case IMAGE_REL_ARM64_SECREL: applySecRel(this, Off, OS, S); break;
case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, Off, OS, S); break;
case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, Off, OS, S); break;
case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, Off, OS, S); break;
case IMAGE_REL_ARM64_SECTION: applySecIdx(Off, OS); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
toString(File));
}
}
@ -234,7 +297,8 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
return;
// Copy section contents from source object file to output file.
ArrayRef<uint8_t> A = getContents();
memcpy(Buf + OutputSectionOff, A.data(), A.size());
if (!A.empty())
memcpy(Buf + OutputSectionOff, A.data(), A.size());
// Apply relocations.
size_t InputSize = getSize();
@ -350,8 +414,8 @@ bool SectionChunk::hasData() const {
return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
}
uint32_t SectionChunk::getPermissions() const {
return Header->Characteristics & PermMask;
uint32_t SectionChunk::getOutputCharacteristics() const {
return Header->Characteristics & (PermMask | TypeMask);
}
bool SectionChunk::isCOMDAT() const {
@ -378,6 +442,7 @@ ArrayRef<uint8_t> SectionChunk::getContents() const {
}
void SectionChunk::replace(SectionChunk *Other) {
Alignment = std::max(Alignment, Other->Alignment);
Other->Repl = Repl;
Other->Live = false;
}
@ -388,7 +453,7 @@ CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
Alignment = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue()));
}
uint32_t CommonChunk::getPermissions() const {
uint32_t CommonChunk::getOutputCharacteristics() const {
return IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ |
IMAGE_SCN_MEM_WRITE;
}
@ -433,7 +498,7 @@ void ImportThunkChunkARM::writeTo(uint8_t *Buf) const {
void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const {
int64_t Off = ImpSymbol->getRVA() & 0xfff;
memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64));
applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA);
applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA, 12);
applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
}
@ -453,12 +518,14 @@ void LocalImportChunk::writeTo(uint8_t *Buf) const {
}
}
void SEHTableChunk::writeTo(uint8_t *Buf) const {
void RVATableChunk::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();
for (const ChunkAndOffset &CO : Syms)
Begin[Cnt++] = CO.InputChunk->getRVA() + CO.Offset;
std::sort(Begin, Begin + Cnt);
assert(std::unique(Begin, Begin + Cnt) == Begin + Cnt &&
"RVA tables should be de-duplicated");
}
// Windows-specific. This class represents a block in .reloc section.
@ -531,5 +598,47 @@ uint8_t Baserel::getDefaultType() {
}
}
std::map<uint32_t, MergeChunk *> MergeChunk::Instances;
MergeChunk::MergeChunk(uint32_t Alignment)
: Builder(StringTableBuilder::RAW, Alignment) {
this->Alignment = Alignment;
}
void MergeChunk::addSection(SectionChunk *C) {
auto *&MC = Instances[C->Alignment];
if (!MC)
MC = make<MergeChunk>(C->Alignment);
MC->Sections.push_back(C);
}
void MergeChunk::finalizeContents() {
for (SectionChunk *C : Sections)
if (C->isLive())
Builder.add(toStringRef(C->getContents()));
Builder.finalize();
for (SectionChunk *C : Sections) {
if (!C->isLive())
continue;
size_t Off = Builder.getOffset(toStringRef(C->getContents()));
C->setOutputSection(Out);
C->setRVA(RVA + Off);
C->OutputSectionOff = OutputSectionOff + Off;
}
}
uint32_t MergeChunk::getOutputCharacteristics() const {
return IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA;
}
size_t MergeChunk::getSize() const {
return Builder.getSize();
}
void MergeChunk::writeTo(uint8_t *Buf) const {
Builder.write(Buf + OutputSectionOff);
}
} // namespace coff
} // namespace lld

View File

@ -16,6 +16,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/COFF.h"
#include <utility>
#include <vector>
@ -37,9 +38,11 @@ class ObjFile;
class OutputSection;
class Symbol;
// Mask for section types (code, data, bss, disacardable, etc.)
// and permissions (writable, readable or executable).
const uint32_t PermMask = 0xFF0000F0;
// Mask for permissions (discardable, writable, readable, executable, etc).
const uint32_t PermMask = 0xFE000000;
// Mask for section types (code, data, bss).
const uint32_t TypeMask = 0x000000E0;
// 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
@ -60,6 +63,10 @@ class Chunk {
// before calling this function.
virtual void writeTo(uint8_t *Buf) const {}
// Called by the writer after an RVA is assigned, but before calling
// getSize().
virtual void finalizeContents() {}
// The writer sets and uses the addresses.
uint64_t getRVA() const { return RVA; }
void setRVA(uint64_t V) { RVA = V; }
@ -70,7 +77,7 @@ class Chunk {
virtual bool hasData() const { return true; }
// Returns readable/writable/executable bits.
virtual uint32_t getPermissions() const { return 0; }
virtual uint32_t getOutputCharacteristics() const { return 0; }
// Returns the section name if this is a section chunk.
// It is illegal to call this function on non-section chunks.
@ -137,7 +144,7 @@ class SectionChunk final : public Chunk {
ArrayRef<uint8_t> getContents() const;
void writeTo(uint8_t *Buf) const override;
bool hasData() const override;
uint32_t getPermissions() const override;
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return SectionName; }
void getBaserels(std::vector<Baserel> *Res) override;
bool isCOMDAT() const;
@ -208,11 +215,11 @@ class SectionChunk final : public Chunk {
// The COMDAT leader symbol if this is a COMDAT chunk.
DefinedRegular *Sym = nullptr;
ArrayRef<coff_relocation> Relocs;
private:
StringRef SectionName;
std::vector<SectionChunk *> AssocChildren;
llvm::iterator_range<const coff_relocation *> Relocs;
size_t NumRelocs;
// Used by the garbage collector.
bool Live;
@ -222,13 +229,40 @@ class SectionChunk final : public Chunk {
uint32_t Class[2] = {0, 0};
};
// This class is used to implement an lld-specific feature (not implemented in
// MSVC) that minimizes the output size by finding string literals sharing tail
// parts and merging them.
//
// If string tail merging is enabled and a section is identified as containing a
// string literal, it is added to a MergeChunk with an appropriate alignment.
// The MergeChunk then tail merges the strings using the StringTableBuilder
// class and assigns RVAs and section offsets to each of the member chunks based
// on the offsets assigned by the StringTableBuilder.
class MergeChunk : public Chunk {
public:
MergeChunk(uint32_t Alignment);
static void addSection(SectionChunk *C);
void finalizeContents() override;
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return ".rdata"; }
size_t getSize() const override;
void writeTo(uint8_t *Buf) const override;
static std::map<uint32_t, MergeChunk *> Instances;
std::vector<SectionChunk *> Sections;
private:
llvm::StringTableBuilder Builder;
};
// 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;
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return ".bss"; }
private:
@ -320,17 +354,41 @@ class LocalImportChunk : public Chunk {
Defined *Sym;
};
// Windows-specific.
// A chunk for SEH table which contains RVAs of safe exception handler
// functions. x86-only.
class SEHTableChunk : public Chunk {
// Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and
// offset into the chunk. Order does not matter as the RVA table will be sorted
// later.
struct ChunkAndOffset {
Chunk *InputChunk;
uint32_t Offset;
struct DenseMapInfo {
static ChunkAndOffset getEmptyKey() {
return {llvm::DenseMapInfo<Chunk *>::getEmptyKey(), 0};
}
static ChunkAndOffset getTombstoneKey() {
return {llvm::DenseMapInfo<Chunk *>::getTombstoneKey(), 0};
}
static unsigned getHashValue(const ChunkAndOffset &CO) {
return llvm::DenseMapInfo<std::pair<Chunk *, uint32_t>>::getHashValue(
{CO.InputChunk, CO.Offset});
}
static bool isEqual(const ChunkAndOffset &LHS, const ChunkAndOffset &RHS) {
return LHS.InputChunk == RHS.InputChunk && LHS.Offset == RHS.Offset;
}
};
};
using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>;
// Table which contains symbol RVAs. Used for /safeseh and /guard:cf.
class RVATableChunk : public Chunk {
public:
explicit SEHTableChunk(std::set<Defined *> S) : Syms(std::move(S)) {}
explicit RVATableChunk(SymbolRVASet S) : Syms(std::move(S)) {}
size_t getSize() const override { return Syms.size() * 4; }
void writeTo(uint8_t *Buf) const override;
private:
std::set<Defined *> Syms;
SymbolRVASet Syms;
};
// Windows-specific.
@ -362,4 +420,10 @@ void applyBranch24T(uint8_t *Off, int32_t V);
} // namespace coff
} // namespace lld
namespace llvm {
template <>
struct DenseMapInfo<lld::coff::ChunkAndOffset>
: lld::coff::ChunkAndOffset::DenseMapInfo {};
}
#endif

View File

@ -10,6 +10,7 @@
#ifndef LLD_COFF_CONFIG_H
#define LLD_COFF_CONFIG_H
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/CachePruning.h"
@ -71,6 +72,12 @@ enum class DebugType {
Fixup = 0x4, /// Relocation Table
};
enum class GuardCFLevel {
Off,
NoLongJmp, // Emit gfids but no longjmp tables
Full, // Enable all protections.
};
// Global configuration.
struct Configuration {
enum ManifestKind { SideBySide, Embed, No };
@ -85,13 +92,19 @@ struct Configuration {
std::string ImportName;
bool DoGC = true;
bool DoICF = true;
bool TailMerge;
bool Relocatable = true;
bool Force = false;
bool Debug = false;
bool DebugDwarf = false;
bool DebugGHashes = false;
bool DebugSymtab = false;
bool ShowTiming = false;
unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
std::vector<std::string> NatvisFiles;
llvm::SmallString<128> PDBAltPath;
llvm::SmallString<128> PDBPath;
llvm::SmallString<128> PDBSourcePath;
std::vector<llvm::StringRef> Argv;
// Symbols in this set are considered as live by the garbage collector.
@ -110,15 +123,18 @@ struct Configuration {
bool SaveTemps = false;
// /guard:cf
GuardCFLevel GuardCF = GuardCFLevel::Off;
// Used for SafeSEH.
Symbol *SEHTable = nullptr;
Symbol *SEHCount = nullptr;
// Used for /opt:lldlto=N
unsigned LTOOptLevel = 2;
unsigned LTOO = 2;
// Used for /opt:lldltojobs=N
unsigned LTOJobs = 0;
unsigned ThinLTOJobs = 0;
// Used for /opt:lldltopartitions=N
unsigned LTOPartitions = 1;
@ -152,6 +168,9 @@ struct Configuration {
// Used for /alternatename.
std::map<StringRef, StringRef> AlternateNames;
// Used for /order.
llvm::StringMap<int> Order;
// Used for /lldmap.
std::string MapFile;
@ -164,7 +183,7 @@ struct Configuration {
uint32_t MinorImageVersion = 0;
uint32_t MajorOSVersion = 6;
uint32_t MinorOSVersion = 0;
bool CanExitEarly = false;
uint32_t Timestamp = 0;
bool DynamicBase = true;
bool AllowBind = true;
bool NxCompat = true;
@ -174,7 +193,12 @@ struct Configuration {
bool HighEntropyVA = false;
bool AppContainer = false;
bool MinGW = false;
bool WarnMissingOrderSymbol = true;
bool WarnLocallyDefinedImported = true;
bool Incremental = true;
bool IntegrityCheck = false;
bool KillAt = false;
bool Repro = false;
};
extern Configuration *Config;

View File

@ -18,8 +18,8 @@
//
//===----------------------------------------------------------------------===//
#include "Chunks.h"
#include "DLL.h"
#include "Chunks.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Path.h"

View File

@ -76,6 +76,11 @@ class EdataContents {
public:
EdataContents();
std::vector<Chunk *> Chunks;
uint64_t getRVA() { return Chunks[0]->getRVA(); }
uint64_t getSize() {
return Chunks.back()->getRVA() + Chunks.back()->getSize() - getRVA();
}
};
} // namespace coff

View File

@ -9,14 +9,18 @@
#include "Driver.h"
#include "Config.h"
#include "ICF.h"
#include "InputFiles.h"
#include "MarkLive.h"
#include "MinGW.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/Args.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Timer.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
@ -35,9 +39,8 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
#include <algorithm>
#include <memory>
#include <future>
#include <memory>
using namespace llvm;
using namespace llvm::object;
@ -47,19 +50,20 @@ using llvm::sys::Process;
namespace lld {
namespace coff {
static Timer InputFileTimer("Input File Reading", Timer::root());
Configuration *Config;
LinkerDriver *Driver;
bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
errorHandler().LogName = Args[0];
errorHandler().LogName = sys::path::filename(Args[0]);
errorHandler().ErrorOS = &Diag;
errorHandler().ColorDiagnostics = Diag.has_colors();
errorHandler().ErrorLimitExceededMsg =
"too many errors emitted, stopping now"
" (use /ERRORLIMIT:0 to see all errors)";
" (use /errorlimit:0 to see all errors)";
errorHandler().ExitEarly = CanExitEarly;
Config = make<Configuration>();
Config->Argv = {Args.begin(), Args.end()};
Config->CanExitEarly = CanExitEarly;
Symtab = make<SymbolTable>();
@ -71,6 +75,9 @@ bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
exitLld(errorCount() ? 1 : 0);
freeArena();
ObjFile::Instances.clear();
ImportFile::Instances.clear();
BitcodeFile::Instances.clear();
return !errorCount();
}
@ -92,7 +99,7 @@ typedef std::pair<std::unique_ptr<MemoryBuffer>, std::error_code> MBErrPair;
// Create a std::future that opens and maps a file using the best strategy for
// the host platform.
static std::future<MBErrPair> createFutureForFile(std::string Path) {
#if LLVM_ON_WIN32
#if _WIN32
// On Windows, file I/O is relatively slow so it is best to do this
// asynchronously.
auto Strategy = std::launch::async;
@ -100,7 +107,9 @@ static std::future<MBErrPair> createFutureForFile(std::string Path) {
auto Strategy = std::launch::deferred;
#endif
return std::async(Strategy, [=]() {
auto MBOrErr = MemoryBuffer::getFile(Path);
auto MBOrErr = MemoryBuffer::getFile(Path,
/*FileSize*/ -1,
/*RequiresNullTerminator*/ false);
if (!MBOrErr)
return MBErrPair{nullptr, MBOrErr.getError()};
return MBErrPair{std::move(*MBOrErr), std::error_code()};
@ -119,40 +128,47 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) {
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB,
bool WholeArchive) {
StringRef Filename = MB->getBufferIdentifier();
MemoryBufferRef MBRef = takeBuffer(std::move(MB));
FilePaths.push_back(MBRef.getBufferIdentifier());
FilePaths.push_back(Filename);
// File type is detected by contents, not by file extension.
switch (identify_magic(MBRef.getBuffer())) {
case file_magic::windows_resource:
Resources.push_back(MBRef);
break;
case file_magic::archive:
if (WholeArchive) {
std::unique_ptr<Archive> File =
CHECK(Archive::create(MBRef),
MBRef.getBufferIdentifier() + ": failed to parse archive");
CHECK(Archive::create(MBRef), Filename + ": failed to parse archive");
for (MemoryBufferRef M : getArchiveMembers(File.get()))
addArchiveBuffer(M, "<whole-archive>", MBRef.getBufferIdentifier());
addArchiveBuffer(M, "<whole-archive>", Filename);
return;
}
Symtab->addFile(make<ArchiveFile>(MBRef));
break;
case file_magic::bitcode:
Symtab->addFile(make<BitcodeFile>(MBRef));
break;
case file_magic::coff_cl_gl_object:
error(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
"Recompile without /GL");
break;
default:
case file_magic::coff_object:
case file_magic::coff_import_library:
Symtab->addFile(make<ObjFile>(MBRef));
break;
case file_magic::coff_cl_gl_object:
error(Filename + ": is not a native COFF file. Recompile without /GL");
break;
case file_magic::pecoff_executable:
if (Filename.endswith_lower(".dll")) {
error(Filename + ": bad file type. Did you specify a DLL instead of an "
"import library?");
break;
}
LLVM_FALLTHROUGH;
default:
error(MBRef.getBufferIdentifier() + ": unknown file type");
break;
}
}
@ -227,7 +243,29 @@ static bool isDecorated(StringRef Sym) {
void LinkerDriver::parseDirectives(StringRef S) {
ArgParser Parser;
// .drectve is always tokenized using Windows shell rules.
opt::InputArgList Args = Parser.parseDirectives(S);
// /EXPORT: option can appear too many times, processing in fastpath.
opt::InputArgList Args;
std::vector<StringRef> Exports;
std::tie(Args, Exports) = Parser.parseDirectives(S);
for (StringRef E : Exports) {
// If a common header file contains dllexported function
// declarations, many object files may end up with having the
// same /EXPORT options. In order to save cost of parsing them,
// we dedup them first.
if (!DirectivesExports.insert(E).second)
continue;
Export Exp = parseExport(E);
if (Config->Machine == I386 && Config->MinGW) {
if (!isDecorated(Exp.Name))
Exp.Name = Saver.save("_" + Exp.Name);
if (!Exp.ExtName.empty() && !isDecorated(Exp.ExtName))
Exp.ExtName = Saver.save("_" + Exp.ExtName);
}
Exp.Directives = true;
Config->Exports.push_back(Exp);
}
for (auto *Arg : Args) {
switch (Arg->getOption().getUnaliasedOption().getID()) {
@ -244,25 +282,6 @@ void LinkerDriver::parseDirectives(StringRef S) {
case OPT_entry:
Config->Entry = addUndefined(mangle(Arg->getValue()));
break;
case OPT_export: {
// If a common header file contains dllexported function
// declarations, many object files may end up with having the
// same /EXPORT options. In order to save cost of parsing them,
// we dedup them first.
if (!DirectivesExports.insert(Arg->getValue()).second)
break;
Export E = parseExport(Arg->getValue());
if (Config->Machine == I386 && Config->MinGW) {
if (!isDecorated(E.Name))
E.Name = Saver.save("_" + E.Name);
if (!E.ExtName.empty() && !isDecorated(E.ExtName))
E.ExtName = Saver.save("_" + E.ExtName);
}
E.Directives = true;
Config->Exports.push_back(E);
break;
}
case OPT_failifmismatch:
checkFailIfMismatch(Arg->getValue());
break;
@ -315,13 +334,24 @@ StringRef LinkerDriver::doFindFile(StringRef Filename) {
return Filename;
}
static Optional<sys::fs::UniqueID> getUniqueID(StringRef Path) {
sys::fs::UniqueID Ret;
if (sys::fs::getUniqueID(Path, Ret))
return None;
return Ret;
}
// 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;
if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path)) {
bool Seen = !VisitedFiles.insert(*ID).second;
if (Seen)
return None;
}
if (Path.endswith_lower(".lib"))
VisitedLibs.insert(sys::path::filename(Path));
return Path;
@ -344,11 +374,14 @@ Optional<StringRef> LinkerDriver::findLib(StringRef Filename) {
return None;
if (!VisitedLibs.insert(Filename.lower()).second)
return None;
StringRef Path = doFindLib(Filename);
if (Config->NoDefaultLibs.count(Path))
return None;
if (!VisitedFiles.insert(Path.lower()).second)
return None;
if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path))
if (!VisitedFiles.insert(*ID).second)
return None;
return Path;
}
@ -383,7 +416,24 @@ StringRef LinkerDriver::mangle(StringRef Sym) {
}
// Windows specific -- find default entry point name.
//
// There are four different entry point functions for Windows executables,
// each of which corresponds to a user-defined "main" function. This function
// infers an entry point from a user-defined "main" function.
StringRef LinkerDriver::findDefaultEntry() {
// As a special case, if /nodefaultlib is given, we directly look for an
// entry point. This is because, if no default library is linked, users
// need to define an entry point instead of a "main".
if (Config->NoDefaultLibAll) {
for (StringRef S : {"mainCRTStartup", "wmainCRTStartup",
"WinMainCRTStartup", "wWinMainCRTStartup"}) {
StringRef Entry = Symtab->findMangle(S);
if (!Entry.empty() && !isa<Undefined>(Symtab->find(Entry)))
return mangle(S);
}
return "";
}
// User-defined main functions and their corresponding entry points.
static const char *Entries[][2] = {
{"main", "mainCRTStartup"},
@ -533,10 +583,49 @@ static void createImportLibrary(bool AsLib) {
Exports.push_back(E2);
}
auto E = writeImportLibrary(getImportName(AsLib), getImplibPath(), Exports,
Config->Machine, false);
handleAllErrors(std::move(E),
[&](ErrorInfoBase &EIB) { error(EIB.message()); });
auto HandleError = [](Error &&E) {
handleAllErrors(std::move(E),
[](ErrorInfoBase &EIB) { error(EIB.message()); });
};
std::string LibName = getImportName(AsLib);
std::string Path = getImplibPath();
if (!Config->Incremental) {
HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine,
Config->MinGW));
return;
}
// If the import library already exists, replace it only if the contents
// have changed.
ErrorOr<std::unique_ptr<MemoryBuffer>> OldBuf = MemoryBuffer::getFile(
Path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false);
if (!OldBuf) {
HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine,
Config->MinGW));
return;
}
SmallString<128> TmpName;
if (std::error_code EC =
sys::fs::createUniqueFile(Path + ".tmp-%%%%%%%%.lib", TmpName))
fatal("cannot create temporary file for import library " + Path + ": " +
EC.message());
if (Error E = writeImportLibrary(LibName, TmpName, Exports, Config->Machine,
Config->MinGW)) {
HandleError(std::move(E));
return;
}
std::unique_ptr<MemoryBuffer> NewBuf = check(MemoryBuffer::getFile(
TmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false));
if ((*OldBuf)->getBuffer() != NewBuf->getBuffer()) {
OldBuf->reset();
HandleError(errorCodeToError(sys::fs::rename(TmpName, Path)));
} else {
sys::fs::remove(TmpName);
}
}
static void parseModuleDefs(StringRef Path) {
@ -569,9 +658,18 @@ static void parseModuleDefs(StringRef Path) {
for (COFFShortExport E1 : M.Exports) {
Export E2;
// In simple cases, only Name is set. Renamed exports are parsed
// and set as "ExtName = Name". If Name has the form "OtherDll.Func",
// it shouldn't be a normal exported function but a forward to another
// DLL instead. This is supported by both MS and GNU linkers.
if (E1.ExtName != E1.Name && StringRef(E1.Name).contains('.')) {
E2.Name = Saver.save(E1.ExtName);
E2.ForwardTo = Saver.save(E1.Name);
Config->Exports.push_back(E2);
continue;
}
E2.Name = Saver.save(E1.Name);
if (E1.isWeak())
E2.ExtName = Saver.save(E1.ExtName);
E2.ExtName = Saver.save(E1.ExtName);
E2.Ordinal = E1.Ordinal;
E2.Noname = E1.Noname;
E2.Data = E1.Data;
@ -634,8 +732,8 @@ filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
log("Creating a temporary archive for " + Path + " to remove bitcode files");
SmallString<128> S;
if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path),
".lib", S))
if (std::error_code EC = sys::fs::createTemporaryFile(
"lld-" + sys::path::stem(Path), ".lib", S))
fatal("cannot create a temporary file: " + EC.message());
std::string Temp = S.str();
TemporaryFiles.push_back(Temp);
@ -711,6 +809,8 @@ void LinkerDriver::enqueueTask(std::function<void()> Task) {
}
bool LinkerDriver::run() {
ScopedTimer T(InputFileTimer);
bool DidWork = !TaskQueue.empty();
while (!TaskQueue.empty()) {
TaskQueue.front()();
@ -719,6 +819,46 @@ bool LinkerDriver::run() {
return DidWork;
}
// Parse an /order file. If an option is given, the linker places
// COMDAT sections in the same order as their names appear in the
// given file.
static void parseOrderFile(StringRef Arg) {
// For some reason, the MSVC linker requires a filename to be
// preceded by "@".
if (!Arg.startswith("@")) {
error("malformed /order option: '@' missing");
return;
}
// Get a list of all comdat sections for error checking.
DenseSet<StringRef> Set;
for (Chunk *C : Symtab->getChunks())
if (auto *Sec = dyn_cast<SectionChunk>(C))
if (Sec->Sym)
Set.insert(Sec->Sym->getName());
// Open a file.
StringRef Path = Arg.substr(1);
std::unique_ptr<MemoryBuffer> MB = CHECK(
MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
// Parse a file. An order file contains one symbol per line.
// All symbols that were not present in a given order file are
// considered to have the lowest priority 0 and are placed at
// end of an output section.
for (std::string S : args::getLines(MB->getMemBufferRef())) {
if (Config->Machine == I386 && !isDecorated(S))
S = "_" + S;
if (Set.count(S) == 0) {
if (Config->WarnMissingOrderSymbol)
warn("/order:" + Arg + ": missing symbol: " + S + " [LNK4037]");
}
else
Config->Order[S] = INT_MIN + Config->Order.size();
}
}
void LinkerDriver::link(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.
@ -734,11 +874,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
InitializeAllTargetMCs();
InitializeAllAsmParsers();
InitializeAllAsmPrinters();
InitializeAllDisassemblers();
// Parse command line options.
ArgParser Parser;
opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
opt::InputArgList Args = Parser.parseLINK(ArgsArr);
// Parse and evaluate -mllvm options.
std::vector<const char *> V;
@ -762,6 +901,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
return;
}
if (Args.hasArg(OPT_show_timing))
Config->ShowTiming = true;
ScopedTimer T(Timer::root());
// Handle --version, which is an lld extension. This option is a bit odd
// because it doesn't start with "/", but we deliberately chose "--" to
// avoid conflict with /version and for compatibility with clang-cl.
@ -804,7 +947,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /ignore
for (auto *Arg : Args.filtered(OPT_ignore)) {
if (StringRef(Arg->getValue()) == "4217")
if (StringRef(Arg->getValue()) == "4037")
Config->WarnMissingOrderSymbol = false;
else if (StringRef(Arg->getValue()) == "4217")
Config->WarnLocallyDefinedImported = false;
// Other warning numbers are ignored.
}
@ -825,6 +970,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /debug
if (Args.hasArg(OPT_debug, OPT_debug_dwarf, OPT_debug_ghash)) {
Config->Debug = true;
Config->Incremental = true;
if (auto *Arg = Args.getLastArg(OPT_debugtype))
Config->DebugTypes = parseDebugType(Arg->getValue());
else
@ -833,9 +979,17 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /pdb
bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash);
if (ShouldCreatePDB)
if (ShouldCreatePDB) {
if (auto *Arg = Args.getLastArg(OPT_pdb))
Config->PDBPath = Arg->getValue();
if (auto *Arg = Args.getLastArg(OPT_pdbaltpath))
Config->PDBAltPath = Arg->getValue();
if (Args.hasArg(OPT_natvis))
Config->NatvisFiles = Args.getAllArgValues(OPT_natvis);
if (auto *Arg = Args.getLastArg(OPT_pdb_source_path))
Config->PDBSourcePath = Arg->getValue();
}
// Handle /noentry
if (Args.hasArg(OPT_noentry)) {
@ -859,6 +1013,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
DynamicBaseArg->getOption().getID() == OPT_dynamicbase_no)
Config->DynamicBase = false;
// MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the
// default setting for any other project type.", but link.exe defaults to
// /FIXED:NO for exe outputs as well. Match behavior, not docs.
bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false);
if (Fixed) {
if (DynamicBaseArg &&
@ -894,6 +1051,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (auto *Arg = Args.getLastArg(OPT_stack))
parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit);
// Handle /guard:cf
if (auto *Arg = Args.getLastArg(OPT_guard))
parseGuard(Arg->getValue());
// Handle /heap
if (auto *Arg = Args.getLastArg(OPT_heap))
parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit);
@ -908,6 +1069,23 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion,
&Config->MinorOSVersion);
// Handle /timestamp
if (llvm::opt::Arg *Arg = Args.getLastArg(OPT_timestamp, OPT_repro)) {
if (Arg->getOption().getID() == OPT_repro) {
Config->Timestamp = 0;
Config->Repro = true;
} else {
Config->Repro = false;
StringRef Value(Arg->getValue());
if (Value.getAsInteger(0, Config->Timestamp))
fatal(Twine("invalid timestamp: ") + Value +
". Expected 32-bit integer");
}
} else {
Config->Repro = false;
Config->Timestamp = time(nullptr);
}
// Handle /alternatename
for (auto *Arg : Args.filtered(OPT_alternatename))
parseAlternateName(Arg->getValue());
@ -921,8 +1099,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->Implib = Arg->getValue();
// Handle /opt.
bool DoGC = !Args.hasArg(OPT_debug);
unsigned ICFLevel = 1; // 0: off, 1: limited, 2: on
bool DoGC = !Args.hasArg(OPT_debug) || Args.hasArg(OPT_profile);
unsigned ICFLevel =
Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on
unsigned TailMerge = 1;
for (auto *Arg : Args.filtered(OPT_opt)) {
std::string Str = StringRef(Arg->getValue()).lower();
SmallVector<StringRef, 1> Vec;
@ -936,14 +1116,18 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
ICFLevel = 2;
} else if (S == "noicf") {
ICFLevel = 0;
} else if (S == "lldtailmerge") {
TailMerge = 2;
} else if (S == "nolldtailmerge") {
TailMerge = 0;
} else if (S.startswith("lldlto=")) {
StringRef OptLevel = S.substr(7);
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
Config->LTOOptLevel > 3)
if (OptLevel.getAsInteger(10, Config->LTOO) || Config->LTOO > 3)
error("/opt:lldlto: invalid optimization level: " + OptLevel);
} else if (S.startswith("lldltojobs=")) {
StringRef Jobs = S.substr(11);
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
if (Jobs.getAsInteger(10, Config->ThinLTOJobs) ||
Config->ThinLTOJobs == 0)
error("/opt:lldltojobs: invalid job count: " + Jobs);
} else if (S.startswith("lldltopartitions=")) {
StringRef N = S.substr(17);
@ -964,11 +1148,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
ICFLevel = 0;
Config->DoGC = DoGC;
Config->DoICF = ICFLevel > 0;
Config->TailMerge = (TailMerge == 1 && Config->DoICF) || TailMerge == 2;
// Handle /lldsavetemps
if (Args.hasArg(OPT_lldsavetemps))
Config->SaveTemps = true;
// Handle /kill-at
if (Args.hasArg(OPT_kill_at))
Config->KillAt = true;
// Handle /lldltocache
if (auto *Arg = Args.getLastArg(OPT_lldltocache))
Config->LTOCache = Arg->getValue();
@ -987,6 +1176,14 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
for (auto *Arg : Args.filtered(OPT_merge))
parseMerge(Arg->getValue());
// Add default section merging rules after user rules. User rules take
// precedence, but we will emit a warning if there is a conflict.
parseMerge(".idata=.rdata");
parseMerge(".didat=.rdata");
parseMerge(".edata=.rdata");
parseMerge(".xdata=.rdata");
parseMerge(".bss=.data");
// Handle /section
for (auto *Arg : Args.filtered(OPT_section))
parseSection(Arg->getValue());
@ -1024,39 +1221,77 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (!Config->ManifestInput.empty() &&
Config->Manifest != Configuration::Embed) {
fatal("/MANIFESTINPUT: requires /MANIFEST:EMBED");
fatal("/manifestinput: requires /manifest:embed");
}
// Handle miscellaneous boolean flags.
Config->AllowBind = Args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
Config->AllowIsolation =
Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
Config->Incremental =
Args.hasFlag(OPT_incremental, OPT_incremental_no,
!Config->DoGC && !Config->DoICF && !Args.hasArg(OPT_order) &&
!Args.hasArg(OPT_profile));
Config->IntegrityCheck =
Args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
Config->TerminalServerAware = Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
Config->TerminalServerAware =
!Config->DLL && Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
Config->DebugDwarf = Args.hasArg(OPT_debug_dwarf);
Config->DebugGHashes = Args.hasArg(OPT_debug_ghash);
Config->DebugSymtab = Args.hasArg(OPT_debug_symtab);
Config->MapFile = getMapFile(Args);
if (Config->Incremental && Args.hasArg(OPT_profile)) {
warn("ignoring '/incremental' due to '/profile' specification");
Config->Incremental = false;
}
if (Config->Incremental && Args.hasArg(OPT_order)) {
warn("ignoring '/incremental' due to '/order' specification");
Config->Incremental = false;
}
if (Config->Incremental && Config->DoGC) {
warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to "
"disable");
Config->Incremental = false;
}
if (Config->Incremental && Config->DoICF) {
warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to "
"disable");
Config->Incremental = false;
}
if (errorCount())
return;
bool WholeArchiveFlag = Args.hasArg(OPT_wholearchive_flag);
std::set<sys::fs::UniqueID> WholeArchives;
for (auto *Arg : Args.filtered(OPT_wholearchive_file))
if (Optional<StringRef> Path = doFindFile(Arg->getValue()))
if (Optional<sys::fs::UniqueID> ID = getUniqueID(*Path))
WholeArchives.insert(*ID);
// A predicate returning true if a given path is an argument for
// /wholearchive:, or /wholearchive is enabled globally.
// This function is a bit tricky because "foo.obj /wholearchive:././foo.obj"
// needs to be handled as "/wholearchive:foo.obj foo.obj".
auto IsWholeArchive = [&](StringRef Path) -> bool {
if (Args.hasArg(OPT_wholearchive_flag))
return true;
if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path))
return WholeArchives.count(*ID);
return false;
};
// Create a list of input files. Files can be given as arguments
// for /defaultlib option.
std::vector<MemoryBufferRef> MBs;
for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) {
switch (Arg->getOption().getID()) {
case OPT_INPUT:
if (Optional<StringRef> Path = findFile(Arg->getValue()))
enqueuePath(*Path, WholeArchiveFlag);
break;
case OPT_wholearchive_file:
if (Optional<StringRef> Path = findFile(Arg->getValue()))
enqueuePath(*Path, true);
break;
}
}
for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file))
if (Optional<StringRef> Path = findFile(Arg->getValue()))
enqueuePath(*Path, IsWholeArchive(*Path));
for (auto *Arg : Args.filtered(OPT_defaultlib))
if (Optional<StringRef> Path = findLib(Arg->getValue()))
enqueuePath(*Path, false);
@ -1160,10 +1395,24 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue());
}
// Put the PDB next to the image if no /pdb flag was passed.
if (ShouldCreatePDB && Config->PDBPath.empty()) {
Config->PDBPath = Config->OutputFile;
sys::path::replace_extension(Config->PDBPath, ".pdb");
if (ShouldCreatePDB) {
// Put the PDB next to the image if no /pdb flag was passed.
if (Config->PDBPath.empty()) {
Config->PDBPath = Config->OutputFile;
sys::path::replace_extension(Config->PDBPath, ".pdb");
}
// The embedded PDB path should be the absolute path to the PDB if no
// /pdbaltpath flag was passed.
if (Config->PDBAltPath.empty()) {
Config->PDBAltPath = Config->PDBPath;
// It's important to make the path absolute and remove dots. This path
// will eventually be written into the PE header, and certain Microsoft
// tools won't work correctly if these assumptions are not held.
sys::fs::make_absolute(Config->PDBAltPath);
sys::path::remove_dots(Config->PDBAltPath);
}
}
// Set default image base if /base is not given.
@ -1176,11 +1425,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
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_count"), 0);
Symtab->addAbsolute(mangle("__guard_fids_table"), 0);
Symtab->addAbsolute(mangle("__guard_flags"), 0x100);
Symtab->addAbsolute(mangle("__guard_flags"), 0);
Symtab->addAbsolute(mangle("__guard_iat_count"), 0);
Symtab->addAbsolute(mangle("__guard_iat_table"), 0);
Symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
@ -1255,7 +1502,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /safeseh.
if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) {
for (ObjFile *File : ObjFile::Instances)
if (!File->SEHCompat)
if (!File->hasSafeSEH())
error("/safeseh: " + File->getName() + " is not compatible with SEH");
if (errorCount())
return;
@ -1275,7 +1522,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
E.Name = Def->getName();
E.Sym = Def;
if (Def->getChunk() &&
!(Def->getChunk()->getPermissions() & IMAGE_SCN_MEM_EXECUTE))
!(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
E.Data = true;
Config->Exports.push_back(E);
});
@ -1318,6 +1565,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (Config->Manifest == Configuration::SideBySide)
createSideBySideManifest();
// Handle /order. We want to do this at this moment because we
// need a complete list of comdat sections to warn on nonexistent
// functions.
if (auto *Arg = Args.getLastArg(OPT_order))
parseOrderFile(Arg->getValue());
// Identify unreferenced COMDAT sections.
if (Config->DoGC)
markLive(Symtab->getChunks());
@ -1328,6 +1581,11 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Write the result.
writeResult();
// Stop early so we can print the results.
Timer::root().stop();
if (Config->ShowTiming)
Timer::root().print();
}
} // namespace coff

View File

@ -21,6 +21,7 @@
#include "llvm/Object/COFF.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/TarWriter.h"
#include <memory>
#include <set>
@ -36,12 +37,6 @@ using llvm::COFF::MachineTypes;
using llvm::COFF::WindowsSubsystem;
using llvm::Optional;
// Implemented in MarkLive.cpp.
void markLive(ArrayRef<Chunk *> Chunks);
// Implemented in ICF.cpp.
void doICF(ArrayRef<Chunk *> Chunks);
class COFFOptTable : public llvm::opt::OptTable {
public:
COFFOptTable();
@ -56,8 +51,10 @@ class ArgParser {
llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); }
// Tokenizes a given string and then parses as command line options in
// .drectve section.
llvm::opt::InputArgList parseDirectives(StringRef S);
// .drectve section. /EXPORT options are returned in second element
// to be processed in fastpath.
std::pair<llvm::opt::InputArgList, std::vector<StringRef>>
parseDirectives(StringRef S);
private:
// Parses command line options.
@ -98,7 +95,11 @@ class LinkerDriver {
// Library search path. The first element is always "" (current directory).
std::vector<StringRef> SearchPaths;
std::set<std::string> VisitedFiles;
// We don't want to add the same file more than once.
// Files are uniquified by their filesystem and file number.
std::set<llvm::sys::fs::UniqueID> VisitedFiles;
std::set<std::string> VisitedLibs;
Symbol *addUndefined(StringRef Sym);
@ -143,6 +144,8 @@ StringRef machineToStr(MachineTypes MT);
// Parses a string in the form of "<integer>[,<integer>]".
void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr);
void parseGuard(StringRef Arg);
// 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);

View File

@ -61,12 +61,7 @@ class Executor {
StringRef Exe = Saver.save(*ExeOrErr);
Args.insert(Args.begin(), Exe);
std::vector<const char *> Vec;
for (StringRef S : Args)
Vec.push_back(S.data());
Vec.push_back(nullptr);
if (sys::ExecuteAndWait(Args[0], Vec.data()) != 0)
if (sys::ExecuteAndWait(Args[0], Args) != 0)
fatal("ExecuteAndWait failed: " +
llvm::join(Args.begin(), Args.end(), " "));
}
@ -128,6 +123,21 @@ void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) {
fatal("invalid number: " + S2);
}
void parseGuard(StringRef FullArg) {
SmallVector<StringRef, 1> SplitArgs;
FullArg.split(SplitArgs, ",");
for (StringRef Arg : SplitArgs) {
if (Arg.equals_lower("no"))
Config->GuardCF = GuardCFLevel::Off;
else if (Arg.equals_lower("nolongjmp"))
Config->GuardCF = GuardCFLevel::NoLongJmp;
else if (Arg.equals_lower("cf") || Arg.equals_lower("longjmp"))
Config->GuardCF = GuardCFLevel::Full;
else
fatal("invalid argument to /guard: " + Arg);
}
}
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
uint32_t *Minor) {
@ -170,6 +180,10 @@ void parseMerge(StringRef S) {
std::tie(From, To) = S.split('=');
if (From.empty() || To.empty())
fatal("/merge: invalid argument: " + S);
if (From == ".rsrc" || To == ".rsrc")
fatal("/merge: cannot merge '.rsrc' with any section");
if (From == ".reloc" || To == ".reloc")
fatal("/merge: cannot merge '.reloc' with any section");
auto Pair = Config->Merge.insert(std::make_pair(From, To));
bool Inserted = Pair.second;
if (!Inserted) {
@ -418,15 +432,15 @@ static std::string createManifestXml() {
return createManifestXmlWithExternalMt(DefaultXml);
}
static std::unique_ptr<MemoryBuffer>
static std::unique_ptr<WritableMemoryBuffer>
createMemoryBufferForManifestRes(size_t ManifestSize) {
size_t ResSize = alignTo(
object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE +
sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +
sizeof(object::WinResHeaderSuffix) + ManifestSize,
object::WIN_RES_DATA_ALIGNMENT);
return MemoryBuffer::getNewMemBuffer(ResSize,
Config->OutputFile + ".manifest.res");
return WritableMemoryBuffer::getNewMemBuffer(ResSize, Config->OutputFile +
".manifest.res");
}
static void writeResFileHeader(char *&Buf) {
@ -465,16 +479,16 @@ static void writeResEntryHeader(char *&Buf, size_t ManifestSize) {
std::unique_ptr<MemoryBuffer> createManifestRes() {
std::string Manifest = createManifestXml();
std::unique_ptr<MemoryBuffer> Res =
std::unique_ptr<WritableMemoryBuffer> Res =
createMemoryBufferForManifestRes(Manifest.size());
char *Buf = const_cast<char *>(Res->getBufferStart());
char *Buf = Res->getBufferStart();
writeResFileHeader(Buf);
writeResEntryHeader(Buf, Manifest.size());
// Copy the manifest data into the .res file.
std::copy(Manifest.begin(), Manifest.end(), Buf);
return Res;
return std::move(Res);
}
void createSideBySideManifest() {
@ -558,9 +572,35 @@ Export parseExport(StringRef Arg) {
static StringRef undecorate(StringRef Sym) {
if (Config->Machine != I386)
return Sym;
// In MSVC mode, a fully decorated stdcall function is exported
// as-is with the leading underscore (with type IMPORT_NAME).
// In MinGW mode, a decorated stdcall function gets the underscore
// removed, just like normal cdecl functions.
if (Sym.startswith("_") && Sym.contains('@') && !Config->MinGW)
return Sym;
return Sym.startswith("_") ? Sym.substr(1) : Sym;
}
// Convert stdcall/fastcall style symbols into unsuffixed symbols,
// with or without a leading underscore. (MinGW specific.)
static StringRef killAt(StringRef Sym, bool Prefix) {
if (Sym.empty())
return Sym;
// Strip any trailing stdcall suffix
Sym = Sym.substr(0, Sym.find('@', 1));
if (!Sym.startswith("@")) {
if (Prefix && !Sym.startswith("_"))
return Saver.save("_" + Sym);
return Sym;
}
// For fastcall, remove the leading @ and replace it with an
// underscore, if prefixes are used.
Sym = Sym.substr(1);
if (Prefix)
Sym = Saver.save("_" + Sym);
return Sym;
}
// Performs error checking on all /export arguments.
// It also sets ordinals.
void fixupExports() {
@ -593,6 +633,15 @@ void fixupExports() {
}
}
if (Config->KillAt && Config->Machine == I386) {
for (Export &E : Config->Exports) {
E.Name = killAt(E.Name, true);
E.ExportName = killAt(E.ExportName, false);
E.ExtName = killAt(E.ExtName, true);
E.SymbolName = killAt(E.SymbolName, true);
}
}
// Uniquefy by name.
DenseMap<StringRef, Export *> Map(Config->Exports.size());
std::vector<Export> V;
@ -702,6 +751,28 @@ static const llvm::opt::OptTable::Info InfoTable[] = {
COFFOptTable::COFFOptTable() : OptTable(InfoTable, true) {}
// Set color diagnostics according to --color-diagnostics={auto,always,never}
// or --no-color-diagnostics flags.
static void handleColorDiagnostics(opt::InputArgList &Args) {
auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
OPT_no_color_diagnostics);
if (!Arg)
return;
if (Arg->getOption().getID() == OPT_color_diagnostics) {
errorHandler().ColorDiagnostics = true;
} else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
errorHandler().ColorDiagnostics = false;
} else {
StringRef S = Arg->getValue();
if (S == "always")
errorHandler().ColorDiagnostics = true;
else if (S == "never")
errorHandler().ColorDiagnostics = false;
else if (S != "auto")
error("unknown option: --color-diagnostics=" + S);
}
}
static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) {
StringRef S = Arg->getValue();
@ -720,50 +791,73 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> Argv) {
// Make InputArgList from string vectors.
unsigned MissingIndex;
unsigned MissingCount;
SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
// We need to get the quoting style for response files before parsing all
// options so we parse here before and ignore all the options but
// --rsp-quoting.
opt::InputArgList Args = Table.ParseArgs(Vec, MissingIndex, MissingCount);
opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount);
// Expand response files (arguments in the form of @<filename>)
// and then parse the argument again.
cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec);
Args = Table.ParseArgs(Vec, MissingIndex, MissingCount);
SmallVector<const char *, 256> ExpandedArgv(Argv.data(), Argv.data() + Argv.size());
cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), ExpandedArgv);
Args = Table.ParseArgs(makeArrayRef(ExpandedArgv).drop_front(), MissingIndex,
MissingCount);
// Print the real command line if response files are expanded.
if (Args.hasArg(OPT_verbose) && Argv.size() != Vec.size()) {
if (Args.hasArg(OPT_verbose) && Argv.size() != ExpandedArgv.size()) {
std::string Msg = "Command line:";
for (const char *S : Vec)
for (const char *S : ExpandedArgv)
Msg += " " + std::string(S);
message(Msg);
}
// Save the command line after response file expansion so we can write it to
// the PDB if necessary.
Config->Argv = {ExpandedArgv.begin(), ExpandedArgv.end()};
// Handle /WX early since it converts missing argument warnings to errors.
errorHandler().FatalWarnings = Args.hasFlag(OPT_WX, OPT_WX_no, false);
if (MissingCount)
fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
handleColorDiagnostics(Args);
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
warn("ignoring unknown argument: " + Arg->getSpelling());
if (Args.hasArg(OPT_lib))
warn("ignoring /lib since it's not the first argument");
return Args;
}
// Tokenizes and parses a given string as command line in .drective section.
opt::InputArgList ArgParser::parseDirectives(StringRef S) {
// Make InputArgList from string vectors.
// /EXPORT options are processed in fastpath.
std::pair<opt::InputArgList, std::vector<StringRef>>
ArgParser::parseDirectives(StringRef S) {
std::vector<StringRef> Exports;
SmallVector<const char *, 16> Rest;
for (StringRef Tok : tokenize(S)) {
if (Tok.startswith_lower("/export:") || Tok.startswith_lower("-export:"))
Exports.push_back(Tok.substr(strlen("/export:")));
else
Rest.push_back(Tok.data());
}
// Make InputArgList from unparsed string vectors.
unsigned MissingIndex;
unsigned MissingCount;
opt::InputArgList Args =
Table.ParseArgs(tokenize(S), MissingIndex, MissingCount);
opt::InputArgList Args = Table.ParseArgs(Rest, MissingIndex, MissingCount);
if (MissingCount)
fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
warn("ignoring unknown argument: " + Arg->getSpelling());
return Args;
return {std::move(Args), std::move(Exports)};
}
// link.exe has an interesting feature. If LINK or _LINK_ environment
@ -773,11 +867,11 @@ opt::InputArgList ArgParser::parseLINK(std::vector<const char *> Argv) {
// Concatenate LINK env and command line arguments, and then parse them.
if (Optional<std::string> S = Process::GetEnv("LINK")) {
std::vector<const char *> V = tokenize(*S);
Argv.insert(Argv.begin(), V.begin(), V.end());
Argv.insert(std::next(Argv.begin()), V.begin(), V.end());
}
if (Optional<std::string> S = Process::GetEnv("_LINK_")) {
std::vector<const char *> V = tokenize(*S);
Argv.insert(Argv.begin(), V.begin(), V.end());
Argv.insert(std::next(Argv.begin()), V.begin(), V.end());
}
return parse(Argv);
}

View File

@ -18,9 +18,11 @@
//
//===----------------------------------------------------------------------===//
#include "ICF.h"
#include "Chunks.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Timer.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Parallel.h"
@ -34,6 +36,8 @@ using namespace llvm;
namespace lld {
namespace coff {
static Timer ICFTimer("ICF", Timer::root());
class ICF {
public:
void run(ArrayRef<Chunk *> V);
@ -41,6 +45,8 @@ class ICF {
private:
void segregate(size_t Begin, size_t End, bool Constant);
bool assocEquals(const SectionChunk *A, const SectionChunk *B);
bool equalsConstant(const SectionChunk *A, const SectionChunk *B);
bool equalsVariable(const SectionChunk *A, const SectionChunk *B);
@ -61,8 +67,8 @@ class ICF {
// Returns a hash value for S.
uint32_t ICF::getHash(SectionChunk *C) {
return hash_combine(C->getPermissions(), C->SectionName, C->NumRelocs,
C->Alignment, uint32_t(C->Header->SizeOfRawData),
return hash_combine(C->getOutputCharacteristics(), C->SectionName,
C->Relocs.size(), uint32_t(C->Header->SizeOfRawData),
C->Checksum, C->getContents());
}
@ -73,21 +79,27 @@ uint32_t ICF::getHash(SectionChunk *C) {
// 2017) says that /opt:icf folds both functions and read-only data.
// Despite that, the MSVC linker folds only functions. We found
// a few instances of programs that are not safe for data merging.
// Therefore, we merge only functions just like the MSVC tool. However, we merge
// identical .xdata sections, because the address of unwind information is
// insignificant to the user program and the Visual C++ linker does this.
// Therefore, we merge only functions just like the MSVC tool. However, we also
// merge read-only sections in a couple of cases where the address of the
// section is insignificant to the user program and the behaviour matches that
// of the Visual C++ linker.
bool ICF::isEligible(SectionChunk *C) {
// Non-comdat chunks, dead chunks, and writable chunks are not elegible.
bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
bool Writable = C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
if (!C->isCOMDAT() || !C->isLive() || Writable)
return false;
// Code sections are eligible.
if (C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
if (C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
return true;
// .xdata unwind info sections are eligble.
return C->getSectionName().split('$').first == ".xdata";
// .pdata and .xdata unwind info sections are eligible.
StringRef OutSecName = C->getSectionName().split('$').first;
if (OutSecName == ".pdata" || OutSecName == ".xdata")
return true;
// So are vtables.
return C->Sym && C->Sym->getName().startswith("??_7");
}
// Split an equivalence class into smaller classes.
@ -116,10 +128,23 @@ void ICF::segregate(size_t Begin, size_t End, bool Constant) {
}
}
// Returns true if two sections' associative children are equal.
bool ICF::assocEquals(const SectionChunk *A, const SectionChunk *B) {
auto ChildClasses = [&](const SectionChunk *SC) {
std::vector<uint32_t> Classes;
for (const SectionChunk *C : SC->children())
if (!C->SectionName.startswith(".debug") &&
C->SectionName != ".gfids$y" && C->SectionName != ".gljmp$y")
Classes.push_back(C->Class[Cnt % 2]);
return Classes;
};
return ChildClasses(A) == ChildClasses(B);
}
// Compare "non-moving" part of two sections, namely everything
// except relocation targets.
bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
if (A->NumRelocs != B->NumRelocs)
if (A->Relocs.size() != B->Relocs.size())
return false;
// Compare relocations.
@ -142,10 +167,11 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
return false;
// Compare section attributes and contents.
return A->getPermissions() == B->getPermissions() &&
A->SectionName == B->SectionName && A->Alignment == B->Alignment &&
return A->getOutputCharacteristics() == B->getOutputCharacteristics() &&
A->SectionName == B->SectionName &&
A->Header->SizeOfRawData == B->Header->SizeOfRawData &&
A->Checksum == B->Checksum && A->getContents() == B->getContents();
A->Checksum == B->Checksum && A->getContents() == B->getContents() &&
assocEquals(A, B);
}
// Compare "moving" part of two sections, namely relocation targets.
@ -161,9 +187,12 @@ bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
return false;
};
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq);
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(),
Eq) &&
assocEquals(A, B);
}
// Find the first Chunk after Begin that has a different class from Begin.
size_t ICF::findBoundary(size_t Begin, size_t End) {
for (size_t I = Begin + 1; I < End; ++I)
if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2])
@ -173,11 +202,8 @@ size_t ICF::findBoundary(size_t Begin, size_t End) {
void ICF::forEachClassRange(size_t Begin, size_t End,
std::function<void(size_t, size_t)> Fn) {
if (Begin > 0)
Begin = findBoundary(Begin - 1, End);
while (Begin < End) {
size_t Mid = findBoundary(Begin, Chunks.size());
size_t Mid = findBoundary(Begin, End);
Fn(Begin, Mid);
Begin = Mid;
}
@ -193,12 +219,22 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
return;
}
// Split sections into 256 shards and call Fn in parallel.
size_t NumShards = 256;
// Shard into non-overlapping intervals, and call Fn in parallel.
// The sharding must be completed before any calls to Fn are made
// so that Fn can modify the Chunks in its shard without causing data
// races.
const size_t NumShards = 256;
size_t Step = Chunks.size() / NumShards;
for_each_n(parallel::par, size_t(0), NumShards, [&](size_t I) {
size_t End = (I == NumShards - 1) ? Chunks.size() : (I + 1) * Step;
forEachClassRange(I * Step, End, Fn);
size_t Boundaries[NumShards + 1];
Boundaries[0] = 0;
Boundaries[NumShards] = Chunks.size();
for_each_n(parallel::par, size_t(1), NumShards, [&](size_t I) {
Boundaries[I] = findBoundary((I - 1) * Step, Chunks.size());
});
for_each_n(parallel::par, size_t(1), NumShards + 1, [&](size_t I) {
if (Boundaries[I - 1] < Boundaries[I]) {
forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn);
}
});
++Cnt;
}
@ -207,6 +243,8 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
// Two sections are considered the same if their section headers,
// contents and relocations are all the same.
void ICF::run(ArrayRef<Chunk *> Vec) {
ScopedTimer T(ICFTimer);
// Collect only mergeable sections and group by hash value.
uint32_t NextId = 1;
for (Chunk *C : Vec) {
@ -218,6 +256,12 @@ void ICF::run(ArrayRef<Chunk *> Vec) {
}
}
// Make sure that ICF doesn't merge sections that are being handled by string
// tail merging.
for (auto &P : MergeChunk::Instances)
for (SectionChunk *SC : P.second->Sections)
SC->Class[0] = NextId++;
// Initially, we use hash values to partition sections.
for_each(parallel::par, Chunks.begin(), Chunks.end(), [&](SectionChunk *SC) {
// Set MSB to 1 to avoid collisions with non-hash classs.

View File

@ -1,4 +1,4 @@
//===- Strings.h ------------------------------------------------*- C++ -*-===//
//===- ICF.h --------------------------------------------------------------===//
//
// The LLVM Linker
//
@ -7,17 +7,20 @@
//
//===----------------------------------------------------------------------===//
#ifndef LLD_COFF_STRINGS_H
#define LLD_COFF_STRINGS_H
#ifndef LLD_COFF_ICF_H
#define LLD_COFF_ICF_H
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include <string>
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
namespace lld {
namespace coff {
llvm::Optional<std::string> demangleMSVC(llvm::StringRef S);
}
}
class Chunk;
void doICF(ArrayRef<Chunk *> Chunks);
} // namespace coff
} // namespace lld
#endif

View File

@ -27,6 +27,7 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Target/TargetOptions.h"
#include <cstring>
#include <system_error>
@ -138,12 +139,13 @@ void ObjFile::initializeChunks() {
if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
SparseChunks[I] = PendingComdat;
else
SparseChunks[I] = readSection(I, nullptr);
SparseChunks[I] = readSection(I, nullptr, "");
}
}
SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
const coff_aux_section_definition *Def) {
const coff_aux_section_definition *Def,
StringRef LeaderName) {
const coff_section *Sec;
StringRef Name;
if (auto EC = COFFObj->getSection(SectionNumber, Sec))
@ -151,15 +153,7 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
if (auto EC = COFFObj->getSectionName(Sec, Name))
fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " +
EC.message());
if (Name == ".sxdata") {
ArrayRef<uint8_t> Data;
COFFObj->getSectionContents(Sec, Data);
if (Data.size() % 4 != 0)
fatal(".sxdata must be an array of symbol table indices");
SXData = {reinterpret_cast<const ulittle32_t *>(Data.data()),
Data.size() / 4};
return nullptr;
}
if (Name == ".drectve") {
ArrayRef<uint8_t> Data;
COFFObj->getSectionContents(Sec, Data);
@ -177,8 +171,8 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
// CodeView needs a linker support. We need to interpret and debug
// info, and then write it to a separate .pdb file.
// Ignore debug info unless /debug is given.
if (!Config->Debug && Name.startswith(".debug"))
// Ignore DWARF debug info unless /debug is given.
if (!Config->Debug && Name.startswith(".debug_"))
return nullptr;
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
@ -191,6 +185,18 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
// linked in the regular manner.
if (C->isCodeView())
DebugChunks.push_back(C);
else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gfids$y")
GuardFidChunks.push_back(C);
else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gljmp$y")
GuardLJmpChunks.push_back(C);
else if (Name == ".sxdata")
SXDataChunks.push_back(C);
else if (Config->TailMerge && Sec->NumberOfRelocations == 0 &&
Name == ".rdata" && LeaderName.startswith("??_C@"))
// COFF sections that look like string literal sections (i.e. no
// relocations, in .rdata, leader symbol name matches the MSVC name mangling
// for string literals) are subject to string tail merging.
MergeChunk::addSection(C);
else
Chunks.push_back(C);
@ -211,7 +217,7 @@ void ObjFile::readAssociativeDefinition(
// the section; otherwise mark it as discarded.
int32_t SectionNumber = Sym.getSectionNumber();
if (Parent) {
SparseChunks[SectionNumber] = readSection(SectionNumber, Def);
SparseChunks[SectionNumber] = readSection(SectionNumber, Def, "");
if (SparseChunks[SectionNumber])
Parent->addAssociative(SparseChunks[SectionNumber]);
} else {
@ -275,6 +281,13 @@ void ObjFile::initializeSymbols() {
if (auto *Def = Sym.getSectionDefinition())
if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
readAssociativeDefinition(Sym, Def);
if (SparseChunks[Sym.getSectionNumber()] == PendingComdat) {
StringRef Name;
COFFObj->getSymbolName(Sym, Name);
log("comdat section " + Name +
" without leader and unassociated, discarding");
continue;
}
Symbols[I] = createRegular(Sym);
}
@ -294,43 +307,46 @@ Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) {
Optional<Symbol *> ObjFile::createDefined(
COFFSymbolRef Sym,
std::vector<const coff_aux_section_definition *> &ComdatDefs) {
StringRef Name;
auto GetName = [&]() {
StringRef S;
COFFObj->getSymbolName(Sym, S);
return S;
};
if (Sym.isCommon()) {
auto *C = make<CommonChunk>(Sym);
Chunks.push_back(C);
COFFObj->getSymbolName(Sym, Name);
Symbol *S =
Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C);
return S;
return Symtab->addCommon(this, GetName(), Sym.getValue(), Sym.getGeneric(),
C);
}
if (Sym.isAbsolute()) {
COFFObj->getSymbolName(Sym, Name);
StringRef Name = GetName();
// 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;
Feat00Flags = Sym.getValue();
return nullptr;
}
if (Sym.isExternal())
return Symtab->addAbsolute(Name, Sym);
else
return make<DefinedAbsolute>(Name, Sym);
return make<DefinedAbsolute>(Name, Sym);
}
int32_t SectionNumber = Sym.getSectionNumber();
if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
return nullptr;
// Reserved sections numbers don't have contents.
if (llvm::COFF::isReservedSectionNumber(SectionNumber))
fatal("broken object file: " + toString(this));
fatal(toString(this) + ": " + GetName() +
" should not refer to special section " + Twine(SectionNumber));
// This symbol references a section which is not present in the section
// header.
if ((uint32_t)SectionNumber >= SparseChunks.size())
fatal("broken object file: " + toString(this));
fatal(toString(this) + ": " + GetName() +
" should not refer to non-existent section " + Twine(SectionNumber));
// Handle comdat leader symbols.
if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) {
@ -338,16 +354,16 @@ Optional<Symbol *> ObjFile::createDefined(
Symbol *Leader;
bool Prevailing;
if (Sym.isExternal()) {
COFFObj->getSymbolName(Sym, Name);
std::tie(Leader, Prevailing) =
Symtab->addComdat(this, Name, Sym.getGeneric());
Symtab->addComdat(this, GetName(), Sym.getGeneric());
} else {
Leader = make<DefinedRegular>(this, /*Name*/ "", false,
/*IsExternal*/ false, Sym.getGeneric());
Prevailing = true;
}
if (Prevailing) {
SectionChunk *C = readSection(SectionNumber, Def);
SectionChunk *C = readSection(SectionNumber, Def, GetName());
SparseChunks[SectionNumber] = C;
C->Sym = cast<DefinedRegular>(Leader);
cast<DefinedRegular>(Leader)->Data = &C->Repl;
@ -429,7 +445,8 @@ void ImportFile::parse() {
// 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)
ThunkSym = Symtab->addImportThunk(Name, ImpSym, Hdr->Machine);
ThunkSym = Symtab->addImportThunk(
Name, cast_or_null<DefinedImportData>(ImpSym), Hdr->Machine);
}
void BitcodeFile::parse() {
@ -462,7 +479,7 @@ void BitcodeFile::parse() {
} else {
Sym = Symtab->addRegular(this, SymName);
}
SymbolBodies.push_back(Sym);
Symbols.push_back(Sym);
}
Directives = Obj->getCOFFLinkerOpts();
}
@ -486,10 +503,7 @@ MachineTypes BitcodeFile::getMachineType() {
// 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);
return sys::path::filename(Path, sys::path::Style::windows);
}
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".

View File

@ -110,6 +110,9 @@ class ObjFile : public InputFile {
MachineTypes getMachineType() override;
ArrayRef<Chunk *> getChunks() { return Chunks; }
ArrayRef<SectionChunk *> getDebugChunks() { return DebugChunks; }
ArrayRef<SectionChunk *> getSXDataChunks() { return SXDataChunks; }
ArrayRef<SectionChunk *> getGuardFidChunks() { return GuardFidChunks; }
ArrayRef<SectionChunk *> getGuardLJmpChunks() { return GuardLJmpChunks; }
ArrayRef<Symbol *> getSymbols() { return Symbols; }
// Returns a Symbol object for the SymbolIndex'th symbol in the
@ -123,13 +126,17 @@ class ObjFile : public InputFile {
static std::vector<ObjFile *> Instances;
// True if this object file is compatible with SEH.
// COFF-specific and x86-only.
bool SEHCompat = false;
// Flags in the absolute @feat.00 symbol if it is present. These usually
// indicate if an object was compiled with certain security features enabled
// like stack guard, safeseh, /guard:cf, or other things.
uint32_t Feat00Flags = 0;
// The symbol table indexes of the safe exception handlers.
// COFF-specific and x86-only.
ArrayRef<llvm::support::ulittle32_t> SXData;
// True if this object file is compatible with SEH. COFF-specific and
// x86-only. COFF spec 5.10.1. The .sxdata section.
bool hasSafeSEH() { return Feat00Flags & 0x1; }
// True if this file was compiled with /guard:cf.
bool hasGuardCF() { return Feat00Flags & 0x800; }
// Pointer to the PDB module descriptor builder. Various debug info records
// will reference object files by "module index", which is here. Things like
@ -143,7 +150,8 @@ class ObjFile : public InputFile {
SectionChunk *
readSection(uint32_t SectionNumber,
const llvm::object::coff_aux_section_definition *Def);
const llvm::object::coff_aux_section_definition *Def,
StringRef LeaderName);
void readAssociativeDefinition(
COFFSymbolRef COFFSym,
@ -165,6 +173,15 @@ class ObjFile : public InputFile {
// CodeView debug info sections.
std::vector<SectionChunk *> DebugChunks;
// Chunks containing symbol table indices of exception handlers. Only used for
// 32-bit x86.
std::vector<SectionChunk *> SXDataChunks;
// Chunks containing symbol table indices of address taken symbols and longjmp
// targets. These are not linked into the final binary when /guard:cf is set.
std::vector<SectionChunk *> GuardFidChunks;
std::vector<SectionChunk *> GuardLJmpChunks;
// 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.
@ -184,15 +201,14 @@ class ObjFile : public InputFile {
// for details about the format.
class ImportFile : public InputFile {
public:
explicit ImportFile(MemoryBufferRef M)
: InputFile(ImportKind, M), Live(!Config->DoGC) {}
explicit ImportFile(MemoryBufferRef M) : InputFile(ImportKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
static std::vector<ImportFile *> Instances;
DefinedImportData *ImpSym = nullptr;
DefinedImportThunk *ThunkSym = nullptr;
Symbol *ImpSym = nullptr;
Symbol *ThunkSym = nullptr;
std::string DLLName;
private:
@ -204,12 +220,15 @@ class ImportFile : public InputFile {
Chunk *Location = nullptr;
// We want to eliminate dllimported symbols if no one actually refers them.
// This "Live" bit is used to keep track of which import library members
// These "Live" bits are used to keep track of which import library members
// are actually in use.
//
// If the Live bit is turned off by MarkLive, Writer will ignore dllimported
// symbols provided by this import library member.
bool Live;
// symbols provided by this import library member. We also track whether the
// imported symbol is used separately from whether the thunk is used in order
// to avoid creating unnecessary thunks.
bool Live = !Config->DoGC;
bool ThunkLive = !Config->DoGC;
};
// Used for LTO.
@ -217,7 +236,7 @@ class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
ArrayRef<Symbol *> getSymbols() { return SymbolBodies; }
ArrayRef<Symbol *> getSymbols() { return Symbols; }
MachineTypes getMachineType() override;
static std::vector<BitcodeFile *> Instances;
std::unique_ptr<llvm::lto::InputFile> Obj;
@ -225,7 +244,7 @@ class BitcodeFile : public InputFile {
private:
void parse() override;
std::vector<Symbol *> SymbolBodies;
std::vector<Symbol *> Symbols;
};
} // namespace coff

View File

@ -12,6 +12,7 @@
#include "InputFiles.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
@ -40,47 +41,32 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::coff;
static void diagnosticHandler(const DiagnosticInfo &DI) {
SmallString<128> ErrStorage;
raw_svector_ostream OS(ErrStorage);
DiagnosticPrinterRawOStream DP(OS);
DI.print(DP);
warn(ErrStorage);
}
static void checkError(Error E) {
handleAllErrors(std::move(E),
[&](ErrorInfoBase &EIB) { error(EIB.message()); });
}
static void saveBuffer(StringRef Buffer, const Twine &Path) {
std::error_code EC;
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
if (EC)
error("cannot create " + Path + ": " + EC.message());
OS << Buffer;
}
static std::unique_ptr<lto::LTO> createLTO() {
lto::Config Conf;
Conf.Options = InitTargetOptionsFromCodeGenFlags();
lto::Config C;
C.Options = InitTargetOptionsFromCodeGenFlags();
// Always emit a section per function/datum with LTO. LLVM LTO should get most
// of the benefit of linker GC, but there are still opportunities for ICF.
C.Options.FunctionSections = true;
C.Options.DataSections = true;
// Use static reloc model on 32-bit x86 because it usually results in more
// compact code, and because there are also known code generation bugs when
// using the PIC model (see PR34306).
if (Config->Machine == COFF::IMAGE_FILE_MACHINE_I386)
Conf.RelocModel = Reloc::Static;
C.RelocModel = Reloc::Static;
else
Conf.RelocModel = Reloc::PIC_;
Conf.DisableVerify = true;
Conf.DiagHandler = diagnosticHandler;
Conf.OptLevel = Config->LTOOptLevel;
C.RelocModel = Reloc::PIC_;
C.DisableVerify = true;
C.DiagHandler = diagnosticHandler;
C.OptLevel = Config->LTOO;
if (Config->SaveTemps)
checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
/*UseInputModulePath*/ true));
checkError(C.addSaveTemps(std::string(Config->OutputFile) + ".",
/*UseInputModulePath*/ true));
lto::ThinBackend Backend;
if (Config->LTOJobs != 0)
Backend = lto::createInProcessThinBackend(Config->LTOJobs);
return llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
if (Config->ThinLTOJobs != 0)
Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
return llvm::make_unique<lto::LTO>(std::move(C), Backend,
Config->LTOPartitions);
}
@ -119,7 +105,7 @@ void BitcodeCompiler::add(BitcodeFile &F) {
// and return the resulting objects.
std::vector<StringRef> BitcodeCompiler::compile() {
unsigned MaxTasks = LTOObj->getMaxTasks();
Buff.resize(MaxTasks);
Buf.resize(MaxTasks);
Files.resize(MaxTasks);
// The /lldltocache option specifies the path to a directory in which to cache
@ -127,15 +113,15 @@ std::vector<StringRef> BitcodeCompiler::compile() {
// specified, configure LTO to use it as the cache directory.
lto::NativeObjectCache Cache;
if (!Config->LTOCache.empty())
Cache = check(
lto::localCache(Config->LTOCache,
[&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
StringRef Path) { Files[Task] = std::move(MB); }));
Cache = check(lto::localCache(
Config->LTOCache, [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
Files[Task] = std::move(MB);
}));
checkError(LTOObj->run(
[&](size_t Task) {
return llvm::make_unique<lto::NativeObjectStream>(
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
llvm::make_unique<raw_svector_ostream>(Buf[Task]));
},
Cache));
@ -144,15 +130,15 @@ std::vector<StringRef> BitcodeCompiler::compile() {
std::vector<StringRef> Ret;
for (unsigned I = 0; I != MaxTasks; ++I) {
if (Buff[I].empty())
if (Buf[I].empty())
continue;
if (Config->SaveTemps) {
if (I == 0)
saveBuffer(Buff[I], Config->OutputFile + ".lto.obj");
saveBuffer(Buf[I], Config->OutputFile + ".lto.obj");
else
saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.obj");
saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.obj");
}
Ret.emplace_back(Buff[I].data(), Buff[I].size());
Ret.emplace_back(Buf[I].data(), Buf[I].size());
}
for (std::unique_ptr<MemoryBuffer> &File : Files)

View File

@ -48,7 +48,7 @@ class BitcodeCompiler {
private:
std::unique_ptr<llvm::lto::LTO> LTOObj;
std::vector<SmallString<0>> Buff;
std::vector<SmallString<0>> Buf;
std::vector<std::unique_ptr<MemoryBuffer>> Files;
};
}

View File

@ -23,7 +23,6 @@
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Parallel.h"
#include "llvm/Support/raw_ostream.h"
@ -37,14 +36,15 @@ using namespace lld::coff;
typedef DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>
SymbolMapTy;
static const std::string Indent8 = " "; // 8 spaces
static const std::string Indent16 = " "; // 16 spaces
// Print out the first three columns of a line.
static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
uint64_t Align) {
OS << format("%08llx %08llx %5lld ", Addr, Size, Align);
}
static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
// Returns a list of all symbols that we want to print out.
static std::vector<DefinedRegular *> getSymbols() {
std::vector<DefinedRegular *> V;
@ -79,7 +79,7 @@ getSymbolStrings(ArrayRef<DefinedRegular *> Syms) {
for_each_n(parallel::par, (size_t)0, Syms.size(), [&](size_t I) {
raw_string_ostream OS(Str[I]);
writeHeader(OS, Syms[I]->getRVA(), 0, 0);
OS << indent(2) << toString(*Syms[I]);
OS << Indent16 << toString(*Syms[I]);
});
DenseMap<DefinedRegular *, std::string> Ret;
@ -108,7 +108,7 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
// Print out file contents.
for (OutputSection *Sec : OutputSections) {
writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize);
OS << Sec->getName() << '\n';
OS << Sec->Name << '\n';
for (Chunk *C : Sec->getChunks()) {
auto *SC = dyn_cast<SectionChunk>(C);
@ -116,7 +116,7 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
continue;
writeHeader(OS, SC->getRVA(), SC->getSize(), SC->Alignment);
OS << indent(1) << SC->File->getName() << ":(" << SC->getSectionName()
OS << Indent8 << SC->File->getName() << ":(" << SC->getSectionName()
<< ")\n";
for (DefinedRegular *Sym : SectionSyms[SC])
OS << SymStr[Sym] << '\n';

View File

@ -9,16 +9,21 @@
#include "Chunks.h"
#include "Symbols.h"
#include "lld/Common/Timer.h"
#include "llvm/ADT/STLExtras.h"
#include <vector>
namespace lld {
namespace coff {
static Timer GCTimer("GC", Timer::root());
// 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(ArrayRef<Chunk *> Chunks) {
ScopedTimer T(GCTimer);
// 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.
@ -43,7 +48,7 @@ void markLive(ArrayRef<Chunk *> Chunks) {
else if (auto *Sym = dyn_cast<DefinedImportData>(B))
Sym->File->Live = true;
else if (auto *Sym = dyn_cast<DefinedImportThunk>(B))
Sym->WrappedSym->File->Live = true;
Sym->WrappedSym->File->Live = Sym->WrappedSym->File->ThunkLive = true;
};
// Add GC root chunks.

24
COFF/MarkLive.h Normal file
View File

@ -0,0 +1,24 @@
//===- MarkLive.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_MARKLIVE_H
#define LLD_COFF_MARKLIVE_H
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
namespace lld {
namespace coff {
void markLive(ArrayRef<Chunk *> Chunks);
} // namespace coff
} // namespace lld
#endif // LLD_COFF_MARKLIVE_H

View File

@ -138,7 +138,7 @@ void coff::writeDefFile(StringRef Name) {
<< "@" << E.Ordinal;
if (auto *Def = dyn_cast_or_null<Defined>(E.Sym)) {
if (Def && Def->getChunk() &&
!(Def->getChunk()->getPermissions() & IMAGE_SCN_MEM_EXECUTE))
!(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
OS << " DATA";
}
OS << "\n";

View File

@ -20,6 +20,10 @@ def align : P<"align", "Section alignment">;
def aligncomm : P<"aligncomm", "Set common symbol alignment">;
def alternatename : P<"alternatename", "Define weak alias">;
def base : P<"base", "Base address of the program">;
def color_diagnostics: Flag<["--"], "color-diagnostics">,
HelpText<"Use colors in diagnostics">;
def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">,
HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">;
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
def delayload : P<"delayload", "Delay loaded DLL name">;
def entry : P<"entry", "Name of entry point symbol">;
@ -28,9 +32,12 @@ def errorlimit : P<"errorlimit",
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 guard : P<"guard", "Control flow guard">;
def heap : P<"heap", "Size of the heap">;
def ignore : P<"ignore", "Specify warning codes to ignore">;
def implib : P<"implib", "Import library name">;
def lib : F<"lib">,
HelpText<"Act like lib.exe; must be first argument if present">;
def libpath : P<"libpath", "Additional library search path">;
def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">;
@ -42,12 +49,18 @@ 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 order : P<"order", "Put functions in order">;
def out : P<"out", "Path to file to write output">;
def natvis : P<"natvis", "Path to natvis file to embed in the PDB">;
def no_color_diagnostics: F<"no-color-diagnostics">,
HelpText<"Do not use colors in diagnostics">;
def pdb : P<"pdb", "PDB file path">;
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
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 timestamp : P<"timestamp", "Specify the PE header timestamp">;
def version : P<"version", "Specify a version number in the PE header">;
def wholearchive_file : P<"wholearchive", "Include all object files from this archive">;
@ -72,12 +85,14 @@ def deffile : Joined<["/", "-"], "def:">,
HelpText<"Use module-definition file">;
def debug : F<"debug">, HelpText<"Embed a symbol table in the image">;
def debug_full : F<"debug:full">, Alias<debug>;
def debugtype : P<"debugtype", "Debug Info Options">;
def dll : F<"dll">, HelpText<"Create a DLL">;
def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">;
def nodefaultlib_all : F<"nodefaultlib">;
def noentry : F<"noentry">;
def profile : F<"profile">;
def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">;
def swaprun_cd : F<"swaprun:cd">;
def swaprun_net : F<"swaprun:net">;
def verbose : F<"verbose">;
@ -102,6 +117,12 @@ defm fixed : B<"fixed", "Disable base relocations",
defm highentropyva : B<"highentropyva",
"Enable 64-bit ASLR (default on 64-bit)",
"Disable 64-bit ASLR">;
defm incremental : B<"incremental",
"Keep original import library if contents are unchanged",
"Overwrite import library even if contents are unchanged">;
defm integritycheck : B<"integritycheck",
"Set FORCE_INTEGRITY bit in PE header",
"No effect (default)">;
defm largeaddressaware : B<"largeaddressaware",
"Enable large addresses (default on 64-bit)",
"Disable large addresses (default on 32-bit)">;
@ -120,10 +141,14 @@ def help_q : Flag<["/?", "-?"], "">, Alias<help>;
// LLD extensions
def debug_ghash : F<"debug:ghash">;
def debug_dwarf : F<"debug:dwarf">;
def debug_symtab : F<"debug:symtab">;
def export_all_symbols : F<"export-all-symbols">;
def kill_at : F<"kill-at">;
def lldmingw : F<"lldmingw">;
def msvclto : F<"msvclto">;
def output_def : Joined<["/", "-"], "output-def:">;
def pdb_source_path : P<"pdbsourcepath",
"Base path used to make relative source file path absolute in PDB">;
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
def dash_dash_version : Flag<["--"], "version">,
@ -132,6 +157,7 @@ def dash_dash_version : Flag<["--"], "version">,
// Flags for debugging
def lldmap : F<"lldmap">;
def lldmap_file : Joined<["/", "-"], "lldmap:">;
def show_timing : F<"time">;
//==============================================================================
// The flags below do nothing. They are defined only for link.exe compatibility.
@ -146,8 +172,6 @@ multiclass QB<string name> {
def functionpadmin : F<"functionpadmin">;
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">;
@ -157,8 +181,6 @@ def delay : QF<"delay">;
def errorreport : QF<"errorreport">;
def idlout : QF<"idlout">;
def maxilksize : QF<"maxilksize">;
def natvis : QF<"natvis">;
def pdbaltpath : QF<"pdbaltpath">;
def tlbid : QF<"tlbid">;
def tlbout : QF<"tlbout">;
def verbose_all : QF<"verbose">;

View File

@ -15,7 +15,7 @@
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/DebugInfo/CodeView/CVDebugRecord.h"
#include "lld/Common/Timer.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
@ -45,8 +45,10 @@
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
#include "llvm/DebugInfo/PDB/PDB.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/CVDebugRecord.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JamCRC.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
@ -61,6 +63,15 @@ using llvm::object::coff_section;
static ExitOnError ExitOnErr;
static Timer TotalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
static Timer AddObjectsTimer("Add Objects", TotalPdbLinkTimer);
static Timer TypeMergingTimer("Type Merging", AddObjectsTimer);
static Timer SymbolMergingTimer("Symbol Merging", AddObjectsTimer);
static Timer GlobalsLayoutTimer("Globals Stream Layout", TotalPdbLinkTimer);
static Timer TpiStreamLayoutTimer("TPI Stream Layout", TotalPdbLinkTimer);
static Timer DiskCommitTimer("Commit to Disk", TotalPdbLinkTimer);
namespace {
/// Map from type index and item index in a type server PDB to the
/// corresponding index in the destination PDB.
@ -74,11 +85,19 @@ class PDBLinker {
public:
PDBLinker(SymbolTable *Symtab)
: Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {}
IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {
// This isn't strictly necessary, but link.exe usually puts an empty string
// as the first "valid" string in the string table, so we do the same in
// order to maintain as much byte-for-byte compatibility as possible.
PDBStrTab.insert("");
}
/// Emit the basic PDB structure: initial streams, headers, etc.
void initialize(const llvm::codeview::DebugInfo &BuildId);
/// Add natvis files specified on the command line.
void addNatvisFiles();
/// Link CodeView from each object file in the symbol table into the PDB.
void addObjectsToPDB();
@ -96,18 +115,16 @@ class PDBLinker {
/// If the object does not use a type server PDB (compiled with /Z7), we merge
/// all the type and item records from the .debug$S stream and fill in the
/// caller-provided ObjectIndexMap.
const CVIndexMap &mergeDebugT(ObjFile *File, CVIndexMap &ObjectIndexMap);
Expected<const CVIndexMap&> mergeDebugT(ObjFile *File,
CVIndexMap &ObjectIndexMap);
const CVIndexMap &maybeMergeTypeServerPDB(ObjFile *File,
TypeServer2Record &TS);
Expected<const CVIndexMap&> maybeMergeTypeServerPDB(ObjFile *File,
TypeServer2Record &TS);
/// Add the section map and section contributions to the PDB.
void addSections(ArrayRef<OutputSection *> OutputSections,
ArrayRef<uint8_t> SectionTable);
void addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule,
OutputSection *OS, Chunk *C);
/// Write the PDB to disk.
void commit();
@ -136,10 +153,19 @@ class PDBLinker {
llvm::SmallString<128> NativePath;
/// A list of other PDBs which are loaded during the linking process and which
/// we need to keep around since the linking operation may reference pointers
/// inside of these PDBs.
llvm::SmallVector<std::unique_ptr<pdb::NativeSession>, 2> LoadedPDBs;
std::vector<pdb::SecMapEntry> SectionMap;
/// Type index mappings of type server PDBs that we've loaded so far.
std::map<GUID, CVIndexMap> TypeServerIndexMappings;
/// List of TypeServer PDBs which cannot be loaded.
/// Cached to prevent repeated load attempts.
std::set<GUID> MissingTypeServerPDBs;
};
}
@ -179,8 +205,8 @@ static bool canUseDebugH(ArrayRef<uint8_t> DebugH) {
DebugH = DebugH.drop_front(sizeof(object::debug_h_header));
return Header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
Header->Version == 0 &&
Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1) &&
(DebugH.size() % 20 == 0);
Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
(DebugH.size() % 8 == 0);
}
static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *File) {
@ -230,8 +256,10 @@ maybeReadTypeServerRecord(CVTypeArray &Types) {
return std::move(TS);
}
const CVIndexMap &PDBLinker::mergeDebugT(ObjFile *File,
CVIndexMap &ObjectIndexMap) {
Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File,
CVIndexMap &ObjectIndexMap) {
ScopedTimer T(TypeMergingTimer);
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
if (Data.empty())
return ObjectIndexMap;
@ -304,11 +332,19 @@ tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
return std::move(NS);
}
const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
TypeServer2Record &TS) {
// First, check if we already loaded a PDB with this GUID. Return the type
Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
TypeServer2Record &TS) {
const GUID& TSId = TS.getGuid();
StringRef TSPath = TS.getName();
// First, check if the PDB has previously failed to load.
if (MissingTypeServerPDBs.count(TSId))
return make_error<pdb::GenericError>(
pdb::generic_error_code::type_server_not_found, TSPath);
// Second, check if we already loaded a PDB with this GUID. Return the type
// index mapping if we have it.
auto Insertion = TypeServerIndexMappings.insert({TS.getGuid(), CVIndexMap()});
auto Insertion = TypeServerIndexMappings.insert({TSId, CVIndexMap()});
CVIndexMap &IndexMap = Insertion.first->second;
if (!Insertion.second)
return IndexMap;
@ -319,23 +355,32 @@ const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
// Check for a PDB at:
// 1. The given file path
// 2. Next to the object file or archive file
auto ExpectedSession = tryToLoadPDB(TS.getGuid(), TS.getName());
auto ExpectedSession = tryToLoadPDB(TSId, TSPath);
if (!ExpectedSession) {
consumeError(ExpectedSession.takeError());
StringRef LocalPath =
!File->ParentName.empty() ? File->ParentName : File->getName();
SmallString<128> Path = sys::path::parent_path(LocalPath);
sys::path::append(
Path, sys::path::filename(TS.getName(), sys::path::Style::windows));
ExpectedSession = tryToLoadPDB(TS.getGuid(), Path);
Path, sys::path::filename(TSPath, sys::path::Style::windows));
ExpectedSession = tryToLoadPDB(TSId, Path);
}
if (auto E = ExpectedSession.takeError()) {
TypeServerIndexMappings.erase(TSId);
MissingTypeServerPDBs.emplace(TSId);
return std::move(E);
}
if (auto E = ExpectedSession.takeError())
fatal("Type server PDB was not found: " + toString(std::move(E)));
auto ExpectedTpi = (*ExpectedSession)->getPDBFile().getPDBTpiStream();
pdb::NativeSession *Session = ExpectedSession->get();
// Keep a strong reference to this PDB, so that it's safe to hold pointers
// into the file.
LoadedPDBs.push_back(std::move(*ExpectedSession));
auto ExpectedTpi = Session->getPDBFile().getPDBTpiStream();
if (auto E = ExpectedTpi.takeError())
fatal("Type server does not have TPI stream: " + toString(std::move(E)));
auto ExpectedIpi = (*ExpectedSession)->getPDBFile().getPDBIpiStream();
auto ExpectedIpi = Session->getPDBFile().getPDBIpiStream();
if (auto E = ExpectedIpi.takeError())
fatal("Type server does not have TPI stream: " + toString(std::move(E)));
@ -412,6 +457,38 @@ static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
}
}
static void
recordStringTableReferenceAtOffset(MutableArrayRef<uint8_t> Contents,
uint32_t Offset,
std::vector<ulittle32_t *> &StrTableRefs) {
Contents =
Contents.drop_front(Offset).take_front(sizeof(support::ulittle32_t));
ulittle32_t *Index = reinterpret_cast<ulittle32_t *>(Contents.data());
StrTableRefs.push_back(Index);
}
static void
recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents,
std::vector<ulittle32_t *> &StrTableRefs) {
// For now we only handle S_FILESTATIC, but we may need the same logic for
// S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any
// PDBs that contain these types of records, so because of the uncertainty
// they are omitted here until we can prove that it's necessary.
switch (Kind) {
case SymbolKind::S_FILESTATIC:
// FileStaticSym::ModFileOffset
recordStringTableReferenceAtOffset(Contents, 4, StrTableRefs);
break;
case SymbolKind::S_DEFRANGE:
case SymbolKind::S_DEFRANGE_SUBFIELD:
log("Not fixing up string table reference in S_DEFRANGE / "
"S_DEFRANGE_SUBFIELD record");
break;
default:
break;
}
}
static SymbolKind symbolKind(ArrayRef<uint8_t> RecordData) {
const RecordPrefix *Prefix =
reinterpret_cast<const RecordPrefix *>(RecordData.data());
@ -628,53 +705,65 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File,
pdb::GSIStreamBuilder &GsiBuilder,
const CVIndexMap &IndexMap,
TypeCollection &IDTable,
std::vector<ulittle32_t *> &StringTableRefs,
BinaryStreamRef SymData) {
// FIXME: Improve error recovery by warning and skipping records when
// possible.
CVSymbolArray Syms;
BinaryStreamReader Reader(SymData);
ExitOnErr(Reader.readArray(Syms, Reader.getLength()));
ArrayRef<uint8_t> SymsBuffer;
cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer));
SmallVector<SymbolScope, 4> Scopes;
for (CVSymbol Sym : Syms) {
// Discover type index references in the record. Skip it if we don't know
// where they are.
SmallVector<TiReference, 32> TypeRefs;
if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) {
log("ignoring unknown symbol record with kind 0x" + utohexstr(Sym.kind()));
continue;
}
// Copy the symbol record so we can mutate it.
MutableArrayRef<uint8_t> NewData = copySymbolForPdb(Sym, Alloc);
auto EC = forEachCodeViewRecord<CVSymbol>(
SymsBuffer, [&](const CVSymbol &Sym) -> llvm::Error {
// Discover type index references in the record. Skip it if we don't
// know where they are.
SmallVector<TiReference, 32> TypeRefs;
if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) {
log("ignoring unknown symbol record with kind 0x" +
utohexstr(Sym.kind()));
return Error::success();
}
// Re-map all the type index references.
MutableArrayRef<uint8_t> Contents =
NewData.drop_front(sizeof(RecordPrefix));
remapTypesInSymbolRecord(File, Sym.kind(), Contents, IndexMap, TypeRefs);
// Copy the symbol record so we can mutate it.
MutableArrayRef<uint8_t> NewData = copySymbolForPdb(Sym, Alloc);
// An object file may have S_xxx_ID symbols, but these get converted to
// "real" symbols in a PDB.
translateIdSymbols(NewData, IDTable);
// Re-map all the type index references.
MutableArrayRef<uint8_t> Contents =
NewData.drop_front(sizeof(RecordPrefix));
remapTypesInSymbolRecord(File, Sym.kind(), Contents, IndexMap,
TypeRefs);
SymbolKind NewKind = symbolKind(NewData);
// An object file may have S_xxx_ID symbols, but these get converted to
// "real" symbols in a PDB.
translateIdSymbols(NewData, IDTable);
// Fill in "Parent" and "End" fields by maintaining a stack of scopes.
CVSymbol NewSym(NewKind, NewData);
if (symbolOpensScope(NewKind))
scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(), NewSym);
else if (symbolEndsScope(NewKind))
scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File);
// If this record refers to an offset in the object file's string table,
// add that item to the global PDB string table and re-write the index.
recordStringTableReferences(Sym.kind(), Contents, StringTableRefs);
// Add the symbol to the globals stream if necessary. Do this before adding
// the symbol to the module since we may need to get the next symbol offset,
// and writing to the module's symbol stream will update that offset.
if (symbolGoesInGlobalsStream(NewSym))
addGlobalSymbol(GsiBuilder, *File, NewSym);
SymbolKind NewKind = symbolKind(NewData);
// Add the symbol to the module.
if (symbolGoesInModuleStream(NewSym))
File->ModuleDBI->addSymbol(NewSym);
}
// Fill in "Parent" and "End" fields by maintaining a stack of scopes.
CVSymbol NewSym(NewKind, NewData);
if (symbolOpensScope(NewKind))
scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(),
NewSym);
else if (symbolEndsScope(NewKind))
scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File);
// Add the symbol to the globals stream if necessary. Do this before
// adding the symbol to the module since we may need to get the next
// symbol offset, and writing to the module's symbol stream will update
// that offset.
if (symbolGoesInGlobalsStream(NewSym))
addGlobalSymbol(GsiBuilder, *File, NewSym);
// Add the symbol to the module.
if (symbolGoesInModuleStream(NewSym))
File->ModuleDBI->addSymbol(NewSym);
return Error::success();
});
cantFail(std::move(EC));
}
// Allocate memory for a .debug$S section and relocate it.
@ -688,6 +777,32 @@ static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
".debug$S");
}
static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) {
OutputSection *OS = C->getOutputSection();
pdb::SectionContrib SC;
memset(&SC, 0, sizeof(SC));
SC.ISect = OS->SectionIndex;
SC.Off = C->getRVA() - OS->getRVA();
SC.Size = C->getSize();
if (auto *SecChunk = dyn_cast<SectionChunk>(C)) {
SC.Characteristics = SecChunk->Header->Characteristics;
SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex();
ArrayRef<uint8_t> Contents = SecChunk->getContents();
JamCRC CRC(0);
ArrayRef<char> CharContents = makeArrayRef(
reinterpret_cast<const char *>(Contents.data()), Contents.size());
CRC.update(CharContents);
SC.DataCrc = CRC.getCRC();
} else {
SC.Characteristics = OS->Header.Characteristics;
// FIXME: When we start creating DBI for import libraries, use those here.
SC.Imod = Modi;
}
SC.RelocCrc = 0; // FIXME
return SC;
}
void PDBLinker::addObjFile(ObjFile *File) {
// Add a module descriptor for every object file. We need to put an absolute
// path to the object into the PDB. If this is a plain object, we make its
@ -702,14 +817,39 @@ void PDBLinker::addObjFile(ObjFile *File) {
File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name));
File->ModuleDBI->setObjFileName(Path);
auto Chunks = File->getChunks();
uint32_t Modi = File->ModuleDBI->getModuleIndex();
for (Chunk *C : Chunks) {
auto *SecChunk = dyn_cast<SectionChunk>(C);
if (!SecChunk || !SecChunk->isLive())
continue;
pdb::SectionContrib SC = createSectionContrib(SecChunk, Modi);
File->ModuleDBI->setFirstSectionContrib(SC);
break;
}
// Before we can process symbol substreams from .debug$S, we need to process
// type information, file checksums, and the string table. Add type info to
// the PDB first, so that we can get the map from object file type and item
// indices to PDB type and item indices.
CVIndexMap ObjectIndexMap;
const CVIndexMap &IndexMap = mergeDebugT(File, ObjectIndexMap);
auto IndexMapResult = mergeDebugT(File, ObjectIndexMap);
// If the .debug$T sections fail to merge, assume there is no debug info.
if (!IndexMapResult) {
warn("Type server PDB for " + Name + " is invalid, ignoring debug info. " +
toString(IndexMapResult.takeError()));
return;
}
const CVIndexMap &IndexMap = *IndexMapResult;
ScopedTimer T(SymbolMergingTimer);
// Now do all live .debug$S sections.
DebugStringTableSubsectionRef CVStrTab;
DebugChecksumsSubsectionRef Checksums;
std::vector<ulittle32_t *> StringTableReferences;
for (SectionChunk *DebugChunk : File->getDebugChunks()) {
if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S")
continue;
@ -723,14 +863,17 @@ void PDBLinker::addObjFile(ObjFile *File) {
BinaryStreamReader Reader(RelocatedDebugContents, support::little);
ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
DebugStringTableSubsectionRef CVStrTab;
DebugChecksumsSubsectionRef Checksums;
for (const DebugSubsectionRecord &SS : Subsections) {
switch (SS.kind()) {
case DebugSubsectionKind::StringTable:
case DebugSubsectionKind::StringTable: {
assert(!CVStrTab.valid() &&
"Encountered multiple string table subsections!");
ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
assert(!Checksums.valid() &&
"Encountered multiple checksum subsections!");
ExitOnErr(Checksums.initialize(SS.getRecordData()));
break;
case DebugSubsectionKind::Lines:
@ -741,10 +884,12 @@ void PDBLinker::addObjFile(ObjFile *File) {
case DebugSubsectionKind::Symbols:
if (Config->DebugGHashes) {
mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
GlobalIDTable, SS.getRecordData());
GlobalIDTable, StringTableReferences,
SS.getRecordData());
} else {
mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
IDTable, SS.getRecordData());
IDTable, StringTableReferences,
SS.getRecordData());
}
break;
default:
@ -752,25 +897,55 @@ void PDBLinker::addObjFile(ObjFile *File) {
break;
}
}
if (Checksums.valid()) {
// Make a new file checksum table that refers to offsets in the PDB-wide
// string table. Generally the string table subsection appears after the
// checksum table, so we have to do this after looping over all the
// subsections.
if (!CVStrTab.valid())
fatal(".debug$S sections must have both a string table subsection "
"and a checksum subsection table or neither");
auto NewChecksums = make_unique<DebugChecksumsSubsection>(PDBStrTab);
for (FileChecksumEntry &FC : Checksums) {
StringRef FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI,
FileName));
NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
}
File->ModuleDBI->addDebugSubsection(std::move(NewChecksums));
}
}
// We should have seen all debug subsections across the entire object file now
// which means that if a StringTable subsection and Checksums subsection were
// present, now is the time to handle them.
if (!CVStrTab.valid()) {
if (Checksums.valid())
fatal(".debug$S sections with a checksums subsection must also contain a "
"string table subsection");
if (!StringTableReferences.empty())
warn("No StringTable subsection was encountered, but there are string "
"table references");
return;
}
// Rewrite each string table reference based on the value that the string
// assumes in the final PDB.
for (ulittle32_t *Ref : StringTableReferences) {
auto ExpectedString = CVStrTab.getString(*Ref);
if (!ExpectedString) {
warn("Invalid string table reference");
consumeError(ExpectedString.takeError());
continue;
}
*Ref = PDBStrTab.insert(*ExpectedString);
}
// Make a new file checksum table that refers to offsets in the PDB-wide
// string table. Generally the string table subsection appears after the
// checksum table, so we have to do this after looping over all the
// subsections.
auto NewChecksums = make_unique<DebugChecksumsSubsection>(PDBStrTab);
for (FileChecksumEntry &FC : Checksums) {
SmallString<128> FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
if (!sys::path::is_absolute(FileName) &&
!Config->PDBSourcePath.empty()) {
SmallString<128> AbsoluteFileName = Config->PDBSourcePath;
sys::path::append(AbsoluteFileName, FileName);
sys::path::native(AbsoluteFileName);
sys::path::remove_dots(AbsoluteFileName, /*remove_dot_dots=*/true);
FileName = std::move(AbsoluteFileName);
}
ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI,
FileName));
NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
}
File->ModuleDBI->addDebugSubsection(std::move(NewChecksums));
}
static PublicSym32 createPublic(Defined *Def) {
@ -793,12 +968,15 @@ static PublicSym32 createPublic(Defined *Def) {
// Add all object files to the PDB. Merge .debug$T sections into IpiData and
// TpiData.
void PDBLinker::addObjectsToPDB() {
ScopedTimer T1(AddObjectsTimer);
for (ObjFile *File : ObjFile::Instances)
addObjFile(File);
Builder.getStringTableBuilder().setStrings(PDBStrTab);
T1.stop();
// Construct TPI and IPI stream contents.
ScopedTimer T2(TpiStreamLayoutTimer);
if (Config->DebugGHashes) {
addTypeInfo(Builder.getTpiBuilder(), GlobalTypeTable);
addTypeInfo(Builder.getIpiBuilder(), GlobalIDTable);
@ -806,7 +984,9 @@ void PDBLinker::addObjectsToPDB() {
addTypeInfo(Builder.getTpiBuilder(), TypeTable);
addTypeInfo(Builder.getIpiBuilder(), IDTable);
}
T2.stop();
ScopedTimer T3(GlobalsLayoutTimer);
// Compute the public and global symbols.
auto &GsiBuilder = Builder.getGsiBuilder();
std::vector<PublicSym32> Publics;
@ -828,6 +1008,35 @@ void PDBLinker::addObjectsToPDB() {
}
}
void PDBLinker::addNatvisFiles() {
for (StringRef File : Config->NatvisFiles) {
ErrorOr<std::unique_ptr<MemoryBuffer>> DataOrErr =
MemoryBuffer::getFile(File);
if (!DataOrErr) {
warn("Cannot open input file: " + File);
continue;
}
Builder.addInjectedSource(File, std::move(*DataOrErr));
}
}
static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) {
switch (Machine) {
case COFF::IMAGE_FILE_MACHINE_AMD64:
return codeview::CPUType::X64;
case COFF::IMAGE_FILE_MACHINE_ARM:
return codeview::CPUType::ARM7;
case COFF::IMAGE_FILE_MACHINE_ARM64:
return codeview::CPUType::ARM64;
case COFF::IMAGE_FILE_MACHINE_ARMNT:
return codeview::CPUType::ARMNT;
case COFF::IMAGE_FILE_MACHINE_I386:
return codeview::CPUType::Intel80386;
default:
llvm_unreachable("Unsupported CPU Type");
}
}
static void addCommonLinkerModuleSymbols(StringRef Path,
pdb::DbiModuleDescriptorBuilder &Mod,
BumpPtrAllocator &Allocator) {
@ -838,7 +1047,7 @@ static void addCommonLinkerModuleSymbols(StringRef Path,
ONS.Name = "* Linker *";
ONS.Signature = 0;
CS.Machine = Config->is64() ? CPUType::X64 : CPUType::Intel80386;
CS.Machine = toCodeViewMachine(Config->Machine);
// Interestingly, if we set the string to 0.0.0.0, then when trying to view
// local variables WinDbg emits an error that private symbols are not present.
// By setting this to a valid MSVC linker version string, local variables are
@ -889,9 +1098,9 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &Mod,
BumpPtrAllocator &Allocator) {
SectionSym Sym(SymbolRecordKind::SectionSym);
Sym.Alignment = 12; // 2^12 = 4KB
Sym.Characteristics = OS.getCharacteristics();
Sym.Characteristics = OS.Header.Characteristics;
Sym.Length = OS.getVirtualSize();
Sym.Name = OS.getName();
Sym.Name = OS.Name;
Sym.Rva = OS.getRVA();
Sym.SectionNumber = OS.SectionIndex;
Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
@ -903,10 +1112,15 @@ void coff::createPDB(SymbolTable *Symtab,
ArrayRef<OutputSection *> OutputSections,
ArrayRef<uint8_t> SectionTable,
const llvm::codeview::DebugInfo &BuildId) {
ScopedTimer T1(TotalPdbLinkTimer);
PDBLinker PDB(Symtab);
PDB.initialize(BuildId);
PDB.addObjectsToPDB();
PDB.addSections(OutputSections, SectionTable);
PDB.addNatvisFiles();
ScopedTimer T2(DiskCommitTimer);
PDB.commit();
}
@ -920,44 +1134,22 @@ void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) {
// Add an Info stream.
auto &InfoBuilder = Builder.getInfoBuilder();
InfoBuilder.setAge(BuildId.PDB70.Age);
GUID uuid;
memcpy(&uuid, &BuildId.PDB70.Signature, sizeof(uuid));
InfoBuilder.setAge(BuildId.PDB70.Age);
InfoBuilder.setGuid(uuid);
InfoBuilder.setSignature(time(nullptr));
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
// Add an empty DBI stream.
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
DbiBuilder.setAge(BuildId.PDB70.Age);
DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
ExitOnErr(DbiBuilder.addDbgStream(pdb::DbgHeaderType::NewFPO, {}));
}
void PDBLinker::addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule,
OutputSection *OS, Chunk *C) {
pdb::SectionContrib SC;
memset(&SC, 0, sizeof(SC));
SC.ISect = OS->SectionIndex;
SC.Off = C->getRVA() - OS->getRVA();
SC.Size = C->getSize();
if (auto *SecChunk = dyn_cast<SectionChunk>(C)) {
SC.Characteristics = SecChunk->Header->Characteristics;
SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex();
ArrayRef<uint8_t> Contents = SecChunk->getContents();
JamCRC CRC(0);
ArrayRef<char> CharContents = makeArrayRef(
reinterpret_cast<const char *>(Contents.data()), Contents.size());
CRC.update(CharContents);
SC.DataCrc = CRC.getCRC();
} else {
SC.Characteristics = OS->getCharacteristics();
// FIXME: When we start creating DBI for import libraries, use those here.
SC.Imod = LinkerModule.getModuleIndex();
}
SC.RelocCrc = 0; // FIXME
Builder.getDbiBuilder().addSectionContrib(SC);
DbiBuilder.setMachineType(Config->Machine);
// Technically we are not link.exe 14.11, but there are known cases where
// debugging tools on Windows expect Microsoft-specific version numbers or
// they fail to work at all. Since we know we produce PDBs that are
// compatible with LINK 14.11, we set that version number here.
DbiBuilder.setBuildNumber(14, 11);
}
void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
@ -975,8 +1167,11 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
// Add section contributions. They must be ordered by ascending RVA.
for (OutputSection *OS : OutputSections) {
addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc);
for (Chunk *C : OS->getChunks())
addSectionContrib(LinkerModule, OS, C);
for (Chunk *C : OS->getChunks()) {
pdb::SectionContrib SC =
createSectionContrib(C, LinkerModule.getModuleIndex());
Builder.getDbiBuilder().addSectionContrib(SC);
}
}
// Add Section Map stream.
@ -995,3 +1190,145 @@ void PDBLinker::commit() {
// Write to a file.
ExitOnErr(Builder.commit(Config->PDBPath));
}
static Expected<StringRef>
getFileName(const DebugStringTableSubsectionRef &Strings,
const DebugChecksumsSubsectionRef &Checksums, uint32_t FileID) {
auto Iter = Checksums.getArray().at(FileID);
if (Iter == Checksums.getArray().end())
return make_error<CodeViewError>(cv_error_code::no_records);
uint32_t Offset = Iter->FileNameOffset;
return Strings.getString(Offset);
}
static uint32_t getSecrelReloc() {
switch (Config->Machine) {
case AMD64:
return COFF::IMAGE_REL_AMD64_SECREL;
case I386:
return COFF::IMAGE_REL_I386_SECREL;
case ARMNT:
return COFF::IMAGE_REL_ARM_SECREL;
case ARM64:
return COFF::IMAGE_REL_ARM64_SECREL;
default:
llvm_unreachable("unknown machine type");
}
}
// Try to find a line table for the given offset Addr into the given chunk C.
// If a line table was found, the line table, the string and checksum tables
// that are used to interpret the line table, and the offset of Addr in the line
// table are stored in the output arguments. Returns whether a line table was
// found.
static bool findLineTable(const SectionChunk *C, uint32_t Addr,
DebugStringTableSubsectionRef &CVStrTab,
DebugChecksumsSubsectionRef &Checksums,
DebugLinesSubsectionRef &Lines,
uint32_t &OffsetInLinetable) {
ExitOnError ExitOnErr;
uint32_t SecrelReloc = getSecrelReloc();
for (SectionChunk *DbgC : C->File->getDebugChunks()) {
if (DbgC->getSectionName() != ".debug$S")
continue;
// Build a mapping of SECREL relocations in DbgC that refer to C.
DenseMap<uint32_t, uint32_t> Secrels;
for (const coff_relocation &R : DbgC->Relocs) {
if (R.Type != SecrelReloc)
continue;
if (auto *S = dyn_cast_or_null<DefinedRegular>(
C->File->getSymbols()[R.SymbolTableIndex]))
if (S->getChunk() == C)
Secrels[R.VirtualAddress] = S->getValue();
}
ArrayRef<uint8_t> Contents =
consumeDebugMagic(DbgC->getContents(), ".debug$S");
DebugSubsectionArray Subsections;
BinaryStreamReader Reader(Contents, support::little);
ExitOnErr(Reader.readArray(Subsections, Contents.size()));
for (const DebugSubsectionRecord &SS : Subsections) {
switch (SS.kind()) {
case DebugSubsectionKind::StringTable: {
assert(!CVStrTab.valid() &&
"Encountered multiple string table subsections!");
ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
assert(!Checksums.valid() &&
"Encountered multiple checksum subsections!");
ExitOnErr(Checksums.initialize(SS.getRecordData()));
break;
case DebugSubsectionKind::Lines: {
ArrayRef<uint8_t> Bytes;
auto Ref = SS.getRecordData();
ExitOnErr(Ref.readLongestContiguousChunk(0, Bytes));
size_t OffsetInDbgC = Bytes.data() - DbgC->getContents().data();
// Check whether this line table refers to C.
auto I = Secrels.find(OffsetInDbgC);
if (I == Secrels.end())
break;
// Check whether this line table covers Addr in C.
DebugLinesSubsectionRef LinesTmp;
ExitOnErr(LinesTmp.initialize(BinaryStreamReader(Ref)));
uint32_t OffsetInC = I->second + LinesTmp.header()->RelocOffset;
if (Addr < OffsetInC || Addr >= OffsetInC + LinesTmp.header()->CodeSize)
break;
assert(!Lines.header() &&
"Encountered multiple line tables for function!");
ExitOnErr(Lines.initialize(BinaryStreamReader(Ref)));
OffsetInLinetable = Addr - OffsetInC;
break;
}
default:
break;
}
if (CVStrTab.valid() && Checksums.valid() && Lines.header())
return true;
}
}
return false;
}
// Use CodeView line tables to resolve a file and line number for the given
// offset into the given chunk and return them, or {"", 0} if a line table was
// not found.
std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *C,
uint32_t Addr) {
ExitOnError ExitOnErr;
DebugStringTableSubsectionRef CVStrTab;
DebugChecksumsSubsectionRef Checksums;
DebugLinesSubsectionRef Lines;
uint32_t OffsetInLinetable;
if (!findLineTable(C, Addr, CVStrTab, Checksums, Lines, OffsetInLinetable))
return {"", 0};
uint32_t NameIndex;
uint32_t LineNumber;
for (LineColumnEntry &Entry : Lines) {
for (const LineNumberEntry &LN : Entry.LineNumbers) {
if (LN.Offset > OffsetInLinetable) {
StringRef Filename =
ExitOnErr(getFileName(CVStrTab, Checksums, NameIndex));
return {Filename, LineNumber};
}
LineInfo LI(LN.Flags);
NameIndex = Entry.NameIndex;
LineNumber = LI.getStartLine();
}
}
StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, NameIndex));
return {Filename, LineNumber};
}

View File

@ -22,12 +22,16 @@ union DebugInfo;
namespace lld {
namespace coff {
class OutputSection;
class SectionChunk;
class SymbolTable;
void createPDB(SymbolTable *Symtab,
llvm::ArrayRef<OutputSection *> OutputSections,
llvm::ArrayRef<uint8_t> SectionTable,
const llvm::codeview::DebugInfo &BuildId);
std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *C,
uint32_t Addr);
}
}

View File

@ -1,35 +0,0 @@
//===- Strings.cpp -------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Strings.h"
#include <mutex>
#if defined(_MSC_VER)
#include <Windows.h>
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")
#endif
using namespace lld;
using namespace lld::coff;
using namespace llvm;
Optional<std::string> coff::demangleMSVC(StringRef S) {
#if defined(_MSC_VER)
// UnDecorateSymbolName is not thread-safe, so we need a mutex.
static std::mutex Mu;
std::lock_guard<std::mutex> Lock(Mu);
char Buf[4096];
if (S.startswith("?"))
if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0))
return std::string(Buf, Len);
#endif
return None;
}

View File

@ -11,9 +11,11 @@
#include "Config.h"
#include "Driver.h"
#include "LTO.h"
#include "PDB.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Timer.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@ -24,6 +26,8 @@ using namespace llvm;
namespace lld {
namespace coff {
static Timer LTOTimer("LTO", Timer::root());
SymbolTable *Symtab;
void SymbolTable::addFile(InputFile *File) {
@ -34,8 +38,9 @@ void SymbolTable::addFile(InputFile *File) {
if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
Config->Machine = MT;
} else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) {
fatal(toString(File) + ": machine type " + machineToStr(MT) +
error(toString(File) + ": machine type " + machineToStr(MT) +
" conflicts with " + machineToStr(Config->Machine));
return;
}
if (auto *F = dyn_cast<ObjFile>(File)) {
@ -61,6 +66,66 @@ static void errorOrWarn(const Twine &S) {
error(S);
}
// Returns the name of the symbol in SC whose value is <= Addr that is closest
// to Addr. This is generally the name of the global variable or function whose
// definition contains Addr.
static StringRef getSymbolName(SectionChunk *SC, uint32_t Addr) {
DefinedRegular *Candidate = nullptr;
for (Symbol *S : SC->File->getSymbols()) {
auto *D = dyn_cast_or_null<DefinedRegular>(S);
if (!D || D->getChunk() != SC || D->getValue() > Addr ||
(Candidate && D->getValue() < Candidate->getValue()))
continue;
Candidate = D;
}
if (!Candidate)
return "";
return Candidate->getName();
}
static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
struct Location {
StringRef SymName;
std::pair<StringRef, uint32_t> FileLine;
};
std::vector<Location> Locations;
for (Chunk *C : File->getChunks()) {
auto *SC = dyn_cast<SectionChunk>(C);
if (!SC)
continue;
for (const coff_relocation &R : SC->Relocs) {
if (R.SymbolTableIndex != SymIndex)
continue;
std::pair<StringRef, uint32_t> FileLine =
getFileLine(SC, R.VirtualAddress);
StringRef SymName = getSymbolName(SC, R.VirtualAddress);
if (!FileLine.first.empty() || !SymName.empty())
Locations.push_back({SymName, FileLine});
}
}
if (Locations.empty())
return "\n>>> referenced by " + toString(File) + "\n";
std::string Out;
llvm::raw_string_ostream OS(Out);
for (Location Loc : Locations) {
OS << "\n>>> referenced by ";
if (!Loc.FileLine.first.empty())
OS << Loc.FileLine.first << ":" << Loc.FileLine.second
<< "\n>>> ";
OS << toString(File);
if (!Loc.SymName.empty())
OS << ":(" << Loc.SymName << ')';
}
OS << '\n';
return OS.str();
}
void SymbolTable::reportRemainingUndefines() {
SmallPtrSet<Symbol *, 8> Undefs;
DenseMap<Symbol *, Symbol *> LocalImports;
@ -120,20 +185,23 @@ void SymbolTable::reportRemainingUndefines() {
if (Config->WarnLocallyDefinedImported)
if (Symbol *Imp = LocalImports.lookup(B))
warn("<root>: locally defined symbol imported: " + Imp->getName() +
" (defined in " + toString(Imp->getFile()) + ")");
" (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
}
for (ObjFile *File : ObjFile::Instances) {
size_t SymIndex = (size_t)-1;
for (Symbol *Sym : File->getSymbols()) {
++SymIndex;
if (!Sym)
continue;
if (Undefs.count(Sym))
errorOrWarn(toString(File) + ": undefined symbol: " + Sym->getName());
errorOrWarn("undefined symbol: " + Sym->getName() +
getSymbolLocations(File, SymIndex));
if (Config->WarnLocallyDefinedImported)
if (Symbol *Imp = LocalImports.lookup(Sym))
warn(toString(File) + ": locally defined symbol imported: " +
Imp->getName() + " (defined in " + toString(Imp->getFile()) +
")");
") [LNK4217]");
}
}
}
@ -142,7 +210,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
Symbol *&Sym = SymMap[CachedHashStringRef(Name)];
if (Sym)
return {Sym, false};
Sym = (Symbol *)make<SymbolUnion>();
Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
Sym->IsUsedInRegularObj = false;
Sym->PendingArchiveLoad = false;
return {Sym, true};
@ -274,30 +342,29 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
return S;
}
DefinedImportData *SymbolTable::addImportData(StringRef N, ImportFile *F) {
Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(N);
S->IsUsedInRegularObj = true;
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
replaceSymbol<DefinedImportData>(S, N, F);
return cast<DefinedImportData>(S);
return S;
}
reportDuplicate(S, F);
return nullptr;
}
DefinedImportThunk *SymbolTable::addImportThunk(StringRef Name,
DefinedImportData *ID,
uint16_t Machine) {
Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
uint16_t Machine) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
S->IsUsedInRegularObj = true;
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
replaceSymbol<DefinedImportThunk>(S, Name, ID, Machine);
return cast<DefinedImportThunk>(S);
return S;
}
reportDuplicate(S, ID->File);
@ -314,10 +381,7 @@ std::vector<Chunk *> SymbolTable::getChunks() {
}
Symbol *SymbolTable::find(StringRef Name) {
auto It = SymMap.find(CachedHashStringRef(Name));
if (It == SymMap.end())
return nullptr;
return It->second;
return SymMap.lookup(CachedHashStringRef(Name));
}
Symbol *SymbolTable::findUnderscore(StringRef Name) {
@ -384,6 +448,8 @@ std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
void SymbolTable::addCombinedLTOObjects() {
if (BitcodeFile::Instances.empty())
return;
ScopedTimer T(LTOTimer);
for (StringRef Object : compileBitcodeFiles()) {
auto *Obj = make<ObjFile>(MemoryBufferRef(Object, "lto.tmp"));
Obj->parse();

View File

@ -92,9 +92,9 @@ class SymbolTable {
Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
const llvm::object::coff_symbol_generic *S = nullptr,
CommonChunk *C = nullptr);
DefinedImportData *addImportData(StringRef N, ImportFile *F);
DefinedImportThunk *addImportThunk(StringRef Name, DefinedImportData *S,
uint16_t Machine);
Symbol *addImportData(StringRef N, ImportFile *F);
Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
uint16_t Machine);
void reportDuplicate(Symbol *Existing, InputFile *NewFile);

View File

@ -9,9 +9,9 @@
#include "Symbols.h"
#include "InputFiles.h"
#include "Strings.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@ -21,7 +21,7 @@ using namespace llvm::object;
// Returns a symbol name for an error message.
std::string lld::toString(coff::Symbol &B) {
if (Optional<std::string> S = coff::demangleMSVC(B.getName()))
if (Optional<std::string> S = lld::demangleMSVC(B.getName()))
return ("\"" + *S + "\" (" + B.getName() + ")").str();
return B.getName();
}
@ -58,7 +58,7 @@ bool Symbol::isLive() const {
if (auto *Imp = dyn_cast<DefinedImportData>(this))
return Imp->File->Live;
if (auto *Imp = dyn_cast<DefinedImportThunk>(this))
return Imp->WrappedSym->File->Live;
return Imp->WrappedSym->File->ThunkLive;
// Assume any other kind of symbol is live.
return true;
}
@ -71,7 +71,7 @@ COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(Sym));
}
uint16_t DefinedAbsolute::OutputSectionIndex = 0;
uint16_t DefinedAbsolute::NumOutputSections;
static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) {
if (Machine == AMD64)

View File

@ -213,11 +213,10 @@ class DefinedAbsolute : public Defined {
uint64_t getRVA() { return VA - Config->ImageBase; }
void setVA(uint64_t V) { VA = V; }
// The sentinel absolute symbol section index. Section index relocations
// against absolute symbols resolve to this 16 bit number, and it is the
// largest valid section index plus one. This is written by the Writer.
static uint16_t OutputSectionIndex;
uint16_t getSecIdx() { return OutputSectionIndex; }
// Section index relocations against absolute symbols resolve to
// this 16 bit number, and it is the largest valid section index
// plus one. This variable keeps it.
static uint16_t NumOutputSections;
private:
uint64_t VA;
@ -416,6 +415,8 @@ union SymbolUnion {
template <typename T, typename... ArgT>
void replaceSymbol(Symbol *S, ArgT &&... Arg) {
static_assert(std::is_trivially_destructible<T>(),
"Symbol types must be trivially destructible");
static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small");
static_assert(alignof(T) <= alignof(SymbolUnion),
"SymbolUnion not aligned enough");

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
#include "Chunks.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/COFF.h"
#include <chrono>
#include <cstdint>
#include <vector>
@ -29,16 +30,14 @@ void writeResult();
// non-overlapping file offsets and RVAs.
class OutputSection {
public:
OutputSection(llvm::StringRef N) : Name(N), Header({}) {}
void setRVA(uint64_t);
void setFileOffset(uint64_t);
OutputSection(llvm::StringRef N, uint32_t Chars) : Name(N) {
Header.Characteristics = Chars;
}
void addChunk(Chunk *C);
llvm::StringRef getName() { return Name; }
void merge(OutputSection *Other);
ArrayRef<Chunk *> getChunks() { return Chunks; }
void addPermissions(uint32_t C);
void setPermissions(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);
@ -60,9 +59,10 @@ class OutputSection {
// N.B. The section index is one based.
uint32_t SectionIndex = 0;
private:
llvm::StringRef Name;
llvm::object::coff_section Header;
llvm::object::coff_section Header = {};
private:
uint32_t StringTableOff = 0;
std::vector<Chunk *> Chunks;
};

View File

@ -18,13 +18,17 @@ using namespace llvm;
using namespace lld;
int lld::args::getInteger(opt::InputArgList &Args, unsigned Key, int Default) {
int V = Default;
if (auto *Arg = Args.getLastArg(Key)) {
StringRef S = Arg->getValue();
if (!to_integer(S, V, 10))
error(Arg->getSpelling() + ": number expected, but got '" + S + "'");
}
return V;
auto *A = Args.getLastArg(Key);
if (!A)
return Default;
int V;
if (to_integer(A->getValue(), V, 10))
return V;
StringRef Spelling = Args.getArgString(A->getIndex());
error(Spelling + ": number expected, but got '" + A->getValue() + "'");
return 0;
}
std::vector<StringRef> lld::args::getStrings(opt::InputArgList &Args, int Id) {

View File

@ -10,6 +10,7 @@ add_lld_library(lldCommon
Strings.cpp
TargetOptionsCommandFlags.cpp
Threads.cpp
Timer.cpp
Version.cpp
ADDITIONAL_HEADER_DIRS

View File

@ -12,7 +12,8 @@
#include "lld/Common/Threads.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_ostream.h"
#include <mutex>
@ -59,6 +60,30 @@ void lld::exitLld(int Val) {
_exit(Val);
}
void lld::diagnosticHandler(const DiagnosticInfo &DI) {
SmallString<128> S;
raw_svector_ostream OS(S);
DiagnosticPrinterRawOStream DP(OS);
DI.print(DP);
switch (DI.getSeverity()) {
case DS_Error:
error(S);
break;
case DS_Warning:
warn(S);
break;
case DS_Remark:
case DS_Note:
message(S);
break;
}
}
void lld::checkError(Error E) {
handleAllErrors(std::move(E),
[&](ErrorInfoBase &EIB) { error(EIB.message()); });
}
void ErrorHandler::print(StringRef S, raw_ostream::Colors C) {
*ErrorOS << LogName << ": ";
if (ColorDiagnostics) {

View File

@ -8,7 +8,21 @@
//===----------------------------------------------------------------------===//
#include "lld/Common/Strings.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/LLVM.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/GlobPattern.h"
#include <algorithm>
#include <mutex>
#include <vector>
#if defined(_MSC_VER)
#include <Windows.h>
// DbgHelp.h must be included after Windows.h.
#include <DbgHelp.h>
#pragma comment(lib, "dbghelp.lib")
#endif
using namespace llvm;
using namespace lld;
@ -30,3 +44,66 @@ Optional<std::string> lld::demangleItanium(StringRef Name) {
free(Buf);
return S;
}
Optional<std::string> lld::demangleMSVC(StringRef S) {
#if defined(_MSC_VER)
// UnDecorateSymbolName is not thread-safe, so we need a mutex.
static std::mutex Mu;
std::lock_guard<std::mutex> Lock(Mu);
char Buf[4096];
if (S.startswith("?"))
if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0))
return std::string(Buf, Len);
#endif
return None;
}
StringMatcher::StringMatcher(ArrayRef<StringRef> Pat) {
for (StringRef S : Pat) {
Expected<GlobPattern> Pat = GlobPattern::create(S);
if (!Pat)
error(toString(Pat.takeError()));
else
Patterns.push_back(*Pat);
}
}
bool StringMatcher::match(StringRef S) const {
for (const GlobPattern &Pat : Patterns)
if (Pat.match(S))
return true;
return false;
}
// Converts a hex string (e.g. "deadbeef") to a vector.
std::vector<uint8_t> lld::parseHex(StringRef S) {
std::vector<uint8_t> Hex;
while (!S.empty()) {
StringRef B = S.substr(0, 2);
S = S.substr(2);
uint8_t H;
if (!to_integer(B, H, 16)) {
error("not a hexadecimal value: " + B);
return {};
}
Hex.push_back(H);
}
return Hex;
}
// Returns true if S is valid as a C language identifier.
bool lld::isValidCIdentifier(StringRef S) {
return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
std::all_of(S.begin() + 1, S.end(),
[](char C) { return C == '_' || isAlnum(C); });
}
// Write the contents of the a buffer to a file
void lld::saveBuffer(StringRef Buffer, const Twine &Path) {
std::error_code EC;
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
if (EC)
error("cannot create " + Path + ": " + EC.message());
OS << Buffer;
}

View File

@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
//
// This file exists as a place for global variables defined in LLVM's
// CodeGen/CommandFlags.def. By putting the resulting object file in
// CodeGen/CommandFlags.inc. By putting the resulting object file in
// an archive and linking with it, the definitions will automatically be
// included when needed and skipped when already present.
//
@ -16,12 +16,12 @@
#include "lld/Common/TargetOptionsCommandFlags.h"
#include "llvm/CodeGen/CommandFlags.def"
#include "llvm/CodeGen/CommandFlags.inc"
#include "llvm/Target/TargetOptions.h"
// Define an externally visible version of
// InitTargetOptionsFromCodeGenFlags, so that its functionality can be
// used without having to include llvm/CodeGen/CommandFlags.def, which
// used without having to include llvm/CodeGen/CommandFlags.inc, which
// would lead to multiple definitions of the command line flags.
llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() {
return ::InitTargetOptionsFromCodeGenFlags();
@ -30,3 +30,5 @@ llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() {
llvm::Optional<llvm::CodeModel::Model> lld::GetCodeModelFromCMModel() {
return getCodeModel();
}
std::string lld::GetCPUStr() { return ::getCPUStr(); }

80
Common/Timer.cpp Normal file
View File

@ -0,0 +1,80 @@
//===- Timer.cpp ----------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Common/Timer.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Format.h"
using namespace lld;
using namespace llvm;
ScopedTimer::ScopedTimer(Timer &T) : T(&T) { T.start(); }
void ScopedTimer::stop() {
if (!T)
return;
T->stop();
T = nullptr;
}
ScopedTimer::~ScopedTimer() { stop(); }
Timer::Timer(llvm::StringRef Name) : Name(Name), Parent(nullptr) {}
Timer::Timer(llvm::StringRef Name, Timer &Parent)
: Name(Name), Parent(&Parent) {}
void Timer::start() {
if (Parent && Total.count() == 0)
Parent->Children.push_back(this);
StartTime = std::chrono::high_resolution_clock::now();
}
void Timer::stop() {
Total += (std::chrono::high_resolution_clock::now() - StartTime);
}
Timer &Timer::root() {
static Timer RootTimer("Total Link Time");
return RootTimer;
}
void Timer::print() {
double TotalDuration = static_cast<double>(root().millis());
// We want to print the grand total under all the intermediate phases, so we
// print all children first, then print the total under that.
for (const auto &Child : Children)
Child->print(1, TotalDuration);
message(std::string(49, '-'));
root().print(0, root().millis(), false);
}
double Timer::millis() const {
return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(
Total)
.count();
}
void Timer::print(int Depth, double TotalDuration, bool Recurse) const {
double P = 100.0 * millis() / TotalDuration;
SmallString<32> Str;
llvm::raw_svector_ostream Stream(Str);
std::string S = std::string(Depth * 2, ' ') + Name + std::string(":");
Stream << format("%-30s%5d ms (%5.1f%%)", S.c_str(), (int)millis(), P);
message(Str);
if (Recurse) {
for (const auto &Child : Children)
Child->print(Depth + 1, TotalDuration);
}
}

View File

@ -34,12 +34,11 @@
#include "LinkerScript.h"
#include "OutputSections.h"
#include "Relocations.h"
#include "Strings.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@ -47,6 +46,7 @@
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
using namespace lld;
@ -341,7 +341,7 @@ static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2,
// patch or 0 if no patch required.
static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
uint64_t Limit) {
uint64_t ISAddr = IS->getParent()->Addr + IS->OutSecOff;
uint64_t ISAddr = IS->getVA(0);
// Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8.
uint64_t InitialPageOff = (ISAddr + Off) & 0xfff;
@ -357,7 +357,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
uint64_t PatchOff = 0;
const uint8_t *Buf = IS->Data.begin();
const uint32_t *InstBuf = reinterpret_cast<const uint32_t *>(Buf + Off);
const ulittle32_t *InstBuf = reinterpret_cast<const ulittle32_t *>(Buf + Off);
uint32_t Instr1 = *InstBuf++;
uint32_t Instr2 = *InstBuf++;
uint32_t Instr3 = *InstBuf++;
@ -405,7 +405,7 @@ lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off)
}
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
return Patchee->getParent()->Addr + Patchee->OutSecOff + PatcheeOffset;
return Patchee->getVA(PatcheeOffset);
}
void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) {
@ -554,9 +554,8 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
if (RelIt != IS->Relocations.end() && RelIt->Type == R_AARCH64_JUMP26)
return;
if (Config->Verbose)
message("detected cortex-a53-843419 erratum sequence starting at " +
utohexstr(AdrpAddr) + " in unpatched output.");
log("detected cortex-a53-843419 erratum sequence starting at " +
utohexstr(AdrpAddr) + " in unpatched output.");
auto *PS = make<Patch843419Section>(IS, PatcheeOffset);
Patches.push_back(PS);
@ -602,7 +601,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
(DataSym == MapSyms.end()) ? IS->Data.size() : (*DataSym)->Value;
while (Off < Limit) {
uint64_t StartAddr = IS->getParent()->Addr + IS->OutSecOff + Off;
uint64_t StartAddr = IS->getVA(Off);
if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit))
implementPatch(StartAddr, PatcheeOffset, IS, Patches);
}

View File

@ -11,7 +11,6 @@
#define LLD_ELF_AARCH64ERRATAFIX_H
#include "lld/Common/LLVM.h"
#include <map>
#include <vector>

View File

@ -34,7 +34,7 @@ class AArch64 final : public TargetInfo {
AArch64();
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
bool isPicRel(RelType Type) const override;
RelType getDynRel(RelType Type) const override;
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
@ -89,6 +89,11 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
return R_TLSDESC_CALL;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
return R_TLS;
case R_AARCH64_CALL26:
case R_AARCH64_CONDBR19:
@ -144,8 +149,10 @@ bool AArch64::usesOnlyLowPageBits(RelType Type) const {
}
}
bool AArch64::isPicRel(RelType Type) const {
return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64;
RelType AArch64::getDynRel(RelType Type) const {
if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64)
return Type;
return R_AARCH64_NONE;
}
void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const {
@ -240,12 +247,12 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_AARCH64_ABS16:
case R_AARCH64_PREL16:
checkIntUInt<16>(Loc, Val, Type);
checkIntUInt(Loc, Val, 16, Type);
write16le(Loc, Val);
break;
case R_AARCH64_ABS32:
case R_AARCH64_PREL32:
checkIntUInt<32>(Loc, Val, Type);
checkIntUInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
case R_AARCH64_ABS64:
@ -260,11 +267,11 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSDESC_ADR_PAGE21:
checkInt<33>(Loc, Val, Type);
checkInt(Loc, Val, 33, Type);
write32AArch64Addr(Loc, Val >> 12);
break;
case R_AARCH64_ADR_PREL_LO21:
checkInt<21>(Loc, Val, Type);
checkInt(Loc, Val, 21, Type);
write32AArch64Addr(Loc, Val);
break;
case R_AARCH64_JUMP26:
@ -278,38 +285,40 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write32le(Loc, 0x14000000);
LLVM_FALLTHROUGH;
case R_AARCH64_CALL26:
checkInt<28>(Loc, Val, Type);
checkInt(Loc, Val, 28, Type);
or32le(Loc, (Val & 0x0FFFFFFC) >> 2);
break;
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19:
checkAlignment<4>(Loc, Val, Type);
checkInt<21>(Loc, Val, Type);
checkAlignment(Loc, Val, 4, Type);
checkInt(Loc, Val, 21, Type);
or32le(Loc, (Val & 0x1FFFFC) << 3);
break;
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case R_AARCH64_TLSDESC_LD64_LO12:
checkAlignment<8>(Loc, Val, Type);
or32le(Loc, (Val & 0xFF8) << 7);
break;
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
or32AArch64Imm(Loc, getBits(Val, 0, 11));
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
checkAlignment<2>(Loc, Val, Type);
case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
checkAlignment(Loc, Val, 2, Type);
or32AArch64Imm(Loc, getBits(Val, 1, 11));
break;
case R_AARCH64_LDST32_ABS_LO12_NC:
checkAlignment<4>(Loc, Val, Type);
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
checkAlignment(Loc, Val, 4, Type);
or32AArch64Imm(Loc, getBits(Val, 2, 11));
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
checkAlignment<8>(Loc, Val, Type);
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
case R_AARCH64_TLSDESC_LD64_LO12:
checkAlignment(Loc, Val, 8, Type);
or32AArch64Imm(Loc, getBits(Val, 3, 11));
break;
case R_AARCH64_LDST128_ABS_LO12_NC:
checkAlignment<16>(Loc, Val, Type);
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
checkAlignment(Loc, Val, 16, Type);
or32AArch64Imm(Loc, getBits(Val, 4, 11));
break;
case R_AARCH64_MOVW_UABS_G0_NC:
@ -325,11 +334,11 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
or32le(Loc, (Val & 0xFFFF000000000000) >> 43);
break;
case R_AARCH64_TSTBR14:
checkInt<16>(Loc, Val, Type);
checkInt(Loc, Val, 16, Type);
or32le(Loc, (Val & 0xFFFC) << 3);
break;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
checkInt<24>(Loc, Val, Type);
checkInt(Loc, Val, 24, Type);
or32AArch64Imm(Loc, Val >> 12);
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
@ -353,7 +362,7 @@ void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// movk x0, #0x10
// nop
// nop
checkUInt<32>(Loc, Val, Type);
checkUInt(Loc, Val, 32, Type);
switch (Type) {
case R_AARCH64_TLSDESC_ADD_LO12:
@ -403,7 +412,7 @@ void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
checkUInt<32>(Loc, Val, Type);
checkUInt(Loc, Val, 32, Type);
if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
// Generate MOVZ.

View File

@ -66,6 +66,7 @@ void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write32le(Loc, Val);
break;
case R_AMDGPU_ABS64:
case R_AMDGPU_REL64:
write64le(Loc, Val);
break;
case R_AMDGPU_GOTPCREL32_HI:
@ -86,6 +87,7 @@ RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S,
case R_AMDGPU_REL32:
case R_AMDGPU_REL32_LO:
case R_AMDGPU_REL32_HI:
case R_AMDGPU_REL64:
return R_PC;
case R_AMDGPU_GOTPCREL:
case R_AMDGPU_GOTPCREL32_LO:

View File

@ -29,7 +29,6 @@ class ARM final : public TargetInfo {
uint32_t calcEFlags() const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
bool isPicRel(RelType Type) const override;
RelType getDynRel(RelType Type) const override;
int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
@ -55,6 +54,7 @@ ARM::ARM() {
TlsGotRel = R_ARM_TLS_TPOFF32;
TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
TlsOffsetRel = R_ARM_TLS_DTPOFF32;
GotBaseSymInGotPlt = false;
GotEntrySize = 4;
GotPltEntrySize = 4;
PltEntrySize = 16;
@ -161,18 +161,10 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
}
}
bool ARM::isPicRel(RelType Type) const {
return (Type == R_ARM_TARGET1 && !Config->Target1Rel) ||
(Type == R_ARM_ABS32);
}
RelType ARM::getDynRel(RelType Type) const {
if (Type == R_ARM_TARGET1 && !Config->Target1Rel)
if ((Type == R_ARM_ABS32) || (Type == R_ARM_TARGET1 && !Config->Target1Rel))
return R_ARM_ABS32;
if (Type == R_ARM_ABS32)
return Type;
// Keep it going with a dummy value so that we can find more reloc errors.
return R_ARM_ABS32;
return R_ARM_NONE;
}
void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const {
@ -392,7 +384,7 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write32le(Loc, 1);
break;
case R_ARM_PREL31:
checkInt<31>(Loc, Val, Type);
checkInt(Loc, Val, 31, Type);
write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000));
break;
case R_ARM_CALL:
@ -401,7 +393,7 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
if (Val & 1) {
// If bit 0 of Val is 1 the target is Thumb, we must select a BLX.
// The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1'
checkInt<26>(Loc, Val, Type);
checkInt(Loc, Val, 26, Type);
write32le(Loc, 0xfa000000 | // opcode
((Val & 2) << 23) | // H
((Val >> 2) & 0x00ffffff)); // imm24
@ -416,16 +408,16 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
checkInt<26>(Loc, Val, Type);
checkInt(Loc, Val, 26, Type);
write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff));
break;
case R_ARM_THM_JUMP11:
checkInt<12>(Loc, Val, Type);
checkInt(Loc, Val, 12, Type);
write16le(Loc, (read32le(Loc) & 0xf800) | ((Val >> 1) & 0x07ff));
break;
case R_ARM_THM_JUMP19:
// Encoding T3: Val = S:J2:J1:imm6:imm11:0
checkInt<21>(Loc, Val, Type);
checkInt(Loc, Val, 21, Type);
write16le(Loc,
(read16le(Loc) & 0xfbc0) | // opcode cond
((Val >> 10) & 0x0400) | // S
@ -451,7 +443,7 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_ARM_THM_JUMP24:
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
// FIXME: Use of I1 and I2 require v6T2ops
checkInt<25>(Loc, Val, Type);
checkInt(Loc, Val, 25, Type);
write16le(Loc,
0xf000 | // opcode
((Val >> 14) & 0x0400) | // S
@ -469,14 +461,14 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
break;
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
checkInt<32>(Loc, Val, Type);
checkInt(Loc, Val, 32, Type);
write32le(Loc, (read32le(Loc) & ~0x000f0fff) |
(((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff));
break;
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVT_PREL:
// Encoding T1: A = imm4:i:imm3:imm8
checkInt<32>(Loc, Val, Type);
checkInt(Loc, Val, 32, Type);
write16le(Loc,
0xf2c0 | // opcode
((Val >> 17) & 0x0400) | // i

97
ELF/Arch/Hexagon.cpp Normal file
View File

@ -0,0 +1,97 @@
//===-- Hexagon.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 "Symbols.h"
#include "Target.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
namespace {
class Hexagon final : public TargetInfo {
public:
uint32_t calcEFlags() const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
// Support V60 only at the moment.
uint32_t Hexagon::calcEFlags() const { return 0x60; }
static uint32_t applyMask(uint32_t Mask, uint32_t Data) {
uint32_t Result = 0;
size_t Off = 0;
for (size_t Bit = 0; Bit != 32; ++Bit) {
uint32_t ValBit = (Data >> Off) & 1;
uint32_t MaskBit = (Mask >> Bit) & 1;
if (MaskBit) {
Result |= (ValBit << Bit);
++Off;
}
}
return Result;
}
RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
case R_HEX_B15_PCREL:
case R_HEX_B15_PCREL_X:
case R_HEX_B22_PCREL:
case R_HEX_B22_PCREL_X:
case R_HEX_B32_PCREL_X:
return R_PC;
default:
return R_ABS;
}
}
static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); }
void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_HEX_NONE:
break;
case R_HEX_B15_PCREL:
or32le(Loc, applyMask(0x00df20fe, Val >> 2));
break;
case R_HEX_B15_PCREL_X:
or32le(Loc, applyMask(0x00df20fe, Val & 0x3f));
break;
case R_HEX_B22_PCREL:
or32le(Loc, applyMask(0x1ff3ffe, Val >> 2));
break;
case R_HEX_B22_PCREL_X:
or32le(Loc, applyMask(0x1ff3ffe, Val & 0x3f));
break;
case R_HEX_B32_PCREL_X:
or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
break;
default:
error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
break;
}
}
TargetInfo *elf::getHexagonTargetInfo() {
static Hexagon Target;
return &Target;
}

View File

@ -32,7 +32,6 @@ template <class ELFT> class MIPS final : public TargetInfo {
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
bool isPicRel(RelType Type) const override;
RelType getDynRel(RelType Type) const override;
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
void writePltHeader(uint8_t *Buf) const override;
@ -50,6 +49,7 @@ template <class ELFT> MIPS<ELFT>::MIPS() {
DefaultMaxPageSize = 65536;
GotEntrySize = sizeof(typename ELFT::uint);
GotPltEntrySize = sizeof(typename ELFT::uint);
GotBaseSymInGotPlt = false;
PltEntrySize = 16;
PltHeaderSize = 32;
CopyRel = R_MIPS_COPY;
@ -101,8 +101,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MIPS_HIGHEST:
case R_MICROMIPS_HI16:
case R_MICROMIPS_LO16:
case R_MICROMIPS_HIGHER:
case R_MICROMIPS_HIGHEST:
// R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate
// offset between start of function and 'gp' value which by default
// equal to the start of .got section. In that case we consider these
@ -124,8 +122,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MIPS_TLS_TPREL_LO16:
case R_MIPS_TLS_TPREL32:
case R_MIPS_TLS_TPREL64:
case R_MICROMIPS_GOT_OFST:
case R_MICROMIPS_SUB:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_TPREL_HI16:
@ -155,7 +151,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MIPS_GOT_DISP:
case R_MIPS_TLS_GOTTPREL:
case R_MICROMIPS_CALL16:
case R_MICROMIPS_GOT_DISP:
case R_MICROMIPS_TLS_GOTTPREL:
return R_MIPS_GOT_OFF;
case R_MIPS_CALL_HI16:
@ -168,7 +163,6 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_MICROMIPS_GOT_LO16:
return R_MIPS_GOT_OFF32;
case R_MIPS_GOT_PAGE:
case R_MICROMIPS_GOT_PAGE:
return R_MIPS_GOT_LOCAL_PAGE;
case R_MIPS_TLS_GD:
case R_MICROMIPS_TLS_GD:
@ -183,12 +177,10 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
}
}
template <class ELFT> bool MIPS<ELFT>::isPicRel(RelType Type) const {
return Type == R_MIPS_32 || Type == R_MIPS_64;
}
template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const {
return RelativeRel;
if (Type == R_MIPS_32 || Type == R_MIPS_64)
return RelativeRel;
return R_MIPS_NONE;
}
template <class ELFT>
@ -213,8 +205,8 @@ template <endianness E> static uint32_t readShuffle(const uint8_t *Loc) {
}
template <endianness E>
static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
uint8_t Shift) {
static void writeValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
uint8_t Shift) {
uint32_t Instr = read32<E>(Loc);
uint32_t Mask = 0xffffffff >> (32 - BitsSize);
uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
@ -222,14 +214,14 @@ static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
}
template <endianness E>
static void writeMicroRelocation32(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
uint8_t Shift) {
static void writeShuffleValue(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
uint8_t Shift) {
// See comments in readShuffle for purpose of this code.
uint16_t *Words = (uint16_t *)Loc;
if (E == support::little)
std::swap(Words[0], Words[1]);
writeRelocation<E>(Loc, V, BitsSize, Shift);
writeValue<E>(Loc, V, BitsSize, Shift);
if (E == support::little)
std::swap(Words[0], Words[1]);
@ -296,13 +288,14 @@ template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
}
write32<E>(Buf + 24, 0x0320f809); // jalr $25
uint32_t JalrInst = Config->ZHazardplt ? 0x0320fc09 : 0x0320f809;
write32<E>(Buf + 24, JalrInst); // jalr.hb $25 or jalr $25
write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
uint64_t GotPlt = InX::GotPlt->getVA();
writeRelocation<E>(Buf, GotPlt + 0x8000, 16, 16);
writeRelocation<E>(Buf + 4, GotPlt, 16, 0);
writeRelocation<E>(Buf + 8, GotPlt, 16, 0);
writeValue<E>(Buf, GotPlt + 0x8000, 16, 16);
writeValue<E>(Buf + 4, GotPlt, 16, 0);
writeValue<E>(Buf + 8, GotPlt, 16, 0);
}
template <class ELFT>
@ -330,13 +323,16 @@ void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
return;
}
uint32_t JrInst = isMipsR6() ? (Config->ZHazardplt ? 0x03200409 : 0x03200009)
: (Config->ZHazardplt ? 0x03200408 : 0x03200008);
write32<E>(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15)
write32<E>(Buf + 8, isMipsR6() ? 0x03200009 : 0x03200008); // jr $25
write32<E>(Buf + 8, JrInst); // jr $25 / jr.hb $25
write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
writeRelocation<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
writeRelocation<E>(Buf + 4, GotPltEntryAddr, 16, 0);
writeRelocation<E>(Buf + 12, GotPltEntryAddr, 16, 0);
writeValue<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
writeValue<E>(Buf + 4, GotPltEntryAddr, 16, 0);
writeValue<E>(Buf + 12, GotPltEntryAddr, 16, 0);
}
template <class ELFT>
@ -455,9 +451,6 @@ calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) {
return std::make_pair(Type2, Val);
if (Type2 == R_MIPS_SUB && (Type3 == R_MIPS_HI16 || Type3 == R_MIPS_LO16))
return std::make_pair(Type3, -Val);
if (Type2 == R_MICROMIPS_SUB &&
(Type3 == R_MICROMIPS_HI16 || Type3 == R_MICROMIPS_LO16))
return std::make_pair(Type3, -Val);
error(getErrorLocation(Loc) + "unsupported relocations combination " +
Twine(Type));
return std::make_pair(Type & 0xff, Val);
@ -467,6 +460,9 @@ template <class ELFT>
void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
const endianness E = ELFT::TargetEndianness;
if (ELFT::Is64Bits || Config->MipsN32Abi)
std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
// Thread pointer and DRP offsets from the start of TLS data area.
// https://www.linux-mips.org/wiki/NPTL
if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 ||
@ -481,9 +477,6 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
Val -= 0x7000;
}
if (ELFT::Is64Bits || Config->MipsN32Abi)
std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
switch (Type) {
case R_MIPS_32:
case R_MIPS_GPREL32:
@ -497,25 +490,25 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
write64<E>(Loc, Val);
break;
case R_MIPS_26:
writeRelocation<E>(Loc, Val, 26, 2);
writeValue<E>(Loc, Val, 26, 2);
break;
case R_MIPS_GOT16:
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
// is updated addend (not a GOT index). In that case write high 16 bits
// to store a correct addend value.
if (Config->Relocatable) {
writeRelocation<E>(Loc, Val + 0x8000, 16, 16);
writeValue<E>(Loc, Val + 0x8000, 16, 16);
} else {
checkInt<16>(Loc, Val, Type);
writeRelocation<E>(Loc, Val, 16, 0);
checkInt(Loc, Val, 16, Type);
writeValue<E>(Loc, Val, 16, 0);
}
break;
case R_MICROMIPS_GOT16:
if (Config->Relocatable) {
writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16);
writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16);
} else {
checkInt<16>(Loc, Val, Type);
writeMicroRelocation32<E>(Loc, Val, 16, 0);
checkInt(Loc, Val, 16, Type);
writeShuffleValue<E>(Loc, Val, 16, 0);
}
break;
case R_MIPS_CALL16:
@ -525,7 +518,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MIPS_TLS_GD:
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_LDM:
checkInt<16>(Loc, Val, Type);
checkInt(Loc, Val, 16, Type);
LLVM_FALLTHROUGH;
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_LO16:
@ -534,28 +527,25 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MIPS_PCLO16:
case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_TPREL_LO16:
writeRelocation<E>(Loc, Val, 16, 0);
writeValue<E>(Loc, Val, 16, 0);
break;
case R_MICROMIPS_GOT_DISP:
case R_MICROMIPS_GOT_PAGE:
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_LDM:
checkInt<16>(Loc, Val, Type);
writeMicroRelocation32<E>(Loc, Val, 16, 0);
checkInt(Loc, Val, 16, Type);
writeShuffleValue<E>(Loc, Val, 16, 0);
break;
case R_MICROMIPS_CALL16:
case R_MICROMIPS_CALL_LO16:
case R_MICROMIPS_GOT_OFST:
case R_MICROMIPS_LO16:
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_GOTTPREL:
case R_MICROMIPS_TLS_TPREL_LO16:
writeMicroRelocation32<E>(Loc, Val, 16, 0);
writeShuffleValue<E>(Loc, Val, 16, 0);
break;
case R_MICROMIPS_GPREL7_S2:
checkInt<7>(Loc, Val, Type);
writeMicroRelocation32<E>(Loc, Val, 7, 2);
checkInt(Loc, Val, 7, Type);
writeShuffleValue<E>(Loc, Val, 7, 2);
break;
case R_MIPS_CALL_HI16:
case R_MIPS_GOT_HI16:
@ -563,86 +553,80 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_MIPS_PCHI16:
case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_TPREL_HI16:
writeRelocation<E>(Loc, Val + 0x8000, 16, 16);
writeValue<E>(Loc, Val + 0x8000, 16, 16);
break;
case R_MICROMIPS_CALL_HI16:
case R_MICROMIPS_GOT_HI16:
case R_MICROMIPS_HI16:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_TPREL_HI16:
writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16);
writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16);
break;
case R_MIPS_HIGHER:
writeRelocation<E>(Loc, Val + 0x80008000, 16, 32);
writeValue<E>(Loc, Val + 0x80008000, 16, 32);
break;
case R_MIPS_HIGHEST:
writeRelocation<E>(Loc, Val + 0x800080008000, 16, 48);
break;
case R_MICROMIPS_HIGHER:
writeMicroRelocation32<E>(Loc, Val + 0x80008000, 16, 32);
break;
case R_MICROMIPS_HIGHEST:
writeMicroRelocation32<E>(Loc, Val + 0x800080008000, 16, 48);
writeValue<E>(Loc, Val + 0x800080008000, 16, 48);
break;
case R_MIPS_JALR:
case R_MICROMIPS_JALR:
// Ignore this optimization relocation for now
break;
case R_MIPS_PC16:
checkAlignment<4>(Loc, Val, Type);
checkInt<18>(Loc, Val, Type);
writeRelocation<E>(Loc, Val, 16, 2);
checkAlignment(Loc, Val, 4, Type);
checkInt(Loc, Val, 18, Type);
writeValue<E>(Loc, Val, 16, 2);
break;
case R_MIPS_PC19_S2:
checkAlignment<4>(Loc, Val, Type);
checkInt<21>(Loc, Val, Type);
writeRelocation<E>(Loc, Val, 19, 2);
checkAlignment(Loc, Val, 4, Type);
checkInt(Loc, Val, 21, Type);
writeValue<E>(Loc, Val, 19, 2);
break;
case R_MIPS_PC21_S2:
checkAlignment<4>(Loc, Val, Type);
checkInt<23>(Loc, Val, Type);
writeRelocation<E>(Loc, Val, 21, 2);
checkAlignment(Loc, Val, 4, Type);
checkInt(Loc, Val, 23, Type);
writeValue<E>(Loc, Val, 21, 2);
break;
case R_MIPS_PC26_S2:
checkAlignment<4>(Loc, Val, Type);
checkInt<28>(Loc, Val, Type);
writeRelocation<E>(Loc, Val, 26, 2);
checkAlignment(Loc, Val, 4, Type);
checkInt(Loc, Val, 28, Type);
writeValue<E>(Loc, Val, 26, 2);
break;
case R_MIPS_PC32:
writeRelocation<E>(Loc, Val, 32, 0);
writeValue<E>(Loc, Val, 32, 0);
break;
case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC26_S1:
checkInt<27>(Loc, Val, Type);
writeMicroRelocation32<E>(Loc, Val, 26, 1);
checkInt(Loc, Val, 27, Type);
writeShuffleValue<E>(Loc, Val, 26, 1);
break;
case R_MICROMIPS_PC7_S1:
checkInt<8>(Loc, Val, Type);
checkInt(Loc, Val, 8, Type);
writeMicroRelocation16<E>(Loc, Val, 7, 1);
break;
case R_MICROMIPS_PC10_S1:
checkInt<11>(Loc, Val, Type);
checkInt(Loc, Val, 11, Type);
writeMicroRelocation16<E>(Loc, Val, 10, 1);
break;
case R_MICROMIPS_PC16_S1:
checkInt<17>(Loc, Val, Type);
writeMicroRelocation32<E>(Loc, Val, 16, 1);
checkInt(Loc, Val, 17, Type);
writeShuffleValue<E>(Loc, Val, 16, 1);
break;
case R_MICROMIPS_PC18_S3:
checkInt<21>(Loc, Val, Type);
writeMicroRelocation32<E>(Loc, Val, 18, 3);
checkInt(Loc, Val, 21, Type);
writeShuffleValue<E>(Loc, Val, 18, 3);
break;
case R_MICROMIPS_PC19_S2:
checkInt<21>(Loc, Val, Type);
writeMicroRelocation32<E>(Loc, Val, 19, 2);
checkInt(Loc, Val, 21, Type);
writeShuffleValue<E>(Loc, Val, 19, 2);
break;
case R_MICROMIPS_PC21_S1:
checkInt<22>(Loc, Val, Type);
writeMicroRelocation32<E>(Loc, Val, 21, 1);
checkInt(Loc, Val, 22, Type);
writeShuffleValue<E>(Loc, Val, 21, 1);
break;
case R_MICROMIPS_PC23_S2:
checkInt<25>(Loc, Val, Type);
writeMicroRelocation32<E>(Loc, Val, 23, 2);
checkInt(Loc, Val, 25, Type);
writeShuffleValue<E>(Loc, Val, 23, 2);
break;
default:
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
@ -651,19 +635,26 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType Type) const {
return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST ||
Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_GOT_OFST;
Type == R_MICROMIPS_LO16;
}
// Return true if the symbol is a PIC function.
template <class ELFT> bool elf::isMipsPIC(const Defined *Sym) {
typedef typename ELFT::Ehdr Elf_Ehdr;
if (!Sym->Section || !Sym->isFunc())
if (!Sym->isFunc())
return false;
auto *Sec = cast<InputSectionBase>(Sym->Section);
const Elf_Ehdr *Hdr = Sec->template getFile<ELFT>()->getObj().getHeader();
return (Sym->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC ||
(Hdr->e_flags & EF_MIPS_PIC);
if (Sym->StOther & STO_MIPS_PIC)
return true;
if (!Sym->Section)
return false;
ObjFile<ELFT> *File =
cast<InputSectionBase>(Sym->Section)->template getFile<ELFT>();
if (!File)
return false;
return File->getObj().getHeader()->e_flags & EF_MIPS_PIC;
}
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {

View File

@ -65,25 +65,30 @@ static StringRef getNanName(bool IsNan2008) {
static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; }
static void checkFlags(ArrayRef<FileFlags> Files) {
assert(!Files.empty() && "expected non-empty file list");
uint32_t ABI = Files[0].Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
bool Nan = Files[0].Flags & EF_MIPS_NAN2008;
bool Fp = Files[0].Flags & EF_MIPS_FP64;
for (const FileFlags &F : Files.slice(1)) {
for (const FileFlags &F : Files) {
if (Config->Is64 && F.Flags & EF_MIPS_MICROMIPS)
error(toString(F.File) + ": microMIPS 64-bit is not supported");
uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
if (ABI != ABI2)
error("target ABI '" + getAbiName(ABI) + "' is incompatible with '" +
getAbiName(ABI2) + "': " + toString(F.File));
error(toString(F.File) + ": ABI '" + getAbiName(ABI2) +
"' is incompatible with target ABI '" + getAbiName(ABI) + "'");
bool Nan2 = F.Flags & EF_MIPS_NAN2008;
if (Nan != Nan2)
error("target -mnan=" + getNanName(Nan) + " is incompatible with -mnan=" +
getNanName(Nan2) + ": " + toString(F.File));
error(toString(F.File) + ": -mnan=" + getNanName(Nan2) +
" is incompatible with target -mnan=" + getNanName(Nan));
bool Fp2 = F.Flags & EF_MIPS_FP64;
if (Fp != Fp2)
error("target -mfp" + getFpName(Fp) + " is incompatible with -mfp" +
getFpName(Fp2) + ": " + toString(F.File));
error(toString(F.File) + ": -mfp" + getFpName(Fp2) +
" is incompatible with target -mfp" + getFpName(Fp));
}
}
@ -102,11 +107,13 @@ static uint32_t getPicFlags(ArrayRef<FileFlags> Files) {
for (const FileFlags &F : Files.slice(1)) {
bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
if (IsPic && !IsPic2)
warn("linking abicalls code " + toString(Files[0].File) +
" with non-abicalls file: " + toString(F.File));
warn(toString(F.File) +
": linking non-abicalls code with abicalls code " +
toString(Files[0].File));
if (!IsPic && IsPic2)
warn("linking non-abicalls code " + toString(Files[0].File) +
" with abicalls file: " + toString(F.File));
warn(toString(F.File) +
": linking abicalls code with non-abicalls code " +
toString(Files[0].File));
}
// Compute the result PIC/non-PIC flag.
@ -326,7 +333,7 @@ static StringRef getMipsFpAbiName(uint8_t FpAbi) {
case Mips::Val_GNU_MIPS_ABI_FP_SOFT:
return "-msoft-float";
case Mips::Val_GNU_MIPS_ABI_FP_OLD_64:
return "-mips32r2 -mfp64 (old)";
return "-mgp32 -mfp64 (old)";
case Mips::Val_GNU_MIPS_ABI_FP_XX:
return "-mfpxx";
case Mips::Val_GNU_MIPS_ABI_FP_64:
@ -343,9 +350,9 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
if (compareMipsFpAbi(NewFlag, OldFlag) >= 0)
return NewFlag;
if (compareMipsFpAbi(OldFlag, NewFlag) < 0)
error("target floating point ABI '" + getMipsFpAbiName(OldFlag) +
"' is incompatible with '" + getMipsFpAbiName(NewFlag) +
"': " + FileName);
error(FileName + ": floating point ABI '" + getMipsFpAbiName(NewFlag) +
"' is incompatible with target floating point ABI '" +
getMipsFpAbiName(OldFlag) + "'");
return OldFlag;
}

View File

@ -21,13 +21,18 @@ using namespace lld::elf;
namespace {
class PPC final : public TargetInfo {
public:
PPC() { GotBaseSymOff = 0x8000; }
PPC();
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
};
} // namespace
PPC::PPC() {
GotBaseSymOff = 0x8000;
GotBaseSymInGotPlt = false;
}
RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {

View File

@ -14,12 +14,14 @@
#include "llvm/Support/Endian.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
static uint64_t PPC64TocOffset = 0x8000;
static uint64_t DynamicThreadPointerOffset = 0x8000;
uint64_t elf::getPPC64TocBase() {
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
@ -39,11 +41,21 @@ namespace {
class PPC64 final : public TargetInfo {
public:
PPC64();
uint32_t calcEFlags() const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void writeGotHeader(uint8_t *Buf) const override;
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint64_t BranchAddr, const Symbol &S) const override;
RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const override;
void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
@ -51,21 +63,35 @@ class PPC64 final : public TargetInfo {
// #higher(value), #highera(value), #highest(value), and #highesta(value)
// macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi
// document.
static uint16_t applyPPCLo(uint64_t V) { return V; }
static uint16_t applyPPCHi(uint64_t V) { return V >> 16; }
static uint16_t applyPPCHa(uint64_t V) { return (V + 0x8000) >> 16; }
static uint16_t applyPPCHigher(uint64_t V) { return V >> 32; }
static uint16_t applyPPCHighera(uint64_t V) { return (V + 0x8000) >> 32; }
static uint16_t applyPPCHighest(uint64_t V) { return V >> 48; }
static uint16_t applyPPCHighesta(uint64_t V) { return (V + 0x8000) >> 48; }
static uint16_t lo(uint64_t V) { return V; }
static uint16_t hi(uint64_t V) { return V >> 16; }
static uint16_t ha(uint64_t V) { return (V + 0x8000) >> 16; }
static uint16_t higher(uint64_t V) { return V >> 32; }
static uint16_t highera(uint64_t V) { return (V + 0x8000) >> 32; }
static uint16_t highest(uint64_t V) { return V >> 48; }
static uint16_t highesta(uint64_t V) { return (V + 0x8000) >> 48; }
PPC64::PPC64() {
PltRel = GotRel = R_PPC64_GLOB_DAT;
GotRel = R_PPC64_GLOB_DAT;
PltRel = R_PPC64_JMP_SLOT;
RelativeRel = R_PPC64_RELATIVE;
IRelativeRel = R_PPC64_IRELATIVE;
GotEntrySize = 8;
PltEntrySize = 4;
GotPltEntrySize = 8;
PltEntrySize = 32;
PltHeaderSize = 0;
GotBaseSymInGotPlt = false;
GotBaseSymOff = 0x8000;
GotHeaderEntriesNum = 1;
GotPltHeaderEntriesNum = 2;
PltHeaderSize = 60;
NeedsThunks = true;
TcbSize = 8;
TlsTpOffset = 0x7000;
TlsModuleIndexRel = R_PPC64_DTPMOD64;
TlsOffsetRel = R_PPC64_DTPREL64;
TlsGotRel = R_PPC64_TPREL64;
// We need 64K pages (at least under glibc/Linux, the loader won't
// set different permissions on a finer granularity than that).
@ -80,6 +106,110 @@ PPC64::PPC64() {
// And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers
// use 0x10000000 as the starting address.
DefaultImageBase = 0x10000000;
TrapInstr =
(Config->IsLE == sys::IsLittleEndianHost) ? 0x7fe00008 : 0x0800e07f;
}
static uint32_t getEFlags(InputFile *File) {
if (Config->EKind == ELF64BEKind)
return cast<ObjFile<ELF64BE>>(File)->getObj().getHeader()->e_flags;
return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags;
}
// This file implements v2 ABI. This function makes sure that all
// object files have v2 or an unspecified version as an ABI version.
uint32_t PPC64::calcEFlags() const {
for (InputFile *F : ObjectFiles) {
uint32_t Flag = getEFlags(F);
if (Flag == 1)
error(toString(F) + ": ABI version 1 is not supported");
else if (Flag > 2)
error(toString(F) + ": unrecognized e_flags: " + Twine(Flag));
}
return 2;
}
void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement.
// The general dynamic code sequence for a global `x` will look like:
// Instruction Relocation Symbol
// addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x
// addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x
// bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x
// R_PPC64_REL24 __tls_get_addr
// nop None None
// Relaxing to local exec entails converting:
// addis r3, r2, x@got@tlsgd@ha into nop
// addi r3, r3, x@got@tlsgd@l into addis r3, r13, x@tprel@ha
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, x@tprel@l
uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U;
switch (Type) {
case R_PPC64_GOT_TLSGD16_HA:
write32(Loc - EndianOffset, 0x60000000); // nop
break;
case R_PPC64_GOT_TLSGD16_LO:
write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13
relocateOne(Loc, R_PPC64_TPREL16_HA, Val);
break;
case R_PPC64_TLSGD:
write32(Loc, 0x60000000); // nop
write32(Loc + 4, 0x38630000); // addi r3, r3
relocateOne(Loc + 4 + EndianOffset, R_PPC64_TPREL16_LO, Val);
break;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement.
// The local dynamic code sequence for a global `x` will look like:
// Instruction Relocation Symbol
// addis r3, r2, x@got@tlsld@ha R_PPC64_GOT_TLSLD16_HA x
// addi r3, r3, x@got@tlsld@l R_PPC64_GOT_TLSLD16_LO x
// bl __tls_get_addr(x@tlsgd) R_PPC64_TLSLD x
// R_PPC64_REL24 __tls_get_addr
// nop None None
// Relaxing to local exec entails converting:
// addis r3, r2, x@got@tlsld@ha into nop
// addi r3, r3, x@got@tlsld@l into addis r3, r13, 0
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, 4096
uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U;
switch (Type) {
case R_PPC64_GOT_TLSLD16_HA:
write32(Loc - EndianOffset, 0x60000000); // nop
break;
case R_PPC64_GOT_TLSLD16_LO:
write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13, 0
break;
case R_PPC64_TLSLD:
write32(Loc, 0x60000000); // nop
write32(Loc + 4, 0x38631000); // addi r3, r3, 4096
break;
case R_PPC64_DTPREL16:
case R_PPC64_DTPREL16_HA:
case R_PPC64_DTPREL16_HI:
case R_PPC64_DTPREL16_DS:
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL16_LO_DS:
case R_PPC64_GOT_DTPREL16_HA:
case R_PPC64_GOT_DTPREL16_LO_DS:
case R_PPC64_GOT_DTPREL16_DS:
case R_PPC64_GOT_DTPREL16_HI:
relocateOne(Loc, Type, Val);
break;
default:
llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
}
}
RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
@ -95,48 +225,162 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
case R_PPC64_TOC:
return R_PPC_TOC;
case R_PPC64_REL24:
return R_PPC_PLT_OPD;
return R_PPC_CALL_PLT;
case R_PPC64_REL16_LO:
case R_PPC64_REL16_HA:
case R_PPC64_REL32:
case R_PPC64_REL64:
return R_PC;
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_HA:
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_GOT_TLSGD16_LO:
return R_TLSGD_GOT;
case R_PPC64_GOT_TLSLD16:
case R_PPC64_GOT_TLSLD16_HA:
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_GOT_TLSLD16_LO:
return R_TLSLD_GOT;
case R_PPC64_GOT_TPREL16_HA:
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_TPREL16_DS:
case R_PPC64_GOT_TPREL16_HI:
return R_GOT_OFF;
case R_PPC64_GOT_DTPREL16_HA:
case R_PPC64_GOT_DTPREL16_LO_DS:
case R_PPC64_GOT_DTPREL16_DS:
case R_PPC64_GOT_DTPREL16_HI:
return R_TLSLD_GOT_OFF;
case R_PPC64_TPREL16:
case R_PPC64_TPREL16_HA:
case R_PPC64_TPREL16_LO:
case R_PPC64_TPREL16_HI:
case R_PPC64_TPREL16_DS:
case R_PPC64_TPREL16_LO_DS:
case R_PPC64_TPREL16_HIGHER:
case R_PPC64_TPREL16_HIGHERA:
case R_PPC64_TPREL16_HIGHEST:
case R_PPC64_TPREL16_HIGHESTA:
return R_TLS;
case R_PPC64_DTPREL16:
case R_PPC64_DTPREL16_DS:
case R_PPC64_DTPREL16_HA:
case R_PPC64_DTPREL16_HI:
case R_PPC64_DTPREL16_HIGHER:
case R_PPC64_DTPREL16_HIGHERA:
case R_PPC64_DTPREL16_HIGHEST:
case R_PPC64_DTPREL16_HIGHESTA:
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL16_LO_DS:
case R_PPC64_DTPREL64:
return R_ABS;
case R_PPC64_TLSGD:
return R_TLSDESC_CALL;
case R_PPC64_TLSLD:
return R_TLSLD_HINT;
case R_PPC64_TLS:
return R_HINT;
default:
return R_ABS;
}
}
void PPC64::writeGotHeader(uint8_t *Buf) const {
write64(Buf, getPPC64TocBase());
}
void PPC64::writePltHeader(uint8_t *Buf) const {
// The generic resolver stub goes first.
write32(Buf + 0, 0x7c0802a6); // mflr r0
write32(Buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8>
write32(Buf + 8, 0x7d6802a6); // mflr r11
write32(Buf + 12, 0x7c0803a6); // mtlr r0
write32(Buf + 16, 0x7d8b6050); // subf r12, r11, r12
write32(Buf + 20, 0x380cffcc); // subi r0,r12,52
write32(Buf + 24, 0x7800f082); // srdi r0,r0,62,2
write32(Buf + 28, 0xe98b002c); // ld r12,44(r11)
write32(Buf + 32, 0x7d6c5a14); // add r11,r12,r11
write32(Buf + 36, 0xe98b0000); // ld r12,0(r11)
write32(Buf + 40, 0xe96b0008); // ld r11,8(r11)
write32(Buf + 44, 0x7d8903a6); // mtctr r12
write32(Buf + 48, 0x4e800420); // bctr
// The 'bcl' instruction will set the link register to the address of the
// following instruction ('mflr r11'). Here we store the offset from that
// instruction to the first entry in the GotPlt section.
int64_t GotPltOffset = InX::GotPlt->getVA() - (InX::Plt->getVA() + 8);
write64(Buf + 52, GotPltOffset);
}
void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
uint64_t Off = GotPltEntryAddr - getPPC64TocBase();
// FIXME: What we should do, in theory, is get the offset of the function
// descriptor in the .opd section, and use that as the offset from %r2 (the
// TOC-base pointer). Instead, we have the GOT-entry offset, and that will
// be a pointer to the function descriptor in the .opd section. Using
// this scheme is simpler, but requires an extra indirection per PLT dispatch.
write32be(Buf, 0xf8410028); // std %r2, 40(%r1)
write32be(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha
write32be(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11)
write32be(Buf + 12, 0xe96c0000); // ld %r11,0(%r12)
write32be(Buf + 16, 0x7d6903a6); // mtctr %r11
write32be(Buf + 20, 0xe84c0008); // ld %r2,8(%r12)
write32be(Buf + 24, 0xe96c0010); // ld %r11,16(%r12)
write32be(Buf + 28, 0x4e800420); // bctr
int32_t Offset = PltHeaderSize + Index * PltEntrySize;
// bl __glink_PLTresolve
write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc));
}
static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
uint64_t V = Val - PPC64TocOffset;
// Relocations relative to the toc-base need to be adjusted by the Toc offset.
uint64_t TocBiasedVal = Val - PPC64TocOffset;
// Relocations relative to dtv[dtpmod] need to be adjusted by the DTP offset.
uint64_t DTPBiasedVal = Val - DynamicThreadPointerOffset;
switch (Type) {
// TOC biased relocation.
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSLD16:
case R_PPC64_TOC16:
return {R_PPC64_ADDR16, V};
return {R_PPC64_ADDR16, TocBiasedVal};
case R_PPC64_TOC16_DS:
return {R_PPC64_ADDR16_DS, V};
case R_PPC64_GOT_TPREL16_DS:
case R_PPC64_GOT_DTPREL16_DS:
return {R_PPC64_ADDR16_DS, TocBiasedVal};
case R_PPC64_GOT_TLSGD16_HA:
case R_PPC64_GOT_TLSLD16_HA:
case R_PPC64_GOT_TPREL16_HA:
case R_PPC64_GOT_DTPREL16_HA:
case R_PPC64_TOC16_HA:
return {R_PPC64_ADDR16_HA, V};
return {R_PPC64_ADDR16_HA, TocBiasedVal};
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_GOT_TPREL16_HI:
case R_PPC64_GOT_DTPREL16_HI:
case R_PPC64_TOC16_HI:
return {R_PPC64_ADDR16_HI, V};
return {R_PPC64_ADDR16_HI, TocBiasedVal};
case R_PPC64_GOT_TLSGD16_LO:
case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_TOC16_LO:
return {R_PPC64_ADDR16_LO, V};
return {R_PPC64_ADDR16_LO, TocBiasedVal};
case R_PPC64_TOC16_LO_DS:
return {R_PPC64_ADDR16_LO_DS, V};
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_DTPREL16_LO_DS:
return {R_PPC64_ADDR16_LO_DS, TocBiasedVal};
// Dynamic Thread pointer biased relocation types.
case R_PPC64_DTPREL16:
return {R_PPC64_ADDR16, DTPBiasedVal};
case R_PPC64_DTPREL16_DS:
return {R_PPC64_ADDR16_DS, DTPBiasedVal};
case R_PPC64_DTPREL16_HA:
return {R_PPC64_ADDR16_HA, DTPBiasedVal};
case R_PPC64_DTPREL16_HI:
return {R_PPC64_ADDR16_HI, DTPBiasedVal};
case R_PPC64_DTPREL16_HIGHER:
return {R_PPC64_ADDR16_HIGHER, DTPBiasedVal};
case R_PPC64_DTPREL16_HIGHERA:
return {R_PPC64_ADDR16_HIGHERA, DTPBiasedVal};
case R_PPC64_DTPREL16_HIGHEST:
return {R_PPC64_ADDR16_HIGHEST, DTPBiasedVal};
case R_PPC64_DTPREL16_HIGHESTA:
return {R_PPC64_ADDR16_HIGHESTA, DTPBiasedVal};
case R_PPC64_DTPREL16_LO:
return {R_PPC64_ADDR16_LO, DTPBiasedVal};
case R_PPC64_DTPREL16_LO_DS:
return {R_PPC64_ADDR16_LO_DS, DTPBiasedVal};
case R_PPC64_DTPREL64:
return {R_PPC64_ADDR64, DTPBiasedVal};
default:
return {Type, Val};
}
@ -149,68 +393,139 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_PPC64_ADDR14: {
checkAlignment<4>(Loc, Val, Type);
checkAlignment(Loc, Val, 4, Type);
// Preserve the AA/LK bits in the branch instruction
uint8_t AALK = Loc[3];
write16be(Loc + 2, (AALK & 3) | (Val & 0xfffc));
write16(Loc + 2, (AALK & 3) | (Val & 0xfffc));
break;
}
case R_PPC64_ADDR16:
checkInt<16>(Loc, Val, Type);
write16be(Loc, Val);
case R_PPC64_TPREL16:
checkInt(Loc, Val, 16, Type);
write16(Loc, Val);
break;
case R_PPC64_ADDR16_DS:
checkInt<16>(Loc, Val, Type);
write16be(Loc, (read16be(Loc) & 3) | (Val & ~3));
case R_PPC64_TPREL16_DS:
checkInt(Loc, Val, 16, Type);
write16(Loc, (read16(Loc) & 3) | (Val & ~3));
break;
case R_PPC64_ADDR16_HA:
case R_PPC64_REL16_HA:
write16be(Loc, applyPPCHa(Val));
case R_PPC64_TPREL16_HA:
write16(Loc, ha(Val));
break;
case R_PPC64_ADDR16_HI:
case R_PPC64_REL16_HI:
write16be(Loc, applyPPCHi(Val));
case R_PPC64_TPREL16_HI:
write16(Loc, hi(Val));
break;
case R_PPC64_ADDR16_HIGHER:
write16be(Loc, applyPPCHigher(Val));
case R_PPC64_TPREL16_HIGHER:
write16(Loc, higher(Val));
break;
case R_PPC64_ADDR16_HIGHERA:
write16be(Loc, applyPPCHighera(Val));
case R_PPC64_TPREL16_HIGHERA:
write16(Loc, highera(Val));
break;
case R_PPC64_ADDR16_HIGHEST:
write16be(Loc, applyPPCHighest(Val));
case R_PPC64_TPREL16_HIGHEST:
write16(Loc, highest(Val));
break;
case R_PPC64_ADDR16_HIGHESTA:
write16be(Loc, applyPPCHighesta(Val));
case R_PPC64_TPREL16_HIGHESTA:
write16(Loc, highesta(Val));
break;
case R_PPC64_ADDR16_LO:
write16be(Loc, applyPPCLo(Val));
case R_PPC64_REL16_LO:
case R_PPC64_TPREL16_LO:
write16(Loc, lo(Val));
break;
case R_PPC64_ADDR16_LO_DS:
case R_PPC64_REL16_LO:
write16be(Loc, (read16be(Loc) & 3) | (applyPPCLo(Val) & ~3));
case R_PPC64_TPREL16_LO_DS:
write16(Loc, (read16(Loc) & 3) | (lo(Val) & ~3));
break;
case R_PPC64_ADDR32:
case R_PPC64_REL32:
checkInt<32>(Loc, Val, Type);
write32be(Loc, Val);
checkInt(Loc, Val, 32, Type);
write32(Loc, Val);
break;
case R_PPC64_ADDR64:
case R_PPC64_REL64:
case R_PPC64_TOC:
write64be(Loc, Val);
write64(Loc, Val);
break;
case R_PPC64_REL24: {
uint32_t Mask = 0x03FFFFFC;
checkInt<24>(Loc, Val, Type);
write32be(Loc, (read32be(Loc) & ~Mask) | (Val & Mask));
checkInt(Loc, Val, 24, Type);
write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
break;
}
case R_PPC64_DTPREL64:
write64(Loc, Val - DynamicThreadPointerOffset);
break;
default:
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint64_t BranchAddr, const Symbol &S) const {
// If a function is in the plt it needs to be called through
// a call stub.
return Type == R_PPC64_REL24 && S.isInPlt();
}
RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const {
if (Expr == R_RELAX_TLS_GD_TO_IE)
return R_RELAX_TLS_GD_TO_IE_GOT_OFF;
if (Expr == R_RELAX_TLS_LD_TO_LE)
return R_RELAX_TLS_LD_TO_LE_ABS;
return Expr;
}
// Reference: 3.7.4.1 of the 64-bit ELF V2 abi supplement.
// The general dynamic code sequence for a global `x` uses 4 instructions.
// Instruction Relocation Symbol
// addis r3, r2, x@got@tlsgd@ha R_PPC64_GOT_TLSGD16_HA x
// addi r3, r3, x@got@tlsgd@l R_PPC64_GOT_TLSGD16_LO x
// bl __tls_get_addr(x@tlsgd) R_PPC64_TLSGD x
// R_PPC64_REL24 __tls_get_addr
// nop None None
//
// Relaxing to initial-exec entails:
// 1) Convert the addis/addi pair that builds the address of the tls_index
// struct for 'x' to an addis/ld pair that loads an offset from a got-entry.
// 2) Convert the call to __tls_get_addr to a nop.
// 3) Convert the nop following the call to an add of the loaded offset to the
// thread pointer.
// Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is
// used as the relaxation hint for both steps 2 and 3.
void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_PPC64_GOT_TLSGD16_HA:
// This is relaxed from addis rT, r2, sym@got@tlsgd@ha to
// addis rT, r2, sym@got@tprel@ha.
relocateOne(Loc, R_PPC64_GOT_TPREL16_HA, Val);
return;
case R_PPC64_GOT_TLSGD16_LO: {
// Relax from addi r3, rA, sym@got@tlsgd@l to
// ld r3, sym@got@tprel@l(rA)
uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U;
uint32_t InputRegister = (read32(Loc - EndianOffset) & (0x1f << 16));
write32(Loc - EndianOffset, 0xE8600000 | InputRegister);
relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val);
return;
}
case R_PPC64_TLSGD:
write32(Loc, 0x60000000); // bl __tls_get_addr(sym@tlsgd) --> nop
write32(Loc + 4, 0x7c636A14); // nop --> add r3, r3, r13
return;
default:
llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");
}
}
TargetInfo *elf::getPPC64TargetInfo() {
static PPC64 Target;
return &Target;

View File

@ -77,23 +77,23 @@ void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_SPARC_32:
case R_SPARC_UA32:
// V-word32
checkUInt<32>(Loc, Val, Type);
checkUInt(Loc, Val, 32, Type);
write32be(Loc, Val);
break;
case R_SPARC_DISP32:
// V-disp32
checkInt<32>(Loc, Val, Type);
checkInt(Loc, Val, 32, Type);
write32be(Loc, Val);
break;
case R_SPARC_WDISP30:
case R_SPARC_WPLT30:
// V-disp30
checkInt<32>(Loc, Val, Type);
checkInt(Loc, Val, 32, Type);
write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff));
break;
case R_SPARC_22:
// V-imm22
checkUInt<22>(Loc, Val, Type);
checkUInt(Loc, Val, 22, Type);
write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff));
break;
case R_SPARC_GOT22:
@ -103,7 +103,7 @@ void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
break;
case R_SPARC_WDISP19:
// V-disp19
checkInt<21>(Loc, Val, Type);
checkInt(Loc, Val, 21, Type);
write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff));
break;
case R_SPARC_GOT10:
@ -137,7 +137,7 @@ void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
};
memcpy(Buf, PltData, sizeof(PltData));
uint64_t Off = PltHeaderSize + Index * PltEntrySize;
uint64_t Off = getPltEntryOffset(Index);
relocateOne(Buf, R_SPARC_22, Off);
relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize));
}

View File

@ -21,7 +21,7 @@ using namespace lld;
using namespace lld::elf;
namespace {
class X86 final : public TargetInfo {
class X86 : public TargetInfo {
public:
X86();
RelExpr getRelExpr(RelType Type, const Symbol &S,
@ -46,7 +46,6 @@ class X86 final : public TargetInfo {
} // namespace
X86::X86() {
GotBaseSymOff = -1;
CopyRel = R_386_COPY;
GotRel = R_386_GLOB_DAT;
PltRel = R_386_JUMP_SLOT;
@ -74,9 +73,9 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
case R_386_TLS_LDO_32:
return R_ABS;
case R_386_TLS_GD:
return R_TLSGD;
return R_TLSGD_GOT_FROM_END;
case R_386_TLS_LDM:
return R_TLSLD;
return R_TLSLD_GOT_FROM_END;
case R_386_PLT32:
return R_PLT_PC;
case R_386_PC8:
@ -224,7 +223,7 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
}
write32le(Buf + 7, RelOff);
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
}
int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
@ -256,15 +255,15 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
// being used for some 16-bit programs such as boot loaders, so
// we want to support them.
checkUInt<8>(Loc, Val, Type);
checkIntUInt(Loc, Val, 8, Type);
*Loc = Val;
break;
case R_386_PC8:
checkInt<8>(Loc, Val, Type);
checkInt(Loc, Val, 8, Type);
*Loc = Val;
break;
case R_386_16:
checkUInt<16>(Loc, Val, Type);
checkIntUInt(Loc, Val, 16, Type);
write16le(Loc, Val);
break;
case R_386_PC16:
@ -278,7 +277,7 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// current location subtracted from it.
// We just check that Val fits in 17 bits. This misses some cases, but
// should have no false positives.
checkInt<17>(Loc, Val, Type);
checkInt(Loc, Val, 17, Type);
write16le(Loc, Val);
break;
case R_386_32:
@ -301,7 +300,7 @@ void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_386_TLS_LE_32:
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
checkInt<32>(Loc, Val, Type);
checkInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
default:
@ -399,7 +398,152 @@ void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
memcpy(Loc - 2, Inst, sizeof(Inst));
}
TargetInfo *elf::getX86TargetInfo() {
static X86 Target;
return &Target;
namespace {
class RetpolinePic : public X86 {
public:
RetpolinePic();
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
};
class RetpolineNoPic : public X86 {
public:
RetpolineNoPic();
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
};
} // namespace
RetpolinePic::RetpolinePic() {
PltHeaderSize = 48;
PltEntrySize = 32;
}
void RetpolinePic::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
write32le(Buf, S.getPltVA() + 17);
}
void RetpolinePic::writePltHeader(uint8_t *Buf) const {
const uint8_t Insn[] = {
0xff, 0xb3, 0, 0, 0, 0, // 0: pushl GOTPLT+4(%ebx)
0x50, // 6: pushl %eax
0x8b, 0x83, 0, 0, 0, 0, // 7: mov GOTPLT+8(%ebx), %eax
0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next
0xf3, 0x90, // 12: loop: pause
0x0f, 0xae, 0xe8, // 14: lfence
0xeb, 0xf9, // 17: jmp loop
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16
0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp)
0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx
0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp)
0x89, 0xc8, // 2b: mov %ecx, %eax
0x59, // 2d: pop %ecx
0xc3, // 2e: ret
0xcc, // 2f: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
uint32_t GotPlt = InX::GotPlt->getVA() - Ebx;
write32le(Buf + 2, GotPlt + 4);
write32le(Buf + 9, GotPlt + 8);
}
void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const uint8_t Insn[] = {
0x50, // pushl %eax
0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax
0xe8, 0, 0, 0, 0, // call plt+0x20
0xe9, 0, 0, 0, 0, // jmp plt+0x12
0x68, 0, 0, 0, 0, // pushl $reloc_offset
0xe9, 0, 0, 0, 0, // jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
unsigned Off = getPltEntryOffset(Index);
write32le(Buf + 3, GotPltEntryAddr - Ebx);
write32le(Buf + 8, -Off - 12 + 32);
write32le(Buf + 13, -Off - 17 + 18);
write32le(Buf + 18, RelOff);
write32le(Buf + 23, -Off - 27);
}
RetpolineNoPic::RetpolineNoPic() {
PltHeaderSize = 48;
PltEntrySize = 32;
}
void RetpolineNoPic::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
write32le(Buf, S.getPltVA() + 16);
}
void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
const uint8_t Insn[] = {
0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4
0x50, // 6: pushl %eax
0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax
0xe8, 0x0f, 0x00, 0x00, 0x00, // c: call next
0xf3, 0x90, // 11: loop: pause
0x0f, 0xae, 0xe8, // 13: lfence
0xeb, 0xf9, // 16: jmp loop
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 18: int3
0xcc, 0xcc, 0xcc, // 1f: int3; .align 16
0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp)
0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx
0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp)
0x89, 0xc8, // 2b: mov %ecx, %eax
0x59, // 2d: pop %ecx
0xc3, // 2e: ret
0xcc, // 2f: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
uint32_t GotPlt = InX::GotPlt->getVA();
write32le(Buf + 2, GotPlt + 4);
write32le(Buf + 8, GotPlt + 8);
}
void RetpolineNoPic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const uint8_t Insn[] = {
0x50, // 0: pushl %eax
0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax
0xe8, 0, 0, 0, 0, // 6: call plt+0x20
0xe9, 0, 0, 0, 0, // b: jmp plt+0x11
0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset
0xe9, 0, 0, 0, 0, // 15: jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
0xcc, // 1f: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
unsigned Off = getPltEntryOffset(Index);
write32le(Buf + 2, GotPltEntryAddr);
write32le(Buf + 7, -Off - 11 + 32);
write32le(Buf + 12, -Off - 16 + 17);
write32le(Buf + 17, RelOff);
write32le(Buf + 22, -Off - 26);
}
TargetInfo *elf::getX86TargetInfo() {
if (Config->ZRetpolineplt) {
if (Config->Pic) {
static RetpolinePic T;
return &T;
}
static RetpolineNoPic T;
return &T;
}
static X86 T;
return &T;
}

View File

@ -23,12 +23,12 @@ using namespace lld;
using namespace lld::elf;
namespace {
template <class ELFT> class X86_64 final : public TargetInfo {
template <class ELFT> class X86_64 : public TargetInfo {
public:
X86_64();
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
bool isPicRel(RelType Type) const override;
RelType getDynRel(RelType Type) const override;
void writeGotPltHeader(uint8_t *Buf) const override;
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
void writePltHeader(uint8_t *Buf) const override;
@ -43,6 +43,8 @@ template <class ELFT> class X86_64 final : public TargetInfo {
void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
bool adjustPrologueForCrossSplitStack(uint8_t *Loc,
uint8_t *End) const override;
private:
void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
@ -51,7 +53,6 @@ template <class ELFT> class X86_64 final : public TargetInfo {
} // namespace
template <class ELFT> X86_64<ELFT>::X86_64() {
GotBaseSymOff = -1;
CopyRel = R_X86_64_COPY;
GotRel = R_X86_64_GLOB_DAT;
PltRel = R_X86_64_JUMP_SLOT;
@ -106,6 +107,11 @@ RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S,
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_GOTTPOFF:
return R_GOT_PC;
case R_X86_64_GOTOFF64:
return R_GOTREL_FROM_END;
case R_X86_64_GOTPC32:
case R_X86_64_GOTPC64:
return R_GOTONLY_PC_FROM_END;
case R_X86_64_NONE:
return R_NONE;
default:
@ -124,7 +130,7 @@ template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const {
template <class ELFT>
void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
// See comments in X86::writeGotPlt.
write32le(Buf, S.getPltVA() + 6);
write64le(Buf, S.getPltVA() + 6);
}
template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const {
@ -153,12 +159,14 @@ void X86_64<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6);
write32le(Buf + 7, Index);
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
}
template <class ELFT> bool X86_64<ELFT>::isPicRel(RelType Type) const {
return Type != R_X86_64_PC32 && Type != R_X86_64_32 &&
Type != R_X86_64_TPOFF32;
template <class ELFT> RelType X86_64<ELFT>::getDynRel(RelType Type) const {
if (Type == R_X86_64_64 || Type == R_X86_64_PC64 || Type == R_X86_64_SIZE32 ||
Type == R_X86_64_SIZE64)
return Type;
return R_X86_64_NONE;
}
template <class ELFT>
@ -285,20 +293,21 @@ template <class ELFT>
void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_X86_64_8:
checkUInt<8>(Loc, Val, Type);
checkUInt(Loc, Val, 8, Type);
*Loc = Val;
break;
case R_X86_64_16:
checkUInt<16>(Loc, Val, Type);
checkUInt(Loc, Val, 16, Type);
write16le(Loc, Val);
break;
case R_X86_64_32:
checkUInt<32>(Loc, Val, Type);
checkUInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
case R_X86_64_32S:
case R_X86_64_TPOFF32:
case R_X86_64_GOT32:
case R_X86_64_GOTPC32:
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
@ -309,7 +318,7 @@ void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_X86_64_TLSLD:
case R_X86_64_DTPOFF32:
case R_X86_64_SIZE32:
checkInt<32>(Loc, Val, Type);
checkInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
case R_X86_64_64:
@ -318,6 +327,8 @@ void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_X86_64_PC64:
case R_X86_64_SIZE64:
case R_X86_64_GOT64:
case R_X86_64_GOTOFF64:
case R_X86_64_GOTPC64:
write64le(Loc, Val);
break;
default:
@ -460,12 +471,180 @@ void X86_64<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const {
write32le(Loc - 1, Val + 1);
}
TargetInfo *elf::getX32TargetInfo() {
static X86_64<ELF32LE> Target;
return &Target;
// This anonymous namespace works around a warning bug in
// old versions of gcc. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace {
// A split-stack prologue starts by checking the amount of stack remaining
// in one of two ways:
// A) Comparing of the stack pointer to a field in the tcb.
// B) Or a load of a stack pointer offset with an lea to r10 or r11.
template <>
bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
uint8_t *End) const {
// Replace "cmp %fs:0x70,%rsp" and subsequent branch
// with "stc, nopl 0x0(%rax,%rax,1)"
if (Loc + 8 < End && memcmp(Loc, "\x64\x48\x3b\x24\x25", 4) == 0) {
memcpy(Loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8);
return true;
}
// Adjust "lea -0x200(%rsp),%r10" to lea "-0x4200(%rsp),%r10"
if (Loc + 7 < End && memcmp(Loc, "\x4c\x8d\x94\x24\x00\xfe\xff", 7) == 0) {
memcpy(Loc, "\x4c\x8d\x94\x24\x00\xbe\xff", 7);
return true;
}
// Adjust "lea -0x200(%rsp),%r11" to lea "-0x4200(%rsp),%r11"
if (Loc + 7 < End && memcmp(Loc, "\x4c\x8d\x9c\x24\x00\xfe\xff", 7) == 0) {
memcpy(Loc, "\x4c\x8d\x9c\x24\x00\xbe\xff", 7);
return true;
}
return false;
}
TargetInfo *elf::getX86_64TargetInfo() {
static X86_64<ELF64LE> Target;
return &Target;
template <>
bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
uint8_t *End) const {
llvm_unreachable("Target doesn't support split stacks.");
}
} // namespace
// These nonstandard PLT entries are to migtigate Spectre v2 security
// vulnerability. In order to mitigate Spectre v2, we want to avoid indirect
// branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT
// entries, we use a CALL followed by MOV and RET to do the same thing as an
// indirect jump. That instruction sequence is so-called "retpoline".
//
// We have two types of retpoline PLTs as a size optimization. If `-z now`
// is specified, all dynamic symbols are resolved at load-time. Thus, when
// that option is given, we can omit code for symbol lazy resolution.
namespace {
template <class ELFT> class Retpoline : public X86_64<ELFT> {
public:
Retpoline();
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
};
template <class ELFT> class RetpolineZNow : public X86_64<ELFT> {
public:
RetpolineZNow();
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override {}
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
};
} // namespace
template <class ELFT> Retpoline<ELFT>::Retpoline() {
TargetInfo::PltHeaderSize = 48;
TargetInfo::PltEntrySize = 32;
}
template <class ELFT>
void Retpoline<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
write64le(Buf, S.getPltVA() + 17);
}
template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const {
const uint8_t Insn[] = {
0xff, 0x35, 0, 0, 0, 0, // 0: pushq GOTPLT+8(%rip)
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 6: mov GOTPLT+16(%rip), %r11
0xe8, 0x0e, 0x00, 0x00, 0x00, // d: callq next
0xf3, 0x90, // 12: loop: pause
0x0f, 0xae, 0xe8, // 14: lfence
0xeb, 0xf9, // 17: jmp loop
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16
0x4c, 0x89, 0x1c, 0x24, // 20: next: mov %r11, (%rsp)
0xc3, // 24: ret
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding
0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
uint64_t GotPlt = InX::GotPlt->getVA();
uint64_t Plt = InX::Plt->getVA();
write32le(Buf + 2, GotPlt - Plt - 6 + 8);
write32le(Buf + 9, GotPlt - Plt - 13 + 16);
}
template <class ELFT>
void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const uint8_t Insn[] = {
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11
0xe8, 0, 0, 0, 0, // 7: callq plt+0x20
0xe9, 0, 0, 0, 0, // c: jmp plt+0x12
0x68, 0, 0, 0, 0, // 11: pushq <relocation index>
0xe9, 0, 0, 0, 0, // 16: jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
uint64_t Off = TargetInfo::getPltEntryOffset(Index);
write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
write32le(Buf + 8, -Off - 12 + 32);
write32le(Buf + 13, -Off - 17 + 18);
write32le(Buf + 18, Index);
write32le(Buf + 23, -Off - 27);
}
template <class ELFT> RetpolineZNow<ELFT>::RetpolineZNow() {
TargetInfo::PltHeaderSize = 32;
TargetInfo::PltEntrySize = 16;
}
template <class ELFT>
void RetpolineZNow<ELFT>::writePltHeader(uint8_t *Buf) const {
const uint8_t Insn[] = {
0xe8, 0x0b, 0x00, 0x00, 0x00, // 0: call next
0xf3, 0x90, // 5: loop: pause
0x0f, 0xae, 0xe8, // 7: lfence
0xeb, 0xf9, // a: jmp loop
0xcc, 0xcc, 0xcc, 0xcc, // c: int3; .align 16
0x4c, 0x89, 0x1c, 0x24, // 10: next: mov %r11, (%rsp)
0xc3, // 14: ret
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 15: int3; padding
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
0xcc, // 1f: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
}
template <class ELFT>
void RetpolineZNow<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const uint8_t Insn[] = {
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11
0xe9, 0, 0, 0, 0, // jmp plt+0
0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
write32le(Buf + 8, -TargetInfo::getPltEntryOffset(Index) - 12);
}
template <class ELFT> static TargetInfo *getTargetInfo() {
if (Config->ZRetpolineplt) {
if (Config->ZNow) {
static RetpolineZNow<ELFT> T;
return &T;
}
static Retpoline<ELFT> T;
return &T;
}
static X86_64<ELFT> T;
return &T;
}
TargetInfo *elf::getX32TargetInfo() { return getTargetInfo<ELF32LE>(); }
TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo<ELF64LE>(); }

View File

@ -12,6 +12,7 @@ add_lld_library(lldELF
Arch/AMDGPU.cpp
Arch/ARM.cpp
Arch/AVR.cpp
Arch/Hexagon.cpp
Arch/Mips.cpp
Arch/MipsArchTree.cpp
Arch/PPC.cpp
@ -19,6 +20,7 @@ add_lld_library(lldELF
Arch/SPARCV9.cpp
Arch/X86.cpp
Arch/X86_64.cpp
CallGraphSort.cpp
Driver.cpp
DriverUtils.cpp
EhFrame.cpp
@ -35,7 +37,6 @@ add_lld_library(lldELF
Relocations.cpp
ScriptLexer.cpp
ScriptParser.cpp
Strings.cpp
SymbolTable.cpp
Symbols.cpp
SyntheticSections.cpp
@ -46,6 +47,7 @@ add_lld_library(lldELF
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
BinaryFormat
BitWriter
Core
DebugInfoDWARF
LTO

249
ELF/CallGraphSort.cpp Normal file
View File

@ -0,0 +1,249 @@
//===- CallGraphSort.cpp --------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// Implementation of Call-Chain Clustering from: Optimizing Function Placement
/// for Large-Scale Data-Center Applications
/// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf
///
/// The goal of this algorithm is to improve runtime performance of the final
/// executable by arranging code sections such that page table and i-cache
/// misses are minimized.
///
/// Definitions:
/// * Cluster
/// * An ordered list of input sections which are layed out as a unit. At the
/// beginning of the algorithm each input section has its own cluster and
/// the weight of the cluster is the sum of the weight of all incomming
/// edges.
/// * Call-Chain Clustering (C³) Heuristic
/// * Defines when and how clusters are combined. Pick the highest weighted
/// input section then add it to its most likely predecessor if it wouldn't
/// penalize it too much.
/// * Density
/// * The weight of the cluster divided by the size of the cluster. This is a
/// proxy for the ammount of execution time spent per byte of the cluster.
///
/// It does so given a call graph profile by the following:
/// * Build a weighted call graph from the call graph profile
/// * Sort input sections by weight
/// * For each input section starting with the highest weight
/// * Find its most likely predecessor cluster
/// * Check if the combined cluster would be too large, or would have too low
/// a density.
/// * If not, then combine the clusters.
/// * Sort non-empty clusters by density
///
//===----------------------------------------------------------------------===//
#include "CallGraphSort.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"
using namespace llvm;
using namespace lld;
using namespace lld::elf;
namespace {
struct Edge {
int From;
uint64_t Weight;
};
struct Cluster {
Cluster(int Sec, size_t S) {
Sections.push_back(Sec);
Size = S;
}
double getDensity() const {
if (Size == 0)
return 0;
return double(Weight) / double(Size);
}
std::vector<int> Sections;
size_t Size = 0;
uint64_t Weight = 0;
uint64_t InitialWeight = 0;
std::vector<Edge> Preds;
};
class CallGraphSort {
public:
CallGraphSort();
DenseMap<const InputSectionBase *, int> run();
private:
std::vector<Cluster> Clusters;
std::vector<const InputSectionBase *> Sections;
void groupClusters();
};
// Maximum ammount the combined cluster density can be worse than the original
// cluster to consider merging.
constexpr int MAX_DENSITY_DEGRADATION = 8;
// Maximum cluster size in bytes.
constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024;
} // end anonymous namespace
// Take the edge list in Config->CallGraphProfile, resolve symbol names to
// Symbols, and generate a graph between InputSections with the provided
// weights.
CallGraphSort::CallGraphSort() {
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
uint64_t> &Profile = Config->CallGraphProfile;
DenseMap<const InputSectionBase *, int> SecToCluster;
auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int {
auto Res = SecToCluster.insert(std::make_pair(IS, Clusters.size()));
if (Res.second) {
Sections.push_back(IS);
Clusters.emplace_back(Clusters.size(), IS->getSize());
}
return Res.first->second;
};
// Create the graph.
for (const auto &C : Profile) {
const auto *FromSB = cast<InputSectionBase>(C.first.first->Repl);
const auto *ToSB = cast<InputSectionBase>(C.first.second->Repl);
uint64_t Weight = C.second;
// Ignore edges between input sections belonging to different output
// sections. This is done because otherwise we would end up with clusters
// containing input sections that can't actually be placed adjacently in the
// output. This messes with the cluster size and density calculations. We
// would also end up moving input sections in other output sections without
// moving them closer to what calls them.
if (FromSB->getOutputSection() != ToSB->getOutputSection())
continue;
int From = GetOrCreateNode(FromSB);
int To = GetOrCreateNode(ToSB);
Clusters[To].Weight += Weight;
if (From == To)
continue;
// Add an edge
Clusters[To].Preds.push_back({From, Weight});
}
for (Cluster &C : Clusters)
C.InitialWeight = C.Weight;
}
// It's bad to merge clusters which would degrade the density too much.
static bool isNewDensityBad(Cluster &A, Cluster &B) {
double NewDensity = double(A.Weight + B.Weight) / double(A.Size + B.Size);
if (NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION)
return true;
return false;
}
static void mergeClusters(Cluster &Into, Cluster &From) {
Into.Sections.insert(Into.Sections.end(), From.Sections.begin(),
From.Sections.end());
Into.Size += From.Size;
Into.Weight += From.Weight;
From.Sections.clear();
From.Size = 0;
From.Weight = 0;
}
// Group InputSections into clusters using the Call-Chain Clustering heuristic
// then sort the clusters by density.
void CallGraphSort::groupClusters() {
std::vector<int> SortedSecs(Clusters.size());
std::vector<Cluster *> SecToCluster(Clusters.size());
for (int SI = 0, SE = Clusters.size(); SI != SE; ++SI) {
SortedSecs[SI] = SI;
SecToCluster[SI] = &Clusters[SI];
}
std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) {
return Clusters[B].getDensity() < Clusters[A].getDensity();
});
for (int SI : SortedSecs) {
// Clusters[SI] is the same as SecToClusters[SI] here because it has not
// been merged into another cluster yet.
Cluster &C = Clusters[SI];
int BestPred = -1;
uint64_t BestWeight = 0;
for (Edge &E : C.Preds) {
if (BestPred == -1 || E.Weight > BestWeight) {
BestPred = E.From;
BestWeight = E.Weight;
}
}
// don't consider merging if the edge is unlikely.
if (BestWeight * 10 <= C.InitialWeight)
continue;
Cluster *PredC = SecToCluster[BestPred];
if (PredC == &C)
continue;
if (C.Size + PredC->Size > MAX_CLUSTER_SIZE)
continue;
if (isNewDensityBad(*PredC, C))
continue;
// NOTE: Consider using a disjoint-set to track section -> cluster mapping
// if this is ever slow.
for (int SI : C.Sections)
SecToCluster[SI] = PredC;
mergeClusters(*PredC, C);
}
// Remove empty or dead nodes. Invalidates all cluster indices.
llvm::erase_if(Clusters, [](const Cluster &C) {
return C.Size == 0 || C.Sections.empty();
});
// Sort by density.
std::stable_sort(Clusters.begin(), Clusters.end(),
[](const Cluster &A, const Cluster &B) {
return A.getDensity() > B.getDensity();
});
}
DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
groupClusters();
// Generate order.
llvm::DenseMap<const InputSectionBase *, int> OrderMap;
ssize_t CurOrder = 1;
for (const Cluster &C : Clusters)
for (int SecIndex : C.Sections)
OrderMap[Sections[SecIndex]] = CurOrder++;
return OrderMap;
}
// Sort sections by the profile data provided by -callgraph-profile-file
//
// This first builds a call graph based on the profile data then merges sections
// according to the C³ huristic. All clusters are then sorted by a density
// metric to further improve locality.
DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() {
return CallGraphSort().run();
}

23
ELF/CallGraphSort.h Normal file
View File

@ -0,0 +1,23 @@
//===- CallGraphSort.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_CALL_GRAPH_SORT_H
#define LLD_ELF_CALL_GRAPH_SORT_H
#include "llvm/ADT/DenseMap.h"
namespace lld {
namespace elf {
class InputSectionBase;
llvm::DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder();
} // namespace elf
} // namespace lld
#endif

View File

@ -10,6 +10,7 @@
#ifndef LLD_ELF_CONFIG_H
#define LLD_ELF_CONFIG_H
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
@ -17,13 +18,13 @@
#include "llvm/Support/CachePruning.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Endian.h"
#include <vector>
namespace lld {
namespace elf {
class InputFile;
class InputSectionBase;
enum ELFKind {
ELFNoneKind,
@ -39,6 +40,9 @@ enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid };
// For --discard-{all,locals,none}.
enum class DiscardPolicy { Default, All, Locals, None };
// For --icf={none,safe,all}.
enum class ICFLevel { None, Safe, All };
// For --strip-{all,debug}.
enum class StripPolicy { None, All, Debug };
@ -79,21 +83,27 @@ struct Configuration {
llvm::StringMap<uint64_t> SectionStartMap;
llvm::StringRef Chroot;
llvm::StringRef DynamicLinker;
llvm::StringRef DwoDir;
llvm::StringRef Entry;
llvm::StringRef Emulation;
llvm::StringRef Fini;
llvm::StringRef Init;
llvm::StringRef LTOAAPipeline;
llvm::StringRef LTONewPmPasses;
llvm::StringRef LTOObjPath;
llvm::StringRef LTOSampleProfile;
llvm::StringRef MapFile;
llvm::StringRef OutputFile;
llvm::StringRef OptRemarksFilename;
llvm::StringRef ProgName;
llvm::StringRef SoName;
llvm::StringRef Sysroot;
llvm::StringRef ThinLTOCacheDir;
llvm::StringRef ThinLTOIndexOnlyArg;
std::pair<llvm::StringRef, llvm::StringRef> ThinLTOObjectSuffixReplace;
std::pair<llvm::StringRef, llvm::StringRef> ThinLTOPrefixReplace;
std::string Rpath;
std::vector<VersionDefinition> VersionDefinitions;
std::vector<llvm::StringRef> Argv;
std::vector<llvm::StringRef> AuxiliaryList;
std::vector<llvm::StringRef> FilterList;
std::vector<llvm::StringRef> SearchPaths;
@ -103,15 +113,20 @@ struct Configuration {
std::vector<SymbolVersion> VersionScriptGlobals;
std::vector<SymbolVersion> VersionScriptLocals;
std::vector<uint8_t> BuildIdVector;
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
uint64_t>
CallGraphProfile;
bool AllowMultipleDefinition;
bool AndroidPackDynRelocs = false;
bool AndroidPackDynRelocs;
bool ARMHasBlx = false;
bool ARMHasMovtMovw = false;
bool ARMJ1J2BranchEncoding = false;
bool AsNeeded = false;
bool Bsymbolic;
bool BsymbolicFunctions;
bool CheckSections;
bool CompressDebugSections;
bool Cref;
bool DefineCommon;
bool Demangle = true;
bool DisableVerify;
@ -123,14 +138,15 @@ struct Configuration {
bool GcSections;
bool GdbIndex;
bool GnuHash = false;
bool GnuUnique;
bool HasDynamicList = false;
bool HasDynSymTab;
bool ICF;
bool ICFData;
bool IgnoreDataAddressEquality;
bool IgnoreFunctionAddressEquality;
bool LTODebugPassManager;
bool LTONewPassManager;
bool MergeArmExidx;
bool MipsN32Abi = false;
bool NoGnuUnique;
bool NoUndefinedVersion;
bool NoinhibitExec;
bool Nostdlib;
bool OFormatBinary;
@ -138,7 +154,9 @@ struct Configuration {
bool OptRemarksWithHotness;
bool Pie;
bool PrintGcSections;
bool PrintIcfSections;
bool Relocatable;
bool RelrPackDynRelocs;
bool SaveTemps;
bool SingleRoRx;
bool Shared;
@ -146,12 +164,21 @@ struct Configuration {
bool SysvHash = false;
bool Target1Rel;
bool Trace;
bool Verbose;
bool ThinLTOEmitImportsFiles;
bool ThinLTOIndexOnly;
bool UndefinedVersion;
bool UseAndroidRelrTags = false;
bool WarnBackrefs;
bool WarnCommon;
bool WarnMissingEntry;
bool WarnSymbolOrdering;
bool WriteAddends;
bool ZCombreloc;
bool ZCopyreloc;
bool ZExecstack;
bool ZNocopyreloc;
bool ZHazardplt;
bool ZInitfirst;
bool ZKeepTextSectionPrefix;
bool ZNodelete;
bool ZNodlopen;
bool ZNow;
@ -159,9 +186,10 @@ struct Configuration {
bool ZRelro;
bool ZRodynamic;
bool ZText;
bool ExitEarly;
bool ZRetpolineplt;
bool ZWxneeded;
DiscardPolicy Discard;
ICFLevel ICF;
OrphanHandlingPolicy OrphanHandling;
SortSectionPolicy SortSection;
StripPolicy Strip;
@ -173,6 +201,7 @@ struct Configuration {
uint16_t EMachine = llvm::ELF::EM_NONE;
llvm::Optional<uint64_t> ImageBase;
uint64_t MaxPageSize;
uint64_t MipsGotSize;
uint64_t ZStackSize;
unsigned LTOPartitions;
unsigned LTOO;
@ -238,6 +267,12 @@ struct Configuration {
// The only instance of Configuration struct.
extern Configuration *Config;
static inline void errorOrWarn(const Twine &Msg) {
if (!Config->NoinhibitExec)
error(Msg);
else
warn(Msg);
}
} // namespace elf
} // namespace lld

View File

@ -30,9 +30,9 @@
#include "InputFiles.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "MarkLive.h"
#include "OutputSections.h"
#include "ScriptParser.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
@ -42,12 +42,16 @@
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
#include "lld/Common/Threads.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/TargetSelect.h"
@ -66,16 +70,18 @@ using namespace lld::elf;
Configuration *elf::Config;
LinkerDriver *elf::Driver;
static void setConfigs();
static void setConfigs(opt::InputArgList &Args);
bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
raw_ostream &Error) {
errorHandler().LogName = Args[0];
errorHandler().LogName = sys::path::filename(Args[0]);
errorHandler().ErrorLimitExceededMsg =
"too many errors emitted, stopping now (use "
"-error-limit=0 to see all errors)";
errorHandler().ErrorOS = &Error;
errorHandler().ExitEarly = CanExitEarly;
errorHandler().ColorDiagnostics = Error.has_colors();
InputSections.clear();
OutputSections.clear();
Tar = nullptr;
@ -88,14 +94,14 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
Driver = make<LinkerDriver>();
Script = make<LinkerScript>();
Symtab = make<SymbolTable>();
Config->Argv = {Args.begin(), Args.end()};
Config->ProgName = Args[0];
Driver->main(Args, CanExitEarly);
Driver->main(Args);
// Exit immediately if we don't need to return to the caller.
// This saves time because the overhead of calling destructors
// for all globally-allocated objects is not negligible.
if (Config->ExitEarly)
if (CanExitEarly)
exitLld(errorCount() ? 1 : 0);
freeArena();
@ -113,7 +119,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
std::pair<ELFKind, uint16_t> Ret =
StringSwitch<std::pair<ELFKind, uint16_t>>(S)
.Cases("aarch64elf", "aarch64linux", {ELF64LEKind, EM_AARCH64})
.Cases("aarch64elf", "aarch64linux", "aarch64_elf64_le_vec",
{ELF64LEKind, EM_AARCH64})
.Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM})
.Case("elf32_x86_64", {ELF32LEKind, EM_X86_64})
.Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS})
@ -122,6 +129,7 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
.Case("elf64ppc", {ELF64BEKind, EM_PPC64})
.Case("elf64lppc", {ELF64LEKind, EM_PPC64})
.Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64})
.Case("elf_i386", {ELF32LEKind, EM_386})
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
@ -228,11 +236,15 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
Files.push_back(
createSharedFile(MBRef, WithLOption ? path::filename(Path) : Path));
return;
default:
case file_magic::bitcode:
case file_magic::elf_relocatable:
if (InLib)
Files.push_back(make<LazyObjFile>(MBRef, "", 0));
else
Files.push_back(createObjectFile(MBRef));
break;
default:
error(Path + ": unknown file type");
}
}
@ -248,18 +260,11 @@ void LinkerDriver::addLibrary(StringRef Name) {
// LTO calls LLVM functions to compile bitcode files to native code.
// Technically this can be delayed until we read bitcode files, but
// we don't bother to do lazily because the initialization is fast.
static void initLLVM(opt::InputArgList &Args) {
static void initLLVM() {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
// Parse and evaluate -mllvm options.
std::vector<const char *> V;
V.push_back("lld (LLVM option parsing)");
for (auto *Arg : Args.filtered(OPT_mllvm))
V.push_back(Arg->getValue());
cl::ParseCommandLineOptions(V.size(), V.data());
}
// Some command line options or some combinations of them are not allowed.
@ -290,7 +295,9 @@ static void checkOptions(opt::InputArgList &Args) {
error("-r and -shared may not be used together");
if (Config->GcSections)
error("-r and --gc-sections may not be used together");
if (Config->ICF)
if (Config->GdbIndex)
error("-r and --gdb-index may not be used together");
if (Config->ICF != ICFLevel::None)
error("-r and --icf may not be used together");
if (Config->Pie)
error("-r and -pie may not be used together");
@ -310,7 +317,37 @@ static bool hasZOption(opt::InputArgList &Args, StringRef Key) {
return false;
}
void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2,
bool Default) {
for (auto *Arg : Args.filtered_reverse(OPT_z)) {
if (K1 == Arg->getValue())
return true;
if (K2 == Arg->getValue())
return false;
}
return Default;
}
static bool isKnown(StringRef S) {
return S == "combreloc" || S == "copyreloc" || S == "defs" ||
S == "execstack" || S == "hazardplt" || S == "initfirst" ||
S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" ||
S == "nocombreloc" || S == "nocopyreloc" || S == "nodelete" ||
S == "nodlopen" || S == "noexecstack" ||
S == "nokeep-text-section-prefix" || S == "norelro" || S == "notext" ||
S == "now" || S == "origin" || S == "relro" || S == "retpolineplt" ||
S == "rodynamic" || S == "text" || S == "wxneeded" ||
S.startswith("max-page-size=") || S.startswith("stack-size=");
}
// Report an error for an unknown -z option.
static void checkZOptions(opt::InputArgList &Args) {
for (auto *Arg : Args.filtered(OPT_z))
if (!isKnown(Arg->getValue()))
error("unknown -z value: " + StringRef(Arg->getValue()));
}
void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
ELFOptTable Parser;
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
@ -319,7 +356,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
// Handle -help
if (Args.hasArg(OPT_help)) {
printHelp(ArgsArr[0]);
printHelp();
return;
}
@ -348,9 +385,6 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
if (Args.hasArg(OPT_version))
return;
Config->ExitEarly = CanExitEarly && !Args.hasArg(OPT_full_shutdown);
errorHandler().ExitEarly = Config->ExitEarly;
if (const char *Path = getReproduceOption(Args)) {
// Note that --reproduce is a debug option so you can ignore it
// if you are trying to understand the whole picture of the code.
@ -368,10 +402,14 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
}
readConfigs(Args);
initLLVM(Args);
checkZOptions(Args);
initLLVM();
createFiles(Args);
if (errorCount())
return;
inferMachineType();
setConfigs();
setConfigs(Args);
checkOptions(Args);
if (errorCount())
return;
@ -482,6 +520,15 @@ static StringRef getDynamicLinker(opt::InputArgList &Args) {
return Arg->getValue();
}
static ICFLevel getICF(opt::InputArgList &Args) {
auto *Arg = Args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all);
if (!Arg || Arg->getOption().getID() == OPT_icf_none)
return ICFLevel::None;
if (Arg->getOption().getID() == OPT_icf_safe)
return ICFLevel::Safe;
return ICFLevel::All;
}
static StripPolicy getStrip(opt::InputArgList &Args) {
if (Args.hasArg(OPT_relocatable))
return StripPolicy::None;
@ -556,6 +603,8 @@ getBuildId(opt::InputArgList &Args) {
return {BuildIdKind::Fast, {}};
StringRef S = Arg->getValue();
if (S == "fast")
return {BuildIdKind::Fast, {}};
if (S == "md5")
return {BuildIdKind::Md5, {}};
if (S == "sha1" || S == "tree")
@ -570,6 +619,57 @@ getBuildId(opt::InputArgList &Args) {
return {BuildIdKind::None, {}};
}
static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &Args) {
StringRef S = Args.getLastArgValue(OPT_pack_dyn_relocs, "none");
if (S == "android")
return {true, false};
if (S == "relr")
return {false, true};
if (S == "android+relr")
return {true, true};
if (S != "none")
error("unknown -pack-dyn-relocs format: " + S);
return {false, false};
}
static void readCallGraph(MemoryBufferRef MB) {
// Build a map from symbol name to section
DenseMap<StringRef, const Symbol *> SymbolNameToSymbol;
for (InputFile *File : ObjectFiles)
for (Symbol *Sym : File->getSymbols())
SymbolNameToSymbol[Sym->getName()] = Sym;
for (StringRef L : args::getLines(MB)) {
SmallVector<StringRef, 3> Fields;
L.split(Fields, ' ');
uint64_t Count;
if (Fields.size() != 3 || !to_integer(Fields[2], Count))
fatal(MB.getBufferIdentifier() + ": parse error");
const Symbol *FromSym = SymbolNameToSymbol.lookup(Fields[0]);
const Symbol *ToSym = SymbolNameToSymbol.lookup(Fields[1]);
if (Config->WarnSymbolOrdering) {
if (!FromSym)
warn(MB.getBufferIdentifier() + ": no such symbol: " + Fields[0]);
if (!ToSym)
warn(MB.getBufferIdentifier() + ": no such symbol: " + Fields[1]);
}
if (!FromSym || !ToSym || Count == 0)
continue;
warnUnorderableSymbol(FromSym);
warnUnorderableSymbol(ToSym);
const Defined *FromSymD = dyn_cast<Defined>(FromSym);
const Defined *ToSymD = dyn_cast<Defined>(ToSym);
if (!FromSymD || !ToSymD)
continue;
const auto *FromSB = dyn_cast_or_null<InputSectionBase>(FromSymD->Section);
const auto *ToSB = dyn_cast_or_null<InputSectionBase>(ToSymD->Section);
if (!FromSB || !ToSB)
continue;
Config->CallGraphProfile[std::make_pair(FromSB, ToSB)] += Count;
}
}
static bool getCompressDebugSections(opt::InputArgList &Args) {
StringRef S = Args.getLastArgValue(OPT_compress_debug_sections, "none");
if (S == "none")
@ -581,54 +681,98 @@ static bool getCompressDebugSections(opt::InputArgList &Args) {
return true;
}
static int parseInt(StringRef S, opt::Arg *Arg) {
int V = 0;
if (!to_integer(S, V, 10))
error(Arg->getSpelling() + ": number expected, but got '" + S + "'");
return V;
static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &Args,
unsigned Id) {
auto *Arg = Args.getLastArg(Id);
if (!Arg)
return {"", ""};
StringRef S = Arg->getValue();
std::pair<StringRef, StringRef> Ret = S.split(';');
if (Ret.second.empty())
error(Arg->getSpelling() + " expects 'old;new' format, but got " + S);
return Ret;
}
// Parse the symbol ordering file and warn for any duplicate entries.
static std::vector<StringRef> getSymbolOrderingFile(MemoryBufferRef MB) {
SetVector<StringRef> Names;
for (StringRef S : args::getLines(MB))
if (!Names.insert(S) && Config->WarnSymbolOrdering)
warn(MB.getBufferIdentifier() + ": duplicate ordered symbol: " + S);
return Names.takeVector();
}
static void parseClangOption(StringRef Opt, const Twine &Msg) {
std::string Err;
raw_string_ostream OS(Err);
const char *Argv[] = {Config->ProgName.data(), Opt.data()};
if (cl::ParseCommandLineOptions(2, Argv, "", &OS))
return;
OS.flush();
error(Msg + ": " + StringRef(Err).trim());
}
// Initializes Config members by the command line options.
void LinkerDriver::readConfigs(opt::InputArgList &Args) {
errorHandler().Verbose = Args.hasArg(OPT_verbose);
errorHandler().FatalWarnings =
Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
Config->AllowMultipleDefinition =
Args.hasArg(OPT_allow_multiple_definition) || hasZOption(Args, "muldefs");
Args.hasFlag(OPT_allow_multiple_definition,
OPT_no_allow_multiple_definition, false) ||
hasZOption(Args, "muldefs");
Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary);
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
Config->CheckSections =
Args.hasFlag(OPT_check_sections, OPT_no_check_sections, true);
Config->Chroot = Args.getLastArgValue(OPT_chroot);
Config->CompressDebugSections = getCompressDebugSections(Args);
Config->Cref = Args.hasFlag(OPT_cref, OPT_no_cref, false);
Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common,
!Args.hasArg(OPT_relocatable));
Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
Config->Discard = getDiscard(Args);
Config->DwoDir = Args.getLastArgValue(OPT_plugin_opt_dwo_dir_eq);
Config->DynamicLinker = getDynamicLinker(Args);
Config->EhFrameHdr =
Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false);
Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
Config->EnableNewDtags =
Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
Config->Entry = Args.getLastArgValue(OPT_entry);
Config->ExportDynamic =
Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
errorHandler().FatalWarnings =
Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
Config->FilterList = args::getStrings(Args, OPT_filter);
Config->Fini = Args.getLastArgValue(OPT_fini, "_fini");
Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419);
Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true);
Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false);
Config->ICF = Args.hasFlag(OPT_icf_all, OPT_icf_none, false);
Config->ICFData = Args.hasArg(OPT_icf_data);
Config->ICF = getICF(Args);
Config->IgnoreDataAddressEquality =
Args.hasArg(OPT_ignore_data_address_equality);
Config->IgnoreFunctionAddressEquality =
Args.hasArg(OPT_ignore_function_address_equality);
Config->Init = Args.getLastArgValue(OPT_init, "_init");
Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline);
Config->LTODebugPassManager = Args.hasArg(OPT_lto_debug_pass_manager);
Config->LTONewPassManager = Args.hasArg(OPT_lto_new_pass_manager);
Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes);
Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
Config->LTOObjPath = Args.getLastArgValue(OPT_plugin_opt_obj_path_eq);
Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
Config->LTOSampleProfile = Args.getLastArgValue(OPT_lto_sample_profile);
Config->MapFile = Args.getLastArgValue(OPT_Map);
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
Config->MipsGotSize = args::getInteger(Args, OPT_mips_got_size, 0xfff0);
Config->MergeArmExidx =
Args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true);
Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec);
Config->Nostdlib = Args.hasArg(OPT_nostdlib);
Config->OFormatBinary = isOutputFormatBinary(Args);
@ -638,7 +782,9 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->Optimize = args::getInteger(Args, OPT_O, 1);
Config->OrphanHandling = getOrphanHandling(Args);
Config->OutputFile = Args.getLastArgValue(OPT_o);
Config->Pie = Args.hasFlag(OPT_pie, OPT_nopie, false);
Config->Pie = Args.hasFlag(OPT_pie, OPT_no_pie, false);
Config->PrintIcfSections =
Args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false);
Config->PrintGcSections =
Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
Config->Rpath = getRpath(Args);
@ -658,46 +804,58 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->ThinLTOCachePolicy = CHECK(
parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
Config->ThinLTOEmitImportsFiles =
Args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files);
Config->ThinLTOIndexOnly = Args.hasArg(OPT_plugin_opt_thinlto_index_only) ||
Args.hasArg(OPT_plugin_opt_thinlto_index_only_eq);
Config->ThinLTOIndexOnlyArg =
Args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq);
Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u);
ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
Config->ThinLTOObjectSuffixReplace =
getOldNewOptions(Args, OPT_plugin_opt_thinlto_object_suffix_replace_eq);
Config->ThinLTOPrefixReplace =
getOldNewOptions(Args, OPT_plugin_opt_thinlto_prefix_replace_eq);
Config->Trace = Args.hasArg(OPT_trace);
Config->Undefined = args::getStrings(Args, OPT_undefined);
Config->UndefinedVersion =
Args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true);
Config->UseAndroidRelrTags = Args.hasFlag(
OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false);
Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args);
Config->Verbose = Args.hasArg(OPT_verbose);
errorHandler().Verbose = Config->Verbose;
Config->WarnCommon = Args.hasArg(OPT_warn_common);
Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
Config->ZExecstack = hasZOption(Args, "execstack");
Config->ZNocopyreloc = hasZOption(Args, "nocopyreloc");
Config->WarnBackrefs =
Args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false);
Config->WarnCommon = Args.hasFlag(OPT_warn_common, OPT_no_warn_common, false);
Config->WarnSymbolOrdering =
Args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
Config->ZCombreloc = getZFlag(Args, "combreloc", "nocombreloc", true);
Config->ZCopyreloc = getZFlag(Args, "copyreloc", "nocopyreloc", true);
Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false);
Config->ZHazardplt = hasZOption(Args, "hazardplt");
Config->ZInitfirst = hasZOption(Args, "initfirst");
Config->ZKeepTextSectionPrefix = getZFlag(
Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
Config->ZNodelete = hasZOption(Args, "nodelete");
Config->ZNodlopen = hasZOption(Args, "nodlopen");
Config->ZNow = hasZOption(Args, "now");
Config->ZNow = getZFlag(Args, "now", "lazy", false);
Config->ZOrigin = hasZOption(Args, "origin");
Config->ZRelro = !hasZOption(Args, "norelro");
Config->ZRelro = getZFlag(Args, "relro", "norelro", true);
Config->ZRetpolineplt = hasZOption(Args, "retpolineplt");
Config->ZRodynamic = hasZOption(Args, "rodynamic");
Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", 0);
Config->ZText = !hasZOption(Args, "notext");
Config->ZText = getZFlag(Args, "text", "notext", true);
Config->ZWxneeded = hasZOption(Args, "wxneeded");
// Parse LTO plugin-related options for compatibility with gold.
for (auto *Arg : Args.filtered(OPT_plugin_opt, OPT_plugin_opt_eq)) {
StringRef S = Arg->getValue();
if (S == "disable-verify")
Config->DisableVerify = true;
else if (S == "save-temps")
Config->SaveTemps = true;
else if (S.startswith("O"))
Config->LTOO = parseInt(S.substr(1), Arg);
else if (S.startswith("lto-partitions="))
Config->LTOPartitions = parseInt(S.substr(15), Arg);
else if (S.startswith("jobs="))
Config->ThinLTOJobs = parseInt(S.substr(5), Arg);
else if (!S.startswith("/") && !S.startswith("-fresolution=") &&
!S.startswith("-pass-through=") && !S.startswith("mcpu=") &&
!S.startswith("thinlto") && S != "-function-sections" &&
S != "-data-sections")
error(Arg->getSpelling() + ": unknown option: " + S);
}
// Parse LTO options.
if (auto *Arg = Args.getLastArg(OPT_plugin_opt_mcpu_eq))
parseClangOption(Saver.save("-mcpu=" + StringRef(Arg->getValue())),
Arg->getSpelling());
for (auto *Arg : Args.filtered(OPT_plugin_opt))
parseClangOption(Arg->getValue(), Arg->getSpelling());
// Parse -mllvm options.
for (auto *Arg : Args.filtered(OPT_mllvm))
parseClangOption(Arg->getValue(), Arg->getSpelling());
if (Config->LTOO > 3)
error("invalid optimization level for LTO: " + Twine(Config->LTOO));
@ -740,17 +898,12 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args);
if (auto *Arg = Args.getLastArg(OPT_pack_dyn_relocs_eq)) {
StringRef S = Arg->getValue();
if (S == "android")
Config->AndroidPackDynRelocs = true;
else if (S != "none")
error("unknown -pack-dyn-relocs format: " + S);
}
std::tie(Config->AndroidPackDynRelocs, Config->RelrPackDynRelocs) =
getPackDynRelocs(Args);
if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
Config->SymbolOrderingFile = args::getLines(*Buffer);
Config->SymbolOrderingFile = getSymbolOrderingFile(*Buffer);
// If --retain-symbol-file is used, we'll keep only the symbols listed in
// the file and discard all others.
@ -778,32 +931,67 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
{Arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false});
}
// If --export-dynamic-symbol=foo is given and symbol foo is defined in
// an object file in an archive file, that object file should be pulled
// out and linked. (It doesn't have to behave like that from technical
// point of view, but this is needed for compatibility with GNU.)
for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
Config->Undefined.push_back(Arg->getValue());
for (auto *Arg : Args.filtered(OPT_version_script))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
readVersionScript(*Buffer);
if (Optional<std::string> Path = searchScript(Arg->getValue())) {
if (Optional<MemoryBufferRef> Buffer = readFile(*Path))
readVersionScript(*Buffer);
} else {
error(Twine("cannot find version script ") + Arg->getValue());
}
}
// Some Config members do not directly correspond to any particular
// command line options, but computed based on other Config values.
// This function initialize such members. See Config.h for the details
// of these values.
static void setConfigs() {
static void setConfigs(opt::InputArgList &Args) {
ELFKind Kind = Config->EKind;
uint16_t Machine = Config->EMachine;
// There is an ILP32 ABI for x86-64, although it's not very popular.
// It is called the x32 ABI.
bool IsX32 = (Kind == ELF32LEKind && Machine == EM_X86_64);
Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs);
Config->Is64 = (Kind == ELF64LEKind || Kind == ELF64BEKind);
Config->IsLE = (Kind == ELF32LEKind || Kind == ELF64LEKind);
Config->Endianness =
Config->IsLE ? support::endianness::little : support::endianness::big;
Config->IsMips64EL = (Kind == ELF64LEKind && Machine == EM_MIPS);
Config->IsRela = Config->Is64 || IsX32 || Config->MipsN32Abi;
Config->Pic = Config->Pie || Config->Shared;
Config->Wordsize = Config->Is64 ? 8 : 4;
// There is an ILP32 ABI for x86-64, although it's not very popular.
// It is called the x32 ABI.
bool IsX32 = (Kind == ELF32LEKind && Machine == EM_X86_64);
// ELF defines two different ways to store relocation addends as shown below:
//
// Rel: Addends are stored to the location where relocations are applied.
// Rela: Addends are stored as part of relocation entry.
//
// In other words, Rela makes it easy to read addends at the price of extra
// 4 or 8 byte for each relocation entry. We don't know why ELF defined two
// different mechanisms in the first place, but this is how the spec is
// defined.
//
// You cannot choose which one, Rel or Rela, you want to use. Instead each
// ABI defines which one you need to use. The following expression expresses
// that.
Config->IsRela =
(Config->Is64 || IsX32 || Machine == EM_PPC) && Machine != EM_MIPS;
// If the output uses REL relocations we must store the dynamic relocation
// addends to the output sections. We also store addends for RELA relocations
// if --apply-dynamic-relocs is used.
// We default to not writing the addends when using RELA relocations since
// any standard conforming tool can find it in r_addend.
Config->WriteAddends = Args.hasFlag(OPT_apply_dynamic_relocs,
OPT_no_apply_dynamic_relocs, false) ||
!Config->IsRela;
}
// Returns a value of "-format" option.
@ -818,6 +1006,10 @@ static bool getBinaryOption(StringRef S) {
}
void LinkerDriver::createFiles(opt::InputArgList &Args) {
// For --{push,pop}-state.
std::vector<std::tuple<bool, bool, bool>> Stack;
// Iterate over argv to process input files and positional arguments.
for (auto *Arg : Args) {
switch (Arg->getOption().getUnaliasedOption().getID()) {
case OPT_library:
@ -826,8 +1018,15 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
case OPT_INPUT:
addFile(Arg->getValue(), /*WithLOption=*/false);
break;
case OPT_defsym: {
StringRef From;
StringRef To;
std::tie(From, To) = StringRef(Arg->getValue()).split('=');
readDefsym(From, MemoryBufferRef(To, "-defsym"));
break;
}
case OPT_script:
if (Optional<std::string> Path = searchLinkerScript(Arg->getValue())) {
if (Optional<std::string> Path = searchScript(Arg->getValue())) {
if (Optional<MemoryBufferRef> MB = readFile(*Path))
readLinkerScript(*MB);
break;
@ -855,11 +1054,48 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
case OPT_no_whole_archive:
InWholeArchive = false;
break;
case OPT_just_symbols:
if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue())) {
Files.push_back(createObjectFile(*MB));
Files.back()->JustSymbols = true;
}
break;
case OPT_start_group:
if (InputFile::IsInGroup)
error("nested --start-group");
InputFile::IsInGroup = true;
break;
case OPT_end_group:
if (!InputFile::IsInGroup)
error("stray --end-group");
InputFile::IsInGroup = false;
++InputFile::NextGroupId;
break;
case OPT_start_lib:
if (InLib)
error("nested --start-lib");
if (InputFile::IsInGroup)
error("may not nest --start-lib in --start-group");
InLib = true;
InputFile::IsInGroup = true;
break;
case OPT_end_lib:
if (!InLib)
error("stray --end-lib");
InLib = false;
InputFile::IsInGroup = false;
++InputFile::NextGroupId;
break;
case OPT_push_state:
Stack.emplace_back(Config->AsNeeded, Config->Static, InWholeArchive);
break;
case OPT_pop_state:
if (Stack.empty()) {
error("unbalanced --push-state/--pop-state");
break;
}
std::tie(Config->AsNeeded, Config->Static, InWholeArchive) = Stack.back();
Stack.pop_back();
break;
}
}
@ -932,14 +1168,6 @@ static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
return Ret;
}
static Optional<StringRef> getArchiveName(InputFile *File) {
if (isa<ArchiveFile>(File))
return File->getName();
if (!File->ArchiveName.empty())
return File->ArchiveName;
return None;
}
// Handles the -exclude-libs option. If a static library file is specified
// by the -exclude-libs option, all public symbols from the archive become
// private unless otherwise specified by version scripts or something.
@ -947,16 +1175,132 @@ static Optional<StringRef> getArchiveName(InputFile *File) {
//
// This is not a popular option, but some programs such as bionic libc use it.
template <class ELFT>
static void excludeLibs(opt::InputArgList &Args, ArrayRef<InputFile *> Files) {
static void excludeLibs(opt::InputArgList &Args) {
DenseSet<StringRef> Libs = getExcludeLibs(Args);
bool All = Libs.count("ALL");
for (InputFile *File : Files)
if (Optional<StringRef> Archive = getArchiveName(File))
if (All || Libs.count(path::filename(*Archive)))
auto Visit = [&](InputFile *File) {
if (!File->ArchiveName.empty())
if (All || Libs.count(path::filename(File->ArchiveName)))
for (Symbol *Sym : File->getSymbols())
if (!Sym->isLocal())
if (!Sym->isLocal() && Sym->File == File)
Sym->VersionId = VER_NDX_LOCAL;
};
for (InputFile *File : ObjectFiles)
Visit(File);
for (BitcodeFile *File : BitcodeFiles)
Visit(File);
}
// Force Sym to be entered in the output. Used for -u or equivalent.
template <class ELFT> static void handleUndefined(StringRef Name) {
Symbol *Sym = Symtab->find(Name);
if (!Sym)
return;
// Since symbol S may not be used inside the program, LTO may
// eliminate it. Mark the symbol as "used" to prevent it.
Sym->IsUsedInRegularObj = true;
if (Sym->isLazy())
Symtab->fetchLazy<ELFT>(Sym);
}
template <class ELFT> static bool shouldDemote(Symbol &Sym) {
// If all references to a DSO happen to be weak, the DSO is not added to
// DT_NEEDED. If that happens, we need to eliminate shared symbols created
// from the DSO. Otherwise, they become dangling references that point to a
// non-existent DSO.
if (auto *S = dyn_cast<SharedSymbol>(&Sym))
return !S->getFile<ELFT>().IsNeeded;
// We are done processing archives, so lazy symbols that were used but not
// found can be converted to undefined. We could also just delete the other
// lazy symbols, but that seems to be more work than it is worth.
return Sym.isLazy() && Sym.IsUsedInRegularObj;
}
// Some files, such as .so or files between -{start,end}-lib may be removed
// after their symbols are added to the symbol table. If that happens, we
// need to remove symbols that refer files that no longer exist, so that
// they won't appear in the symbol table of the output file.
//
// We remove symbols by demoting them to undefined symbol.
template <class ELFT> static void demoteSymbols() {
for (Symbol *Sym : Symtab->getSymbols()) {
if (shouldDemote<ELFT>(*Sym)) {
bool Used = Sym->Used;
replaceSymbol<Undefined>(Sym, nullptr, Sym->getName(), Sym->Binding,
Sym->StOther, Sym->Type);
Sym->Used = Used;
}
}
}
// The section referred to by S is considered address-significant. Set the
// KeepUnique flag on the section if appropriate.
static void markAddrsig(Symbol *S) {
if (auto *D = dyn_cast_or_null<Defined>(S))
if (D->Section)
// We don't need to keep text sections unique under --icf=all even if they
// are address-significant.
if (Config->ICF == ICFLevel::Safe || !(D->Section->Flags & SHF_EXECINSTR))
D->Section->KeepUnique = true;
}
// Record sections that define symbols mentioned in --keep-unique <symbol>
// and symbols referred to by address-significance tables. These sections are
// ineligible for ICF.
template <class ELFT>
static void findKeepUniqueSections(opt::InputArgList &Args) {
for (auto *Arg : Args.filtered(OPT_keep_unique)) {
StringRef Name = Arg->getValue();
auto *D = dyn_cast_or_null<Defined>(Symtab->find(Name));
if (!D || !D->Section) {
warn("could not find symbol " + Name + " to keep unique");
continue;
}
D->Section->KeepUnique = true;
}
// --icf=all --ignore-data-address-equality means that we can ignore
// the dynsym and address-significance tables entirely.
if (Config->ICF == ICFLevel::All && Config->IgnoreDataAddressEquality)
return;
// Symbols in the dynsym could be address-significant in other executables
// or DSOs, so we conservatively mark them as address-significant.
for (Symbol *S : Symtab->getSymbols())
if (S->includeInDynsym())
markAddrsig(S);
// Visit the address-significance table in each object file and mark each
// referenced symbol as address-significant.
for (InputFile *F : ObjectFiles) {
auto *Obj = cast<ObjFile<ELFT>>(F);
ArrayRef<Symbol *> Syms = Obj->getSymbols();
if (Obj->AddrsigSec) {
ArrayRef<uint8_t> Contents =
check(Obj->getObj().getSectionContents(Obj->AddrsigSec));
const uint8_t *Cur = Contents.begin();
while (Cur != Contents.end()) {
unsigned Size;
const char *Err;
uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err);
if (Err)
fatal(toString(F) + ": could not decode addrsig section: " + Err);
markAddrsig(Syms[SymIndex]);
Cur += Size;
}
} else {
// If an object file does not have an address-significance table,
// conservatively mark all of its symbols as address-significant.
for (Symbol *S : Syms)
markAddrsig(S);
}
}
}
// Do actual linking. Note that when this function is called,
@ -1007,14 +1351,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
for (InputFile *F : Files)
Symtab->addFile<ELFT>(F);
// Process -defsym option.
for (auto *Arg : Args.filtered(OPT_defsym)) {
StringRef From;
StringRef To;
std::tie(From, To) = StringRef(Arg->getValue()).split('=');
readDefsym(From, MemoryBufferRef(To, "-defsym"));
}
// Now that we have every file, we can decide if we will need a
// dynamic symbol table.
// We need one if we were asked to export dynamic symbols or if we are
@ -1031,23 +1367,29 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// Handle the `--undefined <sym>` options.
for (StringRef S : Config->Undefined)
Symtab->fetchIfLazy<ELFT>(S);
handleUndefined<ELFT>(S);
// If an entry symbol is in a static archive, pull out that file now
// to complete the symbol table. After this, no new names except a
// few linker-synthesized ones will be added to the symbol table.
Symtab->fetchIfLazy<ELFT>(Config->Entry);
handleUndefined<ELFT>(Config->Entry);
// Return if there were name resolution errors.
if (errorCount())
return;
// Handle undefined symbols in DSOs.
Symtab->scanShlibUndefined<ELFT>();
// Now when we read all script files, we want to finalize order of linker
// script commands, which can be not yet final because of INSERT commands.
Script->processInsertCommands();
// We want to declare linker script's symbols early,
// so that we can version them.
// They also might be exported if referenced by DSOs.
Script->declareSymbols();
// Handle the -exclude-libs option.
if (Args.hasArg(OPT_exclude_libs))
excludeLibs<ELFT>(Args, Files);
excludeLibs<ELFT>(Args);
// Create ElfHeader early. We need a dummy section in
// addReservedSymbols to mark the created symbols as not absolute.
@ -1059,16 +1401,29 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
addReservedSymbols();
// Apply version scripts.
Symtab->scanVersionScript();
//
// For a relocatable output, version scripts don't make sense, and
// parsing a symbol version string (e.g. dropping "@ver1" from a symbol
// name "foo@ver1") rather do harm, so we don't call this if -r is given.
if (!Config->Relocatable)
Symtab->scanVersionScript();
// Create wrapped symbols for -wrap option.
for (auto *Arg : Args.filtered(OPT_wrap))
Symtab->addSymbolWrap<ELFT>(Arg->getValue());
// Do link-time optimization if given files are LLVM bitcode files.
// This compiles bitcode files into real object files.
Symtab->addCombinedLTOObject<ELFT>();
if (errorCount())
return;
// If -thinlto-index-only is given, we should create only "index
// files" and not object files. Index file creation is already done
// in addCombinedLTOObject, so we are done if that's the case.
if (Config->ThinLTOIndexOnly)
return;
// Apply symbol renames for -wrap.
Symtab->applySymbolWrap();
@ -1115,11 +1470,20 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
// and identical code folding.
markLive<ELFT>();
decompressSections();
splitSections<ELFT>();
markLive<ELFT>();
demoteSymbols<ELFT>();
mergeSections();
if (Config->ICF)
if (Config->ICF != ICFLevel::None) {
findKeepUniqueSections<ELFT>(Args);
doIcf<ELFT>();
}
// Read the callgraph now that we know what was gced or icfed
if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
readCallGraph(*Buffer);
// Write the result to the file.
writeResult<ELFT>();

View File

@ -26,7 +26,7 @@ extern class LinkerDriver *Driver;
class LinkerDriver {
public:
void main(ArrayRef<const char *> Args, bool CanExitEarly);
void main(ArrayRef<const char *> Args);
void addFile(StringRef Path, bool WithLOption);
void addLibrary(StringRef Name);
@ -63,11 +63,11 @@ enum {
#undef OPTION
};
void printHelp(const char *Argv0);
void printHelp();
std::string createResponseFile(const llvm::opt::InputArgList &Args);
llvm::Optional<std::string> findFromSearchPaths(StringRef Path);
llvm::Optional<std::string> searchLinkerScript(StringRef Path);
llvm::Optional<std::string> searchScript(StringRef Path);
llvm::Optional<std::string> searchLibrary(StringRef Path);
} // namespace elf

View File

@ -29,6 +29,7 @@
using namespace llvm;
using namespace llvm::sys;
using namespace llvm::opt;
using namespace lld;
using namespace lld::elf;
@ -58,18 +59,18 @@ static void handleColorDiagnostics(opt::InputArgList &Args) {
OPT_no_color_diagnostics);
if (!Arg)
return;
else if (Arg->getOption().getID() == OPT_color_diagnostics)
if (Arg->getOption().getID() == OPT_color_diagnostics) {
errorHandler().ColorDiagnostics = true;
else if (Arg->getOption().getID() == OPT_no_color_diagnostics)
} else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
errorHandler().ColorDiagnostics = false;
else {
} else {
StringRef S = Arg->getValue();
if (S == "always")
errorHandler().ColorDiagnostics = true;
else if (S == "never")
errorHandler().ColorDiagnostics = false;
else if (S != "auto")
error("unknown option: -color-diagnostics=" + S);
error("unknown option: --color-diagnostics=" + S);
}
}
@ -87,6 +88,29 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
return cl::TokenizeGNUCommandLine;
}
// Gold LTO plugin takes a `--plugin-opt foo=bar` option as an alias for
// `--plugin-opt=foo=bar`. We want to handle `--plugin-opt=foo=` as an
// option name and `bar` as a value. Unfortunately, OptParser cannot
// handle an option with a space in it.
//
// In this function, we concatenate command line arguments so that
// `--plugin-opt <foo>` is converted to `--plugin-opt=<foo>`. This is a
// bit hacky, but looks like it is still better than handling --plugin-opt
// options by hand.
static void concatLTOPluginOptions(SmallVectorImpl<const char *> &Args) {
SmallVector<const char *, 256> V;
for (size_t I = 0, E = Args.size(); I != E; ++I) {
StringRef S = Args[I];
if ((S == "-plugin-opt" || S == "--plugin-opt") && I + 1 != E) {
V.push_back(Saver.save(S + "=" + Args[I + 1]).data());
++I;
} else {
V.push_back(Args[I]);
}
}
Args = std::move(V);
}
// Parses a given list of options.
opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
// Make InputArgList from string vectors.
@ -102,6 +126,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
// Expand response files (arguments in the form of @<filename>)
// and then parse the argument again.
cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec);
concatLTOPluginOptions(Vec);
Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
handleColorDiagnostics(Args);
@ -113,9 +138,9 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
return Args;
}
void elf::printHelp(const char *Argv0) {
ELFOptTable().PrintHelp(outs(), Argv0, "lld", false /*ShowHidden*/,
true /*ShowAllAliases*/);
void elf::printHelp() {
ELFOptTable().PrintHelp(outs(), Config->ProgName.data(), "lld",
false /*ShowHidden*/, true /*ShowAllAliases*/);
outs() << "\n";
// Scripts generated by Libtool versions up to at least 2.4.6 (the most
@ -123,13 +148,7 @@ void elf::printHelp(const char *Argv0) {
// in a message for the -help option. If it doesn't match, the scripts
// assume that the linker doesn't support very basic features such as
// shared libraries. Therefore, we need to print out at least "elf".
// Here, we print out all the targets that we support.
outs() << Argv0 << ": supported targets: "
<< "elf32-i386 elf32-iamcu elf32-littlearm elf32-ntradbigmips "
<< "elf32-ntradlittlemips elf32-powerpc elf32-tradbigmips "
<< "elf32-tradlittlemips elf32-x86-64 "
<< "elf64-amdgpu elf64-littleaarch64 elf64-powerpc elf64-tradbigmips "
<< "elf64-tradlittlemips elf64-x86-64\n";
outs() << Config->ProgName << ": supported targets: elf\n";
}
// Reconstructs command line arguments so that so that you can re-run
@ -208,10 +227,10 @@ Optional<std::string> elf::searchLibrary(StringRef Name) {
return None;
}
// If a linker script doesn't exist in the current directory, we also look for
// the script in the '-L' search paths. This matches the behaviour of both '-T'
// and linker script INPUT() directives in ld.bfd.
Optional<std::string> elf::searchLinkerScript(StringRef Name) {
// If a linker/version script doesn't exist in the current directory, we also
// look for the script in the '-L' search paths. This matches the behaviour of
// '-T', --version-script=, and linker script INPUT() command in ld.bfd.
Optional<std::string> elf::searchScript(StringRef Name) {
if (fs::exists(Name))
return Name.str();
return findFromSearchPaths(Name);

View File

@ -20,18 +20,16 @@
#include "Config.h"
#include "InputSection.h"
#include "Relocations.h"
#include "Strings.h"
#include "Target.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::dwarf;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
@ -73,7 +71,7 @@ size_t EhReader::readEhRecordSize() {
// First 4 bytes of CIE/FDE is the size of the record.
// If it is 0xFFFFFFFF, the next 8 bytes contain the size instead,
// but we do not support that format yet.
uint64_t V = read32(D.data(), Config->Endianness);
uint64_t V = read32(D.data());
if (V == UINT32_MAX)
failOn(D.data(), "CIE/FDE too large");
uint64_t Size = V + 4;

View File

@ -44,7 +44,7 @@ using namespace lld::elf;
// The calling thread returns almost immediately.
void elf::unlinkAsync(StringRef Path) {
// Removing a file is async on windows.
#if defined(LLVM_ON_WIN32)
#if defined(_WIN32)
sys::fs::remove(Path);
#else
if (!ThreadsEnabled || !sys::fs::exists(Path) ||

View File

@ -34,7 +34,7 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) {
.Case(".debug_ranges", &RangeSection)
.Case(".debug_line", &LineSection)
.Default(nullptr)) {
Sec->maybeUncompress();
Sec->maybeDecompress();
M->Data = toStringRef(Sec->Data);
M->Sec = Sec;
continue;

View File

@ -49,7 +49,6 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
return LineSection;
}
StringRef getFileName() const override { return ""; }
StringRef getCUIndexSection() const override { return ""; }
StringRef getAbbrevSection() const override { return AbbrevSection; }
StringRef getStringSection() const override { return StrSection; }
StringRef getGnuPubNamesSection() const override {

View File

@ -77,6 +77,8 @@
#include "Config.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Writer.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/BinaryFormat/ELF.h"
@ -112,9 +114,9 @@ template <class ELFT> class ICF {
size_t findBoundary(size_t Begin, size_t End);
void forEachClassRange(size_t Begin, size_t End,
std::function<void(size_t, size_t)> Fn);
llvm::function_ref<void(size_t, size_t)> Fn);
void forEachClass(std::function<void(size_t, size_t)> Fn);
void forEachClass(llvm::function_ref<void(size_t, size_t)> Fn);
std::vector<InputSection *> Sections;
@ -161,15 +163,38 @@ template <class ELFT> static uint32_t getHash(InputSection *S) {
// Returns true if section S is subject of ICF.
static bool isEligible(InputSection *S) {
// Don't merge read only data sections unless --icf-data was passed.
if (!(S->Flags & SHF_EXECINSTR) && !Config->ICFData)
if (!S->Live || S->KeepUnique || !(S->Flags & SHF_ALLOC))
return false;
// .init and .fini contains instructions that must be executed to
// initialize and finalize the process. They cannot and should not
// be merged.
return S->Live && (S->Flags & SHF_ALLOC) && !(S->Flags & SHF_WRITE) &&
S->Name != ".init" && S->Name != ".fini";
// Don't merge writable sections. .data.rel.ro sections are marked as writable
// but are semantically read-only.
if ((S->Flags & SHF_WRITE) && S->Name != ".data.rel.ro" &&
!S->Name.startswith(".data.rel.ro."))
return false;
// SHF_LINK_ORDER sections are ICF'd as a unit with their dependent sections,
// so we don't consider them for ICF individually.
if (S->Flags & SHF_LINK_ORDER)
return false;
// Don't merge synthetic sections as their Data member is not valid and empty.
// The Data member needs to be valid for ICF as it is used by ICF to determine
// the equality of section contents.
if (isa<SyntheticSection>(S))
return false;
// .init and .fini contains instructions that must be executed to initialize
// and finalize the process. They cannot and should not be merged.
if (S->Name == ".init" || S->Name == ".fini")
return false;
// A user program may enumerate sections named with a C identifier using
// __start_* and __stop_* symbols. We cannot ICF any such sections because
// that could change program semantics.
if (isValidCIdentifier(S->Name))
return false;
return true;
}
// Split an equivalence class into smaller classes.
@ -214,9 +239,6 @@ template <class ELFT>
template <class RelTy>
bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
const InputSection *SecB, ArrayRef<RelTy> RB) {
if (RA.size() != RB.size())
return false;
for (size_t I = 0; I < RA.size(); ++I) {
if (RA[I].r_offset != RB[I].r_offset ||
RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL))
@ -284,6 +306,13 @@ bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) {
A->getSize() != B->getSize() || A->Data != B->Data)
return false;
// If two sections have different output sections, we cannot merge them.
// FIXME: This doesn't do the right thing in the case where there is a linker
// script. We probably need to move output section assignment before ICF to
// get the correct behaviour here.
if (getOutputSectionName(A) != getOutputSectionName(B))
return false;
if (A->AreRelocsRela)
return constantEq(A, A->template relas<ELFT>(), B,
B->template relas<ELFT>());
@ -350,17 +379,12 @@ template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) {
// vector. Therefore, Sections vector can be considered as contiguous
// groups of sections, grouped by the class.
//
// This function calls Fn on every group that starts within [Begin, End).
// Note that a group must start in that range but doesn't necessarily
// have to end before End.
// This function calls Fn on every group within [Begin, End).
template <class ELFT>
void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End,
std::function<void(size_t, size_t)> Fn) {
if (Begin > 0)
Begin = findBoundary(Begin - 1, End);
llvm::function_ref<void(size_t, size_t)> Fn) {
while (Begin < End) {
size_t Mid = findBoundary(Begin, Sections.size());
size_t Mid = findBoundary(Begin, End);
Fn(Begin, Mid);
Begin = Mid;
}
@ -368,7 +392,7 @@ void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End,
// Call Fn on each equivalence class.
template <class ELFT>
void ICF<ELFT>::forEachClass(std::function<void(size_t, size_t)> Fn) {
void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> Fn) {
// If threading is disabled or the number of sections are
// too small to use threading, call Fn sequentially.
if (!ThreadsEnabled || Sections.size() < 1024) {
@ -380,16 +404,32 @@ void ICF<ELFT>::forEachClass(std::function<void(size_t, size_t)> Fn) {
Current = Cnt % 2;
Next = (Cnt + 1) % 2;
// Split sections into 256 shards and call Fn in parallel.
size_t NumShards = 256;
// Shard into non-overlapping intervals, and call Fn in parallel.
// The sharding must be completed before any calls to Fn are made
// so that Fn can modify the Chunks in its shard without causing data
// races.
const size_t NumShards = 256;
size_t Step = Sections.size() / NumShards;
parallelForEachN(0, NumShards, [&](size_t I) {
size_t End = (I == NumShards - 1) ? Sections.size() : (I + 1) * Step;
forEachClassRange(I * Step, End, Fn);
size_t Boundaries[NumShards + 1];
Boundaries[0] = 0;
Boundaries[NumShards] = Sections.size();
parallelForEachN(1, NumShards, [&](size_t I) {
Boundaries[I] = findBoundary((I - 1) * Step, Sections.size());
});
parallelForEachN(1, NumShards + 1, [&](size_t I) {
if (Boundaries[I - 1] < Boundaries[I])
forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn);
});
++Cnt;
}
static void print(const Twine &S) {
if (Config->PrintIcfSections)
message(S);
}
// The main function of ICF.
template <class ELFT> void ICF<ELFT>::run() {
// Collect sections to merge.
@ -401,7 +441,7 @@ template <class ELFT> void ICF<ELFT>::run() {
// Initially, we use hash values to partition sections.
parallelForEach(Sections, [&](InputSection *S) {
// Set MSB to 1 to avoid collisions with non-hash IDs.
S->Class[0] = getHash<ELFT>(S) | (1 << 31);
S->Class[0] = getHash<ELFT>(S) | (1U << 31);
});
// From now on, sections in Sections vector are ordered so that sections
@ -424,25 +464,21 @@ template <class ELFT> void ICF<ELFT>::run() {
log("ICF needed " + Twine(Cnt) + " iterations");
// Merge sections by the equivalence class.
forEachClass([&](size_t Begin, size_t End) {
forEachClassRange(0, Sections.size(), [&](size_t Begin, size_t End) {
if (End - Begin == 1)
return;
log("selected " + Sections[Begin]->Name);
print("selected section " + toString(Sections[Begin]));
for (size_t I = Begin + 1; I < End; ++I) {
log(" removed " + Sections[I]->Name);
print(" removing identical section " + toString(Sections[I]));
Sections[Begin]->replace(Sections[I]);
// At this point we know sections merged are fully identical and hence
// we want to remove duplicate implicit dependencies such as link order
// and relocation sections.
for (InputSection *IS : Sections[I]->DependentSections)
IS->Live = false;
}
});
// Mark ARM Exception Index table sections that refer to folded code
// sections as not live. These sections have an implict dependency
// via the link order dependency.
if (Config->EMachine == EM_ARM)
for (InputSectionBase *Sec : InputSections)
if (auto *S = dyn_cast<InputSection>(Sec))
if (S->Flags & SHF_LINK_ORDER)
S->Live = S->getLinkOrderDep()->Live;
}
// ICF entry point function.

View File

@ -12,8 +12,10 @@
namespace lld {
namespace elf {
template <class ELFT> void doIcf();
}
} // namespace elf
} // namespace lld
#endif

View File

@ -38,14 +38,23 @@ using namespace llvm::sys::fs;
using namespace lld;
using namespace lld::elf;
bool InputFile::IsInGroup;
uint32_t InputFile::NextGroupId;
std::vector<BinaryFile *> elf::BinaryFiles;
std::vector<BitcodeFile *> elf::BitcodeFiles;
std::vector<LazyObjFile *> elf::LazyObjFiles;
std::vector<InputFile *> elf::ObjectFiles;
std::vector<InputFile *> elf::SharedFiles;
TarWriter *elf::Tar;
InputFile::InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
InputFile::InputFile(Kind K, MemoryBufferRef M)
: MB(M), GroupId(NextGroupId), FileKind(K) {
// All files within the same --{start,end}-group get the same group ID.
// Otherwise, a new file will get a new group ID.
if (!IsInGroup)
++NextGroupId;
}
Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
// The --chroot option changes our virtual root directory.
@ -55,7 +64,7 @@ Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
log(Path);
auto MBOrErr = MemoryBuffer::getFile(Path);
auto MBOrErr = MemoryBuffer::getFile(Path, -1, false);
if (auto EC = MBOrErr.getError()) {
error("cannot open " + Path + ": " + EC.message());
return None;
@ -115,51 +124,60 @@ std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
}
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(this));
const DWARFObject &Obj = Dwarf.getDWARFObj();
DwarfLine.reset(new DWARFDebugLine);
Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
const DWARFObject &Obj = Dwarf->getDWARFObj();
DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE,
Config->Wordsize);
// The second parameter is offset in .debug_line section
// for compilation unit (CU) of interest. We have only one
// CU (object file), so offset is always 0.
// FIXME: Provide the associated DWARFUnit if there is one. DWARF v5
// needs it in order to find indirect strings.
const DWARFDebugLine::LineTable *LT =
DwarfLine->getOrParseLineTable(LineData, 0, nullptr);
// Return if there is no debug information about CU available.
if (!Dwarf.getNumCompileUnits())
return;
// Loop over variable records and insert them to VariableLoc.
DWARFCompileUnit *CU = Dwarf.getCompileUnitAtIndex(0);
for (const auto &Entry : CU->dies()) {
DWARFDie Die(CU, &Entry);
// Skip all tags that are not variables.
if (Die.getTag() != dwarf::DW_TAG_variable)
for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf->compile_units()) {
auto Report = [](Error Err) {
handleAllErrors(std::move(Err),
[](ErrorInfoBase &Info) { warn(Info.message()); });
};
Expected<const DWARFDebugLine::LineTable *> ExpectedLT =
Dwarf->getLineTableForUnit(CU.get(), Report);
const DWARFDebugLine::LineTable *LT = nullptr;
if (ExpectedLT)
LT = *ExpectedLT;
else
Report(ExpectedLT.takeError());
if (!LT)
continue;
LineTables.push_back(LT);
// Skip if a local variable because we don't need them for generating error
// messages. In general, only non-local symbols can fail to be linked.
if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0))
continue;
// Loop over variable records and insert them to VariableLoc.
for (const auto &Entry : CU->dies()) {
DWARFDie Die(CU.get(), &Entry);
// Skip all tags that are not variables.
if (Die.getTag() != dwarf::DW_TAG_variable)
continue;
// Get the source filename index for the variable.
unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
if (!LT->hasFileAtIndex(File))
continue;
// Skip if a local variable because we don't need them for generating
// error messages. In general, only non-local symbols can fail to be
// linked.
if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0))
continue;
// Get the line number on which the variable is declared.
unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
// Get the source filename index for the variable.
unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
if (!LT->hasFileAtIndex(File))
continue;
// Get the name of the variable and add the collected information to
// VariableLoc. Usually Name is non-empty, but it can be empty if the input
// object file lacks some debug info.
StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), "");
if (!Name.empty())
VariableLoc.insert({Name, {File, Line}});
// Get the line number on which the variable is declared.
unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
// Here we want to take the variable name to add it into VariableLoc.
// Variable can have regular and linkage name associated. At first, we try
// to get linkage name as it can be different, for example when we have
// two variables in different namespaces of the same object. Use common
// name otherwise, but handle the case when it also absent in case if the
// input object file lacks some debug info.
StringRef Name =
dwarf::toString(Die.find(dwarf::DW_AT_linkage_name),
dwarf::toString(Die.find(dwarf::DW_AT_name), ""));
if (!Name.empty())
VariableLoc.insert({Name, {LT, File, Line}});
}
}
}
@ -170,11 +188,6 @@ Optional<std::pair<std::string, unsigned>>
ObjFile<ELFT>::getVariableLoc(StringRef Name) {
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
// There is always only one CU so it's offset is 0.
const DWARFDebugLine::LineTable *LT = DwarfLine->getLineTable(0);
if (!LT)
return None;
// Return if we have no debug information about data object.
auto It = VariableLoc.find(Name);
if (It == VariableLoc.end())
@ -182,12 +195,12 @@ ObjFile<ELFT>::getVariableLoc(StringRef Name) {
// Take file name string from line table.
std::string FileName;
if (!LT->getFileNameByIndex(
It->second.first /* File */, nullptr,
if (!It->second.LT->getFileNameByIndex(
It->second.File, nullptr,
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName))
return None;
return std::make_pair(FileName, It->second.second /*Line*/);
return std::make_pair(FileName, It->second.Line);
}
// Returns source line information for a given offset
@ -197,29 +210,15 @@ Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S,
uint64_t Offset) {
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
// The offset to CU is 0.
const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0);
if (!Tbl)
return None;
// Use fake address calcuated by adding section file offset and offset in
// section. See comments for ObjectInfo class.
DILineInfo Info;
Tbl->getFileLineInfoForAddress(
S->getOffsetInFile() + Offset, nullptr,
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info);
if (Info.Line == 0)
return None;
return Info;
}
// Returns source line information for a given offset
// using DWARF debug info.
template <class ELFT>
std::string ObjFile<ELFT>::getLineInfo(InputSectionBase *S, uint64_t Offset) {
if (Optional<DILineInfo> Info = getDILineInfo(S, Offset))
return Info->FileName + ":" + std::to_string(Info->Line);
return "";
for (const llvm::DWARFDebugLine::LineTable *LT : LineTables)
if (LT->getFileLineInfoForAddress(
S->getOffsetInFile() + Offset, nullptr,
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info))
return Info;
return None;
}
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
@ -249,7 +248,7 @@ ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) {
template <class ELFT>
typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalELFSyms() {
return makeArrayRef(ELFSyms.begin() + FirstNonLocal, ELFSyms.end());
return makeArrayRef(ELFSyms.begin() + FirstGlobal, ELFSyms.end());
}
template <class ELFT>
@ -260,9 +259,9 @@ uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
template <class ELFT>
void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
const Elf_Shdr *Symtab) {
FirstNonLocal = Symtab->sh_info;
FirstGlobal = Symtab->sh_info;
ELFSyms = CHECK(getObj().symbols(Symtab), this);
if (FirstNonLocal == 0 || FirstNonLocal > ELFSyms.size())
if (FirstGlobal == 0 || FirstGlobal > ELFSyms.size())
fatal(toString(this) + ": invalid sh_info in symbol table");
StringTable =
@ -278,13 +277,22 @@ ObjFile<ELFT>::ObjFile(MemoryBufferRef M, StringRef ArchiveName)
template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getLocalSymbols() {
if (this->Symbols.empty())
return {};
return makeArrayRef(this->Symbols).slice(1, this->FirstNonLocal - 1);
return makeArrayRef(this->Symbols).slice(1, this->FirstGlobal - 1);
}
template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getGlobalSymbols() {
return makeArrayRef(this->Symbols).slice(this->FirstGlobal);
}
template <class ELFT>
void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
// Read section and symbol tables.
initializeSections(ComdatGroups);
// Read a section table. JustSymbols is usually false.
if (this->JustSymbols)
initializeJustSymbols();
else
initializeSections(ComdatGroups);
// Read a symbol table.
initializeSymbols();
}
@ -308,7 +316,7 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
// we use a section name as a signature.
//
// Such SHT_GROUP sections are invalid from the perspective of the ELF
// standard, but GNU gold 1.14 (the neweset version as of July 2017) or
// standard, but GNU gold 1.14 (the newest version as of July 2017) or
// older produce such sections as outputs for the -r option, so we need
// a bug-compatibility.
if (Signature.empty() && Sym->getType() == STT_SECTION)
@ -328,9 +336,19 @@ ObjFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
}
template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// We don't merge sections if -O0 (default is -O1). This makes sometimes
// the linker significantly faster, although the output will be bigger.
if (Config->Optimize == 0)
// On a regular link we don't merge sections if -O0 (default is -O1). This
// sometimes makes the linker significantly faster, although the output will
// be bigger.
//
// Doing the same for -r would create a problem as it would combine sections
// with different sh_entsize. One option would be to just copy every SHF_MERGE
// section as is to the output. While this would produce a valid ELF file with
// usable SHF_MERGE sections, tools like (llvm-)?dwarfdump get confused when
// they see two .debug_str. We could have separate logic for combining
// SHF_MERGE sections based both on their name and sh_entsize, but that seems
// to be more trouble than it is worth. Instead, we just use the regular (-O1)
// logic for -r.
if (Config->Optimize == 0 && !Config->Relocatable)
return false;
// A mergeable section with size 0 is useless because they don't have
@ -361,12 +379,33 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
return true;
}
// This is for --just-symbols.
//
// --just-symbols is a very minor feature that allows you to link your
// output against other existing program, so that if you load both your
// program and the other program into memory, your output can refer the
// other program's symbols.
//
// When the option is given, we link "just symbols". The section table is
// initialized with null pointers.
template <class ELFT> void ObjFile<ELFT>::initializeJustSymbols() {
ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this);
this->Sections.resize(ObjSections.size());
for (const Elf_Shdr &Sec : ObjSections) {
if (Sec.sh_type != SHT_SYMTAB)
continue;
this->initSymtab(ObjSections, &Sec);
return;
}
}
template <class ELFT>
void ObjFile<ELFT>::initializeSections(
DenseSet<CachedHashStringRef> &ComdatGroups) {
const ELFFile<ELFT> &Obj = this->getObj();
ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this);
ArrayRef<Elf_Shdr> ObjSections = CHECK(Obj.sections(), this);
uint64_t Size = ObjSections.size();
this->Sections.resize(Size);
this->SectionStringTable =
@ -381,6 +420,17 @@ void ObjFile<ELFT>::initializeSections(
// if -r is given, we'll let the final link discard such sections.
// This is compatible with GNU.
if ((Sec.sh_flags & SHF_EXCLUDE) && !Config->Relocatable) {
if (Sec.sh_type == SHT_LLVM_ADDRSIG) {
// We ignore the address-significance table if we know that the object
// file was created by objcopy or ld -r. This is because these tools
// will reorder the symbols in the symbol table, invalidating the data
// in the address-significance table, which refers to symbols by index.
if (Sec.sh_link != 0)
this->AddrsigSec = &Sec;
else if (Config->ICF == ICFLevel::Safe)
warn(toString(this) + ": --icf=safe is incompatible with object "
"files created using objcopy or ld -r");
}
this->Sections[I] = &InputSection::Discarded;
continue;
}
@ -431,8 +481,15 @@ void ObjFile<ELFT>::initializeSections(
if (Sec.sh_link >= this->Sections.size())
fatal(toString(this) +
": invalid sh_link index: " + Twine(Sec.sh_link));
this->Sections[Sec.sh_link]->DependentSections.push_back(
cast<InputSection>(this->Sections[I]));
InputSectionBase *LinkSec = this->Sections[Sec.sh_link];
InputSection *IS = cast<InputSection>(this->Sections[I]);
LinkSec->DependentSections.push_back(IS);
if (!isa<InputSection>(LinkSec))
error("a section " + IS->Name +
" with SHF_LINK_ORDER should not refer a non-regular "
"section: " +
toString(LinkSec));
}
}
}
@ -527,10 +584,11 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
}
case SHT_RELA:
case SHT_REL: {
// Find the relocation target section and associate this
// section with it. Target can be discarded, for example
// if it is a duplicated member of SHT_GROUP section, we
// do not create or proccess relocatable sections then.
// Find a relocation target section and associate this section with that.
// Target may have been discarded if it is in a different section group
// and the group is discarded, even though it's a violation of the
// spec. We handle that situation gracefully by discarding dangling
// relocation sections.
InputSectionBase *Target = getRelocTarget(Sec);
if (!Target)
return nullptr;
@ -545,32 +603,28 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
fatal(toString(this) +
": multiple relocation sections to one section are not supported");
// Mergeable sections with relocations are tricky because relocations
// need to be taken into account when comparing section contents for
// merging. It's not worth supporting such mergeable sections because
// they are rare and it'd complicates the internal design (we usually
// have to determine if two sections are mergeable early in the link
// process much before applying relocations). We simply handle mergeable
// sections with relocations as non-mergeable.
// ELF spec allows mergeable sections with relocations, but they are
// rare, and it is in practice hard to merge such sections by contents,
// because applying relocations at end of linking changes section
// contents. So, we simply handle such sections as non-mergeable ones.
// Degrading like this is acceptable because section merging is optional.
if (auto *MS = dyn_cast<MergeInputSection>(Target)) {
Target = toRegularSection(MS);
this->Sections[Sec.sh_info] = Target;
}
size_t NumRelocations;
if (Sec.sh_type == SHT_RELA) {
ArrayRef<Elf_Rela> Rels = CHECK(this->getObj().relas(&Sec), this);
Target->FirstRelocation = Rels.begin();
NumRelocations = Rels.size();
Target->NumRelocations = Rels.size();
Target->AreRelocsRela = true;
} else {
ArrayRef<Elf_Rel> Rels = CHECK(this->getObj().rels(&Sec), this);
Target->FirstRelocation = Rels.begin();
NumRelocations = Rels.size();
Target->NumRelocations = Rels.size();
Target->AreRelocsRela = false;
}
assert(isUInt<31>(NumRelocations));
Target->NumRelocations = NumRelocations;
assert(isUInt<31>(Target->NumRelocations));
// Relocation sections processed by the linker are usually removed
// from the output, so returning `nullptr` for the normal case.
@ -602,13 +656,24 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
if (Name == ".note.GNU-stack")
return &InputSection::Discarded;
// Split stacks is a feature to support a discontiguous stack. At least
// as of 2017, it seems that the feature is not being used widely.
// Only GNU gold supports that. We don't. For the details about that,
// see https://gcc.gnu.org/wiki/SplitStacks
// Split stacks is a feature to support a discontiguous stack,
// commonly used in the programming language Go. For the details,
// see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled
// for split stack will include a .note.GNU-split-stack section.
if (Name == ".note.GNU-split-stack") {
error(toString(this) +
": object file compiled with -fsplit-stack is not supported");
if (Config->Relocatable) {
error("Cannot mix split-stack and non-split-stack in a relocatable link");
return &InputSection::Discarded;
}
this->SplitStack = true;
return &InputSection::Discarded;
}
// An object file cmpiled for split stack, but where some of the
// functions were compiled with the no_split_stack_attribute will
// include a .note.GNU-no-split-stack section.
if (Name == ".note.GNU-no-split-stack") {
this->SomeNoSplitStack = true;
return &InputSection::Discarded;
}
@ -620,6 +685,14 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
if (Name.startswith(".gnu.linkonce."))
return &InputSection::Discarded;
// If we are creating a new .build-id section, strip existing .build-id
// sections so that the output won't have more than one .build-id.
// This is not usually a problem because input object files normally don't
// have .build-id sections, but you can create such files by
// "ld.{bfd,gold,lld} -r --build-id", and we want to guard against it.
if (Name == ".note.gnu.build-id" && Config->BuildId != BuildIdKind::None)
return &InputSection::Discarded;
// The linker merges EH (exception handling) frames and creates a
// .eh_frame_hdr section for runtime. So we handle them with a special
// class. For relocatable outputs, they are just passed through.
@ -701,33 +774,33 @@ ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File)
File(std::move(File)) {}
template <class ELFT> void ArchiveFile::parse() {
Symbols.reserve(File->getNumberOfSymbols());
for (const Archive::Symbol &Sym : File->symbols())
Symbols.push_back(Symtab->addLazyArchive<ELFT>(Sym.getName(), *this, Sym));
Symtab->addLazyArchive<ELFT>(Sym.getName(), *this, Sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
std::pair<MemoryBufferRef, uint64_t>
ArchiveFile::getMember(const Archive::Symbol *Sym) {
InputFile *ArchiveFile::fetch(const Archive::Symbol &Sym) {
Archive::Child C =
CHECK(Sym->getMember(), toString(this) +
": could not get the member for symbol " +
Sym->getName());
CHECK(Sym.getMember(), toString(this) +
": could not get the member for symbol " +
Sym.getName());
if (!Seen.insert(C.getChildOffset()).second)
return {MemoryBufferRef(), 0};
return nullptr;
MemoryBufferRef Ret =
MemoryBufferRef MB =
CHECK(C.getMemoryBufferRef(),
toString(this) +
": could not get the buffer for the member defining symbol " +
Sym->getName());
Sym.getName());
if (C.getParent()->isThin() && Tar)
Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), Ret.getBuffer());
if (C.getParent()->isThin())
return {Ret, 0};
return {Ret, C.getChildOffset()};
if (Tar && C.getParent()->isThin())
Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), MB.getBuffer());
InputFile *File = createObjectFile(
MB, getName(), C.getParent()->isThin() ? 0 : C.getChildOffset());
File->GroupId = GroupId;
return File;
}
template <class ELFT>
@ -784,34 +857,42 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
}
}
// Parses ".gnu.version" section which is a parallel array for the symbol table.
// If a given file doesn't have ".gnu.version" section, returns VER_NDX_GLOBAL.
template <class ELFT> std::vector<uint32_t> SharedFile<ELFT>::parseVersyms() {
size_t Size = this->ELFSyms.size() - this->FirstGlobal;
if (!VersymSec)
return std::vector<uint32_t>(Size, VER_NDX_GLOBAL);
const char *Base = this->MB.getBuffer().data();
const Elf_Versym *Versym =
reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) +
this->FirstGlobal;
std::vector<uint32_t> Ret(Size);
for (size_t I = 0; I < Size; ++I)
Ret[I] = Versym[I].vs_index;
return Ret;
}
// Parse the version definitions in the object file if present. Returns a vector
// whose nth element contains a pointer to the Elf_Verdef for version identifier
// n. Version identifiers that are not definitions map to nullptr. The array
// always has at least length 1.
// n. Version identifiers that are not definitions map to nullptr.
template <class ELFT>
std::vector<const typename ELFT::Verdef *>
SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
std::vector<const Elf_Verdef *> Verdefs(1);
// We only need to process symbol versions for this DSO if it has both a
// versym and a verdef section, which indicates that the DSO contains symbol
// version definitions.
if (!VersymSec || !VerdefSec)
return Verdefs;
// The location of the first global versym entry.
const char *Base = this->MB.getBuffer().data();
Versym = reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) +
this->FirstNonLocal;
std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() {
if (!VerdefSec)
return {};
// We cannot determine the largest verdef identifier without inspecting
// every Elf_Verdef, but both bfd and gold assign verdef identifiers
// sequentially starting from 1, so we predict that the largest identifier
// will be VerdefCount.
unsigned VerdefCount = VerdefSec->sh_info;
Verdefs.resize(VerdefCount + 1);
std::vector<const Elf_Verdef *> Verdefs(VerdefCount + 1);
// Build the Verdefs array by following the chain of Elf_Verdef objects
// from the start of the .gnu.version_d section.
const char *Base = this->MB.getBuffer().data();
const char *Verdef = Base + VerdefSec->sh_offset;
for (unsigned I = 0; I != VerdefCount; ++I) {
auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef);
@ -825,74 +906,99 @@ SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
return Verdefs;
}
// Fully parse the shared object file. This must be called after parseSoName().
template <class ELFT> void SharedFile<ELFT>::parseRest() {
// Create mapping from version identifiers to Elf_Verdef entries.
const Elf_Versym *Versym = nullptr;
Verdefs = parseVerdefs(Versym);
// We do not usually care about alignments of data in shared object
// files because the loader takes care of it. However, if we promote a
// DSO symbol to point to .bss due to copy relocation, we need to keep
// the original alignment requirements. We infer it in this function.
template <class ELFT>
uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections,
const Elf_Sym &Sym) {
uint64_t Ret = UINT64_MAX;
if (Sym.st_value)
Ret = 1ULL << countTrailingZeros((uint64_t)Sym.st_value);
if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size())
Ret = std::min<uint64_t>(Ret, Sections[Sym.st_shndx].sh_addralign);
return (Ret > UINT32_MAX) ? 0 : Ret;
}
// Fully parse the shared object file. This must be called after parseSoName().
//
// This function parses symbol versions. If a DSO has version information,
// the file has a ".gnu.version_d" section which contains symbol version
// definitions. Each symbol is associated to one version through a table in
// ".gnu.version" section. That table is a parallel array for the symbol
// table, and each table entry contains an index in ".gnu.version_d".
//
// The special index 0 is reserved for VERF_NDX_LOCAL and 1 is for
// VER_NDX_GLOBAL. There's no table entry for these special versions in
// ".gnu.version_d".
//
// The file format for symbol versioning is perhaps a bit more complicated
// than necessary, but you can easily understand the code if you wrap your
// head around the data structure described above.
template <class ELFT> void SharedFile<ELFT>::parseRest() {
Verdefs = parseVerdefs(); // parse .gnu.version_d
std::vector<uint32_t> Versyms = parseVersyms(); // parse .gnu.version
ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj().sections(), this);
// System libraries can have a lot of symbols with versions. Using a
// fixed buffer for computing the versions name (foo@ver) can save a
// lot of allocations.
SmallString<0> VersionedNameBuffer;
// Add symbols to the symbol table.
Elf_Sym_Range Syms = this->getGlobalELFSyms();
for (const Elf_Sym &Sym : Syms) {
unsigned VersymIndex = VER_NDX_GLOBAL;
if (Versym) {
VersymIndex = Versym->vs_index;
++Versym;
}
bool Hidden = VersymIndex & VERSYM_HIDDEN;
VersymIndex = VersymIndex & ~VERSYM_HIDDEN;
ArrayRef<Elf_Sym> Syms = this->getGlobalELFSyms();
for (size_t I = 0; I < Syms.size(); ++I) {
const Elf_Sym &Sym = Syms[I];
StringRef Name = CHECK(Sym.getName(this->StringTable), this);
if (Sym.isUndefined()) {
Undefs.push_back(Name);
Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(),
Sym.st_other, Sym.getType(),
/*CanOmitFromDynSym=*/false, this);
S->ExportDynamic = true;
continue;
}
// ELF spec requires that all local symbols precede weak or global
// symbols in each symbol table, and the index of first non-local symbol
// is stored to sh_info. If a local symbol appears after some non-local
// symbol, that's a violation of the spec.
if (Sym.getBinding() == STB_LOCAL) {
warn("found local symbol '" + Name +
"' in global part of symbol table in file " + toString(this));
continue;
}
const Elf_Verdef *Ver = nullptr;
if (VersymIndex != VER_NDX_GLOBAL) {
if (VersymIndex >= Verdefs.size() || VersymIndex == VER_NDX_LOCAL) {
error("corrupt input file: version definition index " +
Twine(VersymIndex) + " for symbol " + Name +
" is out of bounds\n>>> defined in " + toString(this));
continue;
}
Ver = Verdefs[VersymIndex];
} else {
VersymIndex = 0;
}
// MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly
// assigns VER_NDX_LOCAL to this section global symbol. Here is a
// workaround for this bug.
uint32_t Idx = Versyms[I] & ~VERSYM_HIDDEN;
if (Config->EMachine == EM_MIPS && Idx == VER_NDX_LOCAL &&
Name == "_gp_disp")
continue;
// We do not usually care about alignments of data in shared object
// files because the loader takes care of it. However, if we promote a
// DSO symbol to point to .bss due to copy relocation, we need to keep
// the original alignment requirements. We infer it here.
uint64_t Alignment = 1;
if (Sym.st_value)
Alignment = 1ULL << countTrailingZeros((uint64_t)Sym.st_value);
if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) {
uint64_t SecAlign = Sections[Sym.st_shndx].sh_addralign;
Alignment = std::min(Alignment, SecAlign);
}
if (Alignment > UINT32_MAX)
error(toString(this) + ": alignment too large: " + Name);
if (!Hidden)
Symtab->addShared(Name, *this, Sym, Alignment, VersymIndex);
uint64_t Alignment = getAlignment(Sections, Sym);
if (!(Versyms[I] & VERSYM_HIDDEN))
Symtab->addShared(Name, *this, Sym, Alignment, Idx);
// Also add the symbol with the versioned name to handle undefined symbols
// with explicit versions.
if (Ver) {
StringRef VerName = this->StringTable.data() + Ver->getAux()->vda_name;
Name = Saver.save(Name + "@" + VerName);
Symtab->addShared(Name, *this, Sym, Alignment, VersymIndex);
if (Idx == VER_NDX_GLOBAL)
continue;
if (Idx >= Verdefs.size() || Idx == VER_NDX_LOCAL) {
error("corrupt input file: version definition index " + Twine(Idx) +
" for symbol " + Name + " is out of bounds\n>>> defined in " +
toString(this));
continue;
}
StringRef VerName =
this->StringTable.data() + Verdefs[Idx]->getAux()->vda_name;
VersionedNameBuffer.clear();
Name = (Name + "@" + VerName).toStringRef(VersionedNameBuffer);
Symtab->addShared(Saver.save(Name), *this, Sym, Alignment, Idx);
}
}
@ -925,8 +1031,9 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
case Triple::x86_64:
return EM_X86_64;
default:
fatal(Path + ": could not infer e_machine from bitcode target triple " +
error(Path + ": could not infer e_machine from bitcode target triple " +
T.str());
return EM_NONE;
}
}
@ -935,17 +1042,21 @@ BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName,
: InputFile(BitcodeKind, MB) {
this->ArchiveName = ArchiveName;
// Here we pass a new MemoryBufferRef which is identified by ArchiveName
// (the fully resolved path of the archive) + member name + offset of the
// member in the archive.
// ThinLTO uses the MemoryBufferRef identifier to access its internal
// data structures and if two archives define two members with the same name,
// this causes a collision which result in only one of the objects being
// taken into consideration at LTO time (which very likely causes undefined
// symbols later in the link stage).
MemoryBufferRef MBRef(MB.getBuffer(),
Saver.save(ArchiveName + MB.getBufferIdentifier() +
utostr(OffsetInArchive)));
std::string Path = MB.getBufferIdentifier().str();
if (Config->ThinLTOIndexOnly)
Path = replaceThinLTOSuffix(MB.getBufferIdentifier());
// ThinLTO assumes that all MemoryBufferRefs given to it have a unique
// name. If two archives define two members with the same name, this
// causes a collision which result in only one of the objects being taken
// into consideration at LTO time (which very likely causes undefined
// symbols later in the link stage). So we append file offset to make
// filename unique.
MemoryBufferRef MBRef(
MB.getBuffer(),
Saver.save(ArchiveName + Path +
(ArchiveName.empty() ? "" : utostr(OffsetInArchive))));
Obj = CHECK(lto::InputFile::create(MBRef), this);
Triple T(Obj->getTargetTriple());
@ -969,7 +1080,7 @@ template <class ELFT>
static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
const lto::InputFile::Symbol &ObjSym,
BitcodeFile &F) {
StringRef NameRef = Saver.save(ObjSym.getName());
StringRef Name = Saver.save(ObjSym.getName());
uint32_t Binding = ObjSym.isWeak() ? STB_WEAK : STB_GLOBAL;
uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE;
@ -978,20 +1089,20 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
int C = ObjSym.getComdatIndex();
if (C != -1 && !KeptComdats[C])
return Symtab->addUndefined<ELFT>(NameRef, Binding, Visibility, Type,
return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type,
CanOmitFromDynSym, &F);
if (ObjSym.isUndefined())
return Symtab->addUndefined<ELFT>(NameRef, Binding, Visibility, Type,
return Symtab->addUndefined<ELFT>(Name, Binding, Visibility, Type,
CanOmitFromDynSym, &F);
if (ObjSym.isCommon())
return Symtab->addCommon(NameRef, ObjSym.getCommonSize(),
return Symtab->addCommon(Name, ObjSym.getCommonSize(),
ObjSym.getCommonAlignment(), Binding, Visibility,
STT_OBJECT, F);
return Symtab->addBitcode(NameRef, Binding, Visibility, Type,
CanOmitFromDynSym, F);
return Symtab->addBitcode(Name, Binding, Visibility, Type, CanOmitFromDynSym,
F);
}
template <class ELFT>
@ -1026,8 +1137,8 @@ static ELFKind getELFKind(MemoryBufferRef MB) {
void BinaryFile::parse() {
ArrayRef<uint8_t> Data = toArrayRef(MB.getBuffer());
auto *Section = make<InputSection>(nullptr, SHF_ALLOC | SHF_WRITE,
SHT_PROGBITS, 8, Data, ".data");
auto *Section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
8, Data, ".data");
Sections.push_back(Section);
// For each input file foo that is embedded to a result as a binary
@ -1047,11 +1158,6 @@ void BinaryFile::parse() {
Data.size(), 0, STB_GLOBAL, nullptr, nullptr);
}
static bool isBitcode(MemoryBufferRef MB) {
using namespace sys::fs;
return identify_magic(MB.getBuffer()) == file_magic::bitcode;
}
InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName,
uint64_t OffsetInArchive) {
if (isBitcode(MB))
@ -1087,9 +1193,9 @@ InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) {
}
MemoryBufferRef LazyObjFile::getBuffer() {
if (Seen)
if (AddedToLink)
return MemoryBufferRef();
Seen = true;
AddedToLink = true;
return MB;
}
@ -1097,68 +1203,74 @@ InputFile *LazyObjFile::fetch() {
MemoryBufferRef MBRef = getBuffer();
if (MBRef.getBuffer().empty())
return nullptr;
return createObjectFile(MBRef, ArchiveName, OffsetInArchive);
InputFile *File = createObjectFile(MBRef, ArchiveName, OffsetInArchive);
File->GroupId = GroupId;
return File;
}
template <class ELFT> void LazyObjFile::parse() {
for (StringRef Sym : getSymbolNames())
Symtab->addLazyObject<ELFT>(Sym, *this);
}
template <class ELFT> std::vector<StringRef> LazyObjFile::getElfSymbols() {
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::SymRange Elf_Sym_Range;
ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(this->MB.getBuffer()));
ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this);
for (const Elf_Shdr &Sec : Sections) {
if (Sec.sh_type != SHT_SYMTAB)
continue;
Elf_Sym_Range Syms = CHECK(Obj.symbols(&Sec), this);
uint32_t FirstNonLocal = Sec.sh_info;
StringRef StringTable =
CHECK(Obj.getStringTableForSymtab(Sec, Sections), this);
std::vector<StringRef> V;
for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal))
if (Sym.st_shndx != SHN_UNDEF)
V.push_back(CHECK(Sym.getName(StringTable), this));
return V;
// A lazy object file wraps either a bitcode file or an ELF file.
if (isBitcode(this->MB)) {
std::unique_ptr<lto::InputFile> Obj =
CHECK(lto::InputFile::create(this->MB), this);
for (const lto::InputFile::Symbol &Sym : Obj->symbols())
if (!Sym.isUndefined())
Symtab->addLazyObject<ELFT>(Saver.save(Sym.getName()), *this);
return;
}
return {};
}
std::vector<StringRef> LazyObjFile::getBitcodeSymbols() {
std::unique_ptr<lto::InputFile> Obj =
CHECK(lto::InputFile::create(this->MB), this);
std::vector<StringRef> V;
for (const lto::InputFile::Symbol &Sym : Obj->symbols())
if (!Sym.isUndefined())
V.push_back(Saver.save(Sym.getName()));
return V;
}
// Returns a vector of globally-visible defined symbol names.
std::vector<StringRef> LazyObjFile::getSymbolNames() {
if (isBitcode(this->MB))
return getBitcodeSymbols();
switch (getELFKind(this->MB)) {
case ELF32LEKind:
return getElfSymbols<ELF32LE>();
addElfSymbols<ELF32LE>();
return;
case ELF32BEKind:
return getElfSymbols<ELF32BE>();
addElfSymbols<ELF32BE>();
return;
case ELF64LEKind:
return getElfSymbols<ELF64LE>();
addElfSymbols<ELF64LE>();
return;
case ELF64BEKind:
return getElfSymbols<ELF64BE>();
addElfSymbols<ELF64BE>();
return;
default:
llvm_unreachable("getELFKind");
}
}
template <class ELFT> void LazyObjFile::addElfSymbols() {
ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(MB.getBuffer()));
ArrayRef<typename ELFT::Shdr> Sections = CHECK(Obj.sections(), this);
for (const typename ELFT::Shdr &Sec : Sections) {
if (Sec.sh_type != SHT_SYMTAB)
continue;
typename ELFT::SymRange Syms = CHECK(Obj.symbols(&Sec), this);
uint32_t FirstGlobal = Sec.sh_info;
StringRef StringTable =
CHECK(Obj.getStringTableForSymtab(Sec, Sections), this);
for (const typename ELFT::Sym &Sym : Syms.slice(FirstGlobal))
if (Sym.st_shndx != SHN_UNDEF)
Symtab->addLazyObject<ELFT>(CHECK(Sym.getName(StringTable), this),
*this);
return;
}
}
std::string elf::replaceThinLTOSuffix(StringRef Path) {
StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first;
StringRef Repl = Config->ThinLTOObjectSuffixReplace.second;
if (!Path.endswith(Suffix)) {
error("-thinlto-object-suffix-replace=" + Suffix + ";" + Repl +
" was given, but " + Path + " does not end with the suffix");
return "";
}
return (Path.drop_back(Suffix.size()) + Repl).str();
}
template void ArchiveFile::parse<ELF32LE>();
template void ArchiveFile::parse<ELF32BE>();
template void ArchiveFile::parse<ELF64LE>();

View File

@ -12,22 +12,20 @@
#include "Config.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Reproduce.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
#include "llvm/IR/Comdat.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Support/Threading.h"
#include <map>
namespace llvm {
class DWARFDebugLine;
class TarWriter;
struct DILineInfo;
namespace lto {
@ -48,7 +46,6 @@ namespace elf {
using llvm::object::Archive;
class Lazy;
class Symbol;
// If -reproduce option is given, all input files are written
@ -90,15 +87,15 @@ class InputFile {
// Returns object file symbols. It is a runtime error to call this
// function on files of other types.
ArrayRef<Symbol *> getSymbols() {
assert(FileKind == ObjKind || FileKind == BitcodeKind ||
FileKind == ArchiveKind);
assert(FileKind == BinaryKind || FileKind == ObjKind ||
FileKind == BitcodeKind);
return Symbols;
}
// Filename of .a which contained this file. If this file was
// not in an archive file, it is the empty string. We use this
// string for creating error messages.
StringRef ArchiveName;
std::string ArchiveName;
// If this is an architecture-specific file, the following members
// have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type.
@ -112,6 +109,20 @@ class InputFile {
std::string getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
uint64_t Offset);
// True if this is an argument for --just-symbols. Usually false.
bool JustSymbols = false;
// GroupId is used for --warn-backrefs which is an optional error
// checking feature. All files within the same --{start,end}-group or
// --{start,end}-lib get the same group ID. Otherwise, each file gets a new
// group ID. For more info, see checkDependency() in SymbolTable.cpp.
uint32_t GroupId;
static bool IsInGroup;
static uint32_t NextGroupId;
// Index of MIPS GOT built for this file.
llvm::Optional<size_t> MipsGotIndex;
protected:
InputFile(Kind K, MemoryBufferRef M);
std::vector<InputSectionBase *> Sections;
@ -144,7 +155,7 @@ template <typename ELFT> class ELFFileBase : public InputFile {
protected:
ArrayRef<Elf_Sym> ELFSyms;
uint32_t FirstNonLocal = 0;
uint32_t FirstGlobal = 0;
ArrayRef<Elf_Word> SymtabSHNDX;
StringRef StringTable;
void initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab);
@ -167,6 +178,7 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; }
ArrayRef<Symbol *> getLocalSymbols();
ArrayRef<Symbol *> getGlobalSymbols();
ObjFile(MemoryBufferRef M, StringRef ArchiveName);
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
@ -182,9 +194,6 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
return getSymbol(SymIndex);
}
// Returns source line information for a given offset.
// If no information is available, returns "".
std::string getLineInfo(InputSectionBase *S, uint64_t Offset);
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef Name);
@ -198,10 +207,22 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
// symbol table.
StringRef SourceFile;
// True if the file defines functions compiled with
// -fsplit-stack. Usually false.
bool SplitStack = false;
// True if the file defines functions compiled with -fsplit-stack,
// but had one or more functions with the no_split_stack attribute.
bool SomeNoSplitStack = false;
// Pointer to this input file's .llvm_addrsig section, if it has one.
const Elf_Shdr *AddrsigSec = nullptr;
private:
void
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
void initializeSymbols();
void initializeJustSymbols();
void initializeDwarf();
InputSectionBase *getRelocTarget(const Elf_Shdr &Sec);
InputSectionBase *createInputSection(const Elf_Shdr &Sec);
@ -217,8 +238,14 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
// reporting. Linker may find reasonable number of errors in a
// single object file, so we cache debugging information in order to
// parse it only once for each object file we link.
std::unique_ptr<llvm::DWARFDebugLine> DwarfLine;
llvm::DenseMap<StringRef, std::pair<unsigned, unsigned>> VariableLoc;
std::unique_ptr<llvm::DWARFContext> Dwarf;
std::vector<const llvm::DWARFDebugLine::LineTable *> LineTables;
struct VarLoc {
const llvm::DWARFDebugLine::LineTable *LT;
unsigned File;
unsigned Line;
};
llvm::DenseMap<StringRef, VarLoc> VariableLoc;
llvm::once_flag InitDwarfLine;
};
@ -242,13 +269,11 @@ class LazyObjFile : public InputFile {
template <class ELFT> void parse();
MemoryBufferRef getBuffer();
InputFile *fetch();
bool AddedToLink = false;
private:
std::vector<StringRef> getSymbolNames();
template <class ELFT> std::vector<StringRef> getElfSymbols();
std::vector<StringRef> getBitcodeSymbols();
template <class ELFT> void addElfSymbols();
bool Seen = false;
uint64_t OffsetInArchive;
};
@ -259,11 +284,11 @@ class ArchiveFile : public InputFile {
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
template <class ELFT> void parse();
// Returns a memory buffer for a given symbol and the offset in the archive
// for the member. An empty memory buffer and an offset of zero
// is returned if we have already returned the same memory buffer.
// (So that we don't instantiate same members more than once.)
std::pair<MemoryBufferRef, uint64_t> getMember(const Archive::Symbol *Sym);
// Pulls out an object file that contains a definition for Sym and
// returns it. If the same file was instantiated before, this
// function returns a nullptr (so we don't instantiate the same file
// more than once.)
InputFile *fetch(const Archive::Symbol &Sym);
private:
std::unique_ptr<Archive> File;
@ -290,7 +315,6 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
typedef typename ELFT::Verdef Elf_Verdef;
typedef typename ELFT::Versym Elf_Versym;
std::vector<StringRef> Undefs;
const Elf_Shdr *VersymSec = nullptr;
const Elf_Shdr *VerdefSec = nullptr;
@ -298,8 +322,6 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
std::vector<const Elf_Verdef *> Verdefs;
std::string SoName;
llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }
static bool classof(const InputFile *F) {
return F->kind() == Base::SharedKind;
}
@ -308,7 +330,9 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
void parseSoName();
void parseRest();
std::vector<const Elf_Verdef *> parseVerdefs(const Elf_Versym *&Versym);
uint32_t getAlignment(ArrayRef<Elf_Shdr> Sections, const Elf_Sym &Sym);
std::vector<const Elf_Verdef *> parseVerdefs();
std::vector<uint32_t> parseVersyms();
struct NeededVer {
// The string table offset of the version name in the output file.
@ -337,8 +361,15 @@ InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "",
uint64_t OffsetInArchive = 0);
InputFile *createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName);
inline bool isBitcode(MemoryBufferRef MB) {
return identify_magic(MB.getBuffer()) == llvm::file_magic::bitcode;
}
std::string replaceThinLTOSuffix(StringRef Path);
extern std::vector<BinaryFile *> BinaryFiles;
extern std::vector<BitcodeFile *> BitcodeFiles;
extern std::vector<LazyObjFile *> LazyObjFiles;
extern std::vector<InputFile *> ObjectFiles;
extern std::vector<InputFile *> SharedFiles;

View File

@ -14,6 +14,7 @@
#include "LinkerScript.h"
#include "OutputSections.h"
#include "Relocations.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
@ -26,7 +27,10 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/xxhash.h"
#include <algorithm>
#include <mutex>
#include <set>
#include <vector>
using namespace llvm;
using namespace llvm::ELF;
@ -45,32 +49,6 @@ std::string lld::toString(const InputSectionBase *Sec) {
return (toString(Sec->File) + ":(" + Sec->Name + ")").str();
}
DenseMap<SectionBase *, int> elf::buildSectionOrder() {
DenseMap<SectionBase *, int> SectionOrder;
if (Config->SymbolOrderingFile.empty())
return SectionOrder;
// Build a map from symbols to their priorities. Symbols that didn't
// appear in the symbol ordering file have the lowest priority 0.
// All explicitly mentioned symbols have negative (higher) priorities.
DenseMap<StringRef, int> SymbolOrder;
int Priority = -Config->SymbolOrderingFile.size();
for (StringRef S : Config->SymbolOrderingFile)
SymbolOrder.insert({S, Priority++});
// Build a map from sections to their priorities.
for (InputFile *File : ObjectFiles) {
for (Symbol *Sym : File->getSymbols()) {
auto *D = dyn_cast<Defined>(Sym);
if (!D || !D->Section)
continue;
int &Priority = SectionOrder[D->Section];
Priority = std::min(Priority, SymbolOrder.lookup(D->getName()));
}
}
return SectionOrder;
}
template <class ELFT>
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &File,
const typename ELFT::Shdr &Hdr) {
@ -168,12 +146,8 @@ uint64_t SectionBase::getOffset(uint64_t Offset) const {
return Offset == uint64_t(-1) ? OS->Size : Offset;
}
case Regular:
return cast<InputSection>(this)->OutSecOff + Offset;
case Synthetic: {
auto *IS = cast<InputSection>(this);
// For synthetic sections we treat offset -1 as the end of the section.
return IS->OutSecOff + (Offset == uint64_t(-1) ? IS->getSize() : Offset);
}
case Synthetic:
return cast<InputSection>(this)->getOffset(Offset);
case EHFrame:
// 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
@ -182,16 +156,21 @@ uint64_t SectionBase::getOffset(uint64_t Offset) const {
case Merge:
const MergeInputSection *MS = cast<MergeInputSection>(this);
if (InputSection *IS = MS->getParent())
return IS->OutSecOff + MS->getOffset(Offset);
return MS->getOffset(Offset);
return IS->getOffset(MS->getParentOffset(Offset));
return MS->getParentOffset(Offset);
}
llvm_unreachable("invalid section kind");
}
uint64_t SectionBase::getVA(uint64_t Offset) const {
const OutputSection *Out = getOutputSection();
return (Out ? Out->Addr : 0) + getOffset(Offset);
}
OutputSection *SectionBase::getOutputSection() {
InputSection *Sec;
if (auto *IS = dyn_cast<InputSection>(this))
return IS->getParent();
Sec = IS;
else if (auto *MS = dyn_cast<MergeInputSection>(this))
Sec = MS->getParent();
else if (auto *EH = dyn_cast<EhInputSection>(this))
@ -201,34 +180,50 @@ OutputSection *SectionBase::getOutputSection() {
return Sec ? Sec->getParent() : nullptr;
}
// Uncompress section contents if required. Note that this function
// Decompress section contents if required. Note that this function
// is called from parallelForEach, so it must be thread-safe.
void InputSectionBase::maybeUncompress() {
if (UncompressBuf || !Decompressor::isCompressedELFSection(Flags, Name))
void InputSectionBase::maybeDecompress() {
if (DecompressBuf)
return;
if (!(Flags & SHF_COMPRESSED) && !Name.startswith(".zdebug"))
return;
// Decompress a section.
Decompressor Dec = check(Decompressor::create(Name, toStringRef(Data),
Config->IsLE, Config->Is64));
size_t Size = Dec.getDecompressedSize();
UncompressBuf.reset(new char[Size]());
if (Error E = Dec.decompress({UncompressBuf.get(), Size}))
DecompressBuf.reset(new char[Size + Name.size()]());
if (Error E = Dec.decompress({DecompressBuf.get(), Size}))
fatal(toString(this) +
": decompress failed: " + llvm::toString(std::move(E)));
Data = makeArrayRef((uint8_t *)UncompressBuf.get(), Size);
Data = makeArrayRef((uint8_t *)DecompressBuf.get(), Size);
Flags &= ~(uint64_t)SHF_COMPRESSED;
// A section name may have been altered if compressed. If that's
// the case, restore the original name. (i.e. ".zdebug_" -> ".debug_")
if (Name.startswith(".zdebug")) {
DecompressBuf[Size] = '.';
memcpy(&DecompressBuf[Size + 1], Name.data() + 2, Name.size() - 2);
Name = StringRef(&DecompressBuf[Size], Name.size() - 1);
}
}
InputSection *InputSectionBase::getLinkOrderDep() const {
if ((Flags & SHF_LINK_ORDER) && Link != 0) {
InputSectionBase *L = File->getSections()[Link];
if (auto *IS = dyn_cast<InputSection>(L))
return IS;
error("a section with SHF_LINK_ORDER should not refer a non-regular "
"section: " +
toString(L));
}
assert(Link);
assert(Flags & SHF_LINK_ORDER);
return cast<InputSection>(File->getSections()[Link]);
}
// Find a function symbol that encloses a given location.
template <class ELFT>
Defined *InputSectionBase::getEnclosingFunction(uint64_t Offset) {
for (Symbol *B : File->getSymbols())
if (Defined *D = dyn_cast<Defined>(B))
if (D->Section == this && D->Type == STT_FUNC &&
D->Value <= Offset && Offset < D->Value + D->Size)
return D;
return nullptr;
}
@ -241,9 +236,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
.str();
// First check if we can get desired values from debugging information.
std::string LineInfo = getFile<ELFT>()->getLineInfo(this, Offset);
if (!LineInfo.empty())
return LineInfo;
if (Optional<DILineInfo> Info = getFile<ELFT>()->getDILineInfo(this, Offset))
return Info->FileName + ":" + std::to_string(Info->Line);
// File->SourceFile contains STT_FILE symbol that contains a
// source file name. If it's missing, we use an object file name.
@ -251,12 +245,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
if (SrcFile.empty())
SrcFile = toString(File);
// Find a function symbol that encloses a given location.
for (Symbol *B : File->getSymbols())
if (auto *D = dyn_cast<Defined>(B))
if (D->Section == this && D->Type == STT_FUNC)
if (D->Value <= Offset && Offset < D->Value + D->Size)
return SrcFile + ":(function " + toString(*D) + ")";
if (Defined *D = getEnclosingFunction<ELFT>(Offset))
return SrcFile + ":(function " + toString(*D) + ")";
// If there's no symbol, print out the offset in the section.
return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str();
@ -292,7 +282,7 @@ std::string InputSectionBase::getObjMsg(uint64_t Off) {
std::string Archive;
if (!File->ArchiveName.empty())
Archive = (" in archive " + File->ArchiveName).str();
Archive = " in archive " + File->ArchiveName;
// Find a symbol that encloses a given location.
for (Symbol *B : File->getSymbols())
@ -345,8 +335,9 @@ template <class ELFT> void InputSection::copyShtGroup(uint8_t *Buf) {
*To++ = Sections[Idx]->getOutputSection()->SectionIndex;
}
InputSectionBase *InputSection::getRelocatedSection() {
assert(Type == SHT_RELA || Type == SHT_REL);
InputSectionBase *InputSection::getRelocatedSection() const {
if (!File || (Type != SHT_RELA && Type != SHT_REL))
return nullptr;
ArrayRef<InputSectionBase *> Sections = File->getSections();
return Sections[Info];
}
@ -365,12 +356,12 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
auto *P = reinterpret_cast<typename ELFT::Rela *>(Buf);
Buf += sizeof(RelTy);
if (Config->IsRela)
if (RelTy::IsRela)
P->r_addend = getAddend<ELFT>(Rel);
// Output section VA is zero for -r, so r_offset is an offset within the
// section, but for --emit-relocs it is an virtual address.
P->r_offset = Sec->getOutputSection()->Addr + Sec->getOffset(Rel.r_offset);
P->r_offset = Sec->getVA(Rel.r_offset);
P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type,
Config->IsMips64EL);
@ -395,17 +386,32 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
continue;
}
if (Config->IsRela) {
P->r_addend =
Sym.getVA(getAddend<ELFT>(Rel)) - Section->getOutputSection()->Addr;
} else if (Config->Relocatable) {
const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset;
Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset,
Target->getImplicitAddend(BufLoc, Type),
&Sym});
}
}
int64_t Addend = getAddend<ELFT>(Rel);
const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset;
if (!RelTy::IsRela)
Addend = Target->getImplicitAddend(BufLoc, Type);
if (Config->EMachine == EM_MIPS && Config->Relocatable &&
Target->getRelExpr(Type, Sym, BufLoc) == R_MIPS_GOTREL) {
// Some MIPS relocations depend on "gp" value. By default,
// this value has 0x7ff0 offset from a .got section. But
// relocatable files produced by a complier or a linker
// might redefine this default value and we must use it
// for a calculation of the relocation result. When we
// generate EXE or DSO it's trivial. Generating a relocatable
// output is more difficult case because the linker does
// not calculate relocations in this mode and loses
// individual "gp" values used by each input object file.
// As a workaround we add the "gp" value to the relocation
// addend and save it back to the file.
Addend += Sec->getFile<ELFT>()->MipsGp0;
}
if (RelTy::IsRela)
P->r_addend = Sym.getVA(Addend) - Section->getOutputSection()->Addr;
else if (Config->Relocatable)
Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset, Addend, &Sym});
}
}
}
@ -481,14 +487,17 @@ static uint64_t getARMStaticBase(const Symbol &Sym) {
return OS->PtLoad->FirstSec->Addr;
}
static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
const Symbol &Sym, RelExpr Expr) {
static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
uint64_t P, const Symbol &Sym, RelExpr Expr) {
switch (Expr) {
case R_INVALID:
return 0;
case R_ABS:
case R_RELAX_TLS_LD_TO_LE_ABS:
case R_RELAX_GOT_PC_NOPIC:
return Sym.getVA(A);
case R_ADDEND:
return A;
case R_ARM_SBREL:
return Sym.getVA(A) - getARMStaticBase(Sym);
case R_GOT:
@ -505,7 +514,9 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
case R_GOT_FROM_END:
case R_RELAX_TLS_GD_TO_IE_END:
return Sym.getGotOffset() + A - InX::Got->getSize();
case R_TLSLD_GOT_OFF:
case R_GOT_OFF:
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
return Sym.getGotOffset() + A;
case R_GOT_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE_PAGE_PC:
@ -516,11 +527,12 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
case R_HINT:
case R_NONE:
case R_TLSDESC_CALL:
case R_TLSLD_HINT:
llvm_unreachable("cannot relocate hint relocs");
case R_MIPS_GOTREL:
return Sym.getVA(A) - InX::MipsGot->getGp();
return Sym.getVA(A) - InX::MipsGot->getGp(File);
case R_MIPS_GOT_GP:
return InX::MipsGot->getGp() + A;
return InX::MipsGot->getGp(File) + A;
case R_MIPS_GOT_GP_PC: {
// R_MIPS_LO16 expression has R_MIPS_GOT_GP_PC type iif the target
// is _gp_disp symbol. In that case we should use the following
@ -529,7 +541,7 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
// microMIPS variants of these relocations use slightly different
// expressions: AHL + GP - P + 3 for %lo() and AHL + GP - P - 1 for %hi()
// to correctly handle less-sugnificant bit of the microMIPS symbol.
uint64_t V = InX::MipsGot->getGp() + A - P;
uint64_t V = InX::MipsGot->getGp(File) + A - P;
if (Type == R_MIPS_LO16 || Type == R_MICROMIPS_LO16)
V += 4;
if (Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_HI16)
@ -540,21 +552,23 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
// If relocation against MIPS local symbol requires GOT entry, this entry
// should be initialized by 'page address'. This address is high 16-bits
// of sum the symbol's value and the addend.
return InX::MipsGot->getVA() + InX::MipsGot->getPageEntryOffset(Sym, A) -
InX::MipsGot->getGp();
return InX::MipsGot->getVA() +
InX::MipsGot->getPageEntryOffset(File, Sym, A) -
InX::MipsGot->getGp(File);
case R_MIPS_GOT_OFF:
case R_MIPS_GOT_OFF32:
// In case of MIPS if a GOT relocation has non-zero addend this addend
// should be applied to the GOT entry content not to the GOT entry offset.
// That is why we use separate expression type.
return InX::MipsGot->getVA() + InX::MipsGot->getSymEntryOffset(Sym, A) -
InX::MipsGot->getGp();
return InX::MipsGot->getVA() +
InX::MipsGot->getSymEntryOffset(File, Sym, A) -
InX::MipsGot->getGp(File);
case R_MIPS_TLSGD:
return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() +
InX::MipsGot->getGlobalDynOffset(Sym) - InX::MipsGot->getGp();
return InX::MipsGot->getVA() + InX::MipsGot->getGlobalDynOffset(File, Sym) -
InX::MipsGot->getGp(File);
case R_MIPS_TLSLD:
return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() +
InX::MipsGot->getTlsIndexOff() - InX::MipsGot->getGp();
return InX::MipsGot->getVA() + InX::MipsGot->getTlsIndexOffset(File) -
InX::MipsGot->getGp(File);
case R_PAGE_PC:
case R_PLT_PAGE_PC: {
uint64_t Dest;
@ -583,25 +597,27 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
case R_PLT:
return Sym.getPltVA() + A;
case R_PLT_PC:
case R_PPC_PLT_OPD:
case R_PPC_CALL_PLT:
return Sym.getPltVA() + A - P;
case R_PPC_OPD: {
case R_PPC_CALL: {
uint64_t SymVA = Sym.getVA(A);
// If we have an undefined weak symbol, we might get here with a symbol
// address of zero. That could overflow, but the code must be unreachable,
// so don't bother doing anything at all.
if (!SymVA)
return 0;
if (Out::Opd) {
// If this is a local call, and we currently have the address of a
// function-descriptor, get the underlying code address instead.
uint64_t OpdStart = Out::Opd->Addr;
uint64_t OpdEnd = OpdStart + Out::Opd->Size;
bool InOpd = OpdStart <= SymVA && SymVA < OpdEnd;
if (InOpd)
SymVA = read64be(&Out::OpdBuf[SymVA - OpdStart]);
}
return SymVA - P;
// PPC64 V2 ABI describes two entry points to a function. The global entry
// point sets up the TOC base pointer. When calling a local function, the
// call should branch to the local entry point rather than the global entry
// point. Section 3.4.1 describes using the 3 most significant bits of the
// st_other field to find out how many instructions there are between the
// local and global entry point.
uint8_t StOther = (Sym.StOther >> 5) & 7;
if (StOther == 0 || StOther == 1)
return SymVA - P;
return SymVA - P + (1LL << StOther);
}
case R_PPC_TOC:
return getPPC64TocBase() + A;
@ -618,25 +634,44 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
// statically to zero.
if (Sym.isTls() && Sym.isUndefWeak())
return 0;
if (Target->TcbSize)
// For TLS variant 1 the TCB is a fixed size, whereas for TLS variant 2 the
// TCB is on unspecified size and content. Targets that implement variant 1
// should set TcbSize.
if (Target->TcbSize) {
// PPC64 V2 ABI has the thread pointer offset into the middle of the TLS
// storage area by TlsTpOffset for efficient addressing TCB and up to
// 4KB 8 B of other thread library information (placed before the TCB).
// Subtracting this offset will get the address of the first TLS block.
if (Target->TlsTpOffset)
return Sym.getVA(A) - Target->TlsTpOffset;
// If thread pointer is not offset into the middle, the first thing in the
// TLS storage area is the TCB. Add the TcbSize to get the address of the
// first TLS block.
return Sym.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align);
}
return Sym.getVA(A) - Out::TlsPhdr->p_memsz;
case R_RELAX_TLS_GD_TO_LE_NEG:
case R_NEG_TLS:
return Out::TlsPhdr->p_memsz - Sym.getVA(A);
case R_SIZE:
return A; // Sym.getSize was already folded into the addend.
return Sym.getSize() + A;
case R_TLSDESC:
return InX::Got->getGlobalDynAddr(Sym) + A;
case R_TLSDESC_PAGE:
return getAArch64Page(InX::Got->getGlobalDynAddr(Sym) + A) -
getAArch64Page(P);
case R_TLSGD:
case R_TLSGD_GOT:
return InX::Got->getGlobalDynOffset(Sym) + A;
case R_TLSGD_GOT_FROM_END:
return InX::Got->getGlobalDynOffset(Sym) + A - InX::Got->getSize();
case R_TLSGD_PC:
return InX::Got->getGlobalDynAddr(Sym) + A - P;
case R_TLSLD:
case R_TLSLD_GOT_FROM_END:
return InX::Got->getTlsIndexOff() + A - InX::Got->getSize();
case R_TLSLD_GOT:
return InX::Got->getTlsIndexOff() + A;
case R_TLSLD_PC:
return InX::Got->getTlsIndexVA() + A - P;
}
@ -656,6 +691,14 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
for (const RelTy &Rel : Rels) {
RelType Type = Rel.getType(Config->IsMips64EL);
// GCC 8.0 or earlier have a bug that they emit R_386_GOTPC relocations
// against _GLOBAL_OFFSET_TABLE_ for .debug_info. The bug has been fixed
// in 2017 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630), but we
// need to keep this bug-compatible code for a while.
if (Config->EMachine == EM_386 && Type == R_386_GOTPC)
continue;
uint64_t Offset = getOffset(Rel.r_offset);
uint8_t *BufLoc = Buf + Offset;
int64_t Addend = getAddend<ELFT>(Rel);
@ -666,17 +709,27 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
RelExpr Expr = Target->getRelExpr(Type, Sym, BufLoc);
if (Expr == R_NONE)
continue;
if (Expr != R_ABS) {
// GCC 8.0 or earlier have a bug that it emits R_386_GOTPC relocations
// against _GLOBAL_OFFSET_TABLE for .debug_info. The bug seems to have
// been fixed in 2017: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630,
// but we need to keep this bug-compatible code for a while.
if (Config->EMachine == EM_386 && Type == R_386_GOTPC)
continue;
error(getLocation<ELFT>(Offset) + ": has non-ABS relocation " +
toString(Type) + " against symbol '" + toString(Sym) + "'");
return;
if (Expr != R_ABS) {
std::string Msg = getLocation<ELFT>(Offset) +
": has non-ABS relocation " + toString(Type) +
" against symbol '" + toString(Sym) + "'";
if (Expr != R_PC) {
error(Msg);
return;
}
// If the control reaches here, we found a PC-relative relocation in a
// non-ALLOC section. Since non-ALLOC section is not loaded into memory
// at runtime, the notion of PC-relative doesn't make sense here. So,
// this is a usage error. However, GNU linkers historically accept such
// relocations without any errors and relocate them as if they were at
// address 0. For bug-compatibilty, we accept them with warnings. We
// know Steel Bank Common Lisp as of 2018 have this bug.
warn(Msg);
Target->relocateOne(BufLoc, Type,
SignExtend64<Bits>(Sym.getVA(Addend - Offset)));
continue;
}
if (Sym.isTls() && !Out::TlsPhdr)
@ -686,15 +739,37 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
}
}
// This is used when '-r' is given.
// For REL targets, InputSection::copyRelocations() may store artificial
// relocations aimed to update addends. They are handled in relocateAlloc()
// for allocatable sections, and this function does the same for
// non-allocatable sections, such as sections with debug information.
static void relocateNonAllocForRelocatable(InputSection *Sec, uint8_t *Buf) {
const unsigned Bits = Config->Is64 ? 64 : 32;
for (const Relocation &Rel : Sec->Relocations) {
// InputSection::copyRelocations() adds only R_ABS relocations.
assert(Rel.Expr == R_ABS);
uint8_t *BufLoc = Buf + Rel.Offset + Sec->OutSecOff;
uint64_t TargetVA = SignExtend64(Rel.Sym->getVA(Rel.Addend), Bits);
Target->relocateOne(BufLoc, Rel.Type, TargetVA);
}
}
template <class ELFT>
void InputSectionBase::relocate(uint8_t *Buf, uint8_t *BufEnd) {
if (Flags & SHF_EXECINSTR)
adjustSplitStackFunctionPrologues<ELFT>(Buf, BufEnd);
if (Flags & SHF_ALLOC) {
relocateAlloc(Buf, BufEnd);
return;
}
auto *Sec = cast<InputSection>(this);
if (Sec->AreRelocsRela)
if (Config->Relocatable)
relocateNonAllocForRelocatable(Sec, Buf);
else if (Sec->AreRelocsRela)
Sec->relocateNonAlloc<ELFT>(Buf, Sec->template relas<ELFT>());
else
Sec->relocateNonAlloc<ELFT>(Buf, Sec->template rels<ELFT>());
@ -705,14 +780,17 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
const unsigned Bits = Config->Wordsize * 8;
for (const Relocation &Rel : Relocations) {
uint64_t Offset = getOffset(Rel.Offset);
uint64_t Offset = Rel.Offset;
if (auto *Sec = dyn_cast<InputSection>(this))
Offset += Sec->OutSecOff;
uint8_t *BufLoc = Buf + Offset;
RelType Type = Rel.Type;
uint64_t AddrLoc = getOutputSection()->Addr + Offset;
RelExpr Expr = Rel.Expr;
uint64_t TargetVA = SignExtend64(
getRelocTargetVA(Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr), Bits);
getRelocTargetVA(File, Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr),
Bits);
switch (Expr) {
case R_RELAX_GOT_PC:
@ -723,6 +801,7 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
Target->relaxTlsIeToLe(BufLoc, Type, TargetVA);
break;
case R_RELAX_TLS_LD_TO_LE:
case R_RELAX_TLS_LD_TO_LE_ABS:
Target->relaxTlsLdToLe(BufLoc, Type, TargetVA);
break;
case R_RELAX_TLS_GD_TO_LE:
@ -731,15 +810,28 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
break;
case R_RELAX_TLS_GD_TO_IE:
case R_RELAX_TLS_GD_TO_IE_ABS:
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
case R_RELAX_TLS_GD_TO_IE_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE_END:
Target->relaxTlsGdToIe(BufLoc, Type, TargetVA);
break;
case R_PPC_PLT_OPD:
case R_PPC_CALL:
// If this is a call to __tls_get_addr, it may be part of a TLS
// sequence that has been relaxed and turned into a nop. In this
// case, we don't want to handle it as a call.
if (read32(BufLoc) == 0x60000000) // nop
break;
// Patch a nop (0x60000000) to a ld.
if (BufLoc + 8 <= BufEnd && read32be(BufLoc + 4) == 0x60000000)
write32be(BufLoc + 4, 0xe8410028); // ld %r2, 40(%r1)
LLVM_FALLTHROUGH;
if (Rel.Sym->NeedsTocRestore) {
if (BufLoc + 8 > BufEnd || read32(BufLoc + 4) != 0x60000000) {
error(getErrorLocation(BufLoc) + "call lacks nop, can't restore toc");
break;
}
write32(BufLoc + 4, 0xe8410018); // ld %r2, 24(%r1)
}
Target->relocateOne(BufLoc, Type, TargetVA);
break;
default:
Target->relocateOne(BufLoc, Type, TargetVA);
break;
@ -747,6 +839,103 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
}
}
// For each function-defining prologue, find any calls to __morestack,
// and replace them with calls to __morestack_non_split.
static void switchMorestackCallsToMorestackNonSplit(
llvm::DenseSet<Defined *>& Prologues,
std::vector<Relocation *>& MorestackCalls) {
// If the target adjusted a function's prologue, all calls to
// __morestack inside that function should be switched to
// __morestack_non_split.
Symbol *MoreStackNonSplit = Symtab->find("__morestack_non_split");
// Sort both collections to compare addresses efficiently.
llvm::sort(MorestackCalls.begin(), MorestackCalls.end(),
[](const Relocation *L, const Relocation *R) {
return L->Offset < R->Offset;
});
std::vector<Defined *> Functions(Prologues.begin(), Prologues.end());
llvm::sort(
Functions.begin(), Functions.end(),
[](const Defined *L, const Defined *R) { return L->Value < R->Value; });
auto It = MorestackCalls.begin();
for (Defined *F : Functions) {
// Find the first call to __morestack within the function.
while (It != MorestackCalls.end() && (*It)->Offset < F->Value)
++It;
// Adjust all calls inside the function.
while (It != MorestackCalls.end() && (*It)->Offset < F->Value + F->Size) {
(*It)->Sym = MoreStackNonSplit;
++It;
}
}
}
static bool
enclosingPrologueAdjusted(uint64_t Offset,
const llvm::DenseSet<Defined *> &Prologues) {
for (Defined *F : Prologues)
if (F->Value <= Offset && Offset < F->Value + F->Size)
return true;
return false;
}
// If a function compiled for split stack calls a function not
// compiled for split stack, then the caller needs its prologue
// adjusted to ensure that the called function will have enough stack
// available. Find those functions, and adjust their prologues.
template <class ELFT>
void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
uint8_t *End) {
if (!getFile<ELFT>()->SplitStack)
return;
llvm::DenseSet<Defined *> AdjustedPrologues;
std::vector<Relocation *> MorestackCalls;
for (Relocation &Rel : Relocations) {
// Local symbols can't possibly be cross-calls, and should have been
// resolved long before this line.
if (Rel.Sym->isLocal())
continue;
Defined *D = dyn_cast<Defined>(Rel.Sym);
// A reference to an undefined symbol was an error, and should not
// have gotten to this point.
if (!D)
continue;
// Ignore calls into the split-stack api.
if (D->getName().startswith("__morestack")) {
if (D->getName().equals("__morestack"))
MorestackCalls.push_back(&Rel);
continue;
}
// A relocation to non-function isn't relevant. Sometimes
// __morestack is not marked as a function, so this check comes
// after the name check.
if (D->Type != STT_FUNC)
continue;
if (enclosingPrologueAdjusted(Rel.Offset, AdjustedPrologues))
continue;
if (Defined *F = getEnclosingFunction<ELFT>(Rel.Offset)) {
if (Target->adjustPrologueForCrossSplitStack(Buf + F->Value, End)) {
AdjustedPrologues.insert(F);
continue;
}
}
if (!getFile<ELFT>()->SomeNoSplitStack)
error("function call at " + getErrorLocation(Buf + Rel.Offset) +
"crosses a split-stack boundary, but unable " +
"to adjust the enclosing function's prologue");
}
switchMorestackCallsToMorestackNonSplit(AdjustedPrologues, MorestackCalls);
}
template <class ELFT> void InputSection::writeTo(uint8_t *Buf) {
if (Type == SHT_NOBITS)
return;
@ -818,10 +1007,6 @@ static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef<RelTy> &Rels,
// .eh_frame is a sequence of CIE or FDE records.
// This function splits an input section into records and returns them.
template <class ELFT> void EhInputSection::split() {
// Early exit if already split.
if (!Pieces.empty())
return;
if (AreRelocsRela)
split<ELFT>(relas<ELFT>());
else
@ -916,15 +1101,9 @@ void MergeInputSection::splitIntoPieces() {
else
splitNonStrings(Data, Entsize);
if (Config->GcSections && (Flags & SHF_ALLOC))
for (uint64_t Off : LiveOffsets)
getSectionPiece(Off)->Live = true;
}
// Do binary search to get a section piece at a given input offset.
SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
auto *This = static_cast<const MergeInputSection *>(this);
return const_cast<SectionPiece *>(This->getSectionPiece(Offset));
OffsetMap.reserve(Pieces.size());
for (size_t I = 0, E = Pieces.size(); I != E; ++I)
OffsetMap[Pieces[I].InputOff] = I;
}
template <class It, class T, class Compare>
@ -940,32 +1119,34 @@ static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) {
return Comp(Value, *First) ? First : First + 1;
}
const SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) const {
if (Data.size() <= Offset)
fatal(toString(this) + ": entry is past the end of the section");
// Do binary search to get a section piece at a given input offset.
static SectionPiece *findSectionPiece(MergeInputSection *Sec, uint64_t Offset) {
if (Sec->Data.size() <= Offset)
fatal(toString(Sec) + ": entry is past the end of the section");
// Find the element this offset points to.
auto I = fastUpperBound(
Pieces.begin(), Pieces.end(), Offset,
Sec->Pieces.begin(), Sec->Pieces.end(), Offset,
[](const uint64_t &A, const SectionPiece &B) { return A < B.InputOff; });
--I;
return &*I;
}
SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
// Find a piece starting at a given offset.
auto It = OffsetMap.find(Offset);
if (It != OffsetMap.end())
return &Pieces[It->second];
// If Offset is not at beginning of a section piece, it is not in the map.
// In that case we need to search from the original section piece vector.
return findSectionPiece(this, Offset);
}
// Returns the offset in an output section for a given input offset.
// Because contents of a mergeable section is not contiguous in output,
// it is not just an addition to a base output offset.
uint64_t MergeInputSection::getOffset(uint64_t Offset) const {
if (!Live)
return 0;
// Initialize OffsetMap lazily.
llvm::call_once(InitOffsetMap, [&] {
OffsetMap.reserve(Pieces.size());
for (size_t I = 0; I < Pieces.size(); ++I)
OffsetMap[Pieces[I].InputOff] = I;
});
uint64_t MergeInputSection::getParentOffset(uint64_t Offset) const {
// Find a string starting at a given offset.
auto It = OffsetMap.find(Offset);
if (It != OffsetMap.end())
@ -973,10 +1154,8 @@ uint64_t MergeInputSection::getOffset(uint64_t Offset) const {
// If Offset is not at beginning of a section piece, it is not in the map.
// In that case we need to search from the original section piece vector.
const SectionPiece &Piece = *getSectionPiece(Offset);
if (!Piece.Live)
return 0;
const SectionPiece &Piece =
*findSectionPiece(const_cast<MergeInputSection *>(this), Offset);
uint64_t Addend = Offset - Piece.InputOff;
return Piece.OutputOff + Addend;
}

View File

@ -18,8 +18,6 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Threading.h"
#include <mutex>
namespace lld {
namespace elf {
@ -63,6 +61,9 @@ class SectionBase {
unsigned Bss : 1;
// Set for sections that should not be folded by ICF.
unsigned KeepUnique : 1;
// These corresponds to the fields in Elf_Shdr.
uint32_t Alignment;
uint64_t Flags;
@ -80,13 +81,15 @@ class SectionBase {
// section.
uint64_t getOffset(uint64_t Offset) const;
uint64_t getVA(uint64_t Offset = 0) const;
protected:
SectionBase(Kind SectionKind, StringRef Name, uint64_t Flags,
uint64_t Entsize, uint64_t Alignment, uint32_t Type,
uint32_t Info, uint32_t Link)
: Name(Name), Repl(this), SectionKind(SectionKind), Live(false),
Bss(false), Alignment(Alignment), Flags(Flags), Entsize(Entsize),
Type(Type), Link(Link), Info(Info) {}
Bss(false), KeepUnique(false), Alignment(Alignment), Flags(Flags),
Entsize(Entsize), Type(Type), Link(Link), Info(Info) {}
};
// This corresponds to a section of an input file.
@ -103,7 +106,7 @@ class InputSectionBase : public SectionBase {
static bool classof(const SectionBase *S) { return S->kind() != Output; }
// The file which contains this section. It's dynamic type is always
// The file which contains this section. Its dynamic type is always
// ObjFile<ELFT>, but in order to avoid ELFT, we use InputFile as
// its static type.
InputFile *File;
@ -161,10 +164,15 @@ class InputSectionBase : public SectionBase {
InputSection *getLinkOrderDep() const;
// Get the function symbol that encloses this offset from within the
// section.
template <class ELFT>
Defined *getEnclosingFunction(uint64_t Offset);
// Compilers emit zlib-compressed debug sections if the -gz option
// is given. This function checks if this section is compressed, and
// if so, decompress in memory.
void maybeUncompress();
void maybeDecompress();
// Returns a source location string. Used to construct an error message.
template <class ELFT> std::string getLocation(uint64_t Offset);
@ -182,6 +190,15 @@ class InputSectionBase : public SectionBase {
// This vector contains such "cooked" relocations.
std::vector<Relocation> Relocations;
// A function compiled with -fsplit-stack calling a function
// compiled without -fsplit-stack needs its prologue adjusted. Find
// such functions and adjust their prologues. This is very similar
// to relocation. See https://gcc.gnu.org/wiki/SplitStacks for more
// information.
template <typename ELFT>
void adjustSplitStackFunctionPrologues(uint8_t *Buf, uint8_t *End);
template <typename T> llvm::ArrayRef<T> getDataAs() const {
size_t S = Data.size();
assert(S % sizeof(T) == 0);
@ -189,9 +206,9 @@ class InputSectionBase : public SectionBase {
}
private:
// A pointer that owns uncompressed data if a section is compressed by zlib.
// A pointer that owns decompressed data if a section is compressed by zlib.
// Since the feature is not used often, this is usually a nullptr.
std::unique_ptr<char[]> UncompressBuf;
std::unique_ptr<char[]> DecompressBuf;
};
// SectionPiece represents a piece of splittable section contents.
@ -200,7 +217,7 @@ class InputSectionBase : public SectionBase {
// be found by looking at the next one).
struct SectionPiece {
SectionPiece(size_t Off, uint32_t Hash, bool Live)
: InputOff(Off), Hash(Hash), OutputOff(-1),
: InputOff(Off), Hash(Hash), OutputOff(0),
Live(Live || !Config->GcSections) {}
uint32_t InputOff;
@ -223,19 +240,14 @@ class MergeInputSection : public InputSectionBase {
static bool classof(const SectionBase *S) { return S->kind() == Merge; }
void splitIntoPieces();
// Mark the piece at a given offset live. Used by GC.
void markLiveAt(uint64_t Offset) {
if (this->Flags & llvm::ELF::SHF_ALLOC)
LiveOffsets.insert(Offset);
}
// Translate an offset in the input section to an offset
// in the output section.
uint64_t getOffset(uint64_t Offset) const;
// Translate an offset in the input section to an offset in the parent
// MergeSyntheticSection.
uint64_t getParentOffset(uint64_t Offset) const;
// Splittable sections are handled as a sequence of data
// rather than a single large blob of data.
std::vector<SectionPiece> Pieces;
llvm::DenseMap<uint32_t, uint32_t> OffsetMap;
// Returns I'th piece's data. This function is very hot when
// string merging is enabled, so we want to inline.
@ -249,18 +261,15 @@ class MergeInputSection : public InputSectionBase {
// Returns the SectionPiece at a given input section offset.
SectionPiece *getSectionPiece(uint64_t Offset);
const SectionPiece *getSectionPiece(uint64_t Offset) const;
const SectionPiece *getSectionPiece(uint64_t Offset) const {
return const_cast<MergeInputSection *>(this)->getSectionPiece(Offset);
}
SyntheticSection *getParent() const;
private:
void splitStrings(ArrayRef<uint8_t> A, size_t Size);
void splitNonStrings(ArrayRef<uint8_t> A, size_t Size);
mutable llvm::DenseMap<uint32_t, uint32_t> OffsetMap;
mutable llvm::once_flag InitOffsetMap;
llvm::DenseSet<uint64_t> LiveOffsets;
};
struct EhSectionPiece {
@ -310,6 +319,8 @@ class InputSection : public InputSectionBase {
// beginning of the output section.
template <class ELFT> void writeTo(uint8_t *Buf);
uint64_t getOffset(uint64_t Offset) const { return OutSecOff + Offset; }
OutputSection *getParent() const;
// This variable has two usages. Initially, it represents an index in the
@ -320,7 +331,7 @@ class InputSection : public InputSectionBase {
static bool classof(const SectionBase *S);
InputSectionBase *getRelocatedSection();
InputSectionBase *getRelocatedSection() const;
template <class ELFT, class RelTy>
void relocateNonAlloc(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
@ -342,10 +353,6 @@ class InputSection : public InputSectionBase {
// The list of all input sections.
extern std::vector<InputSectionBase *> InputSections;
// Builds section order for handling --symbol-ordering-file.
llvm::DenseMap<SectionBase *, int> buildSectionOrder();
} // namespace elf
std::string toString(const elf::InputSectionBase *);

View File

@ -20,6 +20,8 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/Caching.h"
#include "llvm/LTO/Config.h"
@ -29,7 +31,6 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstddef>
#include <memory>
@ -44,70 +45,92 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
// This is for use when debugging LTO.
static void saveBuffer(StringRef Buffer, const Twine &Path) {
// Creates an empty file to store a list of object files for final
// linking of distributed ThinLTO.
static std::unique_ptr<raw_fd_ostream> openFile(StringRef File) {
std::error_code EC;
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
if (EC)
error("cannot create " + Path + ": " + EC.message());
OS << Buffer;
auto Ret =
llvm::make_unique<raw_fd_ostream>(File, EC, sys::fs::OpenFlags::F_None);
if (EC) {
error("cannot open " + File + ": " + EC.message());
return nullptr;
}
return Ret;
}
static void diagnosticHandler(const DiagnosticInfo &DI) {
SmallString<128> ErrStorage;
raw_svector_ostream OS(ErrStorage);
DiagnosticPrinterRawOStream DP(OS);
DI.print(DP);
warn(ErrStorage);
static std::string getThinLTOOutputFile(StringRef ModulePath) {
return lto::getThinLTOOutputFile(ModulePath,
Config->ThinLTOPrefixReplace.first,
Config->ThinLTOPrefixReplace.second);
}
static void checkError(Error E) {
handleAllErrors(std::move(E),
[&](ErrorInfoBase &EIB) { error(EIB.message()); });
}
static std::unique_ptr<lto::LTO> createLTO() {
lto::Config Conf;
static lto::Config createConfig() {
lto::Config C;
// LLD supports the new relocations.
Conf.Options = InitTargetOptionsFromCodeGenFlags();
Conf.Options.RelaxELFRelocations = true;
C.Options = InitTargetOptionsFromCodeGenFlags();
C.Options.RelaxELFRelocations = true;
// Always emit a section per function/datum with LTO.
Conf.Options.FunctionSections = true;
Conf.Options.DataSections = true;
C.Options.FunctionSections = true;
C.Options.DataSections = true;
if (Config->Relocatable)
Conf.RelocModel = None;
C.RelocModel = None;
else if (Config->Pic)
Conf.RelocModel = Reloc::PIC_;
C.RelocModel = Reloc::PIC_;
else
Conf.RelocModel = Reloc::Static;
Conf.CodeModel = GetCodeModelFromCMModel();
Conf.DisableVerify = Config->DisableVerify;
Conf.DiagHandler = diagnosticHandler;
Conf.OptLevel = Config->LTOO;
C.RelocModel = Reloc::Static;
C.CodeModel = GetCodeModelFromCMModel();
C.DisableVerify = Config->DisableVerify;
C.DiagHandler = diagnosticHandler;
C.OptLevel = Config->LTOO;
C.CPU = GetCPUStr();
// Set up a custom pipeline if we've been asked to.
Conf.OptPipeline = Config->LTONewPmPasses;
Conf.AAPipeline = Config->LTOAAPipeline;
C.OptPipeline = Config->LTONewPmPasses;
C.AAPipeline = Config->LTOAAPipeline;
// Set up optimization remarks if we've been asked to.
Conf.RemarksFilename = Config->OptRemarksFilename;
Conf.RemarksWithHotness = Config->OptRemarksWithHotness;
C.RemarksFilename = Config->OptRemarksFilename;
C.RemarksWithHotness = Config->OptRemarksWithHotness;
C.SampleProfile = Config->LTOSampleProfile;
C.UseNewPM = Config->LTONewPassManager;
C.DebugPassManager = Config->LTODebugPassManager;
C.DwoDir = Config->DwoDir;
if (Config->SaveTemps)
checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
/*UseInputModulePath*/ true));
lto::ThinBackend Backend;
if (Config->ThinLTOJobs != -1u)
Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
return llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
Config->LTOPartitions);
checkError(C.addSaveTemps(Config->OutputFile.str() + ".",
/*UseInputModulePath*/ true));
return C;
}
BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {
BitcodeCompiler::BitcodeCompiler() {
// Initialize LTOObj.
lto::ThinBackend Backend;
if (Config->ThinLTOIndexOnly) {
StringRef Path = Config->ThinLTOIndexOnlyArg;
if (!Path.empty())
IndexFile = openFile(Path);
auto OnIndexWrite = [&](const std::string &Identifier) {
ObjectToIndexFileState[Identifier] = true;
};
Backend = lto::createWriteIndexesThinBackend(
Config->ThinLTOPrefixReplace.first, Config->ThinLTOPrefixReplace.second,
Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite);
} else if (Config->ThinLTOJobs != -1U) {
Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
}
LTOObj = llvm::make_unique<lto::LTO>(createConfig(), Backend,
Config->LTOPartitions);
// Initialize UsedStartStop.
for (Symbol *Sym : Symtab->getSymbols()) {
StringRef Name = Sym->getName();
for (StringRef Prefix : {"__start_", "__stop_"})
@ -125,20 +148,20 @@ static void undefine(Symbol *S) {
void BitcodeCompiler::add(BitcodeFile &F) {
lto::InputFile &Obj = *F.Obj;
unsigned SymNum = 0;
std::vector<Symbol *> Syms = F.getSymbols();
bool IsExec = !Config->Shared && !Config->Relocatable;
if (Config->ThinLTOIndexOnly)
ObjectToIndexFileState.insert({Obj.getName(), false});
ArrayRef<Symbol *> Syms = F.getSymbols();
ArrayRef<lto::InputFile::Symbol> ObjSyms = Obj.symbols();
std::vector<lto::SymbolResolution> Resols(Syms.size());
DenseSet<StringRef> ScriptSymbols;
for (BaseCommand *Base : Script->SectionCommands)
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base))
ScriptSymbols.insert(Cmd->Name);
// Provide a resolution to the LTO API for each symbol.
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
Symbol *Sym = Syms[SymNum];
lto::SymbolResolution &R = Resols[SymNum];
++SymNum;
for (size_t I = 0, E = Syms.size(); I != E; ++I) {
Symbol *Sym = Syms[I];
const lto::InputFile::Symbol &ObjSym = ObjSyms[I];
lto::SymbolResolution &R = Resols[I];
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
// reports two symbols for module ASM defined. Without this check, lld
@ -156,25 +179,46 @@ void BitcodeCompiler::add(BitcodeFile &F) {
R.VisibleToRegularObj = Config->Relocatable || Sym->IsUsedInRegularObj ||
(R.Prevailing && Sym->includeInDynsym()) ||
UsedStartStop.count(ObjSym.getSectionName());
const auto *DR = dyn_cast<Defined>(Sym);
R.FinalDefinitionInLinkageUnit =
(IsExec || Sym->Visibility != STV_DEFAULT) && DR &&
// Skip absolute symbols from ELF objects, otherwise PC-rel relocations
// will be generated by for them, triggering linker errors.
// Symbol section is always null for bitcode symbols, hence the check
// for isElf(). Skip linker script defined symbols as well: they have
// no File defined.
!(DR->Section == nullptr && (!Sym->File || Sym->File->isElf()));
if (R.Prevailing)
undefine(Sym);
// We tell LTO to not apply interprocedural optimization for following
// symbols because otherwise LTO would inline them while their values are
// still not final:
// 1) Aliased (with --defsym) or wrapped (with --wrap) symbols.
// 2) Symbols redefined in linker script.
R.LinkerRedefined = !Sym->CanInline || ScriptSymbols.count(Sym->getName());
// We tell LTO to not apply interprocedural optimization for wrapped
// (with --wrap) symbols because otherwise LTO would inline them while
// their values are still not final.
R.LinkerRedefined = !Sym->CanInline;
}
checkError(LTOObj->add(std::move(F.Obj), Resols));
}
static void createEmptyIndex(StringRef ModulePath) {
std::string Path = replaceThinLTOSuffix(getThinLTOOutputFile(ModulePath));
std::unique_ptr<raw_fd_ostream> OS = openFile(Path + ".thinlto.bc");
if (!OS)
return;
ModuleSummaryIndex M(/*HaveGVs*/ false);
M.setSkipModuleByDistributedBackend();
WriteIndexToFile(M, *OS);
if (Config->ThinLTOEmitImportsFiles)
openFile(Path + ".imports");
}
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting ObjectFile(s).
std::vector<InputFile *> BitcodeCompiler::compile() {
std::vector<InputFile *> Ret;
unsigned MaxTasks = LTOObj->getMaxTasks();
Buff.resize(MaxTasks);
Buf.resize(MaxTasks);
Files.resize(MaxTasks);
// The --thinlto-cache-dir option specifies the path to a directory in which
@ -184,35 +228,67 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
if (!Config->ThinLTOCacheDir.empty())
Cache = check(
lto::localCache(Config->ThinLTOCacheDir,
[&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
StringRef Path) { Files[Task] = std::move(MB); }));
[&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
Files[Task] = std::move(MB);
}));
checkError(LTOObj->run(
[&](size_t Task) {
return llvm::make_unique<lto::NativeObjectStream>(
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
llvm::make_unique<raw_svector_ostream>(Buf[Task]));
},
Cache));
// Emit empty index files for non-indexed files
if (Config->ThinLTOIndexOnly) {
for (auto &Identifier : ObjectToIndexFileState)
if (!Identifier.getValue()) {
std::string Path = getThinLTOOutputFile(Identifier.getKey());
openFile(Path + ".thinlto.bc");
if (Config->ThinLTOEmitImportsFiles)
openFile(Path + ".imports");
}
}
// If LazyObjFile has not been added to link, emit empty index files.
// This is needed because this is what GNU gold plugin does and we have a
// distributed build system that depends on that behavior.
if (Config->ThinLTOIndexOnly) {
for (LazyObjFile *F : LazyObjFiles)
if (!F->AddedToLink && isBitcode(F->MB))
createEmptyIndex(F->getName());
if (!Config->LTOObjPath.empty())
saveBuffer(Buf[0], Config->LTOObjPath);
// ThinLTO with index only option is required to generate only the index
// files. After that, we exit from linker and ThinLTO backend runs in a
// distributed environment.
if (IndexFile)
IndexFile->close();
return {};
}
if (!Config->ThinLTOCacheDir.empty())
pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy);
std::vector<InputFile *> Ret;
for (unsigned I = 0; I != MaxTasks; ++I) {
if (Buff[I].empty())
if (Buf[I].empty())
continue;
if (Config->SaveTemps) {
if (I == 0)
saveBuffer(Buff[I], Config->OutputFile + ".lto.o");
saveBuffer(Buf[I], Config->OutputFile + ".lto.o");
else
saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o");
saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.o");
}
InputFile *Obj = createObjectFile(MemoryBufferRef(Buff[I], "lto.tmp"));
InputFile *Obj = createObjectFile(MemoryBufferRef(Buf[I], "lto.tmp"));
Ret.push_back(Obj);
}
for (std::unique_ptr<MemoryBuffer> &File : Files)
if (File)
Ret.push_back(createObjectFile(*File));
return Ret;
}

View File

@ -24,6 +24,7 @@
#include "lld/Common/LLVM.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <vector>
@ -38,6 +39,7 @@ namespace elf {
class BitcodeFile;
class InputFile;
class LazyObjFile;
class BitcodeCompiler {
public:
@ -49,9 +51,11 @@ class BitcodeCompiler {
private:
std::unique_ptr<llvm::lto::LTO> LTOObj;
std::vector<SmallString<0>> Buff;
std::vector<SmallString<0>> Buf;
std::vector<std::unique_ptr<MemoryBuffer>> Files;
llvm::DenseSet<StringRef> UsedStartStop;
std::unique_ptr<llvm::raw_fd_ostream> IndexFile;
llvm::StringMap<bool> ObjectToIndexFileState;
};
} // namespace elf
} // namespace lld

View File

@ -15,13 +15,13 @@
#include "Config.h"
#include "InputSection.h"
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
@ -74,7 +74,7 @@ uint64_t ExprValue::getSectionOffset() const {
// If the alignment is trivial, we don't have to compute the full
// value to know the offset. This allows this function to succeed in
// cases where the output section is not yet known.
if (Alignment == 1)
if (Alignment == 1 && (!Sec || !Sec->getOutputSection()))
return Val;
return getValue() - getSecAddr();
}
@ -102,28 +102,67 @@ OutputSection *LinkerScript::getOrCreateOutputSection(StringRef Name) {
return CmdRef;
}
// Expands the memory region by the specified size.
static void expandMemoryRegion(MemoryRegion *MemRegion, uint64_t Size,
StringRef RegionName, StringRef SecName) {
MemRegion->CurPos += Size;
uint64_t NewSize = MemRegion->CurPos - MemRegion->Origin;
if (NewSize > MemRegion->Length)
error("section '" + SecName + "' will not fit in region '" + RegionName +
"': overflowed by " + Twine(NewSize - MemRegion->Length) + " bytes");
}
void LinkerScript::expandMemoryRegions(uint64_t Size) {
if (Ctx->MemRegion)
expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name,
Ctx->OutSec->Name);
if (Ctx->LMARegion)
expandMemoryRegion(Ctx->LMARegion, Size, Ctx->LMARegion->Name,
Ctx->OutSec->Name);
}
void LinkerScript::expandOutputSection(uint64_t Size) {
Ctx->OutSec->Size += Size;
expandMemoryRegions(Size);
}
void LinkerScript::setDot(Expr E, const Twine &Loc, bool InSec) {
uint64_t Val = E().getValue();
if (Val < Dot && InSec)
error(Loc + ": unable to move location counter backward for: " +
Ctx->OutSec->Name);
Dot = Val;
// Update to location counter means update to section size.
if (InSec)
Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr;
expandOutputSection(Val - Dot);
else
expandMemoryRegions(Val - Dot);
Dot = Val;
}
// Used for handling linker symbol assignments, for both finalizing
// their values and doing early declarations. Returns true if symbol
// should be defined from linker script.
static bool shouldDefineSym(SymbolAssignment *Cmd) {
if (Cmd->Name == ".")
return false;
if (!Cmd->Provide)
return true;
// If a symbol was in PROVIDE(), we need to define it only
// when it is a referenced undefined symbol.
Symbol *B = Symtab->find(Cmd->Name);
if (B && !B->isDefined())
return true;
return false;
}
// This function is called from processSectionCommands,
// while we are fixing the output section layout.
void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
if (Cmd->Name == ".")
return;
// If a symbol was in PROVIDE(), we need to define it only when
// it is a referenced undefined symbol.
Symbol *B = Symtab->find(Cmd->Name);
if (Cmd->Provide && (!B || B->isDefined()))
if (!shouldDefineSym(Cmd))
return;
// Define a symbol.
@ -153,6 +192,76 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
Cmd->Sym = cast<Defined>(Sym);
}
// This function is called from LinkerScript::declareSymbols.
// It creates a placeholder symbol if needed.
static void declareSymbol(SymbolAssignment *Cmd) {
if (!shouldDefineSym(Cmd))
return;
// We can't calculate final value right now.
Symbol *Sym;
uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, /*Type*/ 0, Visibility,
/*CanOmitFromDynSym*/ false,
/*File*/ nullptr);
replaceSymbol<Defined>(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility,
STT_NOTYPE, 0, 0, nullptr);
Cmd->Sym = cast<Defined>(Sym);
Cmd->Provide = false;
}
// This method is used to handle INSERT AFTER statement. Here we rebuild
// the list of script commands to mix sections inserted into.
void LinkerScript::processInsertCommands() {
std::vector<BaseCommand *> V;
auto Insert = [&](std::vector<BaseCommand *> &From) {
V.insert(V.end(), From.begin(), From.end());
From.clear();
};
for (BaseCommand *Base : SectionCommands) {
if (auto *OS = dyn_cast<OutputSection>(Base)) {
Insert(InsertBeforeCommands[OS->Name]);
V.push_back(Base);
Insert(InsertAfterCommands[OS->Name]);
continue;
}
V.push_back(Base);
}
for (auto &Cmds : {InsertBeforeCommands, InsertAfterCommands})
for (const std::pair<StringRef, std::vector<BaseCommand *>> &P : Cmds)
if (!P.second.empty())
error("unable to INSERT AFTER/BEFORE " + P.first +
": section not defined");
SectionCommands = std::move(V);
}
// Symbols defined in script should not be inlined by LTO. At the same time
// we don't know their final values until late stages of link. Here we scan
// over symbol assignment commands and create placeholder symbols if needed.
void LinkerScript::declareSymbols() {
assert(!Ctx);
for (BaseCommand *Base : SectionCommands) {
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
declareSymbol(Cmd);
continue;
}
// If the output section directive has constraints,
// we can't say for sure if it is going to be included or not.
// Skip such sections for now. Improve the checks if we ever
// need symbols from that sections to be declared early.
auto *Sec = cast<OutputSection>(Base);
if (Sec->Constraint != ConstraintKind::NoConstraint)
continue;
for (BaseCommand *Base2 : Sec->SectionCommands)
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base2))
declareSymbol(Cmd);
}
}
// This function is called from assignAddresses, while we are
// fixing the output section addresses. This function is supposed
// to set the final value for a given symbol assignment.
@ -249,23 +358,11 @@ static void sortSections(MutableArrayRef<InputSection *> Vec,
// --sort-section is handled as an inner SORT command.
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
// 4. If no SORT command is given, sort according to --sort-section.
// 5. If no SORT commands are given and --sort-section is not specified,
// apply sorting provided by --symbol-ordering-file if any exist.
static void sortInputSections(
MutableArrayRef<InputSection *> Vec, const SectionPattern &Pat,
const DenseMap<SectionBase *, int> &Order) {
static void sortInputSections(MutableArrayRef<InputSection *> Vec,
const SectionPattern &Pat) {
if (Pat.SortOuter == SortSectionPolicy::None)
return;
if (Pat.SortOuter == SortSectionPolicy::Default &&
Config->SortSection == SortSectionPolicy::Default) {
// If -symbol-ordering-file was given, sort accordingly.
// Usually, Order is empty.
if (!Order.empty())
sortByOrder(Vec, [&](InputSectionBase *S) { return Order.lookup(S); });
return;
}
if (Pat.SortInner == SortSectionPolicy::Default)
sortSections(Vec, Config->SortSection);
else
@ -275,8 +372,7 @@ static void sortInputSections(
// Compute and remember which sections the InputSectionDescription matches.
std::vector<InputSection *>
LinkerScript::computeInputSections(const InputSectionDescription *Cmd,
const DenseMap<SectionBase *, int> &Order) {
LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
std::vector<InputSection *> Ret;
// Collects all sections that satisfy constraints of Cmd.
@ -290,8 +386,11 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd,
// For -emit-relocs we have to ignore entries like
// .rela.dyn : { *(.rela.data) }
// which are common because they are in the default bfd script.
if (Sec->Type == SHT_REL || Sec->Type == SHT_RELA)
continue;
// We do not ignore SHT_REL[A] linker-synthesized sections here because
// want to support scripts that do custom layout for them.
if (auto *IS = dyn_cast<InputSection>(Sec))
if (IS->getRelocatedSection())
continue;
std::string Filename = getFilename(Sec->File);
if (!Cmd->FilePat.match(Filename) ||
@ -307,7 +406,7 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd,
}
sortInputSections(MutableArrayRef<InputSection *>(Ret).slice(SizeBefore),
Pat, Order);
Pat);
}
return Ret;
}
@ -315,22 +414,31 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd,
void LinkerScript::discard(ArrayRef<InputSection *> V) {
for (InputSection *S : V) {
if (S == InX::ShStrTab || S == InX::Dynamic || S == InX::DynSymTab ||
S == InX::DynStrTab)
S == InX::DynStrTab || S == InX::RelaPlt || S == InX::RelaDyn ||
S == InX::RelrDyn)
error("discarding " + S->Name + " section is not allowed");
// You can discard .hash and .gnu.hash sections by linker scripts. Since
// they are synthesized sections, we need to handle them differently than
// other regular sections.
if (S == InX::GnuHashTab)
InX::GnuHashTab = nullptr;
if (S == InX::HashTab)
InX::HashTab = nullptr;
S->Assigned = false;
S->Live = false;
discard(S->DependentSections);
}
}
std::vector<InputSection *> LinkerScript::createInputSectionList(
OutputSection &OutCmd, const DenseMap<SectionBase *, int> &Order) {
std::vector<InputSection *>
LinkerScript::createInputSectionList(OutputSection &OutCmd) {
std::vector<InputSection *> Ret;
for (BaseCommand *Base : OutCmd.SectionCommands) {
if (auto *Cmd = dyn_cast<InputSectionDescription>(Base)) {
Cmd->Sections = computeInputSections(Cmd, Order);
Cmd->Sections = computeInputSections(Cmd);
Ret.insert(Ret.end(), Cmd->Sections.begin(), Cmd->Sections.end());
}
}
@ -359,7 +467,6 @@ void LinkerScript::processSectionCommands() {
Ctx->OutSec = Aether;
size_t I = 0;
DenseMap<SectionBase *, int> Order = buildSectionOrder();
// Add input sections to output sections.
for (BaseCommand *Base : SectionCommands) {
// Handle symbol assignments outside of any output section.
@ -369,12 +476,13 @@ void LinkerScript::processSectionCommands() {
}
if (auto *Sec = dyn_cast<OutputSection>(Base)) {
std::vector<InputSection *> V = createInputSectionList(*Sec, Order);
std::vector<InputSection *> V = createInputSectionList(*Sec);
// The output section name `/DISCARD/' is special.
// Any input section assigned to it is discarded.
if (Sec->Name == "/DISCARD/") {
discard(V);
Sec->SectionCommands.clear();
continue;
}
@ -414,6 +522,8 @@ void LinkerScript::processSectionCommands() {
Sec->SectionIndex = I++;
if (Sec->Noload)
Sec->Type = SHT_NOBITS;
if (Sec->NonAlloc)
Sec->Flags &= ~(uint64_t)SHF_ALLOC;
}
}
Ctx = nullptr;
@ -484,7 +594,7 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
// ignored. We should not have two output .text sections just because one was
// in a group and another was not for example.
//
// It also seems that that wording was a late addition and didn't get the
// It also seems that wording was a late addition and didn't get the
// necessary scrutiny.
//
// Merging sections with different flags is expected by some users. One
@ -529,11 +639,11 @@ static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
void LinkerScript::addOrphanSections() {
unsigned End = SectionCommands.size();
StringMap<OutputSection *> Map;
std::vector<OutputSection *> V;
for (InputSectionBase *S : InputSections) {
auto Add = [&](InputSectionBase *S) {
if (!S->Live || S->Parent)
continue;
return;
StringRef Name = getOutputSectionName(S);
@ -545,12 +655,24 @@ void LinkerScript::addOrphanSections() {
if (OutputSection *Sec =
findByName(makeArrayRef(SectionCommands).slice(0, End), Name)) {
Sec->addSection(cast<InputSection>(S));
continue;
return;
}
if (OutputSection *OS = addInputSec(Map, S, Name))
V.push_back(OS);
assert(S->getOutputSection()->SectionIndex == INT_MAX);
assert(S->getOutputSection()->SectionIndex == UINT32_MAX);
};
// For futher --emit-reloc handling code we need target output section
// to be created before we create relocation output section, so we want
// to create target sections first. We do not want priority handling
// for synthetic sections because them are special.
for (InputSectionBase *IS : InputSections) {
if (auto *Sec = dyn_cast<InputSection>(IS))
if (InputSectionBase *Rel = Sec->getRelocatedSection())
if (auto *RelIS = dyn_cast_or_null<InputSectionBase>(Rel->Parent))
Add(RelIS);
Add(IS);
}
// If no SECTIONS command was given, we should insert sections commands
@ -585,36 +707,15 @@ void LinkerScript::output(InputSection *S) {
// Update output section size after adding each section. This is so that
// SIZEOF works correctly in the case below:
// .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) }
Ctx->OutSec->Size = Pos - Ctx->OutSec->Addr;
// If there is a memory region associated with this input section, then
// place the section in that region and update the region index.
if (Ctx->MemRegion) {
uint64_t &CurOffset = Ctx->MemRegionOffset[Ctx->MemRegion];
CurOffset += Pos - Before;
uint64_t CurSize = CurOffset - Ctx->MemRegion->Origin;
if (CurSize > Ctx->MemRegion->Length) {
uint64_t OverflowAmt = CurSize - Ctx->MemRegion->Length;
error("section '" + Ctx->OutSec->Name + "' will not fit in region '" +
Ctx->MemRegion->Name + "': overflowed by " + Twine(OverflowAmt) +
" bytes");
}
}
expandOutputSection(Pos - Before);
}
void LinkerScript::switchTo(OutputSection *Sec) {
if (Ctx->OutSec == Sec)
return;
Ctx->OutSec = Sec;
Ctx->OutSec->Addr = advance(0, Ctx->OutSec->Alignment);
// If neither AT nor AT> is specified for an allocatable section, the linker
// will set the LMA such that the difference between VMA and LMA for the
// section is the same as the preceding output section in the same region
// https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
if (Ctx->LMAOffset)
Ctx->OutSec->LMAOffset = Ctx->LMAOffset();
uint64_t Before = advance(0, 1);
Ctx->OutSec->Addr = advance(0, Ctx->OutSec->Alignment);
expandMemoryRegions(Ctx->OutSec->Addr - Before);
}
// This function searches for a memory region to place the given output
@ -624,9 +725,8 @@ MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) {
// If a memory region name was specified in the output section command,
// then try to find that region first.
if (!Sec->MemoryRegionName.empty()) {
auto It = MemoryRegions.find(Sec->MemoryRegionName);
if (It != MemoryRegions.end())
return It->second;
if (MemoryRegion *M = MemoryRegions.lookup(Sec->MemoryRegionName))
return M;
error("memory region '" + Sec->MemoryRegionName + "' not declared");
return nullptr;
}
@ -659,24 +759,27 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
setDot(Sec->AddrExpr, Sec->Location, false);
Ctx->MemRegion = Sec->MemRegion;
Ctx->LMARegion = Sec->LMARegion;
if (Ctx->MemRegion)
Dot = Ctx->MemRegionOffset[Ctx->MemRegion];
if (Sec->LMAExpr) {
uint64_t D = Dot;
Ctx->LMAOffset = [=] { return Sec->LMAExpr().getValue() - D; };
}
Dot = Ctx->MemRegion->CurPos;
switchTo(Sec);
// We do not support custom layout for compressed debug sectons.
// At this point we already know their size and have compressed content.
if (Ctx->OutSec->Flags & SHF_COMPRESSED)
return;
if (Sec->LMAExpr)
Ctx->LMAOffset = Sec->LMAExpr().getValue() - Dot;
// The Size previously denoted how many InputSections had been added to this
// section, and was used for sorting SHF_LINK_ORDER sections. Reset it to
// compute the actual size value.
if (MemoryRegion *MR = Sec->LMARegion)
Ctx->LMAOffset = MR->CurPos - Dot;
// If neither AT nor AT> is specified for an allocatable section, the linker
// will set the LMA such that the difference between VMA and LMA for the
// section is the same as the preceding output section in the same region
// https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
if (PhdrEntry *L = Ctx->OutSec->PtLoad)
L->LMAOffset = Ctx->LMAOffset;
// We can call this method multiple times during the creation of
// thunks and want to start over calculation each time.
Sec->Size = 0;
// We visited SectionsCommands from processSectionCommands to
@ -685,7 +788,9 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
for (BaseCommand *Base : Sec->SectionCommands) {
// This handles the assignments to symbol or to the dot.
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
Cmd->Addr = Dot;
assignSymbol(Cmd, true);
Cmd->Size = Dot - Cmd->Addr;
continue;
}
@ -693,15 +798,7 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
if (auto *Cmd = dyn_cast<ByteCommand>(Base)) {
Cmd->Offset = Dot - Ctx->OutSec->Addr;
Dot += Cmd->Size;
if (Ctx->MemRegion)
Ctx->MemRegionOffset[Ctx->MemRegion] += Cmd->Size;
Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr;
continue;
}
// Handle ASSERT().
if (auto *Cmd = dyn_cast<AssertCommand>(Base)) {
Cmd->Expression();
expandOutputSection(Cmd->Size);
continue;
}
@ -726,24 +823,28 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
}
}
void LinkerScript::removeEmptyCommands() {
// It is common practice to use very generic linker scripts. So for any
// given run some of the output sections in the script will be empty.
// We could create corresponding empty output sections, but that would
// clutter the output.
// We instead remove trivially empty sections. The bfd linker seems even
// more aggressive at removing them.
llvm::erase_if(SectionCommands, [&](BaseCommand *Base) {
if (auto *Sec = dyn_cast<OutputSection>(Base))
return !Sec->Live;
static bool isDiscardable(OutputSection &Sec) {
// We do not remove empty sections that are explicitly
// assigned to any segment.
if (!Sec.Phdrs.empty())
return false;
});
}
static bool isAllSectionDescription(const OutputSection &Cmd) {
for (BaseCommand *Base : Cmd.SectionCommands)
// We do not want to remove sections that reference symbols in address and
// other expressions. We add script symbols as undefined, and want to ensure
// all of them are defined in the output, hence have to keep them.
if (Sec.ExpressionsUseSymbols)
return false;
for (BaseCommand *Base : Sec.SectionCommands) {
if (auto Cmd = dyn_cast<SymbolAssignment>(Base))
// Don't create empty output sections just for unreferenced PROVIDE
// symbols.
if (Cmd->Name != "." && !Cmd->Sym)
continue;
if (!isa<InputSectionDescription>(*Base))
return false;
}
return true;
}
@ -759,7 +860,7 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// Given that we want to create the section, we have to worry what impact
// it will have on the link. For example, if we just create a section with
// 0 for flags, it would change which PT_LOADs are created.
// We could remember that that particular section is dummy and ignore it in
// We could remember that particular section is dummy and ignore it in
// other parts of the linker, but unfortunately there are quite a few places
// that would need to change:
// * The program header creation.
@ -770,34 +871,54 @@ void LinkerScript::adjustSectionsBeforeSorting() {
// the previous sections. Only a few flags are needed to keep the impact low.
uint64_t Flags = SHF_ALLOC;
for (BaseCommand *Cmd : SectionCommands) {
for (BaseCommand *&Cmd : SectionCommands) {
auto *Sec = dyn_cast<OutputSection>(Cmd);
if (!Sec)
continue;
if (Sec->Live) {
Flags = Sec->Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR);
continue;
// Handle align (e.g. ".foo : ALIGN(16) { ... }").
if (Sec->AlignExpr)
Sec->Alignment =
std::max<uint32_t>(Sec->Alignment, Sec->AlignExpr().getValue());
// A live output section means that some input section was added to it. It
// might have been removed (if it was empty synthetic section), but we at
// least know the flags.
if (Sec->Live)
Flags = Sec->Flags;
// We do not want to keep any special flags for output section
// in case it is empty.
bool IsEmpty = getInputSections(Sec).empty();
if (IsEmpty)
Sec->Flags = Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR);
if (IsEmpty && isDiscardable(*Sec)) {
Sec->Live = false;
Cmd = nullptr;
}
if (isAllSectionDescription(*Sec))
continue;
Sec->Live = true;
Sec->Flags = Flags;
}
// It is common practice to use very generic linker scripts. So for any
// given run some of the output sections in the script will be empty.
// We could create corresponding empty output sections, but that would
// clutter the output.
// We instead remove trivially empty sections. The bfd linker seems even
// more aggressive at removing them.
llvm::erase_if(SectionCommands, [&](BaseCommand *Base) { return !Base; });
}
void LinkerScript::adjustSectionsAfterSorting() {
// Try and find an appropriate memory region to assign offsets in.
for (BaseCommand *Base : SectionCommands) {
if (auto *Sec = dyn_cast<OutputSection>(Base)) {
if (!Sec->Live)
continue;
if (!Sec->LMARegionName.empty()) {
if (MemoryRegion *M = MemoryRegions.lookup(Sec->LMARegionName))
Sec->LMARegion = M;
else
error("memory region '" + Sec->LMARegionName + "' not declared");
}
Sec->MemRegion = findMemoryRegion(Sec);
// Handle align (e.g. ".foo : ALIGN(16) { ... }").
if (Sec->AlignExpr)
Sec->Alignment =
std::max<uint32_t>(Sec->Alignment, Sec->AlignExpr().getValue());
}
}
@ -808,9 +929,9 @@ void LinkerScript::adjustSectionsAfterSorting() {
// PHDRS { seg PT_LOAD; }
// SECTIONS { .aaa : { *(.aaa) } }
std::vector<StringRef> DefPhdrs;
auto FirstPtLoad =
std::find_if(PhdrsCommands.begin(), PhdrsCommands.end(),
[](const PhdrsCommand &Cmd) { return Cmd.Type == PT_LOAD; });
auto FirstPtLoad = llvm::find_if(PhdrsCommands, [](const PhdrsCommand &Cmd) {
return Cmd.Type == PT_LOAD;
});
if (FirstPtLoad != PhdrsCommands.end())
DefPhdrs.push_back(FirstPtLoad->Name);
@ -839,6 +960,15 @@ static OutputSection *findFirstSection(PhdrEntry *Load) {
return nullptr;
}
static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) {
// If there is no SECTIONS or if the linkerscript is explicit about program
// headers, do our best to allocate them.
if (!Script->HasSectionsCommand || AllocateHeaders)
return 0;
// Otherwise only allocate program headers if that would not add a page.
return alignDown(Min, Config->MaxPageSize);
}
// Try to find an address for the file and program headers output sections,
// which were unconditionally added to the first PT_LOAD segment earlier.
//
@ -862,17 +992,22 @@ void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &Phdrs) {
return;
PhdrEntry *FirstPTLoad = *It;
bool HasExplicitHeaders =
llvm::any_of(PhdrsCommands, [](const PhdrsCommand &Cmd) {
return Cmd.HasPhdrs || Cmd.HasFilehdr;
});
uint64_t HeaderSize = getHeaderSize();
// When linker script with SECTIONS is being used, don't output headers
// unless there's a space for them.
uint64_t Base = HasSectionsCommand ? alignDown(Min, Config->MaxPageSize) : 0;
if (HeaderSize <= Min - Base || Script->hasPhdrsCommands()) {
if (HeaderSize <= Min - computeBase(Min, HasExplicitHeaders)) {
Min = alignDown(Min - HeaderSize, Config->MaxPageSize);
Out::ElfHeader->Addr = Min;
Out::ProgramHeaders->Addr = Min + Out::ElfHeader->Size;
return;
}
// Error if we were explicitly asked to allocate headers.
if (HasExplicitHeaders)
error("could not allocate headers");
Out::ElfHeader->PtLoad = nullptr;
Out::ProgramHeaders->PtLoad = nullptr;
FirstPTLoad->FirstSec = findFirstSection(FirstPTLoad);
@ -883,8 +1018,8 @@ void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &Phdrs) {
LinkerScript::AddressState::AddressState() {
for (auto &MRI : Script->MemoryRegions) {
const MemoryRegion *MR = MRI.second;
MemRegionOffset[MR] = MR->Origin;
MemoryRegion *MR = MRI.second;
MR->CurPos = MR->Origin;
}
}
@ -916,15 +1051,11 @@ void LinkerScript::assignAddresses() {
for (BaseCommand *Base : SectionCommands) {
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
Cmd->Addr = Dot;
assignSymbol(Cmd, false);
Cmd->Size = Dot - Cmd->Addr;
continue;
}
if (auto *Cmd = dyn_cast<AssertCommand>(Base)) {
Cmd->Expression();
continue;
}
assignOffsets(cast<OutputSection>(Base));
}
Ctx = nullptr;
@ -988,9 +1119,9 @@ ExprValue LinkerScript::getSymbolValue(StringRef Name, const Twine &Loc) {
if (Symbol *Sym = Symtab->find(Name)) {
if (auto *DS = dyn_cast<Defined>(Sym))
return {DS->Section, false, DS->Value, Loc};
if (auto *SS = dyn_cast<SharedSymbol>(Sym))
if (!ErrorOnMissingSection || SS->CopyRelSec)
return {SS->CopyRelSec, false, 0, Loc};
if (isa<SharedSymbol>(Sym))
if (!ErrorOnMissingSection)
return {nullptr, false, 0, Loc};
}
error(Loc + ": symbol not found: " + Name);

View File

@ -11,9 +11,9 @@
#define LLD_ELF_LINKER_SCRIPT_H
#include "Config.h"
#include "Strings.h"
#include "Writer.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
@ -75,7 +75,6 @@ enum SectionsCommandKind {
AssignmentKind, // . = expr or <sym> = expr
OutputSectionKind,
InputSectionKind,
AssertKind, // ASSERT(expr)
ByteKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
};
@ -106,6 +105,16 @@ struct SymbolAssignment : BaseCommand {
// Holds file name and line number for error reporting.
std::string Location;
// A string representation of this command. We use this for -Map.
std::string CommandString;
// Address of this assignment command.
unsigned Addr;
// Size of this assignment command. This is usually 0, but if
// you move '.' this may be greater than 0.
unsigned Size;
};
// Linker scripts allow additional constraints to be put on ouput sections.
@ -118,11 +127,17 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
// target memory. Instances of the struct are created by parsing the
// MEMORY command.
struct MemoryRegion {
MemoryRegion(StringRef Name, uint64_t Origin, uint64_t Length, uint32_t Flags,
uint32_t NegFlags)
: Name(Name), Origin(Origin), Length(Length), Flags(Flags),
NegFlags(NegFlags) {}
std::string Name;
uint64_t Origin;
uint64_t Length;
uint32_t Flags;
uint32_t NegFlags;
uint64_t CurPos = 0;
};
// This struct represents one section match pattern in SECTIONS() command.
@ -161,24 +176,23 @@ struct InputSectionDescription : BaseCommand {
std::vector<std::pair<ThunkSection *, uint32_t>> ThunkSections;
};
// Represents an ASSERT().
struct AssertCommand : BaseCommand {
AssertCommand(Expr E) : BaseCommand(AssertKind), Expression(E) {}
static bool classof(const BaseCommand *C) { return C->Kind == AssertKind; }
Expr Expression;
};
// Represents BYTE(), SHORT(), LONG(), or QUAD().
struct ByteCommand : BaseCommand {
ByteCommand(Expr E, unsigned Size)
: BaseCommand(ByteKind), Expression(E), Size(Size) {}
ByteCommand(Expr E, unsigned Size, std::string CommandString)
: BaseCommand(ByteKind), CommandString(CommandString), Expression(E),
Size(Size) {}
static bool classof(const BaseCommand *C) { return C->Kind == ByteKind; }
// Keeps string representing the command. Used for -Map" is perhaps better.
std::string CommandString;
Expr Expression;
// This is just an offset of this assignment command in the output section.
unsigned Offset;
// Size of this data command.
unsigned Size;
};
@ -200,8 +214,8 @@ class LinkerScript final {
uint64_t ThreadBssOffset = 0;
OutputSection *OutSec = nullptr;
MemoryRegion *MemRegion = nullptr;
llvm::DenseMap<const MemoryRegion *, uint64_t> MemRegionOffset;
std::function<uint64_t()> LMAOffset;
MemoryRegion *LMARegion = nullptr;
uint64_t LMAOffset = 0;
};
llvm::DenseMap<StringRef, OutputSection *> NameToOutputSection;
@ -209,14 +223,13 @@ class LinkerScript final {
void addSymbol(SymbolAssignment *Cmd);
void assignSymbol(SymbolAssignment *Cmd, bool InSec);
void setDot(Expr E, const Twine &Loc, bool InSec);
void expandOutputSection(uint64_t Size);
void expandMemoryRegions(uint64_t Size);
std::vector<InputSection *>
computeInputSections(const InputSectionDescription *,
const llvm::DenseMap<SectionBase *, int> &Order);
computeInputSections(const InputSectionDescription *);
std::vector<InputSection *>
createInputSectionList(OutputSection &Cmd,
const llvm::DenseMap<SectionBase *, int> &Order);
std::vector<InputSection *> createInputSectionList(OutputSection &Cmd);
std::vector<size_t> getPhdrIndices(OutputSection *Sec);
@ -251,7 +264,6 @@ class LinkerScript final {
ExprValue getSymbolValue(StringRef Name, const Twine &Loc);
void addOrphanSections();
void removeEmptyCommands();
void adjustSectionsBeforeSorting();
void adjustSectionsAfterSorting();
@ -262,6 +274,10 @@ class LinkerScript final {
void assignAddresses();
void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
void processSectionCommands();
void declareSymbols();
// Used to handle INSERT AFTER statements.
void processInsertCommands();
// SECTIONS command list.
std::vector<BaseCommand *> SectionCommands;
@ -281,6 +297,11 @@ class LinkerScript final {
// A list of symbols referenced by the script.
std::vector<llvm::StringRef> ReferencedSymbols;
// Used to implement INSERT [AFTER|BEFORE]. Contains commands that need
// to be inserted into SECTIONS commands list.
llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertAfterCommands;
llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertBeforeCommands;
};
extern LinkerScript *Script;

View File

@ -23,11 +23,13 @@
#include "InputFiles.h"
#include "LinkerScript.h"
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@ -36,57 +38,46 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
typedef DenseMap<const SectionBase *, SmallVector<Symbol *, 4>> SymbolMapTy;
typedef DenseMap<const SectionBase *, SmallVector<Defined *, 4>> SymbolMapTy;
static const std::string Indent8 = " "; // 8 spaces
static const std::string Indent16 = " "; // 16 spaces
// Print out the first three columns of a line.
static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
uint64_t Align) {
int W = Config->Is64 ? 16 : 8;
OS << format("%0*llx %0*llx %5lld ", W, Addr, W, Size, Align);
static void writeHeader(raw_ostream &OS, uint64_t VMA, uint64_t LMA,
uint64_t Size, uint64_t Align) {
if (Config->Is64)
OS << format("%16llx %16llx %8llx %5lld ", VMA, LMA, Size, Align);
else
OS << format("%8llx %8llx %8llx %5lld ", VMA, LMA, Size, Align);
}
static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
// Returns a list of all symbols that we want to print out.
static std::vector<Symbol *> getSymbols() {
std::vector<Symbol *> V;
for (InputFile *File : ObjectFiles) {
for (Symbol *B : File->getSymbols()) {
if (auto *SS = dyn_cast<SharedSymbol>(B))
if (SS->CopyRelSec || SS->NeedsPltAddr)
V.push_back(SS);
static std::vector<Defined *> getSymbols() {
std::vector<Defined *> V;
for (InputFile *File : ObjectFiles)
for (Symbol *B : File->getSymbols())
if (auto *DR = dyn_cast<Defined>(B))
if (DR->File == File && !DR->isSection() && DR->Section &&
DR->Section->Live)
if (!DR->isSection() && DR->Section && DR->Section->Live &&
(DR->File == File || DR->NeedsPltAddr || DR->Section->Bss))
V.push_back(DR);
}
}
return V;
}
// Returns a map from sections to their symbols.
static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> Syms) {
static SymbolMapTy getSectionSyms(ArrayRef<Defined *> Syms) {
SymbolMapTy Ret;
for (Symbol *S : Syms) {
if (auto *DR = dyn_cast<Defined>(S)) {
Ret[DR->Section].push_back(S);
continue;
}
SharedSymbol *SS = cast<SharedSymbol>(S);
if (SS->CopyRelSec)
Ret[SS->CopyRelSec].push_back(S);
else
Ret[InX::Plt].push_back(S);
}
for (Defined *DR : Syms)
Ret[DR->Section].push_back(DR);
// Sort symbols by address. We want to print out symbols in the
// order in the output file rather than the order they appeared
// in the input files.
for (auto &It : Ret) {
SmallVectorImpl<Symbol *> &V = It.second;
std::sort(V.begin(), V.end(),
[](Symbol *A, Symbol *B) { return A->getVA() < B->getVA(); });
SmallVectorImpl<Defined *> &V = It.second;
std::stable_sort(V.begin(), V.end(), [](Defined *A, Defined *B) {
return A->getVA() < B->getVA();
});
}
return Ret;
}
@ -95,12 +86,15 @@ static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> Syms) {
// Demangling symbols (which is what toString() does) is slow, so
// we do that in batch using parallel-for.
static DenseMap<Symbol *, std::string>
getSymbolStrings(ArrayRef<Symbol *> Syms) {
getSymbolStrings(ArrayRef<Defined *> Syms) {
std::vector<std::string> Str(Syms.size());
parallelForEachN(0, Syms.size(), [&](size_t I) {
raw_string_ostream OS(Str[I]);
writeHeader(OS, Syms[I]->getVA(), Syms[I]->getSize(), 0);
OS << indent(2) << toString(*Syms[I]);
OutputSection *OSec = Syms[I]->getOutputSection();
uint64_t VMA = Syms[I]->getVA();
uint64_t LMA = OSec ? OSec->getLMA() + VMA - OSec->getVA(0) : 0;
writeHeader(OS, VMA, LMA, Syms[I]->getSize(), 1);
OS << Indent16 << toString(*Syms[I]);
});
DenseMap<Symbol *, std::string> Ret;
@ -109,6 +103,44 @@ getSymbolStrings(ArrayRef<Symbol *> Syms) {
return Ret;
}
// Print .eh_frame contents. Since the section consists of EhSectionPieces,
// we need a specialized printer for that section.
//
// .eh_frame tend to contain a lot of section pieces that are contiguous
// both in input file and output file. Such pieces are squashed before
// being displayed to make output compact.
static void printEhFrame(raw_ostream &OS, OutputSection *OSec) {
std::vector<EhSectionPiece> Pieces;
auto Add = [&](const EhSectionPiece &P) {
// If P is adjacent to Last, squash the two.
if (!Pieces.empty()) {
EhSectionPiece &Last = Pieces.back();
if (Last.Sec == P.Sec && Last.InputOff + Last.Size == P.InputOff &&
Last.OutputOff + Last.Size == P.OutputOff) {
Last.Size += P.Size;
return;
}
}
Pieces.push_back(P);
};
// Gather section pieces.
for (const CieRecord *Rec : InX::EhFrame->getCieRecords()) {
Add(*Rec->Cie);
for (const EhSectionPiece *Fde : Rec->Fdes)
Add(*Fde);
}
// Print out section pieces.
for (EhSectionPiece &P : Pieces) {
writeHeader(OS, OSec->Addr + P.OutputOff, OSec->getLMA() + P.OutputOff,
P.Size, 1);
OS << Indent8 << toString(P.Sec->File) << ":(" << P.Sec->Name << "+0x"
<< Twine::utohexstr(P.InputOff) + ")\n";
}
}
void elf::writeMapFile() {
if (Config->MapFile.empty())
return;
@ -122,32 +154,109 @@ void elf::writeMapFile() {
}
// Collect symbol info that we want to print out.
std::vector<Symbol *> Syms = getSymbols();
std::vector<Defined *> Syms = getSymbols();
SymbolMapTy SectionSyms = getSectionSyms(Syms);
DenseMap<Symbol *, std::string> SymStr = getSymbolStrings(Syms);
// Print out the header line.
int W = Config->Is64 ? 16 : 8;
OS << left_justify("Address", W) << ' ' << left_justify("Size", W)
<< " Align Out In Symbol\n";
OS << right_justify("VMA", W) << ' ' << right_justify("LMA", W)
<< " Size Align Out In Symbol\n";
// Print out file contents.
for (OutputSection *OSec : OutputSections) {
writeHeader(OS, OSec->Addr, OSec->Size, OSec->Alignment);
for (BaseCommand *Base : Script->SectionCommands) {
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
if (Cmd->Provide && !Cmd->Sym)
continue;
//FIXME: calculate and print LMA.
writeHeader(OS, Cmd->Addr, 0, Cmd->Size, 1);
OS << Cmd->CommandString << '\n';
continue;
}
auto *OSec = cast<OutputSection>(Base);
writeHeader(OS, OSec->Addr, OSec->getLMA(), OSec->Size, OSec->Alignment);
OS << OSec->Name << '\n';
// Dump symbols for each input section.
for (BaseCommand *Base : OSec->SectionCommands) {
auto *ISD = dyn_cast<InputSectionDescription>(Base);
if (!ISD)
if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
for (InputSection *IS : ISD->Sections) {
if (IS == InX::EhFrame) {
printEhFrame(OS, OSec);
continue;
}
writeHeader(OS, IS->getVA(0), OSec->getLMA() + IS->getOffset(0),
IS->getSize(), IS->Alignment);
OS << Indent8 << toString(IS) << '\n';
for (Symbol *Sym : SectionSyms[IS])
OS << SymStr[Sym] << '\n';
}
continue;
}
if (auto *Cmd = dyn_cast<ByteCommand>(Base)) {
writeHeader(OS, OSec->Addr + Cmd->Offset, OSec->getLMA() + Cmd->Offset,
Cmd->Size, 1);
OS << Indent8 << Cmd->CommandString << '\n';
continue;
}
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
if (Cmd->Provide && !Cmd->Sym)
continue;
writeHeader(OS, Cmd->Addr, OSec->getLMA() + Cmd->Addr - OSec->getVA(0),
Cmd->Size, 1);
OS << Indent8 << Cmd->CommandString << '\n';
continue;
for (InputSection *IS : ISD->Sections) {
writeHeader(OS, OSec->Addr + IS->OutSecOff, IS->getSize(),
IS->Alignment);
OS << indent(1) << toString(IS) << '\n';
for (Symbol *Sym : SectionSyms[IS])
OS << SymStr[Sym] << '\n';
}
}
}
}
static void print(StringRef A, StringRef B) {
outs() << left_justify(A, 49) << " " << B << "\n";
}
// Output a cross reference table to stdout. This is for --cref.
//
// For each global symbol, we print out a file that defines the symbol
// followed by files that uses that symbol. Here is an example.
//
// strlen /lib/x86_64-linux-gnu/libc.so.6
// tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o
// lib/libLLVMSupport.a(PrettyStackTrace.cpp.o)
//
// In this case, strlen is defined by libc.so.6 and used by other two
// files.
void elf::writeCrossReferenceTable() {
if (!Config->Cref)
return;
// Collect symbols and files.
MapVector<Symbol *, SetVector<InputFile *>> Map;
for (InputFile *File : ObjectFiles) {
for (Symbol *Sym : File->getSymbols()) {
if (isa<SharedSymbol>(Sym))
Map[Sym].insert(File);
if (auto *D = dyn_cast<Defined>(Sym))
if (!D->isLocal() && (!D->Section || D->Section->Live))
Map[D].insert(File);
}
}
// Print out a header.
outs() << "Cross Reference Table\n\n";
print("Symbol", "File");
// Print out a table.
for (auto KV : Map) {
Symbol *Sym = KV.first;
SetVector<InputFile *> &Files = KV.second;
print(toString(*Sym), toString(Sym->File));
for (InputFile *File : Files)
if (File != Sym->File)
print("", toString(File));
}
}

View File

@ -13,6 +13,7 @@
namespace lld {
namespace elf {
void writeMapFile();
void writeCrossReferenceTable();
} // namespace elf
} // namespace lld

View File

@ -20,15 +20,15 @@
//
//===----------------------------------------------------------------------===//
#include "MarkLive.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/ELF.h"
#include <functional>
@ -60,8 +60,9 @@ static typename ELFT::uint getAddend(InputSectionBase &Sec,
static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections;
template <class ELFT, class RelT>
static void resolveReloc(InputSectionBase &Sec, RelT &Rel,
std::function<void(InputSectionBase *, uint64_t)> Fn) {
static void
resolveReloc(InputSectionBase &Sec, RelT &Rel,
llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
Symbol &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
// If a symbol is referenced in a live section, it is used.
@ -90,7 +91,7 @@ static void resolveReloc(InputSectionBase &Sec, RelT &Rel,
template <class ELFT>
static void
forEachSuccessor(InputSection &Sec,
std::function<void(InputSectionBase *, uint64_t)> Fn) {
llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
if (Sec.AreRelocsRela) {
for (const typename ELFT::Rela &Rel : Sec.template relas<ELFT>())
resolveReloc<ELFT>(Sec, Rel, Fn);
@ -120,7 +121,7 @@ forEachSuccessor(InputSection &Sec,
template <class ELFT, class RelTy>
static void
scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
std::function<void(InputSectionBase *, uint64_t)> Fn) {
llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
const endianness E = ELFT::TargetEndianness;
for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) {
@ -155,14 +156,10 @@ scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
template <class ELFT>
static void
scanEhFrameSection(EhInputSection &EH,
std::function<void(InputSectionBase *, uint64_t)> Fn) {
llvm::function_ref<void(InputSectionBase *, uint64_t)> Fn) {
if (!EH.NumRelocations)
return;
// Unfortunately we need to split .eh_frame early since some relocations in
// .eh_frame keep other section alive and some don't.
EH.split<ELFT>();
if (EH.AreRelocsRela)
scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Fn);
else
@ -207,7 +204,7 @@ template <class ELFT> static void doGcSections() {
// (splittable) sections, each piece of data has independent liveness bit.
// So we explicitly tell it which offset is in use.
if (auto *MS = dyn_cast<MergeInputSection>(Sec))
MS->markLiveAt(Offset);
MS->getSectionPiece(Offset)->Live = true;
if (Sec->Live)
return;
@ -279,13 +276,18 @@ template <class ELFT> void elf::markLive() {
// The -gc-sections option works only for SHF_ALLOC sections
// (sections that are memory-mapped at runtime). So we can
// unconditionally make non-SHF_ALLOC sections alive.
// unconditionally make non-SHF_ALLOC sections alive except
// SHF_LINK_ORDER and SHT_REL/SHT_RELA sections.
//
// Non SHF_ALLOC sections are not removed even if they are
// Usually, SHF_ALLOC sections are not removed even if they are
// unreachable through relocations because reachability is not
// a good signal whether they are garbage or not (e.g. there is
// usually no section referring to a .comment section, but we
// want to keep it.)
// want to keep it.).
//
// Note on SHF_LINK_ORDER: Such sections contain metadata and they
// have a reverse dependency on the InputSection they are linked with.
// We are able to garbage collect them.
//
// Note on SHF_REL{,A}: Such sections reach here only when -r
// or -emit-reloc were given. And they are subject of garbage
@ -293,8 +295,9 @@ template <class ELFT> void elf::markLive() {
// remove its relocation section.
for (InputSectionBase *Sec : InputSections) {
bool IsAlloc = (Sec->Flags & SHF_ALLOC);
bool IsLinkOrder = (Sec->Flags & SHF_LINK_ORDER);
bool IsRel = (Sec->Type == SHT_REL || Sec->Type == SHT_RELA);
if (!IsAlloc && !IsRel)
if (!IsAlloc && !IsLinkOrder && !IsRel)
Sec->Live = true;
}
@ -305,8 +308,7 @@ template <class ELFT> void elf::markLive() {
if (Config->PrintGcSections)
for (InputSectionBase *Sec : InputSections)
if (!Sec->Live)
message("removing unused section from '" + Sec->Name + "' in file '" +
Sec->File->getName() + "'");
message("removing unused section " + toString(Sec));
}
template void elf::markLive<ELF32LE>();

21
ELF/MarkLive.h Normal file
View File

@ -0,0 +1,21 @@
//===- MarkLive.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_MARKLIVE_H
#define LLD_ELF_MARKLIVE_H
namespace lld {
namespace elf {
template <class ELFT> void markLive();
} // namespace elf
} // namespace lld
#endif // LLD_ELF_MARKLIVE_H

View File

@ -4,66 +4,91 @@ include "llvm/Option/OptParser.td"
// two can precede the option name except those that start with 'o'.
class F<string name>: Flag<["--", "-"], name>;
class J<string name>: Joined<["--", "-"], name>;
class S<string name>: Separate<["--", "-"], name>;
multiclass Eq<string name> {
def "": Separate<["--", "-"], name>;
def _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
multiclass Eq<string name, string help> {
def NAME: Separate<["--", "-"], name>;
def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>,
HelpText<help>;
}
def auxiliary: S<"auxiliary">, HelpText<"Set DT_AUXILIARY field to the specified name">;
multiclass B<string name, string help1, string help2> {
def NAME: Flag<["--", "-"], name>, HelpText<help1>;
def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>;
}
defm auxiliary: Eq<"auxiliary", "Set DT_AUXILIARY field to the specified name">;
def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">;
def Bsymbolic_functions: F<"Bsymbolic-functions">,
HelpText<"Bind defined function symbols locally">;
def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries">;
def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries (default)">;
def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
def build_id: F<"build-id">, HelpText<"Generate build ID note">;
def build_id: F<"build-id">, HelpText<"Alias for --build-id=fast">;
def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">;
def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">,
MetaVarName<"[fast,md5,sha,uuid,0x<hexstring>]">;
defm compress_debug_sections : Eq<"compress-debug-sections">,
HelpText<"Compress DWARF debug sections">;
defm check_sections: B<"check-sections",
"Check section addresses for overlaps (default)",
"Do not check section addresses for overlaps">;
defm defsym: Eq<"defsym">, HelpText<"Define a symbol alias">;
defm compress_debug_sections:
Eq<"compress-debug-sections", "Compress DWARF debug sections">,
MetaVarName<"[none,zlib]">;
defm library_path: Eq<"library-path">,
HelpText<"Add a directory to the library search path">, MetaVarName<"<dir>">;
defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">;
defm library_path:
Eq<"library-path", "Add a directory to the library search path">, MetaVarName<"<dir>">;
def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">;
defm Tbss: Eq<"Tbss">,
HelpText<"Same as --section-start with .bss as the sectionname">;
defm Tbss: Eq<"Tbss", "Same as --section-start with .bss as the sectionname">;
defm Tdata: Eq<"Tdata">,
HelpText<"Same as --section-start with .data as the sectionname">;
defm Tdata: Eq<"Tdata", "Same as --section-start with .data as the sectionname">;
defm Ttext: Eq<"Ttext">,
HelpText<"Same as --section-start with .text as the sectionname">;
defm Ttext: Eq<"Ttext", "Same as --section-start with .text as the sectionname">;
def allow_multiple_definition: F<"allow-multiple-definition">,
HelpText<"Allow multiple definitions">;
defm allow_multiple_definition: B<"allow-multiple-definition",
"Allow multiple definitions",
"Do not allow multiple definitions (default)">;
def as_needed: F<"as-needed">,
HelpText<"Only set DT_NEEDED for shared libraries if used">;
defm apply_dynamic_relocs: B<"apply-dynamic-relocs",
"Apply dynamic relocations to place",
"Do not apply dynamic relocations to place">;
defm as_needed: B<"as-needed",
"Only set DT_NEEDED for shared libraries if used",
"Always set DT_NEEDED for shared libraries (default)">;
defm call_graph_ordering_file:
Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">;
// -chroot doesn't have a help text because it is an internal option.
def chroot: S<"chroot">;
def chroot: Separate<["--", "-"], "chroot">;
def color_diagnostics: F<"color-diagnostics">,
HelpText<"Use colors in diagnostics">;
HelpText<"Alias for --color-diagnostics=always">;
def color_diagnostics_eq: J<"color-diagnostics=">,
HelpText<"Use colors in diagnostics">;
HelpText<"Use colors in diagnostics">,
MetaVarName<"[auto,always,never]">;
def define_common: F<"define-common">,
HelpText<"Assign space to common symbols">;
defm cref: B<"cref",
"Output cross reference table",
"Do not output cross reference table">;
def demangle: F<"demangle">, HelpText<"Demangle symbol names">;
defm define_common: B<"define-common",
"Assign space to common symbols",
"Do not assign space to common symbols">;
defm demangle: B<"demangle",
"Demangle symbol names (default)",
"Do not demangle symbol names">;
def disable_new_dtags: F<"disable-new-dtags">,
HelpText<"Disable new dynamic tags">;
@ -76,158 +101,126 @@ def discard_locals: F<"discard-locals">,
def discard_none: F<"discard-none">,
HelpText<"Keep all symbols in the symbol table">;
def dynamic_linker: S<"dynamic-linker">,
HelpText<"Which dynamic linker to use">;
defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">;
defm dynamic_list: Eq<"dynamic-list">,
HelpText<"Read a list of dynamic symbols">;
defm dynamic_list: Eq<"dynamic-list", "Read a list of dynamic symbols">;
def eh_frame_hdr: F<"eh-frame-hdr">,
HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">;
defm eh_frame_hdr: B<"eh-frame-hdr",
"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header",
"Do not create .eh_frame_hdr section">;
def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">;
def enable_new_dtags: F<"enable-new-dtags">,
HelpText<"Enable new dynamic tags">;
HelpText<"Enable new dynamic tags (default)">;
def end_group: F<"end-group">,
HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">;
def end_lib: F<"end-lib">,
HelpText<"End a grouping of objects that should be treated as if they were together in an archive">;
defm entry: Eq<"entry">, HelpText<"Name of entry point symbol">,
defm entry: Eq<"entry", "Name of entry point symbol">,
MetaVarName<"<entry>">;
defm error_limit: Eq<"error-limit">,
HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
defm error_limit:
Eq<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
def error_unresolved_symbols: F<"error-unresolved-symbols">,
HelpText<"Report unresolved symbols as errors">;
defm exclude_libs: Eq<"exclude-libs">,
HelpText<"Exclude static libraries from automatic export">;
defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">;
def export_dynamic: F<"export-dynamic">,
HelpText<"Put symbols in the dynamic symbol table">;
defm export_dynamic: B<"export-dynamic",
"Put symbols in the dynamic symbol table",
"Do not put symbols in the dynamic symbol table (default)">;
defm export_dynamic_symbol: Eq<"export-dynamic-symbol">,
HelpText<"Put a symbol in the dynamic symbol table">;
defm export_dynamic_symbol:
Eq<"export-dynamic-symbol", "Put a symbol in the dynamic symbol table">;
def fatal_warnings: F<"fatal-warnings">,
HelpText<"Treat warnings as errors">;
defm fatal_warnings: B<"fatal-warnings",
"Treat warnings as errors",
"Do not treat warnings as errors (default)">;
defm filter: Eq<"filter">,
HelpText<"Set DT_FILTER field to the specified name">;
defm filter: Eq<"filter", "Set DT_FILTER field to the specified name">;
defm fini: Eq<"fini">,
HelpText<"Specify a finalizer function">, MetaVarName<"<symbol>">;
defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">;
def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">;
def full_shutdown : F<"full-shutdown">,
HelpText<"Perform a full shutdown instead of calling _exit">;
defm format: Eq<"format", "Change the input format of the inputs following this option">,
MetaVarName<"[default,elf,binary]">;
defm format: Eq<"format">,
HelpText<"Change the input format of the inputs following this option">,
MetaVarName<"<input-format>">;
defm gc_sections: B<"gc-sections",
"Enable garbage collection of unused sections",
"Disable garbage collection of unused sections (default)">;
def gc_sections: F<"gc-sections">,
HelpText<"Enable garbage collection of unused sections">;
defm gdb_index: B<"gdb-index",
"Generate .gdb_index section",
"Do not generate .gdb_index section (default)">;
def gdb_index: F<"gdb-index">,
HelpText<"Generate .gdb_index section">;
defm gnu_unique: B<"gnu-unique",
"Enable STB_GNU_UNIQUE symbol binding (default)",
"Disable STB_GNU_UNIQUE symbol binding">;
defm hash_style: Eq<"hash-style">,
HelpText<"Specify hash style (sysv, gnu or both)">;
defm hash_style: Eq<"hash-style", "Specify hash style (sysv, gnu or both)">;
def help: F<"help">, HelpText<"Print option help">;
def icf_all: F<"icf=all">, HelpText<"Enable identical code folding">;
def icf_data: F<"icf-data">,
HelpText<"Enable ICF to also fold identical read only data">;
def icf_safe: F<"icf=safe">, HelpText<"Enable safe identical code folding">;
def icf_none: F<"icf=none">, HelpText<"Disable identical code folding">;
def icf_none: F<"icf=none">, HelpText<"Disable identical code folding (default)">;
defm image_base : Eq<"image-base">, HelpText<"Set the base address">;
def ignore_function_address_equality: F<"ignore-function-address-equality">,
HelpText<"lld can break the address equality of functions">;
defm init: Eq<"init">, HelpText<"Specify an initializer function">,
def ignore_data_address_equality: F<"ignore-data-address-equality">,
HelpText<"lld can break the address equality of data">;
defm image_base: Eq<"image-base", "Set the base address">;
defm init: Eq<"init", "Specify an initializer function">,
MetaVarName<"<symbol>">;
defm library: Eq<"library">, HelpText<"Root name of library to use">,
MetaVarName<"<libName>">;
defm just_symbols: Eq<"just-symbols", "Just link symbols">;
def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">;
defm keep_unique: Eq<"keep-unique", "Do not fold this symbol during ICF">;
defm library: Eq<"library", "Root name of library to use">,
MetaVarName<"<libName>">;
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
defm Map: Eq<"Map">, HelpText<"Print a link map to the specified file">;
defm Map: Eq<"Map", "Print a link map to the specified file">;
def merge_exidx_entries: F<"merge-exidx-entries">,
HelpText<"Enable merging .ARM.exidx entries">;
defm merge_exidx_entries: B<"merge-exidx-entries",
"Enable merging .ARM.exidx entries (default)",
"Disable merging .ARM.exidx entries">;
def nostdlib: F<"nostdlib">,
HelpText<"Only search directories specified on the command line">;
def no_as_needed: F<"no-as-needed">,
HelpText<"Always DT_NEEDED for shared libraries">;
def no_color_diagnostics: F<"no-color-diagnostics">,
HelpText<"Do not use colors in diagnostics">;
def no_define_common: F<"no-define-common">,
HelpText<"Do not assign space to common symbols">;
def no_demangle: F<"no-demangle">,
HelpText<"Do not demangle symbol names">;
def no_dynamic_linker: F<"no-dynamic-linker">,
HelpText<"Inhibit output of .interp section">;
def no_eh_frame_hdr: F<"no-eh-frame-hdr">,
HelpText<"Do not create .eh_frame_hdr section">;
def no_export_dynamic: F<"no-export-dynamic">;
def no_fatal_warnings: F<"no-fatal-warnings">;
def no_gc_sections: F<"no-gc-sections">,
HelpText<"Disable garbage collection of unused sections">;
def no_gdb_index: F<"no-gdb-index">,
HelpText<"Do not generate .gdb_index section">;
def no_gnu_unique: F<"no-gnu-unique">,
HelpText<"Disable STB_GNU_UNIQUE symbol binding">;
def no_merge_exidx_entries: F<"no-merge-exidx-entries">,
HelpText<"Disable merging .ARM.exidx entries">;
def no_threads: F<"no-threads">,
HelpText<"Do not run the linker multi-threaded">;
def no_whole_archive: F<"no-whole-archive">,
HelpText<"Restores the default behavior of loading archive members">;
def noinhibit_exec: F<"noinhibit-exec">,
HelpText<"Retain the executable output file whenever it is still usable">;
def nopie: F<"nopie">, HelpText<"Do not create a position independent executable">;
def no_omagic: Flag<["--"], "no-omagic">, MetaVarName<"<magic>">,
def no_omagic: F<"no-omagic">, MetaVarName<"<magic>">,
HelpText<"Do not set the text data sections to be writable">;
def no_print_gc_sections: F<"no-print-gc-sections">,
HelpText<"Do not list removed unused sections">;
def no_rosegment: F<"no-rosegment">,
HelpText<"Do not put read-only non-executable sections in their own segment">;
def no_undefined: F<"no-undefined">,
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
def no_undefined_version: F<"no-undefined-version">,
HelpText<"Report version scripts that refer undefined symbols">;
def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
HelpText<"Path to file to write output">;
@ -237,42 +230,62 @@ def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
HelpText<"Set the text and data sections to be readable and writable">;
defm orphan_handling: Eq<"orphan-handling">,
HelpText<"Control how orphan sections are handled when linker script used">;
defm orphan_handling:
Eq<"orphan-handling", "Control how orphan sections are handled when linker script used">;
def pack_dyn_relocs_eq: J<"pack-dyn-relocs=">, MetaVarName<"<format>">,
HelpText<"Pack dynamic relocations in the given format (none or android)">;
defm pack_dyn_relocs:
Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">,
MetaVarName<"[none,android,relr,android+relr]">;
def pie: F<"pie">, HelpText<"Create a position independent executable">;
defm use_android_relr_tags: B<"use-android-relr-tags",
"Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*",
"Use SHT_RELR / DT_RELR* tags (default)">;
def print_gc_sections: F<"print-gc-sections">,
HelpText<"List removed unused sections">;
defm pie: B<"pie",
"Create a position independent executable",
"Do not create a position independent executable (default)">;
defm print_gc_sections: B<"print-gc-sections",
"List removed unused sections",
"Do not list removed unused sections (default)">;
defm print_icf_sections: B<"print-icf-sections",
"List identical folded sections",
"Do not list identical folded sections (default)">;
def pop_state: F<"pop-state">,
HelpText<"Undo the effect of -push-state">;
def push_state: F<"push-state">,
HelpText<"Save the current state of -as-needed, -static and -whole-archive">;
def print_map: F<"print-map">,
HelpText<"Print a link map to the standard output">;
defm reproduce: Eq<"reproduce">,
HelpText<"Dump linker invocation and input files for debugging">;
defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">;
defm rpath: Eq<"rpath">, HelpText<"Add a DT_RUNPATH to the output">;
defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">;
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
defm retain_symbols_file: Eq<"retain-symbols-file">,
HelpText<"Retain only the symbols listed in the file">,
defm retain_symbols_file:
Eq<"retain-symbols-file", "Retain only the symbols listed in the file">,
MetaVarName<"<file>">;
defm script: Eq<"script">, HelpText<"Read linker script">;
defm script: Eq<"script", "Read linker script">;
def section_start: S<"section-start">, MetaVarName<"<address>">,
HelpText<"Set address of section">;
defm section_start: Eq<"section-start", "Set address of section">,
MetaVarName<"<address>">;
def shared: F<"shared">, HelpText<"Build a shared object">;
defm soname: Eq<"soname">, HelpText<"Set DT_SONAME">;
defm soname: Eq<"soname", "Set DT_SONAME">;
defm sort_section: Eq<"sort-section">,
HelpText<"Specifies sections sorting rule when linkerscript is used">;
defm sort_section:
Eq<"sort-section", "Specifies sections sorting rule when linkerscript is used">;
def start_group: F<"start-group">,
HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">;
def start_lib: F<"start-lib">,
HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">;
@ -281,33 +294,39 @@ def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
def symbol_ordering_file: S<"symbol-ordering-file">,
HelpText<"Layout sections in the order specified by symbol file">;
defm symbol_ordering_file:
Eq<"symbol-ordering-file", "Layout sections to place symbols in the order specified by symbol ordering file">;
defm sysroot: Eq<"sysroot">, HelpText<"Set the system root">;
defm sysroot: Eq<"sysroot", "Set the system root">;
def target1_rel: F<"target1-rel">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_REL32">;
def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32">;
def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32 (default)">;
defm target2: Eq<"target2">,
HelpText<"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
defm target2:
Eq<"target2", "Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
MetaVarName<"<type>">;
def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
defm threads: B<"threads",
"Run the linker multi-threaded (default)",
"Do not run the linker multi-threaded">;
def trace: F<"trace">, HelpText<"Print the names of the input files">;
defm trace_symbol : Eq<"trace-symbol">, HelpText<"Trace references to symbols">;
defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">;
defm undefined: Eq<"undefined">,
HelpText<"Force undefined symbol during linking">;
defm undefined: Eq<"undefined", "Force undefined symbol during linking">,
MetaVarName<"<symbol>">;
defm unresolved_symbols: Eq<"unresolved-symbols">,
HelpText<"Determine how to handle unresolved symbols">;
defm unresolved_symbols:
Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">;
defm rsp_quoting: Eq<"rsp-quoting">,
HelpText<"Quoting style for response files. Values supported: windows|posix">;
defm undefined_version: B<"undefined-version",
"Allow unused version in version script (default)",
"Report version scripts that refer undefined symbols">;
defm rsp_quoting: Eq<"rsp-quoting", "Quoting style for response files">,
MetaVarName<"[posix,windows]">;
def v: Flag<["-"], "v">, HelpText<"Display the version number">;
@ -315,91 +334,123 @@ def verbose: F<"verbose">, HelpText<"Verbose mode">;
def version: F<"version">, HelpText<"Display the version number and exit">;
defm version_script: Eq<"version-script">, HelpText<"Read a version script">;
defm version_script: Eq<"version-script", "Read a version script">;
def warn_common: F<"warn-common">,
HelpText<"Warn about duplicate common symbols">;
defm warn_backrefs: B<"warn-backrefs",
"Warn about backward symbol references to fetch archive members",
"Do not warn about backward symbol references to fetch archive members (default)">;
defm warn_common: B<"warn-common",
"Warn about duplicate common symbols",
"Do not warn about duplicate common symbols (default)">;
defm warn_symbol_ordering: B<"warn-symbol-ordering",
"Warn about problems with the symbol ordering file (default)",
"Do not warn about problems with the symbol ordering file">;
def warn_unresolved_symbols: F<"warn-unresolved-symbols">,
HelpText<"Report unresolved symbols as warnings">;
def whole_archive: F<"whole-archive">,
HelpText<"Force load of all members in a static library">;
defm whole_archive: B<"whole-archive",
"Force load of all members in a static library",
"Do not force load of all members in a static library (default)">;
defm wrap: Eq<"wrap">, HelpText<"Use wrapper functions for symbol">,
MetaVarName<"<symbol>">;
defm wrap: Eq<"wrap", "Use wrapper functions for symbol">,
MetaVarName<"<symbol>=<symbol>">;
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
HelpText<"Linker option extensions">;
// Aliases
def alias_auxiliary: Separate<["-"], "f">, Alias<auxiliary>;
def alias_Bdynamic_call_shared: F<"call_shared">, Alias<Bdynamic>;
def alias_Bdynamic_dy: F<"dy">, Alias<Bdynamic>;
def alias_Bstatic_dn: F<"dn">, Alias<Bstatic>;
def alias_Bstatic_non_shared: F<"non_shared">, Alias<Bstatic>;
def alias_Bstatic_static: F<"static">, Alias<Bstatic>;
def alias_define_common_d: Flag<["-"], "d">, Alias<define_common>;
def alias_define_common_dc: F<"dc">, Alias<define_common>;
def alias_define_common_dp: F<"dp">, Alias<define_common>;
def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
def alias_emit_relocs: Flag<["-"], "q">, Alias<emit_relocs>;
def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
def alias_export_dynamic_E: Flag<["-"], "E">, Alias<export_dynamic>;
def alias_filter: Separate<["-"], "F">, Alias<filter>;
def alias_format_b: S<"b">, Alias<format>;
def alias_library: JoinedOrSeparate<["-"], "l">, Alias<library>;
def alias_library_path: JoinedOrSeparate<["-"], "L">, Alias<library_path>;
def alias_omagic: Flag<["-"], "N">, Alias<omagic>;
def alias_o_output: Joined<["--"], "output=">, Alias<o>;
def alias_o_output2 : Separate<["--"], "output">, Alias<o>;
def alias_pie_pic_executable: F<"pic-executable">, Alias<pie>;
def alias_print_map_M: Flag<["-"], "M">, Alias<print_map>;
def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
def alias_rpath_R: JoinedOrSeparate<["-"], "R">, Alias<rpath>;
def alias_script_T: JoinedOrSeparate<["-"], "T">, Alias<script>;
def alias_shared_Bshareable: F<"Bshareable">, Alias<shared>;
def alias_soname_h: JoinedOrSeparate<["-"], "h">, Alias<soname>;
def alias_strip_all: Flag<["-"], "s">, Alias<strip_all>;
def alias_strip_debug_S: Flag<["-"], "S">, Alias<strip_debug>;
def alias_trace: Flag<["-"], "t">, Alias<trace>;
def alias_trace_symbol_y : JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>;
def alias_Ttext_segment: S<"Ttext-segment">, Alias<Ttext>;
def alias_Ttext_segment_eq: J<"Ttext-segment=">, Alias<Ttext>;
def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
def alias_version_V: Flag<["-"], "V">, Alias<version>;
// 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: F<"end-group">;
def end_group_paren: Flag<["-"], ")">;
def start_group: F<"start-group">;
def start_group_paren: Flag<["-"], "(">;
def: Separate<["-"], "f">, Alias<auxiliary>, HelpText<"Alias for --auxiliary">;
def: F<"call_shared">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
def: F<"dy">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
def: F<"dn">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
def: F<"non_shared">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
def: F<"static">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
def: Flag<["-"], "d">, Alias<define_common>, HelpText<"Alias for --define-common">;
def: F<"dc">, Alias<define_common>, HelpText<"Alias for --define-common">;
def: F<"dp">, Alias<define_common>, HelpText<"Alias for --define-common">;
def: Flag<["-"], "x">, Alias<discard_all>, HelpText<"Alias for --discard-all">;
def: Flag<["-"], "X">, Alias<discard_locals>, HelpText<"Alias for --discard-locals">;
def: Flag<["-"], "q">, Alias<emit_relocs>, HelpText<"Alias for --emit-relocs">;
def: Flag<["-"], ")">, Alias<end_group>, HelpText<"Alias for --end-group">;
def: JoinedOrSeparate<["-"], "e">, Alias<entry>, HelpText<"Alias for --entry">;
def: Flag<["-"], "E">, Alias<export_dynamic>, HelpText<"Alias for --export-dynamic">;
def: Separate<["-"], "F">, Alias<filter>, HelpText<"Alias for --filter">;
def: Separate<["-"], "b">, Alias<format>, HelpText<"Alias for --format">;
def: JoinedOrSeparate<["-"], "l">, Alias<library>, HelpText<"Alias for --library">;
def: JoinedOrSeparate<["-"], "L">, Alias<library_path>, HelpText<"Alias for --library-path">;
def: F<"no-pic-executable">, Alias<no_pie>, HelpText<"Alias for --no-pie">;
def: Flag<["-"], "N">, Alias<omagic>, HelpText<"Alias for --omagic">;
def: Joined<["--"], "output=">, Alias<o>, HelpText<"Alias for -o">;
def: Separate<["--"], "output">, Alias<o>, HelpText<"Alias for -o">;
def: F<"pic-executable">, Alias<pie>, HelpText<"Alias for --pie">;
def: Flag<["-"], "M">, Alias<print_map>, HelpText<"Alias for --print-map">;
def: Flag<["-"], "r">, Alias<relocatable>, HelpText<"Alias for --relocatable">;
def: JoinedOrSeparate<["-"], "R">, Alias<rpath>, HelpText<"Alias for --rpath">;
def: JoinedOrSeparate<["-"], "T">, Alias<script>, HelpText<"Alias for --script">;
def: F<"Bshareable">, Alias<shared>, HelpText<"Alias for --shared">;
def: JoinedOrSeparate<["-"], "h">, Alias<soname>, HelpText<"Alias for --soname">;
def: Flag<["-"], "(">, Alias<start_group>, HelpText<"Alias for --start-group">;
def: Flag<["-"], "s">, Alias<strip_all>, HelpText<"Alias for --strip-all">;
def: Flag<["-"], "S">, Alias<strip_debug>, HelpText<"Alias for --strip-debug">;
def: Flag<["-"], "t">, Alias<trace>, HelpText<"Alias for --trace">;
def: JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>, HelpText<"Alias for --trace-symbol">;
def: Separate<["-", "--"], "Ttext-segment">, Alias<Ttext>, HelpText<"Alias for --Ttext">;
def: Joined<["-", "--"], "Ttext-segment=">, Alias<Ttext>, HelpText<"Alias for --Ttext">;
def: JoinedOrSeparate<["-"], "u">, Alias<undefined>, HelpText<"Alias for --undefined">;
def: Flag<["-"], "V">, Alias<version>, HelpText<"Alias for --version">;
// LTO-related options.
def lto_aa_pipeline: J<"lto-aa-pipeline=">,
HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">;
def lto_debug_pass_manager: F<"lto-debug-pass-manager">,
HelpText<"Debug new pass manager">;
def lto_new_pass_manager: F<"lto-new-pass-manager">,
HelpText<"Use new pass manager">;
def lto_newpm_passes: J<"lto-newpm-passes=">,
HelpText<"Passes to run during LTO">;
def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">;
def lto_partitions: J<"lto-partitions=">,
HelpText<"Number of LTO codegen partitions">;
def lto_sample_profile: J<"lto-sample-profile=">,
HelpText<"Sample profile file path">;
def disable_verify: F<"disable-verify">;
def mllvm: S<"mllvm">;
defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">;
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
HelpText<"YAML output file for optimization remarks">;
def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
HelpText<"Include hotness informations in the optimization remarks file">;
defm plugin_opt: Eq<"plugin-opt">,
HelpText<"specifies LTO options for compatibility with GNU linkers">;
HelpText<"Include hotness information in the optimization remarks file">;
defm plugin_opt: Eq<"plugin-opt", "specifies LTO options for compatibility with GNU linkers">;
def save_temps: F<"save-temps">;
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">;
def thinlto_cache_policy: S<"thinlto-cache-policy">,
HelpText<"Pruning policy for the ThinLTO cache">;
defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">;
def: F<"plugin-opt=debug-pass-manager">,
Alias<lto_debug_pass_manager>, HelpText<"Alias for -lto-debug-pass-manager">;
def: F<"plugin-opt=disable-verify">, Alias<disable_verify>, HelpText<"Alias for -disable-verify">;
def plugin_opt_dwo_dir_eq: J<"plugin-opt=dwo_dir=">,
HelpText<"Directory to store .dwo files when LTO and debug fission are used">;
def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for -thinlto-jobs">;
def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for -lto-partitions">;
def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
def: F<"plugin-opt=new-pass-manager">,
Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">;
def plugin_opt_obj_path_eq: J<"plugin-opt=obj-path=">;
def: J<"plugin-opt=sample-profile=">,
Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">;
def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">;
def plugin_opt_thinlto_emit_imports_files: F<"plugin-opt=thinlto-emit-imports-files">;
def plugin_opt_thinlto_index_only: F<"plugin-opt=thinlto-index-only">;
def plugin_opt_thinlto_index_only_eq: J<"plugin-opt=thinlto-index-only=">;
def plugin_opt_thinlto_object_suffix_replace_eq: J<"plugin-opt=thinlto-object-suffix-replace=">;
def plugin_opt_thinlto_prefix_replace_eq: J<"plugin-opt=thinlto-prefix-replace=">;
// Ignore LTO plugin-related options.
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
// for ld.gold and ld.bfd to get LTO working. But it's not for lld which doesn't
@ -407,31 +458,38 @@ def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
// just ignore the option on lld side as it's easier. In fact, the linker could
// be called 'ld' and understanding which linker is used would require parsing of
// --version output.
def plugin: S<"plugin">;
def plugin_eq: J<"plugin=">;
defm plugin: Eq<"plugin", "Ignored for compatibility with GNU linkers">;
def plugin_opt_fresolution_eq: J<"plugin-opt=-fresolution=">;
def plugin_opt_pass_through_eq: J<"plugin-opt=-pass-through=">;
def plugin_opt_thinlto: J<"plugin-opt=thinlto">;
def plugin_opt_slash: J<"plugin-opt=/">;
// Options listed below are silently ignored for now for compatibility.
def allow_shlib_undefined: F<"allow-shlib-undefined">;
def cref: F<"cref">;
def detect_odr_violations: F<"detect-odr-violations">;
def g: Flag<["-"], "g">;
def long_plt: F<"long-plt">;
def no_add_needed: F<"no-add-needed">;
def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">;
def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">;
def no_ctors_in_init_array: F<"no-ctors-in-init-array">;
def no_keep_memory: F<"no-keep-memory">;
def no_mmap_output_file: F<"no-mmap-output-file">;
def no_warn_common: F<"no-warn-common">;
def no_warn_mismatch: F<"no-warn-mismatch">;
def rpath_link: S<"rpath-link">;
def rpath_link_eq: J<"rpath-link=">;
def sort_common: F<"sort-common">;
def stats: F<"stats">;
def warn_execstack: F<"warn-execstack">;
def warn_once: F<"warn-once">;
def warn_shared_textrel: F<"warn-shared-textrel">;
def EB : F<"EB">;
def EL : F<"EL">;
def G: JoinedOrSeparate<["-"], "G">;
def Qy : F<"Qy">;
def: F<"allow-shlib-undefined">;
def: F<"detect-odr-violations">;
def: Flag<["-"], "g">;
def: F<"long-plt">;
def: F<"no-add-needed">;
def: F<"no-allow-shlib-undefined">;
def: F<"no-copy-dt-needed-entries">;
def: F<"no-ctors-in-init-array">;
def: F<"no-keep-memory">;
def: F<"no-mmap-output-file">;
def: F<"no-warn-mismatch">;
def: Separate<["--", "-"], "rpath-link">;
def: J<"rpath-link=">;
def: F<"sort-common">;
def: F<"stats">;
def: F<"warn-execstack">;
def: F<"warn-once">;
def: F<"warn-shared-textrel">;
def: F<"EB">;
def: F<"EL">;
def: JoinedOrSeparate<["-"], "G">;
def: F<"Qy">;
// Hidden option used for testing MIPS multi-GOT implementation.
defm mips_got_size:
Eq<"mips-got-size", "Max size of a single MIPS GOT. 0x10000 by default.">,
Flags<[HelpHidden]>;

View File

@ -10,11 +10,11 @@
#include "OutputSections.h"
#include "Config.h"
#include "LinkerScript.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Support/Compression.h"
@ -25,15 +25,12 @@
using namespace llvm;
using namespace llvm::dwarf;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
uint8_t Out::First;
OutputSection *Out::Opd;
uint8_t *Out::OpdBuf;
PhdrEntry *Out::TlsPhdr;
OutputSection *Out::DebugInfo;
OutputSection *Out::ElfHeader;
@ -45,7 +42,9 @@ OutputSection *Out::FiniArray;
std::vector<OutputSection *> elf::OutputSections;
uint32_t OutputSection::getPhdrFlags() const {
uint32_t Ret = PF_R;
uint32_t Ret = 0;
if (Config->EMachine != EM_ARM || !(Flags & SHF_ARM_PURECODE))
Ret |= PF_R;
if (Flags & SHF_WRITE)
Ret |= PF_W;
if (Flags & SHF_EXECINSTR)
@ -70,9 +69,7 @@ void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) {
OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
: BaseCommand(OutputSectionKind),
SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
/*Info*/ 0,
/*Link*/ 0),
SectionIndex(INT_MAX) {
/*Info*/ 0, /*Link*/ 0) {
Live = false;
}
@ -91,13 +88,15 @@ static bool canMergeToProgbits(unsigned Type) {
void OutputSection::addSection(InputSection *IS) {
if (!Live) {
// If IS is the first section to be added to this section,
// initialize Type and Entsize from IS.
// initialize Type, Entsize and flags from IS.
Live = true;
Type = IS->Type;
Entsize = IS->Entsize;
Flags = IS->Flags;
} else {
// Otherwise, check if new type or flags are compatible with existing ones.
if ((Flags & (SHF_ALLOC | SHF_TLS)) != (IS->Flags & (SHF_ALLOC | SHF_TLS)))
unsigned Mask = SHF_ALLOC | SHF_TLS | SHF_LINK_ORDER;
if ((Flags & Mask) != (IS->Flags & Mask))
error("incompatible section flags for " + Name + "\n>>> " + toString(IS) +
": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name +
": 0x" + utohexstr(Flags));
@ -114,9 +113,14 @@ void OutputSection::addSection(InputSection *IS) {
}
IS->Parent = this;
Flags |= IS->Flags;
uint64_t AndMask =
Config->EMachine == EM_ARM ? (uint64_t)SHF_ARM_PURECODE : 0;
uint64_t OrMask = ~AndMask;
uint64_t AndFlags = (Flags & IS->Flags) & AndMask;
uint64_t OrFlags = (Flags | IS->Flags) & OrMask;
Flags = AndFlags | OrFlags;
Alignment = std::max(Alignment, IS->Alignment);
IS->OutSecOff = Size++;
// If this section contains a table of fixed-size entries, sh_entsize
// holds the element size. If it contains elements of different size we
@ -134,8 +138,8 @@ void OutputSection::addSection(InputSection *IS) {
}
}
void elf::sortByOrder(MutableArrayRef<InputSection *> In,
std::function<int(InputSectionBase *S)> Order) {
static void sortByOrder(MutableArrayRef<InputSection *> In,
llvm::function_ref<int(InputSectionBase *S)> Order) {
typedef std::pair<int, InputSection *> Pair;
auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; };
@ -158,11 +162,11 @@ bool OutputSection::classof(const BaseCommand *C) {
return C->Kind == OutputSectionKind;
}
void OutputSection::sort(std::function<int(InputSectionBase *S)> Order) {
void OutputSection::sort(llvm::function_ref<int(InputSectionBase *S)> Order) {
assert(Live);
assert(SectionCommands.size() == 1);
sortByOrder(cast<InputSectionDescription>(SectionCommands[0])->Sections,
Order);
for (BaseCommand *B : SectionCommands)
if (auto *ISD = dyn_cast<InputSectionDescription>(B))
sortByOrder(ISD->Sections, Order);
}
// Fill [Buf, Buf + Size) with Filler.
@ -183,15 +187,6 @@ template <class ELFT> void OutputSection::maybeCompress() {
!Name.startswith(".debug_"))
return;
// Calculate the section offsets and size pre-compression.
Size = 0;
for (BaseCommand *Cmd : SectionCommands)
if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
for (InputSection *IS : ISD->Sections) {
IS->OutSecOff = alignTo(Size, IS->Alignment);
this->Size = IS->OutSecOff + IS->getSize();
}
// Create a section header.
ZDebugHeader.resize(sizeof(Elf_Chdr));
auto *Hdr = reinterpret_cast<Elf_Chdr *>(ZDebugHeader.data());
@ -214,11 +209,11 @@ static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) {
if (Size == 1)
*Buf = Data;
else if (Size == 2)
write16(Buf, Data, Config->Endianness);
write16(Buf, Data);
else if (Size == 4)
write32(Buf, Data, Config->Endianness);
write32(Buf, Data);
else if (Size == 8)
write64(Buf, Data, Config->Endianness);
write64(Buf, Data);
else
llvm_unreachable("unsupported Size argument");
}
@ -240,12 +235,7 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
}
// Write leading padding.
std::vector<InputSection *> Sections;
for (BaseCommand *Cmd : SectionCommands)
if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
for (InputSection *IS : ISD->Sections)
if (IS->Live)
Sections.push_back(IS);
std::vector<InputSection *> Sections = getInputSections(this);
uint32_t Filler = getFiller();
if (Filler)
fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler);
@ -290,17 +280,13 @@ static void finalizeShtGroup(OutputSection *OS,
}
template <class ELFT> void OutputSection::finalize() {
InputSection *First = nullptr;
for (BaseCommand *Base : SectionCommands) {
if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
if (ISD->Sections.empty())
continue;
if (First == nullptr)
First = ISD->Sections.front();
}
if (isa<ByteCommand>(Base) && Type == SHT_NOBITS)
Type = SHT_PROGBITS;
}
if (Type == SHT_NOBITS)
for (BaseCommand *Base : SectionCommands)
if (isa<ByteCommand>(Base))
Type = SHT_PROGBITS;
std::vector<InputSection *> V = getInputSections(this);
InputSection *First = V.empty() ? nullptr : V[0];
if (Flags & SHF_LINK_ORDER) {
// We must preserve the link order dependency of sections with the
@ -376,8 +362,6 @@ static bool compCtors(const InputSection *A, const InputSection *B) {
assert(Y.startswith(".ctors") || Y.startswith(".dtors"));
X = X.substr(6);
Y = Y.substr(6);
if (X.empty() && Y.empty())
return false;
return X < Y;
}
@ -403,6 +387,14 @@ int elf::getPriority(StringRef S) {
return V;
}
std::vector<InputSection *> elf::getInputSections(OutputSection *OS) {
std::vector<InputSection *> Ret;
for (BaseCommand *Base : OS->SectionCommands)
if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
Ret.insert(Ret.end(), ISD->Sections.begin(), ISD->Sections.end());
return Ret;
}
// Sorts input sections by section name suffixes, so that .foo.N comes
// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections.
// We want to keep the original order if the priorities are the same

View File

@ -14,7 +14,6 @@
#include "InputSection.h"
#include "LinkerScript.h"
#include "Relocations.h"
#include "lld/Common/LLVM.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELF.h"
@ -49,10 +48,10 @@ class OutputSection final : public BaseCommand, public SectionBase {
static bool classof(const BaseCommand *C);
uint64_t getLMA() const { return Addr + LMAOffset; }
uint64_t getLMA() const { return PtLoad ? Addr + PtLoad->LMAOffset : Addr; }
template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
unsigned SectionIndex;
uint32_t SectionIndex = UINT32_MAX;
unsigned SortRank;
uint32_t getPhdrFlags() const;
@ -78,7 +77,6 @@ class OutputSection final : public BaseCommand, public SectionBase {
// The following fields correspond to Elf_Shdr members.
uint64_t Offset = 0;
uint64_t LMAOffset = 0;
uint64_t Addr = 0;
uint32_t ShName = 0;
@ -89,6 +87,7 @@ class OutputSection final : public BaseCommand, public SectionBase {
// The following members are normally only used in linker scripts.
MemoryRegion *MemRegion = nullptr;
MemoryRegion *LMARegion = nullptr;
Expr AddrExpr;
Expr AlignExpr;
Expr LMAExpr;
@ -99,13 +98,17 @@ class OutputSection final : public BaseCommand, public SectionBase {
ConstraintKind Constraint = ConstraintKind::NoConstraint;
std::string Location;
std::string MemoryRegionName;
std::string LMARegionName;
bool NonAlloc = false;
bool Noload = false;
bool ExpressionsUseSymbols = false;
bool InOverlay = false;
template <class ELFT> void finalize();
template <class ELFT> void writeTo(uint8_t *Buf);
template <class ELFT> void maybeCompress();
void sort(std::function<int(InputSectionBase *S)> Order);
void sort(llvm::function_ref<int(InputSectionBase *S)> Order);
void sortInitFini();
void sortCtorsDtors();
@ -119,13 +122,13 @@ class OutputSection final : public BaseCommand, public SectionBase {
int getPriority(StringRef S);
std::vector<InputSection *> getInputSections(OutputSection* OS);
// All output sections that are handled by the linker specially are
// globally accessible. Writer initializes them, so don't use them
// until Writer is initialized.
struct Out {
static uint8_t First;
static OutputSection *Opd;
static uint8_t *OpdBuf;
static PhdrEntry *TlsPhdr;
static OutputSection *DebugInfo;
static OutputSection *ElfHeader;
@ -142,8 +145,6 @@ namespace lld {
namespace elf {
uint64_t getHeaderSize();
void sortByOrder(llvm::MutableArrayRef<InputSection *> In,
std::function<int(InputSectionBase *S)> Order);
extern std::vector<OutputSection *> OutputSections;
} // namespace elf

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,7 @@ class Symbol;
class InputSection;
class InputSectionBase;
class OutputSection;
class OutputSection;
class SectionBase;
// Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL.
typedef uint32_t RelType;
@ -32,6 +32,7 @@ typedef uint32_t RelType;
enum RelExpr {
R_INVALID,
R_ABS,
R_ADDEND,
R_ARM_SBREL,
R_GOT,
R_GOTONLY_PC,
@ -58,27 +59,33 @@ enum RelExpr {
R_PLT,
R_PLT_PAGE_PC,
R_PLT_PC,
R_PPC_OPD,
R_PPC_PLT_OPD,
R_PPC_CALL,
R_PPC_CALL_PLT,
R_PPC_TOC,
R_RELAX_GOT_PC,
R_RELAX_GOT_PC_NOPIC,
R_RELAX_TLS_GD_TO_IE,
R_RELAX_TLS_GD_TO_IE_ABS,
R_RELAX_TLS_GD_TO_IE_END,
R_RELAX_TLS_GD_TO_IE_GOT_OFF,
R_RELAX_TLS_GD_TO_IE_PAGE_PC,
R_RELAX_TLS_GD_TO_LE,
R_RELAX_TLS_GD_TO_LE_NEG,
R_RELAX_TLS_IE_TO_LE,
R_RELAX_TLS_LD_TO_LE,
R_RELAX_TLS_LD_TO_LE_ABS,
R_SIZE,
R_TLS,
R_TLSDESC,
R_TLSDESC_CALL,
R_TLSDESC_PAGE,
R_TLSGD,
R_TLSGD_GOT,
R_TLSGD_GOT_FROM_END,
R_TLSGD_PC,
R_TLSLD,
R_TLSLD_GOT,
R_TLSLD_GOT_FROM_END,
R_TLSLD_GOT_OFF,
R_TLSLD_HINT,
R_TLSLD_PC,
};
@ -150,7 +157,7 @@ class ThunkCreator {
void forEachInputSectionDescription(
ArrayRef<OutputSection *> OutputSections,
std::function<void(OutputSection *, InputSectionDescription *)> Fn);
llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn);
std::pair<Thunk *, bool> getThunk(Symbol &Sym, RelType Type, uint64_t Src);
@ -160,6 +167,8 @@ class ThunkCreator {
bool normalizeExistingThunk(Relocation &Rel, uint64_t Src);
// Record all the available Thunks for a Symbol
llvm::DenseMap<std::pair<SectionBase *, uint64_t>, std::vector<Thunk *>>
ThunkedSymbolsBySection;
llvm::DenseMap<Symbol *, std::vector<Thunk *>> ThunkedSymbols;
// Find a Thunk from the Thunks symbol definition, we can use this to find

View File

@ -66,8 +66,6 @@ size_t ScriptLexer::getColumnNumber() {
std::string ScriptLexer::getCurrentLocation() {
std::string Filename = getCurrentMB().getBufferIdentifier();
if (!Pos)
return Filename;
return (Filename + ":" + Twine(getLineNumber())).str();
}
@ -116,8 +114,9 @@ void ScriptLexer::tokenize(MemoryBufferRef MB) {
}
// ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>".
// "|", "||", "&" and "&&" are different operators.
if (S.startswith("<<") || S.startswith("<=") || S.startswith(">>") ||
S.startswith(">=")) {
S.startswith(">=") || S.startswith("||") || S.startswith("&&")) {
Vec.push_back(S.substr(0, 2));
S = S.substr(2);
continue;
@ -282,10 +281,7 @@ static bool encloses(StringRef S, StringRef T) {
MemoryBufferRef ScriptLexer::getCurrentMB() {
// Find input buffer containing the current token.
assert(!MBs.empty());
if (!Pos)
return MBs[0];
assert(!MBs.empty() && Pos > 0);
for (MemoryBufferRef MB : MBs)
if (encloses(MB.getBuffer(), Tokens[Pos - 1]))
return MB;

View File

@ -63,6 +63,7 @@ class ScriptParser final : ScriptLexer {
void readExtern();
void readGroup();
void readInclude();
void readInput();
void readMemory();
void readOutput();
void readOutputArch();
@ -74,12 +75,14 @@ class ScriptParser final : ScriptLexer {
void readVersion();
void readVersionScriptCommand();
SymbolAssignment *readAssignment(StringRef Name);
SymbolAssignment *readSymbolAssignment(StringRef Name);
ByteCommand *readByteCommand(StringRef Tok);
uint32_t readFill();
uint32_t parseFill(StringRef Tok);
void readSectionAddressType(OutputSection *Cmd);
OutputSection *readOverlaySectionDescription();
OutputSection *readOutputSectionDescription(StringRef OutSec);
std::vector<BaseCommand *> readOverlay();
std::vector<StringRef> readOutputSectionPhdrs();
InputSectionDescription *readInputSectionDescription(StringRef Tok);
StringMatcher readFilePatterns();
@ -88,16 +91,16 @@ class ScriptParser final : ScriptLexer {
unsigned readPhdrType();
SortSectionPolicy readSortKind();
SymbolAssignment *readProvideHidden(bool Provide, bool Hidden);
SymbolAssignment *readProvideOrAssignment(StringRef Tok);
SymbolAssignment *readAssignment(StringRef Tok);
void readSort();
AssertCommand *readAssert();
Expr readAssertExpr();
Expr readAssert();
Expr readConstant();
Expr getPageSize();
uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
std::pair<uint32_t, uint32_t> readMemoryAttributes();
Expr combine(StringRef Op, Expr L, Expr R);
Expr readExpr();
Expr readExpr1(Expr Lhs, int MinPrec);
StringRef readParenLiteral();
@ -157,17 +160,6 @@ static ExprValue sub(ExprValue A, ExprValue B) {
return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc};
}
static ExprValue mul(ExprValue A, ExprValue B) {
return A.getValue() * B.getValue();
}
static ExprValue div(ExprValue A, ExprValue B) {
if (uint64_t BV = B.getValue())
return A.getValue() / BV;
error("division by zero");
return 0;
}
static ExprValue bitAnd(ExprValue A, ExprValue B) {
moveAbsRight(A, B);
return {A.Sec, A.ForceAbsolute,
@ -237,16 +229,16 @@ void ScriptParser::readLinkerScript() {
if (Tok == ";")
continue;
if (Tok == "ASSERT") {
Script->SectionCommands.push_back(readAssert());
} else if (Tok == "ENTRY") {
if (Tok == "ENTRY") {
readEntry();
} else if (Tok == "EXTERN") {
readExtern();
} else if (Tok == "GROUP" || Tok == "INPUT") {
} else if (Tok == "GROUP") {
readGroup();
} else if (Tok == "INCLUDE") {
readInclude();
} else if (Tok == "INPUT") {
readInput();
} else if (Tok == "MEMORY") {
readMemory();
} else if (Tok == "OUTPUT") {
@ -265,7 +257,7 @@ void ScriptParser::readLinkerScript() {
readSections();
} else if (Tok == "VERSION") {
readVersion();
} else if (SymbolAssignment *Cmd = readProvideOrAssignment(Tok)) {
} else if (SymbolAssignment *Cmd = readAssignment(Tok)) {
Script->SectionCommands.push_back(Cmd);
} else {
setError("unknown directive: " + Tok);
@ -336,13 +328,12 @@ void ScriptParser::readExtern() {
}
void ScriptParser::readGroup() {
expect("(");
while (!errorCount() && !consume(")")) {
if (consume("AS_NEEDED"))
readAsNeeded();
else
addFile(unquote(next()));
}
bool Orig = InputFile::IsInGroup;
InputFile::IsInGroup = true;
readInput();
InputFile::IsInGroup = Orig;
if (!Orig)
++InputFile::NextGroupId;
}
void ScriptParser::readInclude() {
@ -353,7 +344,7 @@ void ScriptParser::readInclude() {
return;
}
if (Optional<std::string> Path = searchLinkerScript(Tok)) {
if (Optional<std::string> Path = searchScript(Tok)) {
if (Optional<MemoryBufferRef> MB = readFile(*Path))
tokenize(*MB);
return;
@ -361,6 +352,16 @@ void ScriptParser::readInclude() {
setError("cannot find linker script " + Tok);
}
void ScriptParser::readInput() {
expect("(");
while (!errorCount() && !consume(")")) {
if (consume("AS_NEEDED"))
readAsNeeded();
else
addFile(unquote(next()));
}
}
void ScriptParser::readOutput() {
// -o <file> takes predecence over OUTPUT(<file>).
expect("(");
@ -437,6 +438,49 @@ void ScriptParser::readSearchDir() {
expect(")");
}
// This reads an overlay description. Overlays are used to describe output
// sections that use the same virtual memory range and normally would trigger
// linker's sections sanity check failures.
// https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description
std::vector<BaseCommand *> ScriptParser::readOverlay() {
// VA and LMA expressions are optional, though for simplicity of
// implementation we assume they are not. That is what OVERLAY was designed
// for first of all: to allow sections with overlapping VAs at different LMAs.
Expr AddrExpr = readExpr();
expect(":");
expect("AT");
Expr LMAExpr = readParenExpr();
expect("{");
std::vector<BaseCommand *> V;
OutputSection *Prev = nullptr;
while (!errorCount() && !consume("}")) {
// VA is the same for all sections. The LMAs are consecutive in memory
// starting from the base load address specified.
OutputSection *OS = readOverlaySectionDescription();
OS->AddrExpr = AddrExpr;
if (Prev)
OS->LMAExpr = [=] { return Prev->getLMA() + Prev->Size; };
else
OS->LMAExpr = LMAExpr;
V.push_back(OS);
Prev = OS;
}
// According to the specification, at the end of the overlay, the location
// counter should be equal to the overlay base address plus size of the
// largest section seen in the overlay.
// Here we want to create the Dot assignment command to achieve that.
Expr MoveDot = [=] {
uint64_t Max = 0;
for (BaseCommand *Cmd : V)
Max = std::max(Max, cast<OutputSection>(Cmd)->Size);
return AddrExpr().getValue() + Max;
};
V.push_back(make<SymbolAssignment>(".", MoveDot, getCurrentLocation()));
return V;
}
void ScriptParser::readSections() {
Script->HasSectionsCommand = true;
@ -446,26 +490,48 @@ void ScriptParser::readSections() {
Config->SingleRoRx = true;
expect("{");
std::vector<BaseCommand *> V;
while (!errorCount() && !consume("}")) {
StringRef Tok = next();
BaseCommand *Cmd = readProvideOrAssignment(Tok);
if (!Cmd) {
if (Tok == "ASSERT")
Cmd = readAssert();
else
Cmd = readOutputSectionDescription(Tok);
if (Tok == "OVERLAY") {
for (BaseCommand *Cmd : readOverlay())
V.push_back(Cmd);
continue;
}
Script->SectionCommands.push_back(Cmd);
if (BaseCommand *Cmd = readAssignment(Tok))
V.push_back(Cmd);
else
V.push_back(readOutputSectionDescription(Tok));
}
if (!atEOF() && consume("INSERT")) {
std::vector<BaseCommand *> *Dest = nullptr;
if (consume("AFTER"))
Dest = &Script->InsertAfterCommands[next()];
else if (consume("BEFORE"))
Dest = &Script->InsertBeforeCommands[next()];
else
setError("expected AFTER/BEFORE, but got '" + next() + "'");
if (Dest)
Dest->insert(Dest->end(), V.begin(), V.end());
return;
}
Script->SectionCommands.insert(Script->SectionCommands.end(), V.begin(),
V.end());
}
static int precedence(StringRef Op) {
return StringSwitch<int>(Op)
.Cases("*", "/", 5)
.Cases("+", "-", 4)
.Cases("<<", ">>", 3)
.Cases("<", "<=", ">", ">=", "==", "!=", 2)
.Cases("&", "|", 1)
.Cases("*", "/", "%", 8)
.Cases("+", "-", 7)
.Cases("<<", ">>", 6)
.Cases("<", "<=", ">", ">=", "==", "!=", 5)
.Case("&", 4)
.Case("|", 3)
.Case("&&", 2)
.Case("||", 1)
.Default(-1);
}
@ -588,11 +654,7 @@ void ScriptParser::readSort() {
expect(")");
}
AssertCommand *ScriptParser::readAssert() {
return make<AssertCommand>(readAssertExpr());
}
Expr ScriptParser::readAssertExpr() {
Expr ScriptParser::readAssert() {
expect("(");
Expr E = readExpr();
expect(",");
@ -617,12 +679,14 @@ uint32_t ScriptParser::readFill() {
return V;
}
// Reads an expression and/or the special directive "(NOLOAD)" for an
// output section definition.
// Reads an expression and/or the special directive for an output
// section definition. Directive is one of following: "(NOLOAD)",
// "(COPY)", "(INFO)" or "(OVERLAY)".
//
// An output section name can be followed by an address expression
// and/or by "(NOLOAD)". This grammar is not LL(1) because "(" can be
// interpreted as either the beginning of some expression or "(NOLOAD)".
// and/or directive. This grammar is not LL(1) because "(" can be
// interpreted as either the beginning of some expression or beginning
// of directive.
//
// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html
@ -633,6 +697,11 @@ void ScriptParser::readSectionAddressType(OutputSection *Cmd) {
Cmd->Noload = true;
return;
}
if (consume("COPY") || consume("INFO") || consume("OVERLAY")) {
expect(")");
Cmd->NonAlloc = true;
return;
}
Cmd->AddrExpr = readExpr();
expect(")");
} else {
@ -657,10 +726,23 @@ static Expr checkAlignment(Expr E, std::string &Loc) {
};
}
OutputSection *ScriptParser::readOverlaySectionDescription() {
OutputSection *Cmd =
Script->createOutputSection(next(), getCurrentLocation());
Cmd->InOverlay = true;
expect("{");
while (!errorCount() && !consume("}"))
Cmd->SectionCommands.push_back(readInputSectionRules(next()));
Cmd->Phdrs = readOutputSectionPhdrs();
return Cmd;
}
OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
OutputSection *Cmd =
Script->createOutputSection(OutSec, getCurrentLocation());
size_t SymbolsReferenced = Script->ReferencedSymbols.size();
if (peek() != ":")
readSectionAddressType(Cmd);
expect(":");
@ -684,13 +766,10 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
StringRef Tok = next();
if (Tok == ";") {
// Empty commands are allowed. Do nothing here.
} else if (SymbolAssignment *Assign = readProvideOrAssignment(Tok)) {
} else if (SymbolAssignment *Assign = readAssignment(Tok)) {
Cmd->SectionCommands.push_back(Assign);
} else if (ByteCommand *Data = readByteCommand(Tok)) {
Cmd->SectionCommands.push_back(Data);
} else if (Tok == "ASSERT") {
Cmd->SectionCommands.push_back(readAssert());
expect(";");
} else if (Tok == "CONSTRUCTORS") {
// CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors
// by name. This is for very old file formats such as ECOFF/XCOFF.
@ -709,6 +788,14 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
if (consume(">"))
Cmd->MemoryRegionName = next();
if (consume("AT")) {
expect(">");
Cmd->LMARegionName = next();
}
if (Cmd->LMAExpr && !Cmd->LMARegionName.empty())
error("section can't have both LMA and a load region");
Cmd->Phdrs = readOutputSectionPhdrs();
if (consume("="))
@ -719,6 +806,8 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
// Consume optional comma following output section command.
consume(",");
if (Script->ReferencedSymbols.size() > SymbolsReferenced)
Cmd->ExpressionsUseSymbols = true;
return Cmd;
}
@ -741,30 +830,39 @@ uint32_t ScriptParser::parseFill(StringRef Tok) {
SymbolAssignment *ScriptParser::readProvideHidden(bool Provide, bool Hidden) {
expect("(");
SymbolAssignment *Cmd = readAssignment(next());
SymbolAssignment *Cmd = readSymbolAssignment(next());
Cmd->Provide = Provide;
Cmd->Hidden = Hidden;
expect(")");
expect(";");
return Cmd;
}
SymbolAssignment *ScriptParser::readProvideOrAssignment(StringRef Tok) {
SymbolAssignment *ScriptParser::readAssignment(StringRef Tok) {
// Assert expression returns Dot, so this is equal to ".=."
if (Tok == "ASSERT")
return make<SymbolAssignment>(".", readAssert(), getCurrentLocation());
size_t OldPos = Pos;
SymbolAssignment *Cmd = nullptr;
if (peek() == "=" || peek() == "+=") {
Cmd = readAssignment(Tok);
expect(";");
} else if (Tok == "PROVIDE") {
if (peek() == "=" || peek() == "+=")
Cmd = readSymbolAssignment(Tok);
else if (Tok == "PROVIDE")
Cmd = readProvideHidden(true, false);
} else if (Tok == "HIDDEN") {
else if (Tok == "HIDDEN")
Cmd = readProvideHidden(false, true);
} else if (Tok == "PROVIDE_HIDDEN") {
else if (Tok == "PROVIDE_HIDDEN")
Cmd = readProvideHidden(true, true);
if (Cmd) {
Cmd->CommandString =
Tok.str() + " " +
llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
expect(";");
}
return Cmd;
}
SymbolAssignment *ScriptParser::readAssignment(StringRef Name) {
SymbolAssignment *ScriptParser::readSymbolAssignment(StringRef Name) {
StringRef Op = next();
assert(Op == "=" || Op == "+=");
Expr E = readExpr();
@ -787,15 +885,31 @@ Expr ScriptParser::readExpr() {
return E;
}
static Expr combine(StringRef Op, Expr L, Expr R) {
Expr ScriptParser::combine(StringRef Op, Expr L, Expr R) {
if (Op == "+")
return [=] { return add(L(), R()); };
if (Op == "-")
return [=] { return sub(L(), R()); };
if (Op == "*")
return [=] { return mul(L(), R()); };
if (Op == "/")
return [=] { return div(L(), R()); };
return [=] { return L().getValue() * R().getValue(); };
if (Op == "/") {
std::string Loc = getCurrentLocation();
return [=]() -> uint64_t {
if (uint64_t RV = R().getValue())
return L().getValue() / RV;
error(Loc + ": division by zero");
return 0;
};
}
if (Op == "%") {
std::string Loc = getCurrentLocation();
return [=]() -> uint64_t {
if (uint64_t RV = R().getValue())
return L().getValue() % RV;
error(Loc + ": modulo by zero");
return 0;
};
}
if (Op == "<<")
return [=] { return L().getValue() << R().getValue(); };
if (Op == ">>")
@ -812,6 +926,10 @@ static Expr combine(StringRef Op, Expr L, Expr R) {
return [=] { return L().getValue() == R().getValue(); };
if (Op == "!=")
return [=] { return L().getValue() != R().getValue(); };
if (Op == "||")
return [=] { return L().getValue() || R().getValue(); };
if (Op == "&&")
return [=] { return L().getValue() && R().getValue(); };
if (Op == "&")
return [=] { return bitAnd(L(), R()); };
if (Op == "|")
@ -865,20 +983,13 @@ Expr ScriptParser::readConstant() {
if (S == "MAXPAGESIZE")
return [] { return Config->MaxPageSize; };
setError("unknown constant: " + S);
return {};
return [] { return 0; };
}
// Parses Tok as an integer. It recognizes hexadecimal (prefixed with
// "0x" or suffixed with "H") and decimal numbers. Decimal numbers may
// have "K" (Ki) or "M" (Mi) suffixes.
static Optional<uint64_t> parseInt(StringRef Tok) {
// Negative number
if (Tok.startswith("-")) {
if (Optional<uint64_t> Val = parseInt(Tok.substr(1)))
return -*Val;
return None;
}
// Hexadecimal
uint64_t Val;
if (Tok.startswith_lower("0x")) {
@ -917,12 +1028,21 @@ ByteCommand *ScriptParser::readByteCommand(StringRef Tok) {
.Default(-1);
if (Size == -1)
return nullptr;
return make<ByteCommand>(readParenExpr(), Size);
size_t OldPos = Pos;
Expr E = readParenExpr();
std::string CommandString =
Tok.str() + " " +
llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
return make<ByteCommand>(E, Size, CommandString);
}
StringRef ScriptParser::readParenLiteral() {
expect("(");
bool Orig = InExpr;
InExpr = false;
StringRef Tok = next();
InExpr = Orig;
expect(")");
return Tok;
}
@ -995,7 +1115,7 @@ Expr ScriptParser::readPrimary() {
};
}
if (Tok == "ASSERT")
return readAssertExpr();
return readAssert();
if (Tok == "CONSTANT")
return readConstant();
if (Tok == "DATA_SEGMENT_ALIGN") {
@ -1032,8 +1152,10 @@ Expr ScriptParser::readPrimary() {
}
if (Tok == "LENGTH") {
StringRef Name = readParenLiteral();
if (Script->MemoryRegions.count(Name) == 0)
if (Script->MemoryRegions.count(Name) == 0) {
setError("memory region not defined: " + Name);
return [] { return 0; };
}
return [=] { return Script->MemoryRegions[Name]->Length; };
}
if (Tok == "LOADADDR") {
@ -1044,10 +1166,22 @@ Expr ScriptParser::readPrimary() {
return Cmd->getLMA();
};
}
if (Tok == "MAX" || Tok == "MIN") {
expect("(");
Expr A = readExpr();
expect(",");
Expr B = readExpr();
expect(")");
if (Tok == "MIN")
return [=] { return std::min(A().getValue(), B().getValue()); };
return [=] { return std::max(A().getValue(), B().getValue()); };
}
if (Tok == "ORIGIN") {
StringRef Name = readParenLiteral();
if (Script->MemoryRegions.count(Name) == 0)
if (Script->MemoryRegions.count(Name) == 0) {
setError("memory region not defined: " + Name);
return [] { return 0; };
}
return [=] { return Script->MemoryRegions[Name]->Origin; };
}
if (Tok == "SEGMENT_START") {
@ -1229,6 +1363,9 @@ ScriptParser::readSymbols() {
// Reads an "extern C++" directive, e.g.,
// "extern "C++" { ns::*; "f(int, double)"; };"
//
// The last semicolon is optional. E.g. this is OK:
// "extern "C++" { ns::*; "f(int, double)" };"
std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
StringRef Tok = next();
bool IsCXX = Tok == "\"C++\"";
@ -1241,6 +1378,8 @@ std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
StringRef Tok = next();
bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok);
Ret.push_back({unquote(Tok), IsCXX, HasWildcard});
if (consume("}"))
return Ret;
expect(";");
}
@ -1280,11 +1419,10 @@ void ScriptParser::readMemory() {
uint64_t Length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
if (Script->MemoryRegions.count(Name))
MemoryRegion *MR =
make<MemoryRegion>(Name, Origin, Length, Flags, NegFlags);
if (!Script->MemoryRegions.insert({Name, MR}).second)
setError("region '" + Name + "' already defined");
MemoryRegion *MR = make<MemoryRegion>();
*MR = {Name, Origin, Length, Flags, NegFlags};
Script->MemoryRegions[Name] = MR;
}
}

View File

@ -1,62 +0,0 @@
//===- Strings.cpp -------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Strings.h"
#include "Config.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Demangle/Demangle.h"
#include <algorithm>
#include <cstring>
using namespace llvm;
using namespace lld;
using namespace lld::elf;
StringMatcher::StringMatcher(ArrayRef<StringRef> Pat) {
for (StringRef S : Pat) {
Expected<GlobPattern> Pat = GlobPattern::create(S);
if (!Pat)
error(toString(Pat.takeError()));
else
Patterns.push_back(*Pat);
}
}
bool StringMatcher::match(StringRef S) const {
for (const GlobPattern &Pat : Patterns)
if (Pat.match(S))
return true;
return false;
}
// Converts a hex string (e.g. "deadbeef") to a vector.
std::vector<uint8_t> elf::parseHex(StringRef S) {
std::vector<uint8_t> Hex;
while (!S.empty()) {
StringRef B = S.substr(0, 2);
S = S.substr(2);
uint8_t H;
if (!to_integer(B, H, 16)) {
error("not a hexadecimal value: " + B);
return {};
}
Hex.push_back(H);
}
return Hex;
}
// Returns true if S is valid as a C language identifier.
bool elf::isValidCIdentifier(StringRef S) {
return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
std::all_of(S.begin() + 1, S.end(),
[](char C) { return C == '_' || isAlnum(C); });
}

View File

@ -1,75 +0,0 @@
//===- Strings.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_STRINGS_H
#define LLD_ELF_STRINGS_H
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/GlobPattern.h"
#include <vector>
namespace lld {
namespace elf {
std::vector<uint8_t> parseHex(StringRef S);
bool isValidCIdentifier(StringRef S);
// This is a lazy version of StringRef. String size is computed lazily
// when it is needed. It is more efficient than StringRef to instantiate
// if you have a string whose size is unknown.
//
// ELF string tables contain a lot of null-terminated strings.
// Most of them are not necessary for the linker because they are names
// of local symbols and the linker doesn't use local symbol names for
// name resolution. So, we use this class to represents strings read
// from string tables.
class StringRefZ {
public:
StringRefZ() : Start(nullptr), Size(0) {}
StringRefZ(const char *S, size_t Size) : Start(S), Size(Size) {}
/*implicit*/ StringRefZ(const char *S) : Start(S), Size(-1) {}
/*implicit*/ StringRefZ(llvm::StringRef S)
: Start(S.data()), Size(S.size()) {}
operator llvm::StringRef() const {
if (Size == (size_t)-1)
Size = strlen(Start);
return {Start, Size};
}
private:
const char *Start;
mutable size_t Size;
};
// This class represents multiple glob patterns.
class StringMatcher {
public:
StringMatcher() = default;
explicit StringMatcher(ArrayRef<StringRef> Pat);
bool match(StringRef S) const;
private:
std::vector<llvm::GlobPattern> Patterns;
};
inline ArrayRef<uint8_t> toArrayRef(StringRef S) {
return {(const uint8_t *)S.data(), S.size()};
}
} // namespace elf
} // namespace lld
#endif

View File

@ -38,7 +38,7 @@ static InputFile *getFirstElf() {
return ObjectFiles[0];
if (!SharedFiles.empty())
return SharedFiles[0];
return nullptr;
return BitcodeFiles[0];
}
// All input object files must be for the same architecture
@ -82,6 +82,7 @@ template <class ELFT> void SymbolTable::addFile(InputFile *File) {
// Lazy object file
if (auto *F = dyn_cast<LazyObjFile>(File)) {
LazyObjFiles.push_back(F);
F->parse<ELFT>();
return;
}
@ -117,7 +118,7 @@ template <class ELFT> void SymbolTable::addFile(InputFile *File) {
// not in native object file format but in the LLVM bitcode format.
// This function compiles bitcode files into a few big native files
// using LLVM functions and replaces bitcode symbols with the results.
// Because all bitcode files that consist of a program are passed
// Because all bitcode files that the program consists of are passed
// to the compiler at once, it can do whole-program optimization.
template <class ELFT> void SymbolTable::addCombinedLTOObject() {
if (BitcodeFiles.empty())
@ -130,7 +131,10 @@ template <class ELFT> void SymbolTable::addCombinedLTOObject() {
for (InputFile *File : LTO->compile()) {
DenseSet<CachedHashStringRef> DummyGroups;
cast<ObjFile<ELFT>>(File)->parse(DummyGroups);
auto *Obj = cast<ObjFile<ELFT>>(File);
Obj->parse(DummyGroups);
for (Symbol *Sym : Obj->getGlobalSymbols())
Sym->parseSymbolVersion();
ObjectFiles.push_back(File);
}
}
@ -154,6 +158,12 @@ template <class ELFT> void SymbolTable::addSymbolWrap(StringRef Name) {
Symbol *Sym = find(Name);
if (!Sym)
return;
// Do not wrap the same symbol twice.
for (const WrappedSymbol &S : WrappedSymbols)
if (S.Sym == Sym)
return;
Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name));
Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name));
WrappedSymbols.push_back({Sym, Real, Wrap});
@ -186,7 +196,7 @@ void SymbolTable::applySymbolWrap() {
// First, make a copy of __real_sym.
Symbol *Real = nullptr;
if (W.Real->isDefined()) {
Real = (Symbol *)make<SymbolUnion>();
Real = reinterpret_cast<Symbol *>(make<SymbolUnion>());
memcpy(Real, W.Real, sizeof(SymbolUnion));
}
@ -234,8 +244,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
Symbol *Sym;
if (IsNew) {
Sym = (Symbol *)make<SymbolUnion>();
Sym->InVersionScript = false;
Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
Sym->Visibility = STV_DEFAULT;
Sym->IsUsedInRegularObj = false;
Sym->ExportDynamic = false;
@ -294,26 +303,88 @@ Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding,
uint8_t Visibility = getVisibility(StOther);
std::tie(S, WasInserted) =
insert(Name, Type, Visibility, CanOmitFromDynSym, File);
// An undefined symbol with non default visibility must be satisfied
// in the same DSO.
if (WasInserted || (isa<SharedSymbol>(S) && Visibility != STV_DEFAULT)) {
replaceSymbol<Undefined>(S, File, Name, Binding, StOther, Type);
return S;
}
if (S->isShared() || S->isLazy() || (S->isUndefined() && Binding != STB_WEAK))
S->Binding = Binding;
if (Binding != STB_WEAK) {
if (!Config->GcSections && Binding != STB_WEAK)
if (auto *SS = dyn_cast<SharedSymbol>(S))
if (!Config->GcSections)
SS->getFile<ELFT>().IsNeeded = true;
}
if (auto *L = dyn_cast<Lazy>(S)) {
SS->getFile<ELFT>().IsNeeded = true;
if (S->isLazy()) {
// An undefined weak will not fetch archive members. See comment on Lazy in
// Symbols.h for the details.
if (Binding == STB_WEAK)
L->Type = Type;
else if (InputFile *F = L->fetch())
addFile<ELFT>(F);
if (Binding == STB_WEAK) {
S->Type = Type;
return S;
}
// Do extra check for --warn-backrefs.
//
// --warn-backrefs is an option to prevent an undefined reference from
// fetching an archive member written earlier in the command line. It can be
// used to keep compatibility with GNU linkers to some degree.
// I'll explain the feature and why you may find it useful in this comment.
//
// lld's symbol resolution semantics is more relaxed than traditional Unix
// linkers. For example,
//
// ld.lld foo.a bar.o
//
// succeeds even if bar.o contains an undefined symbol that has to be
// resolved by some object file in foo.a. Traditional Unix linkers don't
// allow this kind of backward reference, as they visit each file only once
// from left to right in the command line while resolving all undefined
// symbols at the moment of visiting.
//
// In the above case, since there's no undefined symbol when a linker visits
// foo.a, no files are pulled out from foo.a, and because the linker forgets
// about foo.a after visiting, it can't resolve undefined symbols in bar.o
// that could have been resolved otherwise.
//
// That lld accepts more relaxed form means that (besides it'd make more
// sense) you can accidentally write a command line or a build file that
// works only with lld, even if you have a plan to distribute it to wider
// users who may be using GNU linkers. With --warn-backrefs, you can detect
// a library order that doesn't work with other Unix linkers.
//
// The option is also useful to detect cyclic dependencies between static
// archives. Again, lld accepts
//
// ld.lld foo.a bar.a
//
// even if foo.a and bar.a depend on each other. With --warn-backrefs, it is
// handled as an error.
//
// Here is how the option works. We assign a group ID to each file. A file
// with a smaller group ID can pull out object files from an archive file
// with an equal or greater group ID. Otherwise, it is a reverse dependency
// and an error.
//
// A file outside --{start,end}-group gets a fresh ID when instantiated. All
// files within the same --{start,end}-group get the same group ID. E.g.
//
// ld.lld A B --start-group C D --end-group E
//
// A forms group 0. B form group 1. C and D (including their member object
// files) form group 2. E forms group 3. I think that you can see how this
// group assignment rule simulates the traditional linker's semantics.
bool Backref =
Config->WarnBackrefs && File && S->File->GroupId < File->GroupId;
fetchLazy<ELFT>(S);
// We don't report backward references to weak symbols as they can be
// overridden later.
if (Backref && S->Binding != STB_WEAK)
warn("backward reference detected: " + Name + " in " + toString(File) +
" refers to " + toString(S->File));
}
return S;
}
@ -381,7 +452,11 @@ Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
bool WasInserted;
std::tie(S, WasInserted) = insert(N, Type, getVisibility(StOther),
/*CanOmitFromDynSym*/ false, &File);
int Cmp = compareDefined(S, WasInserted, Binding, N);
if (Cmp < 0)
return S;
if (Cmp > 0) {
auto *Bss = make<BssSection>("COMMON", Size, Alignment);
Bss->File = &File;
@ -389,45 +464,43 @@ Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
InputSections.push_back(Bss);
replaceSymbol<Defined>(S, &File, N, Binding, StOther, Type, 0, Size, Bss);
} else if (Cmp == 0) {
auto *D = cast<Defined>(S);
auto *Bss = dyn_cast_or_null<BssSection>(D->Section);
if (!Bss) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
warn("common " + S->getName() + " is overridden");
return S;
}
return S;
}
auto *D = cast<Defined>(S);
auto *Bss = dyn_cast_or_null<BssSection>(D->Section);
if (!Bss) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
warn("multiple common of " + D->getName());
warn("common " + S->getName() + " is overridden");
return S;
}
Bss->Alignment = std::max(Bss->Alignment, Alignment);
if (Size > Bss->Size) {
D->File = Bss->File = &File;
D->Size = Bss->Size = Size;
}
if (Config->WarnCommon)
warn("multiple common of " + D->getName());
Bss->Alignment = std::max(Bss->Alignment, Alignment);
if (Size > Bss->Size) {
D->File = Bss->File = &File;
D->Size = Bss->Size = Size;
}
return S;
}
static void warnOrError(const Twine &Msg) {
if (Config->AllowMultipleDefinition)
warn(Msg);
else
error(Msg);
}
static void reportDuplicate(Symbol *Sym, InputFile *NewFile) {
warnOrError("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
if (!Config->AllowMultipleDefinition)
error("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
}
static void reportDuplicate(Symbol *Sym, InputSectionBase *ErrSec,
uint64_t ErrOffset) {
static void reportDuplicate(Symbol *Sym, InputFile *NewFile,
InputSectionBase *ErrSec, uint64_t ErrOffset) {
if (Config->AllowMultipleDefinition)
return;
Defined *D = cast<Defined>(Sym);
if (!D->Section || !ErrSec) {
reportDuplicate(Sym, ErrSec ? ErrSec->File : nullptr);
reportDuplicate(Sym, NewFile);
return;
}
@ -451,7 +524,7 @@ static void reportDuplicate(Symbol *Sym, InputSectionBase *ErrSec,
if (!Src2.empty())
Msg += Src2 + "\n>>> ";
Msg += Obj2;
warnOrError(Msg);
error(Msg);
}
Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
@ -467,7 +540,8 @@ Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
replaceSymbol<Defined>(S, File, Name, Binding, StOther, Type, Value, Size,
Section);
else if (Cmp == 0)
reportDuplicate(S, dyn_cast_or_null<InputSectionBase>(Section), Value);
reportDuplicate(S, File, dyn_cast_or_null<InputSectionBase>(Section),
Value);
return S;
}
@ -488,15 +562,16 @@ void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> &File,
// An undefined symbol with non default visibility must be satisfied
// in the same DSO.
if (WasInserted || ((S->isUndefined() || S->isLazy()) &&
S->getVisibility() == STV_DEFAULT)) {
if (WasInserted ||
((S->isUndefined() || S->isLazy()) && S->Visibility == STV_DEFAULT)) {
uint8_t Binding = S->Binding;
bool WasUndefined = S->isUndefined();
replaceSymbol<SharedSymbol>(S, File, Name, Sym.getBinding(), Sym.st_other,
Sym.getType(), Sym.st_value, Sym.st_size,
Alignment, VerdefIndex);
if (!WasInserted) {
S->Binding = Binding;
if (!S->isWeak() && !Config->GcSections)
if (!S->isWeak() && !Config->GcSections && WasUndefined)
File.IsNeeded = true;
}
}
@ -527,85 +602,58 @@ Symbol *SymbolTable::find(StringRef Name) {
return SymVector[It->second];
}
template <class ELFT>
Symbol *SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &F,
const object::Archive::Symbol Sym) {
// This is used to handle lazy symbols. May replace existent
// symbol with lazy version or request to Fetch it.
template <class ELFT, typename LazyT, typename... ArgT>
static void replaceOrFetchLazy(StringRef Name, InputFile &File,
llvm::function_ref<InputFile *()> Fetch,
ArgT &&... Arg) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
std::tie(S, WasInserted) = Symtab->insert(Name);
if (WasInserted) {
replaceSymbol<LazyArchive>(S, F, Sym, Symbol::UnknownType);
return S;
replaceSymbol<LazyT>(S, File, Symbol::UnknownType,
std::forward<ArgT>(Arg)...);
return;
}
if (!S->isUndefined())
return S;
return;
// An undefined weak will not fetch archive members. See comment on Lazy in
// Symbols.h for the details.
if (S->isWeak()) {
replaceSymbol<LazyArchive>(S, F, Sym, S->Type);
replaceSymbol<LazyT>(S, File, S->Type, std::forward<ArgT>(Arg)...);
S->Binding = STB_WEAK;
return S;
return;
}
std::pair<MemoryBufferRef, uint64_t> MBInfo = F.getMember(&Sym);
if (!MBInfo.first.getBuffer().empty())
addFile<ELFT>(createObjectFile(MBInfo.first, F.getName(), MBInfo.second));
return S;
if (InputFile *F = Fetch())
Symtab->addFile<ELFT>(F);
}
template <class ELFT>
void SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &F,
const object::Archive::Symbol Sym) {
replaceOrFetchLazy<ELFT, LazyArchive>(Name, F, [&]() { return F.fetch(Sym); },
Sym);
}
template <class ELFT>
void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &Obj) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
replaceSymbol<LazyObject>(S, Obj, Name, Symbol::UnknownType);
return;
}
if (!S->isUndefined())
return;
// See comment for addLazyArchive above.
if (S->isWeak())
replaceSymbol<LazyObject>(S, Obj, Name, S->Type);
else if (InputFile *F = Obj.fetch())
addFile<ELFT>(F);
replaceOrFetchLazy<ELFT, LazyObject>(Name, Obj, [&]() { return Obj.fetch(); },
Name);
}
// If we already saw this symbol, force loading its file.
template <class ELFT> void SymbolTable::fetchIfLazy(StringRef Name) {
if (Symbol *B = find(Name)) {
// Mark the symbol not to be eliminated by LTO
// even if it is a bitcode symbol.
B->IsUsedInRegularObj = true;
if (auto *L = dyn_cast<Lazy>(B))
if (InputFile *File = L->fetch())
addFile<ELFT>(File);
template <class ELFT> void SymbolTable::fetchLazy(Symbol *Sym) {
if (auto *S = dyn_cast<LazyArchive>(Sym)) {
if (InputFile *File = S->fetch())
addFile<ELFT>(File);
return;
}
}
// 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::scanShlibUndefined() {
for (InputFile *F : SharedFiles) {
for (StringRef U : cast<SharedFile<ELFT>>(F)->getUndefinedSymbols()) {
Symbol *Sym = find(U);
if (!Sym || !Sym->isDefined())
continue;
Sym->ExportDynamic = true;
// If -dynamic-list is given, the default version is set to
// VER_NDX_LOCAL, which prevents a symbol to be exported via .dynsym.
// Set to VER_NDX_GLOBAL so the symbol will be handled as if it were
// specified by -dynamic-list.
Sym->VersionId = VER_NDX_GLOBAL;
}
}
auto *S = cast<LazyObject>(Sym);
if (InputFile *File = cast<LazyObjFile>(S->File)->fetch())
addFile<ELFT>(File);
}
// Initialize DemangledSyms with a map from demangled symbols to symbol
@ -704,7 +752,7 @@ void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
// Get a list of symbols which we need to assign the version to.
std::vector<Symbol *> Syms = findByVersion(Ver);
if (Syms.empty()) {
if (Config->NoUndefinedVersion)
if (!Config->UndefinedVersion)
error("version script assignment of '" + VersionName + "' to symbol '" +
Ver.Name + "' failed: symbol not defined");
return;
@ -718,10 +766,10 @@ void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
if (Sym->getName().contains('@'))
continue;
if (Sym->InVersionScript)
warn("duplicate symbol '" + Ver.Name + "' in version script");
if (Sym->VersionId != Config->DefaultSymbolVersion &&
Sym->VersionId != VersionId)
error("duplicate symbol '" + Ver.Name + "' in version script");
Sym->VersionId = VersionId;
Sym->InVersionScript = true;
}
}
@ -769,6 +817,11 @@ void SymbolTable::scanVersionScript() {
Sym->parseSymbolVersion();
}
template void SymbolTable::addFile<ELF32LE>(InputFile *);
template void SymbolTable::addFile<ELF32BE>(InputFile *);
template void SymbolTable::addFile<ELF64LE>(InputFile *);
template void SymbolTable::addFile<ELF64BE>(InputFile *);
template void SymbolTable::addSymbolWrap<ELF32LE>(StringRef);
template void SymbolTable::addSymbolWrap<ELF32BE>(StringRef);
template void SymbolTable::addSymbolWrap<ELF64LE>(StringRef);
@ -793,16 +846,16 @@ template void SymbolTable::addCombinedLTOObject<ELF32BE>();
template void SymbolTable::addCombinedLTOObject<ELF64LE>();
template void SymbolTable::addCombinedLTOObject<ELF64BE>();
template Symbol *
template void
SymbolTable::addLazyArchive<ELF32LE>(StringRef, ArchiveFile &,
const object::Archive::Symbol);
template Symbol *
template void
SymbolTable::addLazyArchive<ELF32BE>(StringRef, ArchiveFile &,
const object::Archive::Symbol);
template Symbol *
template void
SymbolTable::addLazyArchive<ELF64LE>(StringRef, ArchiveFile &,
const object::Archive::Symbol);
template Symbol *
template void
SymbolTable::addLazyArchive<ELF64BE>(StringRef, ArchiveFile &,
const object::Archive::Symbol);
@ -811,6 +864,11 @@ template void SymbolTable::addLazyObject<ELF32BE>(StringRef, LazyObjFile &);
template void SymbolTable::addLazyObject<ELF64LE>(StringRef, LazyObjFile &);
template void SymbolTable::addLazyObject<ELF64BE>(StringRef, LazyObjFile &);
template void SymbolTable::fetchLazy<ELF32LE>(Symbol *);
template void SymbolTable::fetchLazy<ELF32BE>(Symbol *);
template void SymbolTable::fetchLazy<ELF64LE>(Symbol *);
template void SymbolTable::fetchLazy<ELF64BE>(Symbol *);
template void SymbolTable::addShared<ELF32LE>(StringRef, SharedFile<ELF32LE> &,
const typename ELF32LE::Sym &,
uint32_t Alignment, uint32_t);
@ -823,13 +881,3 @@ template void SymbolTable::addShared<ELF64LE>(StringRef, SharedFile<ELF64LE> &,
template void SymbolTable::addShared<ELF64BE>(StringRef, SharedFile<ELF64BE> &,
const typename ELF64BE::Sym &,
uint32_t Alignment, uint32_t);
template void SymbolTable::fetchIfLazy<ELF32LE>(StringRef);
template void SymbolTable::fetchIfLazy<ELF32BE>(StringRef);
template void SymbolTable::fetchIfLazy<ELF64LE>(StringRef);
template void SymbolTable::fetchIfLazy<ELF64BE>(StringRef);
template void SymbolTable::scanShlibUndefined<ELF32LE>();
template void SymbolTable::scanShlibUndefined<ELF32BE>();
template void SymbolTable::scanShlibUndefined<ELF64LE>();
template void SymbolTable::scanShlibUndefined<ELF64BE>();

View File

@ -12,7 +12,7 @@
#include "InputFiles.h"
#include "LTO.h"
#include "Strings.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseMap.h"
@ -60,8 +60,8 @@ class SymbolTable {
uint32_t VerdefIndex);
template <class ELFT>
Symbol *addLazyArchive(StringRef Name, ArchiveFile &F,
const llvm::object::Archive::Symbol S);
void addLazyArchive(StringRef Name, ArchiveFile &F,
const llvm::object::Archive::Symbol S);
template <class ELFT> void addLazyObject(StringRef Name, LazyObjFile &Obj);
@ -77,8 +77,8 @@ class SymbolTable {
uint8_t Visibility, bool CanOmitFromDynSym,
InputFile *File);
template <class ELFT> void fetchIfLazy(StringRef Name);
template <class ELFT> void scanShlibUndefined();
template <class ELFT> void fetchLazy(Symbol *Sym);
void scanVersionScript();
Symbol *find(StringRef Name);
@ -90,7 +90,6 @@ class SymbolTable {
private:
std::vector<Symbol *> findByVersion(SymbolVersion Ver);
std::vector<Symbol *> findAllByVersion(SymbolVersion Ver);
void defsym(Symbol *Dst, Symbol *Src);
llvm::StringMap<std::vector<Symbol *>> &getDemangledSyms();
void handleAnonymousVersion();

View File

@ -14,7 +14,6 @@
#include "SyntheticSections.h"
#include "Target.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
@ -39,6 +38,7 @@ Defined *ElfSym::GlobalOffsetTable;
Defined *ElfSym::MipsGp;
Defined *ElfSym::MipsGpDisp;
Defined *ElfSym::MipsLocalGp;
Defined *ElfSym::RelaIpltEnd;
static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
switch (Sym.kind()) {
@ -58,6 +58,7 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
return D.Value;
IS = IS->Repl;
uint64_t Offset = D.Value;
// An object in an SHF_MERGE section might be referenced via a
@ -76,8 +77,6 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
Addend = 0;
}
const OutputSection *OutSec = IS->getOutputSection();
// In the typical case, this is actually very simple and boils
// down to adding together 3 numbers:
// 1. The address of the output section.
@ -88,7 +87,7 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
// If you understand the data structures involved with this next
// line (and how they get built), then you have a pretty good
// understanding of the linker.
uint64_t VA = (OutSec ? OutSec->Addr : 0) + IS->getOffset(Offset);
uint64_t VA = IS->getVA(Offset);
if (D.isTls() && !Config->Relocatable) {
if (!Out::TlsPhdr)
@ -98,20 +97,12 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
}
return VA;
}
case Symbol::SharedKind: {
auto &SS = cast<SharedSymbol>(Sym);
if (SS.CopyRelSec)
return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff;
if (SS.NeedsPltAddr)
return Sym.getPltVA();
return 0;
}
case Symbol::SharedKind:
case Symbol::UndefinedKind:
return 0;
case Symbol::LazyArchiveKind:
case Symbol::LazyObjectKind:
assert(Sym.IsUsedInRegularObj && "lazy symbol reached writer");
return 0;
llvm_unreachable("lazy symbol reached writer");
}
llvm_unreachable("invalid symbol kind");
}
@ -134,22 +125,26 @@ uint64_t Symbol::getGotPltVA() const {
}
uint64_t Symbol::getGotPltOffset() const {
return GotPltIndex * Target->GotPltEntrySize;
if (IsInIgot)
return PltIndex * Target->GotPltEntrySize;
return (PltIndex + Target->GotPltHeaderEntriesNum) * Target->GotPltEntrySize;
}
uint64_t Symbol::getPltVA() const {
if (this->IsInIplt)
return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize;
return InX::Plt->getVA() + Target->PltHeaderSize +
PltIndex * Target->PltEntrySize;
return InX::Plt->getVA() + Target->getPltEntryOffset(PltIndex);
}
uint64_t Symbol::getPltOffset() const {
assert(!this->IsInIplt);
return Target->getPltEntryOffset(PltIndex);
}
uint64_t Symbol::getSize() const {
if (const auto *DR = dyn_cast<Defined>(this))
return DR->Size;
if (const auto *S = dyn_cast<SharedSymbol>(this))
return S->Size;
return 0;
return cast<SharedSymbol>(this)->Size;
}
OutputSection *Symbol::getOutputSection() const {
@ -158,13 +153,6 @@ OutputSection *Symbol::getOutputSection() const {
return Sec->Repl->getOutputSection();
return nullptr;
}
if (auto *S = dyn_cast<SharedSymbol>(this)) {
if (S->CopyRelSec)
return S->CopyRelSec->getParent();
return nullptr;
}
return nullptr;
}
@ -180,7 +168,7 @@ void Symbol::parseSymbolVersion() {
return;
// Truncate the symbol name so that it doesn't include the version string.
Name = {S.data(), Pos};
NameSize = Pos;
// If this is not in this DSO, it is not a definition.
if (!isDefined())
@ -206,33 +194,15 @@ void Symbol::parseSymbolVersion() {
// It is an error if the specified version is not defined.
// Usually version script is not provided when linking executable,
// but we may still want to override a versioned symbol from DSO,
// so we do not report error in this case.
if (Config->Shared)
// so we do not report error in this case. We also do not error
// if the symbol has a local version as it won't be in the dynamic
// symbol table.
if (Config->Shared && VersionId != VER_NDX_LOCAL)
error(toString(File) + ": symbol " + S + " has undefined version " +
Verstr);
}
InputFile *Lazy::fetch() {
if (auto *S = dyn_cast<LazyArchive>(this))
return S->fetch();
return cast<LazyObject>(this)->fetch();
}
ArchiveFile &LazyArchive::getFile() { return *cast<ArchiveFile>(File); }
InputFile *LazyArchive::fetch() {
std::pair<MemoryBufferRef, uint64_t> MBInfo = getFile().getMember(&Sym);
// getMember returns an empty buffer if the member was already
// read from the library.
if (MBInfo.first.getBuffer().empty())
return nullptr;
return createObjectFile(MBInfo.first, getFile().getName(), MBInfo.second);
}
LazyObjFile &LazyObject::getFile() { return *cast<LazyObjFile>(File); }
InputFile *LazyObject::fetch() { return getFile().fetch(); }
InputFile *LazyArchive::fetch() { return cast<ArchiveFile>(File)->fetch(Sym); }
uint8_t Symbol::computeBinding() const {
if (Config->Relocatable)
@ -241,7 +211,7 @@ uint8_t Symbol::computeBinding() const {
return STB_LOCAL;
if (VersionId == VER_NDX_LOCAL && isDefined())
return STB_LOCAL;
if (Config->NoGnuUnique && Binding == STB_GNU_UNIQUE)
if (!Config->GnuUnique && Binding == STB_GNU_UNIQUE)
return STB_GLOBAL;
return Binding;
}
@ -273,6 +243,27 @@ void elf::printTraceSymbol(Symbol *Sym) {
message(toString(Sym->File) + S + Sym->getName());
}
void elf::warnUnorderableSymbol(const Symbol *Sym) {
if (!Config->WarnSymbolOrdering)
return;
const InputFile *File = Sym->File;
auto *D = dyn_cast<Defined>(Sym);
auto Warn = [&](StringRef S) { warn(toString(File) + S + Sym->getName()); };
if (Sym->isUndefined())
Warn(": unable to order undefined symbol: ");
else if (Sym->isShared())
Warn(": unable to order shared symbol: ");
else if (D && !D->Section)
Warn(": unable to order absolute symbol: ");
else if (D && isa<OutputSection>(D->Section))
Warn(": unable to order synthetic symbol: ");
else if (D && !D->Section->Repl->Live)
Warn(": unable to order discarded symbol: ");
}
// Returns a symbol for an error message.
std::string lld::toString(const Symbol &B) {
if (Config->Demangle)

View File

@ -7,8 +7,7 @@
//
//===----------------------------------------------------------------------===//
//
// All symbols are handled as SymbolBodies regardless of their types.
// This file defines various types of SymbolBodies.
// This file defines various types of Symbols.
//
//===----------------------------------------------------------------------===//
@ -16,9 +15,8 @@
#define LLD_ELF_SYMBOLS_H
#include "InputSection.h"
#include "Strings.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Strings.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
@ -34,6 +32,20 @@ template <class ELFT> class ObjFile;
class OutputSection;
template <class ELFT> class SharedFile;
// This is a StringRef-like container that doesn't run strlen().
//
// ELF string tables contain a lot of null-terminated strings. Most of them
// are not necessary for the linker because they are names of local symbols,
// and the linker doesn't use local symbol names for name resolution. So, we
// use this class to represents strings read from string tables.
struct StringRefZ {
StringRefZ(const char *S) : Data(S), Size(-1) {}
StringRefZ(StringRef S) : Data(S.data()), Size(S.size()) {}
const char *Data;
const uint32_t Size;
};
// The base class for real symbol classes.
class Symbol {
public:
@ -47,6 +59,25 @@ class Symbol {
Kind kind() const { return static_cast<Kind>(SymbolKind); }
// The file from which this symbol was created.
InputFile *File;
protected:
const char *NameData;
mutable uint32_t NameSize;
public:
uint32_t DynsymIndex = 0;
uint32_t GotIndex = -1;
uint32_t PltIndex = -1;
uint32_t GlobalDynIndex = -1;
// This field is a index to the symbol's version definition.
uint32_t VerdefIndex = -1;
// Version definition index.
uint16_t VersionId;
// Symbol binding. This is not overwritten by replaceSymbol to track
// changes during resolution. In particular:
// - An undefined weak is still weak when it resolves to a shared library.
@ -54,8 +85,11 @@ class Symbol {
// remember it is weak.
uint8_t Binding;
// Version definition index.
uint16_t VersionId;
// The following fields have the same meaning as the ELF symbol attributes.
uint8_t Type; // symbol type
uint8_t StOther; // st_other field value
const uint8_t SymbolKind;
// Symbol visibility. This is the computed minimum visibility of all
// observed non-DSO symbols.
@ -81,12 +115,6 @@ class Symbol {
// True if this symbol is specified by --trace-symbol option.
unsigned Traced : 1;
// This symbol version was found in a version script.
unsigned InVersionScript : 1;
// The file from which this symbol was created.
InputFile *File;
bool includeInDynsym() const;
uint8_t computeBinding() const;
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
@ -100,15 +128,15 @@ class Symbol {
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
}
// True is this is an undefined weak symbol. This only works once
// all input files have been added.
bool isUndefWeak() const {
// See comment on Lazy the details.
return isWeak() && (isUndefined() || isLazy());
// True if this is an undefined weak symbol.
bool isUndefWeak() const { return isWeak() && isUndefined(); }
StringRef getName() const {
if (NameSize == (uint32_t)-1)
NameSize = strlen(NameData);
return {NameData, NameSize};
}
StringRef getName() const { return Name; }
uint8_t getVisibility() const { return StOther & 0x3; }
void parseSymbolVersion();
bool isInGot() const { return GotIndex != -1U; }
@ -121,34 +149,22 @@ class Symbol {
uint64_t getGotPltOffset() const;
uint64_t getGotPltVA() const;
uint64_t getPltVA() const;
uint64_t getPltOffset() const;
uint64_t getSize() const;
OutputSection *getOutputSection() const;
uint32_t DynsymIndex = 0;
uint32_t GotIndex = -1;
uint32_t GotPltIndex = -1;
uint32_t PltIndex = -1;
uint32_t GlobalDynIndex = -1;
protected:
Symbol(Kind K, InputFile *File, StringRefZ Name, uint8_t Binding,
uint8_t StOther, uint8_t Type)
: Binding(Binding), File(File), SymbolKind(K), NeedsPltAddr(false),
IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
IsInIgot(false), IsPreemptible(false), Used(!Config->GcSections),
Type(Type), StOther(StOther), Name(Name) {}
const unsigned SymbolKind : 8;
: File(File), NameData(Name.Data), NameSize(Name.Size), Binding(Binding),
Type(Type), StOther(StOther), SymbolKind(K), NeedsPltAddr(false),
IsInIplt(false), IsInIgot(false), IsPreemptible(false),
Used(!Config->GcSections), NeedsTocRestore(false) {}
public:
// True the symbol should point to its PLT entry.
// For SharedSymbol only.
unsigned NeedsPltAddr : 1;
// True if this symbol has an entry in the global part of MIPS GOT.
unsigned IsInGlobalMipsGot : 1;
// True if this symbol is referenced by 32-bit GOT relocations.
unsigned Is32BitMipsGot : 1;
// True if this symbol is in the Iplt sub-section of the Plt.
unsigned IsInIplt : 1;
@ -156,14 +172,15 @@ class Symbol {
// True if this symbol is in the Igot sub-section of the .got.plt or .got.
unsigned IsInIgot : 1;
// True if this symbol is preemptible at load time.
unsigned IsPreemptible : 1;
// True if an undefined or shared symbol is used from a live section.
unsigned Used : 1;
// The following fields have the same meaning as the ELF symbol attributes.
uint8_t Type; // symbol type
uint8_t StOther; // st_other field value
// True if a call to this symbol needs to be followed by a restore of the
// PPC64 toc pointer.
unsigned NeedsTocRestore : 1;
// The Type field may also have this value. It means that we have not yet seen
// a non-Lazy symbol with this name, so we don't know what its type is. The
@ -178,9 +195,6 @@ class Symbol {
bool isGnuIFunc() const { return Type == llvm::ELF::STT_GNU_IFUNC; }
bool isObject() const { return Type == llvm::ELF::STT_OBJECT; }
bool isFile() const { return Type == llvm::ELF::STT_FILE; }
protected:
StringRefZ Name;
};
// Represents a symbol that is defined in the current output file.
@ -214,8 +228,9 @@ class SharedSymbol : public Symbol {
SharedSymbol(InputFile &File, StringRef Name, uint8_t Binding,
uint8_t StOther, uint8_t Type, uint64_t Value, uint64_t Size,
uint32_t Alignment, uint32_t VerdefIndex)
: Symbol(SharedKind, &File, Name, Binding, StOther, Type), Value(Value),
Size(Size), VerdefIndex(VerdefIndex), Alignment(Alignment) {
: Symbol(SharedKind, &File, Name, Binding, StOther, Type),
Alignment(Alignment), Value(Value), Size(Size) {
this->VerdefIndex = VerdefIndex;
// GNU ifunc is a mechanism to allow user-supplied functions to
// resolve PLT slot values at load-time. This is contrary to the
// regular symbol resolution scheme in which symbols are resolved just
@ -240,54 +255,36 @@ class SharedSymbol : public Symbol {
return *cast<SharedFile<ELFT>>(File);
}
// If not null, there is a copy relocation to this section.
InputSection *CopyRelSec = nullptr;
uint32_t Alignment;
uint64_t Value; // st_value
uint64_t Size; // st_size
// This field is a index to the symbol's version definition.
uint32_t VerdefIndex;
uint32_t Alignment;
};
// This represents a symbol that is not yet in the link, but we know where to
// find it if needed. If the resolver finds both Undefined and Lazy for the same
// name, it will ask the Lazy to load a file.
// LazyArchive and LazyObject represent a symbols that is not yet in the link,
// but we know where to find it if needed. If the resolver finds both Undefined
// and Lazy for the same name, it will ask the Lazy to load a file.
//
// A special complication is the handling of weak undefined symbols. They should
// not load a file, but we have to remember we have seen both the weak undefined
// and the lazy. We represent that with a lazy symbol with a weak binding. This
// means that code looking for undefined symbols normally also has to take lazy
// symbols into consideration.
class Lazy : public Symbol {
public:
static bool classof(const Symbol *S) { return S->isLazy(); }
// Returns an object file for this symbol, or a nullptr if the file
// was already returned.
InputFile *fetch();
protected:
Lazy(Kind K, InputFile &File, StringRef Name, uint8_t Type)
: Symbol(K, &File, Name, llvm::ELF::STB_GLOBAL, llvm::ELF::STV_DEFAULT,
Type) {}
};
// 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.
class LazyArchive : public Lazy {
class LazyArchive : public Symbol {
public:
LazyArchive(InputFile &File, const llvm::object::Archive::Symbol S,
uint8_t Type)
: Lazy(LazyArchiveKind, File, S.getName(), Type), Sym(S) {}
LazyArchive(InputFile &File, uint8_t Type,
const llvm::object::Archive::Symbol S)
: Symbol(LazyArchiveKind, &File, S.getName(), llvm::ELF::STB_GLOBAL,
llvm::ELF::STV_DEFAULT, Type),
Sym(S) {}
static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; }
ArchiveFile &getFile();
InputFile *fetch();
private:
@ -296,15 +293,13 @@ class LazyArchive : public Lazy {
// LazyObject symbols represents symbols in object files between
// --start-lib and --end-lib options.
class LazyObject : public Lazy {
class LazyObject : public Symbol {
public:
LazyObject(InputFile &File, StringRef Name, uint8_t Type)
: Lazy(LazyObjectKind, File, Name, Type) {}
LazyObject(InputFile &File, uint8_t Type, StringRef Name)
: Symbol(LazyObjectKind, &File, Name, llvm::ELF::STB_GLOBAL,
llvm::ELF::STV_DEFAULT, Type) {}
static bool classof(const Symbol *S) { return S->kind() == LazyObjectKind; }
LazyObjFile &getFile();
InputFile *fetch();
};
// Some linker-generated symbols need to be created as
@ -334,6 +329,9 @@ struct ElfSym {
static Defined *MipsGp;
static Defined *MipsGpDisp;
static Defined *MipsLocalGp;
// __rela_iplt_end or __rel_iplt_end
static Defined *RelaIpltEnd;
};
// A buffer class that is large enough to hold any Symbol-derived
@ -351,6 +349,8 @@ void printTraceSymbol(Symbol *Sym);
template <typename T, typename... ArgT>
void replaceSymbol(Symbol *S, ArgT &&... Arg) {
static_assert(std::is_trivially_destructible<T>(),
"Symbol types must be trivially destructible");
static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
static_assert(alignof(T) <= alignof(SymbolUnion),
"SymbolUnion not aligned enough");
@ -367,13 +367,14 @@ void replaceSymbol(Symbol *S, ArgT &&... Arg) {
S->ExportDynamic = Sym.ExportDynamic;
S->CanInline = Sym.CanInline;
S->Traced = Sym.Traced;
S->InVersionScript = Sym.InVersionScript;
// Print out a log message if --trace-symbol was specified.
// This is for debugging.
if (S->Traced)
printTraceSymbol(S);
}
void warnUnorderableSymbol(const Symbol *Sym);
} // namespace elf
std::string toString(const elf::Symbol &B);

File diff suppressed because it is too large Load Diff

View File

@ -26,10 +26,12 @@
#include "InputSection.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Support/Endian.h"
#include <functional>
namespace lld {
namespace elf {
class Defined;
class SharedSymbol;
class SyntheticSection : public InputSection {
@ -51,7 +53,6 @@ class SyntheticSection : public InputSection {
// If any additional finalization of contents are needed post thunk creation.
virtual void postThunkContents() {}
virtual bool empty() const { return false; }
uint64_t getVA() const;
static bool classof(const SectionBase *D) {
return D->kind() == InputSectionBase::Synthetic;
@ -78,13 +79,18 @@ class EhFrameSection final : public SyntheticSection {
size_t NumFdes = 0;
struct FdeData {
uint32_t Pc;
uint32_t FdeVA;
uint32_t PcRel;
uint32_t FdeVARel;
};
std::vector<FdeData> getFdeData() const;
ArrayRef<CieRecord *> getCieRecords() const { return CieRecords; }
private:
// This is used only when parsing EhInputSection. We keep it here to avoid
// allocating one for each EhInputSection.
llvm::DenseMap<size_t, CieRecord *> OffsetToCie;
uint64_t Size = 0;
template <class ELFT, class RelTy>
@ -173,12 +179,21 @@ class MipsGotSection final : public SyntheticSection {
bool updateAllocSize() override;
void finalizeContents() override;
bool empty() const override;
void addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr);
bool addDynTlsEntry(Symbol &Sym);
bool addTlsIndex();
uint64_t getPageEntryOffset(const Symbol &B, int64_t Addend) const;
uint64_t getSymEntryOffset(const Symbol &B, int64_t Addend) const;
uint64_t getGlobalDynOffset(const Symbol &B) const;
// Join separate GOTs built for each input file to generate
// primary and optional multiple secondary GOTs.
template <class ELFT> void build();
void addEntry(InputFile &File, Symbol &Sym, int64_t Addend, RelExpr Expr);
void addDynTlsEntry(InputFile &File, Symbol &Sym);
void addTlsIndex(InputFile &File);
uint64_t getPageEntryOffset(const InputFile *F, const Symbol &S,
int64_t Addend) const;
uint64_t getSymEntryOffset(const InputFile *F, const Symbol &S,
int64_t Addend) const;
uint64_t getGlobalDynOffset(const InputFile *F, const Symbol &S) const;
uint64_t getTlsIndexOffset(const InputFile *F) const;
// 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
@ -190,13 +205,8 @@ class MipsGotSection final : public SyntheticSection {
// the number of reserved entries.
unsigned getLocalEntriesNum() const;
// Returns offset of TLS part of the MIPS GOT table. This part goes
// after 'local' and 'global' entries.
uint64_t getTlsOffset() const;
uint32_t getTlsIndexOff() const { return TlsIndexOff; }
uint64_t getGp() const;
// Return _gp value for primary GOT (nullptr) or particular input file.
uint64_t getGp(const InputFile *F = nullptr) const;
private:
// MIPS GOT consists of three parts: local, global and tls. Each part
@ -235,32 +245,110 @@ class MipsGotSection final : public SyntheticSection {
// addressing, but MIPS ABI requires that these entries be present in GOT.
// TLS entries:
// Entries created by TLS relocations.
//
// If the sum of local, global and tls entries is less than 64K only single
// got is enough. Otherwise, multi-got is created. Series of primary and
// multiple secondary GOTs have the following layout:
// - Primary GOT
// Header
// Local entries
// Global entries
// Relocation only entries
// TLS entries
//
// - Secondary GOT
// Local entries
// Global entries
// TLS entries
// ...
//
// All GOT entries required by relocations from a single input file entirely
// belong to either primary or one of secondary GOTs. To reference GOT entries
// each GOT has its own _gp value points to the "middle" of the GOT.
// In the code this value loaded to the register which is used for GOT access.
//
// MIPS 32 function's prologue:
// lui v0,0x0
// 0: R_MIPS_HI16 _gp_disp
// addiu v0,v0,0
// 4: R_MIPS_LO16 _gp_disp
//
// MIPS 64:
// lui at,0x0
// 14: R_MIPS_GPREL16 main
//
// Dynamic linker does not know anything about secondary GOTs and cannot
// use a regular MIPS mechanism for GOT entries initialization. So we have
// to use an approach accepted by other architectures and create dynamic
// relocations R_MIPS_REL32 to initialize global entries (and local in case
// of PIC code) in secondary GOTs. But ironically MIPS dynamic linker
// requires GOT entries and correspondingly ordered dynamic symbol table
// entries to deal with dynamic relocations. To handle this problem
// relocation-only section in the primary GOT contains entries for all
// symbols referenced in global parts of secondary GOTs. Although the sum
// of local and normal global entries of the primary got should be less
// than 64K, the size of the primary got (including relocation-only entries
// can be greater than 64K, because parts of the primary got that overflow
// the 64K limit are used only by the dynamic linker at dynamic link-time
// and not by 16-bit gp-relative addressing at run-time.
//
// For complete multi-GOT description see the following link
// https://dmz-portal.mips.com/wiki/MIPS_Multi_GOT
// Number of "Header" entries.
static const unsigned HeaderEntriesNum = 2;
// Number of allocated "Page" entries.
uint32_t PageEntriesNum = 0;
// Map output sections referenced by MIPS GOT relocations
// to the first index of "Page" entries allocated for this section.
llvm::SmallMapVector<const OutputSection *, size_t, 16> PageIndexMap;
typedef std::pair<const Symbol *, uint64_t> GotEntry;
typedef std::vector<GotEntry> GotEntries;
// Map from Symbol-Addend pair to the GOT index.
llvm::DenseMap<GotEntry, size_t> EntryIndexMap;
// Local entries (16-bit access).
GotEntries LocalEntries;
// Local entries (32-bit access).
GotEntries LocalEntries32;
// Normal and reloc-only global entries.
GotEntries GlobalEntries;
// TLS entries.
std::vector<const Symbol *> TlsEntries;
uint32_t TlsIndexOff = -1;
uint64_t Size = 0;
size_t LocalEntriesNum = 0;
// Symbol and addend.
typedef std::pair<Symbol *, int64_t> GotEntry;
struct FileGot {
InputFile *File = nullptr;
size_t StartIndex = 0;
struct PageBlock {
size_t FirstIndex = 0;
size_t Count = 0;
};
// Map output sections referenced by MIPS GOT relocations
// to the description (index/count) "page" entries allocated
// for this section.
llvm::SmallMapVector<const OutputSection *, PageBlock, 16> PagesMap;
// Maps from Symbol+Addend pair or just Symbol to the GOT entry index.
llvm::MapVector<GotEntry, size_t> Local16;
llvm::MapVector<GotEntry, size_t> Local32;
llvm::MapVector<Symbol *, size_t> Global;
llvm::MapVector<Symbol *, size_t> Relocs;
llvm::MapVector<Symbol *, size_t> Tls;
// Set of symbols referenced by dynamic TLS relocations.
llvm::MapVector<Symbol *, size_t> DynTlsSymbols;
// Total number of all entries.
size_t getEntriesNum() const;
// Number of "page" entries.
size_t getPageEntriesNum() const;
// Number of entries require 16-bit index to access.
size_t getIndexedEntriesNum() const;
bool isOverflow() const;
};
// Container of GOT created for each input file.
// After building a final series of GOTs this container
// holds primary and secondary GOT's.
std::vector<FileGot> Gots;
// Return (and create if necessary) `FileGot`.
FileGot &getGot(InputFile &F);
// Try to merge two GOTs. In case of success the `Dst` contains
// result of merging and the function returns true. In case of
// ovwerflow the `Dst` is unchanged and the function returns false.
bool tryMergeGots(FileGot & Dst, FileGot & Src, bool IsPrimary);
};
class GotPltSection final : public SyntheticSection {
@ -269,7 +357,7 @@ class GotPltSection final : public SyntheticSection {
void addEntry(Symbol &Sym);
size_t getSize() const override;
void writeTo(uint8_t *Buf) override;
bool empty() const override { return Entries.empty(); }
bool empty() const override;
private:
std::vector<const Symbol *> Entries;
@ -310,30 +398,48 @@ class StringTableSection final : public SyntheticSection {
class DynamicReloc {
public:
DynamicReloc(uint32_t Type, const InputSectionBase *InputSec,
DynamicReloc(RelType Type, const InputSectionBase *InputSec,
uint64_t OffsetInSec, bool UseSymVA, Symbol *Sym, int64_t Addend)
: Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec),
UseSymVA(UseSymVA), Addend(Addend) {}
UseSymVA(UseSymVA), Addend(Addend), OutputSec(nullptr) {}
// This constructor records dynamic relocation settings used by MIPS
// multi-GOT implementation. It's to relocate addresses of 64kb pages
// lie inside the output section.
DynamicReloc(RelType Type, const InputSectionBase *InputSec,
uint64_t OffsetInSec, const OutputSection *OutputSec,
int64_t Addend)
: Type(Type), Sym(nullptr), InputSec(InputSec), OffsetInSec(OffsetInSec),
UseSymVA(false), Addend(Addend), OutputSec(OutputSec) {}
uint64_t getOffset() const;
int64_t getAddend() const;
uint32_t getSymIndex() const;
const InputSectionBase *getInputSec() const { return InputSec; }
uint32_t Type;
// Computes the addend of the dynamic relocation. Note that this is not the
// same as the Addend member variable as it also includes the symbol address
// if UseSymVA is true.
int64_t computeAddend() const;
RelType Type;
private:
Symbol *Sym;
const InputSectionBase *InputSec = nullptr;
uint64_t OffsetInSec;
// If this member is true, the dynamic relocation will not be against the
// symbol but will instead be a relative relocation that simply adds the
// load address. This means we need to write the symbol virtual address
// plus the original addend as the final relocation addend.
bool UseSymVA;
int64_t Addend;
const OutputSection *OutputSec;
};
template <class ELFT> class DynamicSection final : public SyntheticSection {
typedef typename ELFT::Dyn Elf_Dyn;
typedef typename ELFT::Rel Elf_Rel;
typedef typename ELFT::Rela Elf_Rela;
typedef typename ELFT::Relr Elf_Relr;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
@ -350,6 +456,7 @@ template <class ELFT> class DynamicSection final : public SyntheticSection {
void add(int32_t Tag, std::function<uint64_t()> Fn);
void addInt(int32_t Tag, uint64_t Val);
void addInSec(int32_t Tag, InputSection *Sec);
void addInSecRelative(int32_t Tag, InputSection *Sec);
void addOutSec(int32_t Tag, OutputSection *Sec);
void addSize(int32_t Tag, OutputSection *Sec);
void addSym(int32_t Tag, Symbol *Sym);
@ -361,6 +468,13 @@ class RelocationBaseSection : public SyntheticSection {
public:
RelocationBaseSection(StringRef Name, uint32_t Type, int32_t DynamicTag,
int32_t SizeDynamicTag);
void addReloc(RelType DynType, InputSectionBase *IS, uint64_t OffsetInSec,
Symbol *Sym);
// Add a dynamic relocation that might need an addend. This takes care of
// writing the addend to the output section if needed.
void addReloc(RelType DynType, InputSectionBase *InputSec,
uint64_t OffsetInSec, Symbol *Sym, int64_t Addend, RelExpr Expr,
RelType Type);
void addReloc(const DynamicReloc &Reloc);
bool empty() const override { return Relocs.empty(); }
size_t getSize() const override { return Relocs.size() * this->Entsize; }
@ -405,6 +519,39 @@ class AndroidPackedRelocationSection final : public RelocationBaseSection {
SmallVector<char, 0> RelocData;
};
struct RelativeReloc {
uint64_t getOffset() const { return InputSec->getVA(OffsetInSec); }
const InputSectionBase *InputSec;
uint64_t OffsetInSec;
};
class RelrBaseSection : public SyntheticSection {
public:
RelrBaseSection();
std::vector<RelativeReloc> Relocs;
};
// RelrSection is used to encode offsets for relative relocations.
// Proposal for adding SHT_RELR sections to generic-abi is here:
// https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg
// For more details, see the comment in RelrSection::updateAllocSize().
template <class ELFT> class RelrSection final : public RelrBaseSection {
typedef typename ELFT::Relr Elf_Relr;
public:
RelrSection();
bool updateAllocSize() override;
size_t getSize() const override { return RelrRelocs.size() * this->Entsize; }
void writeTo(uint8_t *Buf) override {
memcpy(Buf, RelrRelocs.data(), getSize());
}
private:
std::vector<Elf_Relr> RelrRelocs;
};
struct SymbolTableEntry {
Symbol *Sym;
size_t StrTabOffset;
@ -455,7 +602,7 @@ class GnuHashTableSection final : public SyntheticSection {
void addSymbols(std::vector<SymbolTableEntry> &Symbols);
private:
size_t getShift2() const { return Config->Is64 ? 6 : 5; }
enum { Shift2 = 6 };
void writeBloomFilter(uint8_t *Buf);
void writeHashTable(uint8_t *Buf);
@ -484,13 +631,13 @@ class HashTableSection final : public SyntheticSection {
size_t Size = 0;
};
// The PltSection is used for both the Plt and Iplt. The former always has a
// The PltSection is used for both the Plt and Iplt. The former usually has a
// header as its first entry that is used at run-time to resolve lazy binding.
// The latter is used for GNU Ifunc symbols, that will be subject to a
// Target->IRelativeRel.
class PltSection : public SyntheticSection {
public:
PltSection(size_t HeaderSize);
PltSection(bool IsIplt);
void writeTo(uint8_t *Buf) override;
size_t getSize() const override;
bool empty() const override { return Entries.empty(); }
@ -501,13 +648,12 @@ class PltSection : public SyntheticSection {
private:
unsigned getPltRelocOff() const;
std::vector<std::pair<const Symbol *, unsigned>> Entries;
// Iplt always has HeaderSize of 0, the Plt HeaderSize is always non-zero
size_t HeaderSize;
bool IsIplt;
};
// GdbIndexChunk is created for each .debug_info section and contains
// information to create a part of .gdb_index for a given input section.
struct GdbIndexChunk {
class GdbIndexSection final : public SyntheticSection {
public:
struct AddressEntry {
InputSection *Section;
uint64_t LowAddress;
@ -522,59 +668,51 @@ struct GdbIndexChunk {
struct NameTypeEntry {
llvm::CachedHashStringRef Name;
uint8_t Type;
uint32_t Type;
};
InputSection *DebugInfoSec;
std::vector<AddressEntry> AddressAreas;
std::vector<CuEntry> CompilationUnits;
std::vector<NameTypeEntry> NamesAndTypes;
};
struct GdbChunk {
InputSection *Sec;
std::vector<AddressEntry> AddressAreas;
std::vector<CuEntry> CompilationUnits;
};
// The symbol type for the .gdb_index section.
struct GdbSymbol {
uint32_t NameHash;
size_t NameOffset;
size_t CuVectorIndex;
};
struct GdbSymbol {
llvm::CachedHashStringRef Name;
std::vector<uint32_t> CuVector;
uint32_t NameOff;
uint32_t CuVectorOff;
};
class GdbIndexSection final : public SyntheticSection {
public:
GdbIndexSection(std::vector<GdbIndexChunk> &&Chunks);
GdbIndexSection();
template <typename ELFT> static GdbIndexSection *create();
void writeTo(uint8_t *Buf) override;
size_t getSize() const override;
size_t getSize() const override { return Size; }
bool empty() const override;
private:
void fixCuIndex();
std::vector<std::vector<uint32_t>> createCuVectors();
std::vector<GdbSymbol *> createGdbSymtab();
struct GdbIndexHeader {
llvm::support::ulittle32_t Version;
llvm::support::ulittle32_t CuListOff;
llvm::support::ulittle32_t CuTypesOff;
llvm::support::ulittle32_t AddressAreaOff;
llvm::support::ulittle32_t SymtabOff;
llvm::support::ulittle32_t ConstantPoolOff;
};
void initOutputSize();
size_t computeSymtabSize() const;
// Each chunk contains information gathered from debug sections of a
// single object file.
std::vector<GdbChunk> Chunks;
// A symbol table for this .gdb_index section.
std::vector<GdbSymbol *> GdbSymtab;
std::vector<GdbSymbol> Symbols;
// CU vector is a part of constant pool area of section.
std::vector<std::vector<uint32_t>> CuVectors;
// Symbol table contents.
llvm::DenseMap<llvm::CachedHashStringRef, GdbSymbol *> Symbols;
// Each chunk contains information gathered from a debug sections of single
// object and used to build different areas of gdb index.
std::vector<GdbIndexChunk> Chunks;
static constexpr uint32_t CuListOffset = 24;
uint32_t CuTypesOffset;
uint32_t SymtabOffset;
uint32_t ConstantPoolOffset;
uint32_t StringPoolOffset;
uint32_t StringPoolSize;
std::vector<size_t> CuVectorOffsets;
size_t Size;
};
template <class ELFT> GdbIndexSection *createGdbIndex();
// --eh-frame-hdr option tells linker to construct a header for all the
// .eh_frame sections. This header is placed to a section named .eh_frame_hdr
// and also to a PT_GNU_EH_FRAME segment.
@ -653,7 +791,7 @@ template <class ELFT> class VersionNeedSection final : public SyntheticSection {
public:
VersionNeedSection();
void addSymbol(SharedSymbol *SS);
void addSymbol(Symbol *Sym);
void finalizeContents() override;
void writeTo(uint8_t *Buf) override;
size_t getSize() const override;
@ -668,13 +806,12 @@ template <class ELFT> class VersionNeedSection final : public SyntheticSection {
class MergeSyntheticSection : public SyntheticSection {
public:
void addSection(MergeInputSection *MS);
std::vector<MergeInputSection *> Sections;
protected:
MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
uint32_t Alignment)
: SyntheticSection(Flags, Type, Alignment, Name) {}
std::vector<MergeInputSection *> Sections;
};
class MergeTailSection final : public MergeSyntheticSection {
@ -787,7 +924,12 @@ class ARMExidxSentinelSection : public SyntheticSection {
void writeTo(uint8_t *Buf) override;
bool empty() const override;
InputSection *Highest = 0;
static bool classof(const SectionBase *D);
// The last section referenced by a regular .ARM.exidx section.
// It is found and filled in Writer<ELFT>::resolveShfLinkOrder().
// The sentinel points at the end of that section.
InputSection *Highest = nullptr;
};
// A container for one or more linker generated thunks. Instances of these
@ -805,19 +947,21 @@ class ThunkSection : public SyntheticSection {
size_t getSize() const override { return Size; }
void writeTo(uint8_t *Buf) override;
InputSection *getTargetInputSection() const;
bool assignOffsets();
private:
std::vector<const Thunk *> Thunks;
std::vector<Thunk *> Thunks;
size_t Size = 0;
};
InputSection *createInterpSection();
MergeInputSection *createCommentSection();
void decompressSections();
template <class ELFT> void splitSections();
void mergeSections();
Symbol *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
uint64_t Size, InputSectionBase &Section);
Defined *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
uint64_t Size, InputSectionBase &Section);
// Linker generated sections which can be used as inputs.
struct InX {
@ -842,6 +986,7 @@ struct InX {
static PltSection *Plt;
static PltSection *Iplt;
static RelocationBaseSection *RelaDyn;
static RelrBaseSection *RelrDyn;
static RelocationBaseSection *RelaPlt;
static RelocationBaseSection *RelaIplt;
static StringTableSection *ShStrTab;

View File

@ -60,6 +60,8 @@ TargetInfo *elf::getTarget() {
return getARMTargetInfo();
case EM_AVR:
return getAVRTargetInfo();
case EM_HEXAGON:
return getHexagonTargetInfo();
case EM_MIPS:
switch (Config->EKind) {
case ELF32LEKind:
@ -87,29 +89,29 @@ TargetInfo *elf::getTarget() {
fatal("unknown target machine");
}
template <class ELFT> static std::string getErrorLoc(const uint8_t *Loc) {
template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *Loc) {
for (InputSectionBase *D : InputSections) {
auto *IS = dyn_cast<InputSection>(D);
if (!IS || !IS->getParent())
auto *IS = cast<InputSection>(D);
if (!IS->getParent())
continue;
uint8_t *ISLoc = IS->getParent()->Loc + IS->OutSecOff;
if (ISLoc <= Loc && Loc < ISLoc + IS->getSize())
return IS->template getLocation<ELFT>(Loc - ISLoc) + ": ";
return {IS, IS->template getLocation<ELFT>(Loc - ISLoc) + ": "};
}
return "";
return {};
}
std::string elf::getErrorLocation(const uint8_t *Loc) {
ErrorPlace elf::getErrorPlace(const uint8_t *Loc) {
switch (Config->EKind) {
case ELF32LEKind:
return getErrorLoc<ELF32LE>(Loc);
return getErrPlace<ELF32LE>(Loc);
case ELF32BEKind:
return getErrorLoc<ELF32BE>(Loc);
return getErrPlace<ELF32BE>(Loc);
case ELF64LEKind:
return getErrorLoc<ELF64LE>(Loc);
return getErrPlace<ELF64LE>(Loc);
case ELF64BEKind:
return getErrorLoc<ELF64BE>(Loc);
return getErrPlace<ELF64BE>(Loc);
default:
llvm_unreachable("unknown ELF type");
}
@ -128,6 +130,12 @@ bool TargetInfo::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
return false;
}
bool TargetInfo::adjustPrologueForCrossSplitStack(uint8_t *Loc,
uint8_t *End) const {
llvm_unreachable("Target doesn't support split stacks.");
}
bool TargetInfo::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
return true;
}

View File

@ -25,9 +25,9 @@ class Symbol;
class TargetInfo {
public:
virtual uint32_t calcEFlags() const { return 0; }
virtual bool isPicRel(RelType Type) const { return true; }
virtual RelType getDynRel(RelType Type) const { return Type; }
virtual void writeGotPltHeader(uint8_t *Buf) const {}
virtual void writeGotHeader(uint8_t *Buf) const {}
virtual void writeGotPlt(uint8_t *Buf, const Symbol &S) const {};
virtual void writeIgotPlt(uint8_t *Buf, const Symbol &S) const;
virtual int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const;
@ -43,8 +43,12 @@ class TargetInfo {
virtual void addPltHeaderSymbols(InputSection &IS) const {}
virtual void addPltSymbols(InputSection &IS, uint64_t Off) const {}
unsigned getPltEntryOffset(unsigned Index) const {
return Index * PltEntrySize + PltHeaderSize;
}
// Returns true if a relocation only uses the low bits of a value such that
// all those bits are in in the same page. For example, if the relocation
// all those bits are in the same page. For example, if the relocation
// only uses the low 12 bits in a system with 4k pages. If this is true, the
// bits will always have the same value at runtime and we don't have to emit
// a dynamic relocation.
@ -55,6 +59,13 @@ class TargetInfo {
virtual bool needsThunk(RelExpr Expr, RelType RelocType,
const InputFile *File, uint64_t BranchAddr,
const Symbol &S) const;
// The function with a prologue starting at Loc was compiled with
// -fsplit-stack and it calls a function compiled without. Adjust the prologue
// to do the right thing. See https://gcc.gnu.org/wiki/SplitStacks.
virtual bool adjustPrologueForCrossSplitStack(uint8_t *Loc,
uint8_t *End) const;
// Return true if we can reach Dst from Src with Relocation RelocType
virtual bool inBranchRange(RelType Type, uint64_t Src,
uint64_t Dst) const;
@ -71,9 +82,10 @@ class TargetInfo {
uint64_t getImageBase();
// Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
// end of .got
// Offset of _GLOBAL_OFFSET_TABLE_ from base of .got or .got.plt section.
uint64_t GotBaseSymOff = 0;
// True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got.
bool GotBaseSymInGotPlt = true;
// On systems with range extensions we place collections of Thunks at
// regular spacings that enable the majority of branches reach the Thunks.
@ -97,9 +109,18 @@ class TargetInfo {
// to support lazy loading.
unsigned GotPltHeaderEntriesNum = 3;
// Set to 0 for variant 2
// On PPC ELF V2 abi, the first entry in the .got is the .TOC.
unsigned GotHeaderEntriesNum = 0;
// For TLS variant 1, the TCB is a fixed size specified by the Target.
// For variant 2, the TCB is an unspecified size.
// Set to 0 for variant 2.
unsigned TcbSize = 0;
// Set to the offset (in bytes) that the thread pointer is initialized to
// point to, relative to the start of the thread local storage.
unsigned TlsTpOffset = 0;
bool NeedsThunks = false;
// A 4-byte field corresponding to one or more trap instructions, used to pad
@ -126,6 +147,7 @@ TargetInfo *getAArch64TargetInfo();
TargetInfo *getAMDGPUTargetInfo();
TargetInfo *getARMTargetInfo();
TargetInfo *getAVRTargetInfo();
TargetInfo *getHexagonTargetInfo();
TargetInfo *getPPC64TargetInfo();
TargetInfo *getPPCTargetInfo();
TargetInfo *getSPARCV9TargetInfo();
@ -134,7 +156,17 @@ TargetInfo *getX86TargetInfo();
TargetInfo *getX86_64TargetInfo();
template <class ELFT> TargetInfo *getMipsTargetInfo();
std::string getErrorLocation(const uint8_t *Loc);
struct ErrorPlace {
InputSectionBase *IS;
std::string Loc;
};
// Returns input section and corresponding source string for the given location.
ErrorPlace getErrorPlace(const uint8_t *Loc);
static inline std::string getErrorLocation(const uint8_t *Loc) {
return getErrorPlace(Loc).Loc;
}
uint64_t getPPC64TocBase();
uint64_t getAArch64Page(uint64_t Expr);
@ -146,39 +178,74 @@ template <class ELFT> bool isMipsPIC(const Defined *Sym);
static inline void reportRangeError(uint8_t *Loc, RelType Type, const Twine &V,
int64_t Min, uint64_t Max) {
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
" out of range: " + V + " is not in [" + Twine(Min) + ", " +
Twine(Max) + "]");
ErrorPlace ErrPlace = getErrorPlace(Loc);
StringRef Hint;
if (ErrPlace.IS && ErrPlace.IS->Name.startswith(".debug"))
Hint = "; consider recompiling with -fdebug-types-section to reduce size "
"of debug sections";
error(ErrPlace.Loc + "relocation " + lld::toString(Type) +
" out of range: " + V.str() + " is not in [" + Twine(Min).str() + ", " +
Twine(Max).str() + "]" + Hint);
}
template <unsigned N>
static void checkInt(uint8_t *Loc, int64_t V, RelType Type) {
if (!llvm::isInt<N>(V))
// Sign-extend Nth bit all the way to MSB.
inline int64_t signExtend(uint64_t V, int N) {
return int64_t(V << (64 - N)) >> (64 - N);
}
// Make sure that V can be represented as an N bit signed integer.
inline void checkInt(uint8_t *Loc, int64_t V, int N, RelType Type) {
if (V != signExtend(V, N))
reportRangeError(Loc, Type, Twine(V), llvm::minIntN(N), llvm::maxIntN(N));
}
template <unsigned N>
static void checkUInt(uint8_t *Loc, uint64_t V, RelType Type) {
if (!llvm::isUInt<N>(V))
// Make sure that V can be represented as an N bit unsigned integer.
inline void checkUInt(uint8_t *Loc, uint64_t V, int N, RelType Type) {
if ((V >> N) != 0)
reportRangeError(Loc, Type, Twine(V), 0, llvm::maxUIntN(N));
}
template <unsigned N>
static void checkIntUInt(uint8_t *Loc, uint64_t V, RelType Type) {
if (!llvm::isInt<N>(V) && !llvm::isUInt<N>(V))
// For the error message we should cast V to a signed integer so that error
// messages show a small negative value rather than an extremely large one
// Make sure that V can be represented as an N bit signed or unsigned integer.
inline void checkIntUInt(uint8_t *Loc, uint64_t V, int N, RelType Type) {
// For the error message we should cast V to a signed integer so that error
// messages show a small negative value rather than an extremely large one
if (V != (uint64_t)signExtend(V, N) && (V >> N) != 0)
reportRangeError(Loc, Type, Twine((int64_t)V), llvm::minIntN(N),
llvm::maxUIntN(N));
llvm::maxIntN(N));
}
template <unsigned N>
static void checkAlignment(uint8_t *Loc, uint64_t V, RelType Type) {
inline void checkAlignment(uint8_t *Loc, uint64_t V, int N, RelType Type) {
if ((V & (N - 1)) != 0)
error(getErrorLocation(Loc) + "improper alignment for relocation " +
lld::toString(Type) + ": 0x" + llvm::utohexstr(V) +
" is not aligned to " + Twine(N) + " bytes");
}
// Endianness-aware read/write.
inline uint16_t read16(const void *P) {
return llvm::support::endian::read16(P, Config->Endianness);
}
inline uint32_t read32(const void *P) {
return llvm::support::endian::read32(P, Config->Endianness);
}
inline uint64_t read64(const void *P) {
return llvm::support::endian::read64(P, Config->Endianness);
}
inline void write16(void *P, uint16_t V) {
llvm::support::endian::write16(P, V, Config->Endianness);
}
inline void write32(void *P, uint32_t V) {
llvm::support::endian::write32(P, V, Config->Endianness);
}
inline void write64(void *P, uint64_t V) {
llvm::support::endian::write64(P, V, Config->Endianness);
}
} // namespace elf
} // namespace lld

View File

@ -40,7 +40,6 @@
using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
namespace lld {
@ -52,59 +51,112 @@ namespace {
class AArch64ABSLongThunk final : public Thunk {
public:
AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
uint32_t size() const override { return 16; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
uint32_t size() override { return 16; }
void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
};
class AArch64ADRPThunk final : public Thunk {
public:
AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {}
uint32_t size() const override { return 12; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
uint32_t size() override { return 12; }
void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
};
// Base class for ARM thunks.
//
// An ARM thunk may be either short or long. A short thunk is simply a branch
// (B) instruction, and it may be used to call ARM functions when the distance
// from the thunk to the target is less than 32MB. Long thunks can branch to any
// virtual address and can switch between ARM and Thumb, and they are
// implemented in the derived classes. This class tries to create a short thunk
// if the target is in range, otherwise it creates a long thunk.
class ARMThunk : public Thunk {
public:
ARMThunk(Symbol &Dest) : Thunk(Dest) {}
bool mayUseShortThunk();
uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
void writeTo(uint8_t *Buf) override;
bool isCompatibleWith(RelType Type) const override;
// Returns the size of a long thunk.
virtual uint32_t sizeLong() = 0;
// Writes a long thunk to Buf.
virtual void writeLong(uint8_t *Buf) = 0;
private:
// This field tracks whether all previously considered layouts would allow
// this thunk to be short. If we have ever needed a long thunk, we always
// create a long thunk, even if the thunk may be short given the current
// distance to the target. We do this because transitioning from long to short
// can create layout oscillations in certain corner cases which would prevent
// the layout from converging.
bool MayUseShortThunk = true;
};
// Base class for Thumb-2 thunks.
//
// This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction
// which has a range of 16MB.
class ThumbThunk : public Thunk {
public:
ThumbThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
bool mayUseShortThunk();
uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
void writeTo(uint8_t *Buf) override;
bool isCompatibleWith(RelType Type) const override;
// Returns the size of a long thunk.
virtual uint32_t sizeLong() = 0;
// Writes a long thunk to Buf.
virtual void writeLong(uint8_t *Buf) = 0;
private:
// See comment in ARMThunk above.
bool MayUseShortThunk = true;
};
// Specific ARM Thunk implementations. The naming convention is:
// Source State, TargetState, Target Requirement, ABS or PI, Range
class ARMV7ABSLongThunk final : public Thunk {
class ARMV7ABSLongThunk final : public ARMThunk {
public:
ARMV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
ARMV7ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
uint32_t size() const override { return 12; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
uint32_t sizeLong() override { return 12; }
void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
bool isCompatibleWith(RelType Type) const override;
};
class ARMV7PILongThunk final : public Thunk {
class ARMV7PILongThunk final : public ARMThunk {
public:
ARMV7PILongThunk(Symbol &Dest) : Thunk(Dest) {}
ARMV7PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
uint32_t size() const override { return 16; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
uint32_t sizeLong() override { return 16; }
void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
bool isCompatibleWith(RelType Type) const override;
};
class ThumbV7ABSLongThunk final : public Thunk {
class ThumbV7ABSLongThunk final : public ThumbThunk {
public:
ThumbV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
ThumbV7ABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
uint32_t size() const override { return 10; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
uint32_t sizeLong() override { return 10; }
void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
bool isCompatibleWith(RelType Type) const override;
};
class ThumbV7PILongThunk final : public Thunk {
class ThumbV7PILongThunk final : public ThumbThunk {
public:
ThumbV7PILongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
ThumbV7PILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
uint32_t size() const override { return 12; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
uint32_t sizeLong() override { return 12; }
void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
bool isCompatibleWith(RelType Type) const override;
};
// MIPS LA25 thunk
@ -112,8 +164,8 @@ class MipsThunk final : public Thunk {
public:
MipsThunk(Symbol &Dest) : Thunk(Dest) {}
uint32_t size() const override { return 16; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
uint32_t size() override { return 16; }
void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
InputSection *getTargetInputSection() const override;
};
@ -123,8 +175,8 @@ class MicroMipsThunk final : public Thunk {
public:
MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {}
uint32_t size() const override { return 14; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
uint32_t size() override { return 14; }
void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
InputSection *getTargetInputSection() const override;
};
@ -134,14 +186,44 @@ class MicroMipsR6Thunk final : public Thunk {
public:
MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {}
uint32_t size() const override { return 12; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
uint32_t size() override { return 12; }
void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
InputSection *getTargetInputSection() const override;
};
// PPC64 Plt call stubs.
// Any call site that needs to call through a plt entry needs a call stub in
// the .text section. The call stub is responsible for:
// 1) Saving the toc-pointer to the stack.
// 2) Loading the target functions address from the procedure linkage table into
// r12 for use by the target functions global entry point, and into the count
// register.
// 3) Transfering control to the target function through an indirect branch.
class PPC64PltCallStub final : public Thunk {
public:
PPC64PltCallStub(Symbol &Dest) : Thunk(Dest) {}
uint32_t size() override { return 20; }
void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
};
} // end anonymous namespace
Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
InputSectionBase &Section) {
Defined *D = addSyntheticLocal(Name, Type, Value, /*Size=*/0, Section);
Syms.push_back(D);
return D;
}
void Thunk::setOffset(uint64_t NewOffset) {
for (Defined *D : Syms)
D->Value = D->Value - Offset + NewOffset;
Offset = NewOffset;
}
// AArch64 long range Thunks
static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
@ -149,7 +231,7 @@ static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
return V;
}
void AArch64ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
void AArch64ABSLongThunk::writeTo(uint8_t *Buf) {
const uint8_t Data[] = {
0x50, 0x00, 0x00, 0x58, // ldr x16, L0
0x00, 0x02, 0x1f, 0xd6, // br x16
@ -162,11 +244,10 @@ void AArch64ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
ThunkSym = addSyntheticLocal(
Saver.save("__AArch64AbsLongThunk_" + Destination.getName()), STT_FUNC,
Offset, size(), IS);
addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS);
addSyntheticLocal("$d", STT_NOTYPE, Offset + 8, 0, IS);
addSymbol(Saver.save("__AArch64AbsLongThunk_" + Destination.getName()),
STT_FUNC, 0, IS);
addSymbol("$x", STT_NOTYPE, 0, IS);
addSymbol("$d", STT_NOTYPE, 8, IS);
}
// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
@ -174,26 +255,24 @@ void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
// clang and gcc do not support the large code model for position independent
// code so it is safe to use this for position independent thunks without
// worrying about the destination being more than 4Gb away.
void AArch64ADRPThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
void AArch64ADRPThunk::writeTo(uint8_t *Buf) {
const uint8_t Data[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
0x00, 0x02, 0x1f, 0xd6, // br x16
};
uint64_t S = getAArch64ThunkDestVA(Destination);
uint64_t P = ThunkSym->getVA();
uint64_t P = getThunkTargetSym()->getVA();
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
getAArch64Page(S) - getAArch64Page(P));
Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S);
}
void AArch64ADRPThunk::addSymbols(ThunkSection &IS)
{
ThunkSym = addSyntheticLocal(
Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
Offset, size(), IS);
addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS);
void AArch64ADRPThunk::addSymbols(ThunkSection &IS) {
addSymbol(Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
0, IS);
addSymbol("$x", STT_NOTYPE, 0, IS);
}
// ARM Target Thunks
@ -202,7 +281,81 @@ static uint64_t getARMThunkDestVA(const Symbol &S) {
return SignExtend64<32>(V);
}
void ARMV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
// This function returns true if the target is not Thumb and is within 2^26, and
// it has not previously returned false (see comment for MayUseShortThunk).
bool ARMThunk::mayUseShortThunk() {
if (!MayUseShortThunk)
return false;
uint64_t S = getARMThunkDestVA(Destination);
if (S & 1) {
MayUseShortThunk = false;
return false;
}
uint64_t P = getThunkTargetSym()->getVA();
int64_t Offset = S - P - 8;
MayUseShortThunk = llvm::isInt<26>(Offset);
return MayUseShortThunk;
}
void ARMThunk::writeTo(uint8_t *Buf) {
if (!mayUseShortThunk()) {
writeLong(Buf);
return;
}
uint64_t S = getARMThunkDestVA(Destination);
uint64_t P = getThunkTargetSym()->getVA();
int64_t Offset = S - P - 8;
const uint8_t Data[] = {
0x00, 0x00, 0x00, 0xea, // b S
};
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_ARM_JUMP24, Offset);
}
bool ARMThunk::isCompatibleWith(RelType Type) const {
// Thumb branch relocations can't use BLX
return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
}
// This function returns true if the target is Thumb and is within 2^25, and
// it has not previously returned false (see comment for MayUseShortThunk).
bool ThumbThunk::mayUseShortThunk() {
if (!MayUseShortThunk)
return false;
uint64_t S = getARMThunkDestVA(Destination);
if ((S & 1) == 0) {
MayUseShortThunk = false;
return false;
}
uint64_t P = getThunkTargetSym()->getVA() & ~1;
int64_t Offset = S - P - 4;
MayUseShortThunk = llvm::isInt<25>(Offset);
return MayUseShortThunk;
}
void ThumbThunk::writeTo(uint8_t *Buf) {
if (!mayUseShortThunk()) {
writeLong(Buf);
return;
}
uint64_t S = getARMThunkDestVA(Destination);
uint64_t P = getThunkTargetSym()->getVA();
int64_t Offset = S - P - 4;
const uint8_t Data[] = {
0x00, 0xf0, 0x00, 0xb0, // b.w S
};
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset);
}
bool ThumbThunk::isCompatibleWith(RelType Type) const {
// ARM branch relocations can't use BLX
return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
}
void ARMV7ABSLongThunk::writeLong(uint8_t *Buf) {
const uint8_t Data[] = {
0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S
@ -215,18 +368,12 @@ void ARMV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
ThunkSym = addSyntheticLocal(
Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
Offset, size(), IS);
addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS);
addSymbol(Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()),
STT_FUNC, 0, IS);
addSymbol("$a", STT_NOTYPE, 0, IS);
}
bool ARMV7ABSLongThunk::isCompatibleWith(RelType Type) const {
// Thumb branch relocations can't use BLX
return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
}
void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
void ThumbV7ABSLongThunk::writeLong(uint8_t *Buf) {
const uint8_t Data[] = {
0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S
@ -239,18 +386,12 @@ void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
ThunkSym = addSyntheticLocal(
Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
Offset | 0x1, size(), IS);
addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS);
addSymbol(Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()),
STT_FUNC, 1, IS);
addSymbol("$t", STT_NOTYPE, 0, IS);
}
bool ThumbV7ABSLongThunk::isCompatibleWith(RelType Type) const {
// ARM branch relocations can't use BLX
return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
}
void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
void ARMV7PILongThunk::writeLong(uint8_t *Buf) {
const uint8_t Data[] = {
0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) + 8)
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P) + 8)
@ -258,7 +399,7 @@ void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
0x1c, 0xff, 0x2f, 0xe1, // bx r12
};
uint64_t S = getARMThunkDestVA(Destination);
uint64_t P = ThunkSym->getVA();
uint64_t P = getThunkTargetSym()->getVA();
uint64_t Offset = S - P - 16;
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
@ -266,18 +407,12 @@ void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
ThunkSym = addSyntheticLocal(
Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
Offset, size(), IS);
addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS);
addSymbol(Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
0, IS);
addSymbol("$a", STT_NOTYPE, 0, IS);
}
bool ARMV7PILongThunk::isCompatibleWith(RelType Type) const {
// Thumb branch relocations can't use BLX
return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
}
void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
void ThumbV7PILongThunk::writeLong(uint8_t *Buf) {
const uint8_t Data[] = {
0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
@ -285,7 +420,7 @@ void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
0x60, 0x47, // bx r12
};
uint64_t S = getARMThunkDestVA(Destination);
uint64_t P = ThunkSym->getVA() & ~0x1;
uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
uint64_t Offset = S - P - 12;
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
@ -293,32 +428,25 @@ void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
}
void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
ThunkSym = addSyntheticLocal(
Saver.save("__ThumbV7PILongThunk_" + Destination.getName()), STT_FUNC,
Offset | 0x1, size(), IS);
addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS);
}
bool ThumbV7PILongThunk::isCompatibleWith(RelType Type) const {
// ARM branch relocations can't use BLX
return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
addSymbol(Saver.save("__ThumbV7PILongThunk_" + Destination.getName()),
STT_FUNC, 1, IS);
addSymbol("$t", STT_NOTYPE, 0, IS);
}
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
void MipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
void MipsThunk::writeTo(uint8_t *Buf) {
uint64_t S = Destination.getVA();
write32(Buf, 0x3c190000, Config->Endianness); // lui $25, %hi(func)
write32(Buf + 4, 0x08000000 | (S >> 2), Config->Endianness); // j func
write32(Buf + 8, 0x27390000, Config->Endianness); // addiu $25, $25, %lo(func)
write32(Buf + 12, 0x00000000, Config->Endianness); // nop
write32(Buf, 0x3c190000); // lui $25, %hi(func)
write32(Buf + 4, 0x08000000 | (S >> 2)); // j func
write32(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
write32(Buf + 12, 0x00000000); // nop
Target->relocateOne(Buf, R_MIPS_HI16, S);
Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
}
void MipsThunk::addSymbols(ThunkSection &IS) {
ThunkSym =
addSyntheticLocal(Saver.save("__LA25Thunk_" + Destination.getName()),
STT_FUNC, Offset, size(), IS);
addSymbol(Saver.save("__LA25Thunk_" + Destination.getName()), STT_FUNC, 0,
IS);
}
InputSection *MipsThunk::getTargetInputSection() const {
@ -328,22 +456,21 @@ InputSection *MipsThunk::getTargetInputSection() const {
// Write microMIPS R2-R5 LA25 thunk code
// to call PIC function from the non-PIC one.
void MicroMipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
void MicroMipsThunk::writeTo(uint8_t *Buf) {
uint64_t S = Destination.getVA() | 1;
write16(Buf, 0x41b9, Config->Endianness); // lui $25, %hi(func)
write16(Buf + 4, 0xd400, Config->Endianness); // j func
write16(Buf + 8, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func)
write16(Buf + 12, 0x0c00, Config->Endianness); // nop
write16(Buf, 0x41b9); // lui $25, %hi(func)
write16(Buf + 4, 0xd400); // j func
write16(Buf + 8, 0x3339); // addiu $25, $25, %lo(func)
write16(Buf + 12, 0x0c00); // nop
Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S);
Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S);
}
void MicroMipsThunk::addSymbols(ThunkSection &IS) {
ThunkSym =
addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
STT_FUNC, Offset, size(), IS);
ThunkSym->StOther |= STO_MIPS_MICROMIPS;
Defined *D = addSymbol(
Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
D->StOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsThunk::getTargetInputSection() const {
@ -353,22 +480,21 @@ InputSection *MicroMipsThunk::getTargetInputSection() const {
// Write microMIPS R6 LA25 thunk code
// to call PIC function from the non-PIC one.
void MicroMipsR6Thunk::writeTo(uint8_t *Buf, ThunkSection &) const {
void MicroMipsR6Thunk::writeTo(uint8_t *Buf) {
uint64_t S = Destination.getVA() | 1;
uint64_t P = ThunkSym->getVA();
write16(Buf, 0x1320, Config->Endianness); // lui $25, %hi(func)
write16(Buf + 4, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func)
write16(Buf + 8, 0x9400, Config->Endianness); // bc func
uint64_t P = getThunkTargetSym()->getVA();
write16(Buf, 0x1320); // lui $25, %hi(func)
write16(Buf + 4, 0x3339); // addiu $25, $25, %lo(func)
write16(Buf + 8, 0x9400); // bc func
Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S);
Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12);
}
void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) {
ThunkSym =
addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
STT_FUNC, Offset, size(), IS);
ThunkSym->StOther |= STO_MIPS_MICROMIPS;
Defined *D = addSymbol(
Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
D->StOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
@ -376,6 +502,25 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
return dyn_cast<InputSection>(DR.Section);
}
void PPC64PltCallStub::writeTo(uint8_t *Buf) {
int64_t Off = Destination.getGotPltVA() - getPPC64TocBase();
// Need to add 0x8000 to offset to account for the low bits being signed.
uint16_t OffHa = (Off + 0x8000) >> 16;
uint16_t OffLo = Off;
write32(Buf + 0, 0xf8410018); // std r2,24(r1)
write32(Buf + 4, 0x3d820000 | OffHa); // addis r12,r2, X@plt@to@ha
write32(Buf + 8, 0xe98c0000 | OffLo); // ld r12,X@plt@toc@l(r12)
write32(Buf + 12, 0x7d8903a6); // mtctr r12
write32(Buf + 16, 0x4e800420); // bctr
}
void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
Defined *S = addSymbol(Saver.save("__plt_" + Destination.getName()), STT_FUNC,
0, IS);
S->NeedsTocRestore = true;
}
Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
Thunk::~Thunk() = default;
@ -419,15 +564,26 @@ static Thunk *addThunkMips(RelType Type, Symbol &S) {
return make<MipsThunk>(S);
}
static Thunk *addThunkPPC64(RelType Type, Symbol &S) {
if (Type == R_PPC64_REL24)
return make<PPC64PltCallStub>(S);
fatal("unexpected relocation type");
}
Thunk *addThunk(RelType Type, Symbol &S) {
if (Config->EMachine == EM_AARCH64)
return addThunkAArch64(Type, S);
else if (Config->EMachine == EM_ARM)
if (Config->EMachine == EM_ARM)
return addThunkArm(Type, S);
else if (Config->EMachine == EM_MIPS)
if (Config->EMachine == EM_MIPS)
return addThunkMips(Type, S);
llvm_unreachable("add Thunk only supported for ARM and Mips");
return nullptr;
if (Config->EMachine == EM_PPC64)
return addThunkPPC64(Type, S);
llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
}
} // end namespace elf

View File

@ -14,6 +14,7 @@
namespace lld {
namespace elf {
class Defined;
class Symbol;
class ThunkSection;
// Class to describe an instance of a Thunk.
@ -30,12 +31,17 @@ class Thunk {
Thunk(Symbol &Destination);
virtual ~Thunk();
virtual uint32_t size() const { return 0; }
virtual void writeTo(uint8_t *Buf, ThunkSection &IS) const {}
virtual uint32_t size() = 0;
virtual void writeTo(uint8_t *Buf) = 0;
// All Thunks must define at least one symbol ThunkSym so that we can
// redirect relocations to it.
virtual void addSymbols(ThunkSection &IS) {}
// All Thunks must define at least one symbol, known as the thunk target
// symbol, so that we can redirect relocations to it. The thunk may define
// additional symbols, but these are never targets for relocations.
virtual void addSymbols(ThunkSection &IS) = 0;
void setOffset(uint64_t Offset);
Defined *addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
InputSectionBase &Section);
// Some Thunks must be placed immediately before their Target as they elide
// a branch and fall through to the first Symbol in the Target.
@ -45,10 +51,12 @@ class Thunk {
// compatible with it.
virtual bool isCompatibleWith(RelType Type) const { return true; }
Defined *getThunkTargetSym() const { return Syms[0]; }
// The alignment requirement for this Thunk, defaults to the size of the
// typical code section alignment.
Symbol &Destination;
Symbol *ThunkSym;
llvm::SmallVector<Defined *, 3> Syms;
uint64_t Offset = 0;
uint32_t Alignment = 4;
};

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,6 @@ class InputSectionBase;
template <class ELFT> class ObjFile;
class SymbolTable;
template <class ELFT> void writeResult();
template <class ELFT> void markLive();
// This describes a program header entry.
// Each contains type, access flags and range of output sections that will be
@ -44,10 +43,12 @@ struct PhdrEntry {
OutputSection *FirstSec = nullptr;
OutputSection *LastSec = nullptr;
bool HasLMA = false;
uint64_t LMAOffset = 0;
};
void addReservedSymbols();
llvm::StringRef getOutputSectionName(InputSectionBase *S);
llvm::StringRef getOutputSectionName(const InputSectionBase *S);
template <class ELFT> uint32_t calcMipsEFlags();

View File

@ -4,7 +4,7 @@ lld License
University of Illinois/NCSA
Open Source License
Copyright (c) 2011-2016 by the contributors listed in CREDITS.TXT
Copyright (c) 2011-2018 by the contributors listed in CREDITS.TXT
All rights reserved.
Developed by:

View File

@ -136,6 +136,8 @@ bool mingw::link(ArrayRef<const char *> ArgsArr, raw_ostream &Diag) {
Add("-output-def:" + StringRef(A->getValue()));
if (auto *A = Args.getLastArg(OPT_image_base))
Add("-base:" + StringRef(A->getValue()));
if (auto *A = Args.getLastArg(OPT_map))
Add("-lldmap:" + StringRef(A->getValue()));
if (auto *A = Args.getLastArg(OPT_o))
Add("-out:" + StringRef(A->getValue()));
@ -144,16 +146,25 @@ bool mingw::link(ArrayRef<const char *> ArgsArr, raw_ostream &Diag) {
else
Add("-out:a.exe");
if (auto *A = Args.getLastArg(OPT_pdb)) {
Add("-debug");
Add("-pdb:" + StringRef(A->getValue()));
} else if (Args.hasArg(OPT_strip_debug)) {
Add("-debug:symtab");
} else if (!Args.hasArg(OPT_strip_all)) {
Add("-debug:dwarf");
}
if (Args.hasArg(OPT_shared))
Add("-dll");
if (Args.hasArg(OPT_verbose))
Add("-verbose");
if (Args.hasArg(OPT_export_all_symbols))
Add("-export-all-symbols");
if (!Args.hasArg(OPT_strip_all))
Add("-debug:dwarf");
if (Args.hasArg(OPT_large_address_aware))
Add("-largeaddressaware");
if (Args.hasArg(OPT_kill_at))
Add("-kill-at");
if (Args.getLastArgValue(OPT_m) != "thumb2pe" &&
Args.getLastArgValue(OPT_m) != "arm64pe" && !Args.hasArg(OPT_dynamicbase))

View File

@ -14,9 +14,12 @@ def export_all_symbols: F<"export-all-symbols">,
def gc_sections: F<"gc-sections">, HelpText<"Remove unused sections">;
def icf: J<"icf=">, HelpText<"Identical code folding">;
def image_base: S<"image-base">, HelpText<"Base address of the program">;
def kill_at: F<"kill-at">, HelpText<"Remove @n from exported symbols">;
def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
HelpText<"Root name of library to use">;
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
def map: S<"Map">, HelpText<"Output a linker map">;
def map_eq: J<"Map=">, Alias<map>;
def no_whole_archive: F<"no-whole-archive">,
HelpText<"No longer include all object files for following archives">;
def large_address_aware: Flag<["--"], "large-address-aware">,
@ -32,6 +35,8 @@ def subs: S<"subsystem">, HelpText<"Specify subsystem">;
def stack: S<"stack">;
def strip_all: F<"strip-all">,
HelpText<"Omit all symbol information from the output binary">;
def strip_debug: F<"strip-debug">,
HelpText<"Omit all debug information, but keep symbol information">;
def whole_archive: F<"whole-archive">,
HelpText<"Include all object files for following archives">;
def verbose: F<"verbose">, HelpText<"Verbose mode">;
@ -40,6 +45,7 @@ def verbose: F<"verbose">, HelpText<"Verbose mode">;
def _HASH_HASH_HASH : Flag<["-"], "###">,
HelpText<"Print (but do not run) the commands to run for this compilation">;
def mllvm: S<"mllvm">;
def pdb: S<"pdb">, HelpText<"Specify output PDB debug information file">;
def Xlink : J<"Xlink=">, MetaVarName<"<arg>">,
HelpText<"Pass <arg> to the COFF linker">;
@ -51,6 +57,7 @@ def build_id: F<"build-id">;
def disable_auto_image_base: F<"disable-auto-image-base">;
def enable_auto_image_base: F<"enable-auto-image-base">;
def enable_auto_import: F<"enable-auto-import">;
def end_group: F<"end-group">;
def full_shutdown: Flag<["--"], "full-shutdown">;
def high_entropy_va: F<"high-entropy-va">, HelpText<"Enable 64-bit ASLR">;
def major_image_version: S<"major-image-version">;
@ -59,6 +66,7 @@ def no_seh: F<"no-seh">;
def nxcompat: F<"nxcompat">, HelpText<"Enable data execution prevention">;
def pic_executable: F<"pic-executable">;
def sysroot: J<"sysroot">, HelpText<"Sysroot">;
def start_group: F<"start-group">;
def tsaware: F<"tsaware">, HelpText<"Create Terminal Server aware executable">;
def v: Flag<["-"], "v">, HelpText<"Display the version number">;
def version: F<"version">, HelpText<"Display the version number and exit">;
@ -66,3 +74,4 @@ def version: F<"version">, HelpText<"Display the version number and exit">;
// Alias
def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
def alias_strip_s: Flag<["-"], "s">, Alias<strip_all>;
def alias_strip_S: Flag<["-"], "S">, Alias<strip_debug>;

View File

@ -10,7 +10,7 @@ macro(add_lld_library name)
llvm_add_library(${name} ${ARG_ENABLE_SHARED} ${ARG_UNPARSED_ARGUMENTS})
set_target_properties(${name} PROPERTIES FOLDER "lld libraries")
if (LLD_BUILD_TOOLS)
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
if(${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR
NOT LLVM_DISTRIBUTION_COMPONENTS)
set(export_to_lldtargets EXPORT lldTargets)

View File

@ -1,21 +1,21 @@
=======================
LLD 6.0.0 Release Notes
LLD 7.0.0 Release Notes
=======================
.. contents::
:local:
.. warning::
These are in-progress notes for the upcoming LLVM 6.0.0 release.
These are in-progress notes for the upcoming LLVM 7.0.0 release.
Release notes for previous releases can be found on
`the Download Page <http://releases.llvm.org/download.html>`_.
Introduction
============
This document contains the release notes for the LLD linker, release 6.0.0.
Here we describe the status of LLD, including major improvements
from the previous release. All LLD releases may be downloaded
This document contains the release notes for the lld linker, release 7.0.0.
Here we describe the status of lld, including major improvements
from the previous release. All lld releases may be downloaded
from the `LLVM releases web site <http://llvm.org/releases/>`_.
Non-comprehensive list of changes in this release

View File

@ -18,7 +18,7 @@ the WebAssembly tool conventions
https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md.
This is object format that the llvm will produce when run with the
``wasm32-unknown-unknown-wasm`` target. To build llvm with WebAssembly support
``wasm32-unknown-unknown`` target. To build llvm with WebAssembly support
currently requires enabling the experimental backed using
``-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly``.

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