Vendor import of lld trunk r338150:
https://llvm.org/svn/llvm-project/lld/trunk@338150
This commit is contained in:
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
@ -18,7 +18,6 @@ add_lld_library(lldCOFF
|
||||
MarkLive.cpp
|
||||
MinGW.cpp
|
||||
PDB.cpp
|
||||
Strings.cpp
|
||||
SymbolTable.cpp
|
||||
Symbols.cpp
|
||||
Writer.cpp
|
||||
|
175
COFF/Chunks.cpp
175
COFF/Chunks.cpp
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
434
COFF/Driver.cpp
434
COFF/Driver.cpp
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
90
COFF/ICF.cpp
90
COFF/ICF.cpp
@ -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.
|
||||
|
@ -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
|
@ -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)".
|
||||
|
@ -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
|
||||
|
||||
|
72
COFF/LTO.cpp
72
COFF/LTO.cpp
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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
24
COFF/MarkLive.h
Normal 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
|
@ -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";
|
||||
|
@ -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">;
|
||||
|
571
COFF/PDB.cpp
571
COFF/PDB.cpp
@ -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};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
|
829
COFF/Writer.cpp
829
COFF/Writer.cpp
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -10,6 +10,7 @@ add_lld_library(lldCommon
|
||||
Strings.cpp
|
||||
TargetOptionsCommandFlags.cpp
|
||||
Threads.cpp
|
||||
Timer.cpp
|
||||
Version.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
80
Common/Timer.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
#define LLD_ELF_AARCH64ERRATAFIX_H
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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
97
ELF/Arch/Hexagon.cpp
Normal 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;
|
||||
}
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
170
ELF/Arch/X86.cpp
170
ELF/Arch/X86.cpp
@ -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;
|
||||
}
|
||||
|
@ -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>(); }
|
||||
|
@ -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
249
ELF/CallGraphSort.cpp
Normal 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
23
ELF/CallGraphSort.h
Normal 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
|
55
ELF/Config.h
55
ELF/Config.h
@ -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
|
||||
|
||||
|
590
ELF/Driver.cpp
590
ELF/Driver.cpp
@ -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>();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) ||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
116
ELF/ICF.cpp
116
ELF/ICF.cpp
@ -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.
|
||||
|
@ -12,8 +12,10 @@
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
template <class ELFT> void doIcf();
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
|
@ -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>();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 *);
|
||||
|
218
ELF/LTO.cpp
218
ELF/LTO.cpp
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
213
ELF/MapFile.cpp
213
ELF/MapFile.cpp
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
void writeMapFile();
|
||||
void writeCrossReferenceTable();
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
@ -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
21
ELF/MarkLive.h
Normal 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
|
520
ELF/Options.td
520
ELF/Options.td
@ -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]>;
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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); });
|
||||
}
|
@ -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
|
@ -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>();
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
159
ELF/Symbols.h
159
ELF/Symbols.h
@ -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
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
113
ELF/Target.h
113
ELF/Target.h
@ -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
|
||||
|
||||
|
390
ELF/Thunks.cpp
390
ELF/Thunks.cpp
@ -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
|
||||
|
20
ELF/Thunks.h
20
ELF/Thunks.h
@ -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;
|
||||
};
|
||||
|
754
ELF/Writer.cpp
754
ELF/Writer.cpp
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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>;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user