Merge lld trunk r351319, resolve conflicts, and update FREEBSD-Xlist.
This commit is contained in:
commit
0e23b2ff8c
@ -11,6 +11,7 @@
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/BinaryFormat/COFF.h"
|
||||
@ -44,6 +45,22 @@ SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
|
||||
Live = !Config->DoGC || !isCOMDAT();
|
||||
}
|
||||
|
||||
// Initialize the RelocTargets vector, to allow redirecting certain relocations
|
||||
// to a thunk instead of the actual symbol the relocation's symbol table index
|
||||
// indicates.
|
||||
void SectionChunk::readRelocTargets() {
|
||||
assert(RelocTargets.empty());
|
||||
RelocTargets.reserve(Relocs.size());
|
||||
for (const coff_relocation &Rel : Relocs)
|
||||
RelocTargets.push_back(File->getSymbol(Rel.SymbolTableIndex));
|
||||
}
|
||||
|
||||
// Reset RelocTargets to their original targets before thunks were added.
|
||||
void SectionChunk::resetRelocTargets() {
|
||||
for (size_t I = 0, E = Relocs.size(); I < E; ++I)
|
||||
RelocTargets[I] = File->getSymbol(Relocs[I].SymbolTableIndex);
|
||||
}
|
||||
|
||||
static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
|
||||
static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }
|
||||
static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); }
|
||||
@ -58,7 +75,8 @@ static bool checkSecRel(const SectionChunk *Sec, OutputSection *OS) {
|
||||
return true;
|
||||
if (Sec->isCodeView())
|
||||
return false;
|
||||
fatal("SECREL relocation cannot be applied to absolute symbols");
|
||||
error("SECREL relocation cannot be applied to absolute symbols");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
|
||||
@ -98,7 +116,7 @@ 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) + " in " +
|
||||
error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
|
||||
toString(File));
|
||||
}
|
||||
}
|
||||
@ -113,7 +131,7 @@ 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) + " in " +
|
||||
error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
|
||||
toString(File));
|
||||
}
|
||||
}
|
||||
@ -123,16 +141,22 @@ static void applyMOV(uint8_t *Off, uint16_t V) {
|
||||
write16le(Off + 2, (read16le(Off + 2) & 0x8f00) | ((V & 0x700) << 4) | (V & 0xff));
|
||||
}
|
||||
|
||||
static uint16_t readMOV(uint8_t *Off) {
|
||||
static uint16_t readMOV(uint8_t *Off, bool MOVT) {
|
||||
uint16_t Op1 = read16le(Off);
|
||||
if ((Op1 & 0xfbf0) != (MOVT ? 0xf2c0 : 0xf240))
|
||||
error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") +
|
||||
" instruction in MOV32T relocation");
|
||||
uint16_t Op2 = read16le(Off + 2);
|
||||
if ((Op2 & 0x8000) != 0)
|
||||
error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") +
|
||||
" instruction in MOV32T relocation");
|
||||
return (Op2 & 0x00ff) | ((Op2 >> 4) & 0x0700) | ((Op1 << 1) & 0x0800) |
|
||||
((Op1 & 0x000f) << 12);
|
||||
}
|
||||
|
||||
void applyMOV32T(uint8_t *Off, uint32_t V) {
|
||||
uint16_t ImmW = readMOV(Off); // read MOVW operand
|
||||
uint16_t ImmT = readMOV(Off + 4); // read MOVT operand
|
||||
uint16_t ImmW = readMOV(Off, false); // read MOVW operand
|
||||
uint16_t ImmT = readMOV(Off + 4, true); // read MOVT operand
|
||||
uint32_t Imm = ImmW | (ImmT << 16);
|
||||
V += Imm; // add the immediate offset
|
||||
applyMOV(Off, V); // set MOVW operand
|
||||
@ -141,7 +165,7 @@ void applyMOV32T(uint8_t *Off, uint32_t V) {
|
||||
|
||||
static void applyBranch20T(uint8_t *Off, int32_t V) {
|
||||
if (!isInt<21>(V))
|
||||
fatal("relocation out of range");
|
||||
error("relocation out of range");
|
||||
uint32_t S = V < 0 ? 1 : 0;
|
||||
uint32_t J1 = (V >> 19) & 1;
|
||||
uint32_t J2 = (V >> 18) & 1;
|
||||
@ -151,7 +175,7 @@ static void applyBranch20T(uint8_t *Off, int32_t V) {
|
||||
|
||||
void applyBranch24T(uint8_t *Off, int32_t V) {
|
||||
if (!isInt<25>(V))
|
||||
fatal("relocation out of range");
|
||||
error("relocation out of range");
|
||||
uint32_t S = V < 0 ? 1 : 0;
|
||||
uint32_t J1 = ((~V >> 23) & 1) ^ S;
|
||||
uint32_t J2 = ((~V >> 22) & 1) ^ S;
|
||||
@ -176,7 +200,7 @@ 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) + " in " +
|
||||
error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
|
||||
toString(File));
|
||||
}
|
||||
}
|
||||
@ -184,7 +208,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
|
||||
// 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, int Shift) {
|
||||
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;
|
||||
@ -198,7 +222,7 @@ static void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) {
|
||||
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
|
||||
// Optionally limit the range of the written immediate by one or more bits
|
||||
// (RangeLimit).
|
||||
static void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) {
|
||||
void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) {
|
||||
uint32_t Orig = read32le(Off);
|
||||
Imm += (Orig >> 10) & 0xFFF;
|
||||
Orig &= ~(0xFFF << 10);
|
||||
@ -221,7 +245,7 @@ static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) {
|
||||
if ((Orig & 0x4800000) == 0x4800000)
|
||||
Size += 4;
|
||||
if ((Imm & ((1 << Size) - 1)) != 0)
|
||||
fatal("misaligned ldr/str offset");
|
||||
error("misaligned ldr/str offset");
|
||||
applyArm64Imm(Off, Imm >> Size, Size);
|
||||
}
|
||||
|
||||
@ -250,21 +274,21 @@ static void applySecRelLdr(const SectionChunk *Sec, uint8_t *Off,
|
||||
applyArm64Ldr(Off, (S - OS->getRVA()) & 0xfff);
|
||||
}
|
||||
|
||||
static void applyArm64Branch26(uint8_t *Off, int64_t V) {
|
||||
void applyArm64Branch26(uint8_t *Off, int64_t V) {
|
||||
if (!isInt<28>(V))
|
||||
fatal("relocation out of range");
|
||||
error("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");
|
||||
error("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");
|
||||
error("relocation out of range");
|
||||
or32(Off, (V & 0x0000FFFC) << 3);
|
||||
}
|
||||
|
||||
@ -287,11 +311,37 @@ void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS,
|
||||
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) + " in " +
|
||||
error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
|
||||
toString(File));
|
||||
}
|
||||
}
|
||||
|
||||
static void maybeReportRelocationToDiscarded(const SectionChunk *FromChunk,
|
||||
Defined *Sym,
|
||||
const coff_relocation &Rel) {
|
||||
// Don't report these errors when the relocation comes from a debug info
|
||||
// section or in mingw mode. MinGW mode object files (built by GCC) can
|
||||
// have leftover sections with relocations against discarded comdat
|
||||
// sections. Such sections are left as is, with relocations untouched.
|
||||
if (FromChunk->isCodeView() || FromChunk->isDWARF() || Config->MinGW)
|
||||
return;
|
||||
|
||||
// Get the name of the symbol. If it's null, it was discarded early, so we
|
||||
// have to go back to the object file.
|
||||
ObjFile *File = FromChunk->File;
|
||||
StringRef Name;
|
||||
if (Sym) {
|
||||
Name = Sym->getName();
|
||||
} else {
|
||||
COFFSymbolRef COFFSym =
|
||||
check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex));
|
||||
File->getCOFFObj()->getSymbolName(COFFSym, Name);
|
||||
}
|
||||
|
||||
error("relocation against symbol in discarded section: " + Name +
|
||||
getSymbolLocations(File, Rel.SymbolTableIndex));
|
||||
}
|
||||
|
||||
void SectionChunk::writeTo(uint8_t *Buf) const {
|
||||
if (!hasData())
|
||||
return;
|
||||
@ -302,46 +352,40 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
|
||||
|
||||
// Apply relocations.
|
||||
size_t InputSize = getSize();
|
||||
for (const coff_relocation &Rel : Relocs) {
|
||||
for (size_t I = 0, E = Relocs.size(); I < E; I++) {
|
||||
const coff_relocation &Rel = Relocs[I];
|
||||
|
||||
// Check for an invalid relocation offset. This check isn't perfect, because
|
||||
// we don't have the relocation size, which is only known after checking the
|
||||
// machine and relocation type. As a result, a relocation may overwrite the
|
||||
// beginning of the following input section.
|
||||
if (Rel.VirtualAddress >= InputSize)
|
||||
fatal("relocation points beyond the end of its parent section");
|
||||
if (Rel.VirtualAddress >= InputSize) {
|
||||
error("relocation points beyond the end of its parent section");
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
|
||||
|
||||
// Use the potentially remapped Symbol instead of the one that the
|
||||
// relocation points to.
|
||||
auto *Sym = dyn_cast_or_null<Defined>(RelocTargets[I]);
|
||||
|
||||
// Get the output section of the symbol for this relocation. The output
|
||||
// section is needed to compute SECREL and SECTION relocations used in debug
|
||||
// info.
|
||||
auto *Sym =
|
||||
dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex));
|
||||
if (!Sym) {
|
||||
if (isCodeView() || isDWARF())
|
||||
continue;
|
||||
// Symbols in early discarded sections are represented using null pointers,
|
||||
// so we need to retrieve the name from the object file.
|
||||
COFFSymbolRef Sym =
|
||||
check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex));
|
||||
StringRef Name;
|
||||
File->getCOFFObj()->getSymbolName(Sym, Name);
|
||||
fatal("relocation against symbol in discarded section: " + Name);
|
||||
}
|
||||
Chunk *C = Sym->getChunk();
|
||||
Chunk *C = Sym ? Sym->getChunk() : nullptr;
|
||||
OutputSection *OS = C ? C->getOutputSection() : nullptr;
|
||||
|
||||
// Only absolute and __ImageBase symbols lack an output section. For any
|
||||
// other symbol, this indicates that the chunk was discarded. Normally
|
||||
// relocations against discarded sections are an error. However, debug info
|
||||
// sections are not GC roots and can end up with these kinds of relocations.
|
||||
// Skip these relocations.
|
||||
if (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym)) {
|
||||
if (isCodeView() || isDWARF())
|
||||
continue;
|
||||
fatal("relocation against symbol in discarded section: " +
|
||||
Sym->getName());
|
||||
// Skip the relocation if it refers to a discarded section, and diagnose it
|
||||
// as an error if appropriate. If a symbol was discarded early, it may be
|
||||
// null. If it was discarded late, the output section will be null, unless
|
||||
// it was an absolute or synthetic symbol.
|
||||
if (!Sym ||
|
||||
(!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym))) {
|
||||
maybeReportRelocationToDiscarded(this, Sym, Rel);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t S = Sym->getRVA();
|
||||
|
||||
// Compute the RVA of the relocation for relative relocations.
|
||||
@ -399,17 +443,125 @@ static uint8_t getBaserelType(const coff_relocation &Rel) {
|
||||
// fixed by the loader if load-time relocation is needed.
|
||||
// Only called when base relocation is enabled.
|
||||
void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
|
||||
for (const coff_relocation &Rel : Relocs) {
|
||||
for (size_t I = 0, E = Relocs.size(); I < E; I++) {
|
||||
const coff_relocation &Rel = Relocs[I];
|
||||
uint8_t Ty = getBaserelType(Rel);
|
||||
if (Ty == IMAGE_REL_BASED_ABSOLUTE)
|
||||
continue;
|
||||
Symbol *Target = File->getSymbol(Rel.SymbolTableIndex);
|
||||
// Use the potentially remapped Symbol instead of the one that the
|
||||
// relocation points to.
|
||||
Symbol *Target = RelocTargets[I];
|
||||
if (!Target || isa<DefinedAbsolute>(Target))
|
||||
continue;
|
||||
Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
|
||||
}
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
// Check whether a static relocation of type Type can be deferred and
|
||||
// handled at runtime as a pseudo relocation (for references to a module
|
||||
// local variable, which turned out to actually need to be imported from
|
||||
// another DLL) This returns the size the relocation is supposed to update,
|
||||
// in bits, or 0 if the relocation cannot be handled as a runtime pseudo
|
||||
// relocation.
|
||||
static int getRuntimePseudoRelocSize(uint16_t Type) {
|
||||
// Relocations that either contain an absolute address, or a plain
|
||||
// relative offset, since the runtime pseudo reloc implementation
|
||||
// adds 8/16/32/64 bit values to a memory address.
|
||||
//
|
||||
// Given a pseudo relocation entry,
|
||||
//
|
||||
// typedef struct {
|
||||
// DWORD sym;
|
||||
// DWORD target;
|
||||
// DWORD flags;
|
||||
// } runtime_pseudo_reloc_item_v2;
|
||||
//
|
||||
// the runtime relocation performs this adjustment:
|
||||
// *(base + .target) += *(base + .sym) - (base + .sym)
|
||||
//
|
||||
// This works for both absolute addresses (IMAGE_REL_*_ADDR32/64,
|
||||
// IMAGE_REL_I386_DIR32, where the memory location initially contains
|
||||
// the address of the IAT slot, and for relative addresses (IMAGE_REL*_REL32),
|
||||
// where the memory location originally contains the relative offset to the
|
||||
// IAT slot.
|
||||
//
|
||||
// This requires the target address to be writable, either directly out of
|
||||
// the image, or temporarily changed at runtime with VirtualProtect.
|
||||
// Since this only operates on direct address values, it doesn't work for
|
||||
// ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations.
|
||||
switch (Config->Machine) {
|
||||
case AMD64:
|
||||
switch (Type) {
|
||||
case IMAGE_REL_AMD64_ADDR64:
|
||||
return 64;
|
||||
case IMAGE_REL_AMD64_ADDR32:
|
||||
case IMAGE_REL_AMD64_REL32:
|
||||
case IMAGE_REL_AMD64_REL32_1:
|
||||
case IMAGE_REL_AMD64_REL32_2:
|
||||
case IMAGE_REL_AMD64_REL32_3:
|
||||
case IMAGE_REL_AMD64_REL32_4:
|
||||
case IMAGE_REL_AMD64_REL32_5:
|
||||
return 32;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case I386:
|
||||
switch (Type) {
|
||||
case IMAGE_REL_I386_DIR32:
|
||||
case IMAGE_REL_I386_REL32:
|
||||
return 32;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case ARMNT:
|
||||
switch (Type) {
|
||||
case IMAGE_REL_ARM_ADDR32:
|
||||
return 32;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case ARM64:
|
||||
switch (Type) {
|
||||
case IMAGE_REL_ARM64_ADDR64:
|
||||
return 64;
|
||||
case IMAGE_REL_ARM64_ADDR32:
|
||||
return 32;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("unknown machine type");
|
||||
}
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
// Append information to the provided vector about all relocations that
|
||||
// need to be handled at runtime as runtime pseudo relocations (references
|
||||
// to a module local variable, which turned out to actually need to be
|
||||
// imported from another DLL).
|
||||
void SectionChunk::getRuntimePseudoRelocs(
|
||||
std::vector<RuntimePseudoReloc> &Res) {
|
||||
for (const coff_relocation &Rel : Relocs) {
|
||||
auto *Target =
|
||||
dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex));
|
||||
if (!Target || !Target->IsRuntimePseudoReloc)
|
||||
continue;
|
||||
int SizeInBits = getRuntimePseudoRelocSize(Rel.Type);
|
||||
if (SizeInBits == 0) {
|
||||
error("unable to automatically import from " + Target->getName() +
|
||||
" with relocation type " +
|
||||
File->getCOFFObj()->getRelocationTypeName(Rel.Type) + " in " +
|
||||
toString(File));
|
||||
continue;
|
||||
}
|
||||
// SizeInBits is used to initialize the Flags field; currently no
|
||||
// other flags are defined.
|
||||
Res.emplace_back(
|
||||
RuntimePseudoReloc(Target, this, Rel.VirtualAddress, SizeInBits));
|
||||
}
|
||||
}
|
||||
|
||||
bool SectionChunk::hasData() const {
|
||||
return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
|
||||
}
|
||||
@ -447,6 +599,13 @@ void SectionChunk::replace(SectionChunk *Other) {
|
||||
Other->Live = false;
|
||||
}
|
||||
|
||||
uint32_t SectionChunk::getSectionNumber() const {
|
||||
DataRefImpl R;
|
||||
R.p = reinterpret_cast<uintptr_t>(Header);
|
||||
SectionRef S(R, File->getCOFFObj());
|
||||
return S.getIndex() + 1;
|
||||
}
|
||||
|
||||
CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
|
||||
// Common symbols are aligned on natural boundaries up to 32 bytes.
|
||||
// This is what MSVC link.exe does.
|
||||
@ -460,6 +619,7 @@ uint32_t CommonChunk::getOutputCharacteristics() const {
|
||||
|
||||
void StringChunk::writeTo(uint8_t *Buf) const {
|
||||
memcpy(Buf + OutputSectionOff, Str.data(), Str.size());
|
||||
Buf[OutputSectionOff + Str.size()] = '\0';
|
||||
}
|
||||
|
||||
ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) {
|
||||
@ -502,13 +662,30 @@ void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const {
|
||||
applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
|
||||
}
|
||||
|
||||
// A Thumb2, PIC, non-interworking range extension thunk.
|
||||
const uint8_t ArmThunk[] = {
|
||||
0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
|
||||
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
|
||||
0xe7, 0x44, // L1: add pc, ip
|
||||
};
|
||||
|
||||
size_t RangeExtensionThunk::getSize() const {
|
||||
assert(Config->Machine == ARMNT);
|
||||
return sizeof(ArmThunk);
|
||||
}
|
||||
|
||||
void RangeExtensionThunk::writeTo(uint8_t *Buf) const {
|
||||
assert(Config->Machine == ARMNT);
|
||||
uint64_t Offset = Target->getRVA() - RVA - 12;
|
||||
memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk));
|
||||
applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset));
|
||||
}
|
||||
|
||||
void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
|
||||
Res->emplace_back(getRVA());
|
||||
}
|
||||
|
||||
size_t LocalImportChunk::getSize() const {
|
||||
return Config->is64() ? 8 : 4;
|
||||
}
|
||||
size_t LocalImportChunk::getSize() const { return Config->Wordsize; }
|
||||
|
||||
void LocalImportChunk::writeTo(uint8_t *Buf) const {
|
||||
if (Config->is64()) {
|
||||
@ -528,6 +705,34 @@ void RVATableChunk::writeTo(uint8_t *Buf) const {
|
||||
"RVA tables should be de-duplicated");
|
||||
}
|
||||
|
||||
// MinGW specific, for the "automatic import of variables from DLLs" feature.
|
||||
size_t PseudoRelocTableChunk::getSize() const {
|
||||
if (Relocs.empty())
|
||||
return 0;
|
||||
return 12 + 12 * Relocs.size();
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const {
|
||||
if (Relocs.empty())
|
||||
return;
|
||||
|
||||
ulittle32_t *Table = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff);
|
||||
// This is the list header, to signal the runtime pseudo relocation v2
|
||||
// format.
|
||||
Table[0] = 0;
|
||||
Table[1] = 0;
|
||||
Table[2] = 1;
|
||||
|
||||
size_t Idx = 3;
|
||||
for (const RuntimePseudoReloc &RPR : Relocs) {
|
||||
Table[Idx + 0] = RPR.Sym->getRVA();
|
||||
Table[Idx + 1] = RPR.Target->getRVA() + RPR.TargetOffset;
|
||||
Table[Idx + 2] = RPR.Flags;
|
||||
Idx += 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Windows-specific. This class represents a block in .reloc section.
|
||||
// The format is described here.
|
||||
//
|
||||
@ -613,13 +818,16 @@ void MergeChunk::addSection(SectionChunk *C) {
|
||||
}
|
||||
|
||||
void MergeChunk::finalizeContents() {
|
||||
for (SectionChunk *C : Sections)
|
||||
if (C->isLive())
|
||||
Builder.add(toStringRef(C->getContents()));
|
||||
Builder.finalize();
|
||||
if (!Finalized) {
|
||||
for (SectionChunk *C : Sections)
|
||||
if (C->Live)
|
||||
Builder.add(toStringRef(C->getContents()));
|
||||
Builder.finalize();
|
||||
Finalized = true;
|
||||
}
|
||||
|
||||
for (SectionChunk *C : Sections) {
|
||||
if (!C->isLive())
|
||||
if (!C->Live)
|
||||
continue;
|
||||
size_t Off = Builder.getOffset(toStringRef(C->getContents()));
|
||||
C->setOutputSection(Out);
|
||||
@ -640,5 +848,16 @@ void MergeChunk::writeTo(uint8_t *Buf) const {
|
||||
Builder.write(Buf + OutputSectionOff);
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
size_t AbsolutePointerChunk::getSize() const { return Config->Wordsize; }
|
||||
|
||||
void AbsolutePointerChunk::writeTo(uint8_t *Buf) const {
|
||||
if (Config->is64()) {
|
||||
write64le(Buf + OutputSectionOff, Value);
|
||||
} else {
|
||||
write32le(Buf + OutputSectionOff, Value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
@ -36,6 +36,7 @@ class DefinedImportData;
|
||||
class DefinedRegular;
|
||||
class ObjFile;
|
||||
class OutputSection;
|
||||
class RuntimePseudoReloc;
|
||||
class Symbol;
|
||||
|
||||
// Mask for permissions (discardable, writable, readable, executable, etc).
|
||||
@ -63,6 +64,13 @@ public:
|
||||
// before calling this function.
|
||||
virtual void writeTo(uint8_t *Buf) const {}
|
||||
|
||||
// Called by the writer once before assigning addresses and writing
|
||||
// the output.
|
||||
virtual void readRelocTargets() {}
|
||||
|
||||
// Called if restarting thunk addition.
|
||||
virtual void resetRelocTargets() {}
|
||||
|
||||
// Called by the writer after an RVA is assigned, but before calling
|
||||
// getSize().
|
||||
virtual void finalizeContents() {}
|
||||
@ -114,6 +122,10 @@ protected:
|
||||
public:
|
||||
// The offset from beginning of the output section. The writer sets a value.
|
||||
uint64_t OutputSectionOff = 0;
|
||||
|
||||
// Whether this section needs to be kept distinct from other sections during
|
||||
// ICF. This is set by the driver using address-significance tables.
|
||||
bool KeepUnique = false;
|
||||
};
|
||||
|
||||
// A chunk corresponding a section of an input file.
|
||||
@ -140,6 +152,8 @@ public:
|
||||
|
||||
SectionChunk(ObjFile *File, const coff_section *Header);
|
||||
static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
|
||||
void readRelocTargets() override;
|
||||
void resetRelocTargets() override;
|
||||
size_t getSize() const override { return Header->SizeOfRawData; }
|
||||
ArrayRef<uint8_t> getContents() const;
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
@ -157,6 +171,8 @@ public:
|
||||
void applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
|
||||
uint64_t P) const;
|
||||
|
||||
void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &Res);
|
||||
|
||||
// Called if the garbage collector decides to not include this chunk
|
||||
// in a final output. It's supposed to print out a log message to stdout.
|
||||
void printDiscardedMessage() const;
|
||||
@ -167,16 +183,6 @@ public:
|
||||
|
||||
StringRef getDebugName() override;
|
||||
|
||||
// Returns true if the chunk was not dropped by GC.
|
||||
bool isLive() { return Live; }
|
||||
|
||||
// Used by the garbage collector.
|
||||
void markLive() {
|
||||
assert(Config->DoGC && "should only mark things live from GC");
|
||||
assert(!isLive() && "Cannot mark an already live section!");
|
||||
Live = true;
|
||||
}
|
||||
|
||||
// True if this is a codeview debug info chunk. These will not be laid out in
|
||||
// the image. Instead they will end up in the PDB, if one is requested.
|
||||
bool isCodeView() const {
|
||||
@ -197,10 +203,13 @@ public:
|
||||
// Allow iteration over the associated child chunks for this section.
|
||||
ArrayRef<SectionChunk *> children() const { return AssocChildren; }
|
||||
|
||||
// The section ID this chunk belongs to in its Obj.
|
||||
uint32_t getSectionNumber() const;
|
||||
|
||||
// A pointer pointing to a replacement for this chunk.
|
||||
// Initially it points to "this" object. If this chunk is merged
|
||||
// with other chunk by ICF, it points to another chunk,
|
||||
// and this chunk is considrered as dead.
|
||||
// and this chunk is considered as dead.
|
||||
SectionChunk *Repl;
|
||||
|
||||
// The CRC of the contents as described in the COFF spec 4.5.5.
|
||||
@ -217,13 +226,17 @@ public:
|
||||
|
||||
ArrayRef<coff_relocation> Relocs;
|
||||
|
||||
// Used by the garbage collector.
|
||||
bool Live;
|
||||
|
||||
// When inserting a thunk, we need to adjust a relocation to point to
|
||||
// the thunk instead of the actual original target Symbol.
|
||||
std::vector<Symbol *> RelocTargets;
|
||||
|
||||
private:
|
||||
StringRef SectionName;
|
||||
std::vector<SectionChunk *> AssocChildren;
|
||||
|
||||
// Used by the garbage collector.
|
||||
bool Live;
|
||||
|
||||
// Used for ICF (Identical COMDAT Folding)
|
||||
void replace(SectionChunk *Other);
|
||||
uint32_t Class[2] = {0, 0};
|
||||
@ -254,6 +267,7 @@ public:
|
||||
|
||||
private:
|
||||
llvm::StringTableBuilder Builder;
|
||||
bool Finalized = false;
|
||||
};
|
||||
|
||||
// A chunk for common symbols. Common chunks don't have actual data.
|
||||
@ -297,7 +311,7 @@ static const uint8_t ImportThunkARM64[] = {
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// A chunk for DLL import jump table entry. In a final output, it's
|
||||
// A chunk for DLL import jump table entry. In a final output, its
|
||||
// contents will be a JMP instruction to some __imp_ symbol.
|
||||
class ImportThunkChunkX64 : public Chunk {
|
||||
public:
|
||||
@ -341,12 +355,21 @@ private:
|
||||
Defined *ImpSymbol;
|
||||
};
|
||||
|
||||
class RangeExtensionThunk : public Chunk {
|
||||
public:
|
||||
explicit RangeExtensionThunk(Defined *T) : Target(T) {}
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
Defined *Target;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// See comments for DefinedLocalImport class.
|
||||
class LocalImportChunk : public Chunk {
|
||||
public:
|
||||
explicit LocalImportChunk(Defined *S) : Sym(S) {
|
||||
Alignment = Config->is64() ? 8 : 4;
|
||||
Alignment = Config->Wordsize;
|
||||
}
|
||||
size_t getSize() const override;
|
||||
void getBaserels(std::vector<Baserel> *Res) override;
|
||||
@ -416,9 +439,73 @@ public:
|
||||
uint8_t Type;
|
||||
};
|
||||
|
||||
// This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a
|
||||
// specific place in a section, without any data. This is used for the MinGW
|
||||
// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept
|
||||
// of an empty chunk isn't MinGW specific.
|
||||
class EmptyChunk : public Chunk {
|
||||
public:
|
||||
EmptyChunk() {}
|
||||
size_t getSize() const override { return 0; }
|
||||
void writeTo(uint8_t *Buf) const override {}
|
||||
};
|
||||
|
||||
// MinGW specific, for the "automatic import of variables from DLLs" feature.
|
||||
// This provides the table of runtime pseudo relocations, for variable
|
||||
// references that turned out to need to be imported from a DLL even though
|
||||
// the reference didn't use the dllimport attribute. The MinGW runtime will
|
||||
// process this table after loading, before handling control over to user
|
||||
// code.
|
||||
class PseudoRelocTableChunk : public Chunk {
|
||||
public:
|
||||
PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &Relocs)
|
||||
: Relocs(std::move(Relocs)) {
|
||||
Alignment = 4;
|
||||
}
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
private:
|
||||
std::vector<RuntimePseudoReloc> Relocs;
|
||||
};
|
||||
|
||||
// MinGW specific; information about one individual location in the image
|
||||
// that needs to be fixed up at runtime after loading. This represents
|
||||
// one individual element in the PseudoRelocTableChunk table.
|
||||
class RuntimePseudoReloc {
|
||||
public:
|
||||
RuntimePseudoReloc(Defined *Sym, SectionChunk *Target, uint32_t TargetOffset,
|
||||
int Flags)
|
||||
: Sym(Sym), Target(Target), TargetOffset(TargetOffset), Flags(Flags) {}
|
||||
|
||||
Defined *Sym;
|
||||
SectionChunk *Target;
|
||||
uint32_t TargetOffset;
|
||||
// The Flags field contains the size of the relocation, in bits. No other
|
||||
// flags are currently defined.
|
||||
int Flags;
|
||||
};
|
||||
|
||||
// MinGW specific. A Chunk that contains one pointer-sized absolute value.
|
||||
class AbsolutePointerChunk : public Chunk {
|
||||
public:
|
||||
AbsolutePointerChunk(uint64_t Value) : Value(Value) {
|
||||
Alignment = getSize();
|
||||
}
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
private:
|
||||
uint64_t Value;
|
||||
};
|
||||
|
||||
void applyMOV32T(uint8_t *Off, uint32_t V);
|
||||
void applyBranch24T(uint8_t *Off, int32_t V);
|
||||
|
||||
void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift);
|
||||
void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit);
|
||||
void applyArm64Branch26(uint8_t *Off, int64_t V);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
|
@ -84,6 +84,7 @@ struct Configuration {
|
||||
bool is64() { return Machine == AMD64 || Machine == ARM64; }
|
||||
|
||||
llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
size_t Wordsize;
|
||||
bool Verbose = false;
|
||||
WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
|
||||
Symbol *Entry = nullptr;
|
||||
@ -94,7 +95,8 @@ struct Configuration {
|
||||
bool DoICF = true;
|
||||
bool TailMerge;
|
||||
bool Relocatable = true;
|
||||
bool Force = false;
|
||||
bool ForceMultiple = false;
|
||||
bool ForceUnresolved = false;
|
||||
bool Debug = false;
|
||||
bool DebugDwarf = false;
|
||||
bool DebugGHashes = false;
|
||||
@ -195,6 +197,7 @@ struct Configuration {
|
||||
bool MinGW = false;
|
||||
bool WarnMissingOrderSymbol = true;
|
||||
bool WarnLocallyDefinedImported = true;
|
||||
bool WarnDebugInfoUnusable = true;
|
||||
bool Incremental = true;
|
||||
bool IntegrityCheck = false;
|
||||
bool KillAt = false;
|
||||
|
@ -35,8 +35,6 @@ namespace {
|
||||
|
||||
// Import table
|
||||
|
||||
static int ptrSize() { return Config->is64() ? 8 : 4; }
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class HintNameChunk : public Chunk {
|
||||
public:
|
||||
@ -61,8 +59,8 @@ private:
|
||||
// A chunk for the import descriptor table.
|
||||
class LookupChunk : public Chunk {
|
||||
public:
|
||||
explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = ptrSize(); }
|
||||
size_t getSize() const override { return ptrSize(); }
|
||||
explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = Config->Wordsize; }
|
||||
size_t getSize() const override { return Config->Wordsize; }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
write32le(Buf + OutputSectionOff, HintName->getRVA());
|
||||
@ -76,8 +74,10 @@ public:
|
||||
// See Microsoft PE/COFF spec 7.1. Import Header for details.
|
||||
class OrdinalOnlyChunk : public Chunk {
|
||||
public:
|
||||
explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) { Alignment = ptrSize(); }
|
||||
size_t getSize() const override { return ptrSize(); }
|
||||
explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {
|
||||
Alignment = Config->Wordsize;
|
||||
}
|
||||
size_t getSize() const override { return Config->Wordsize; }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
// An import-by-ordinal slot has MSB 1 to indicate that
|
||||
@ -230,6 +230,36 @@ static const uint8_t ThunkARM[] = {
|
||||
0x60, 0x47, // bx ip
|
||||
};
|
||||
|
||||
static const uint8_t ThunkARM64[] = {
|
||||
0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_<FUNCNAME>
|
||||
0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_<FUNCNAME>
|
||||
0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]!
|
||||
0xfd, 0x03, 0x00, 0x91, // mov x29, sp
|
||||
0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16]
|
||||
0xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32]
|
||||
0xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48]
|
||||
0xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64]
|
||||
0xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80]
|
||||
0xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112]
|
||||
0xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144]
|
||||
0xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176]
|
||||
0xe1, 0x03, 0x11, 0xaa, // mov x1, x17
|
||||
0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR
|
||||
0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR
|
||||
0x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper2
|
||||
0xf0, 0x03, 0x00, 0xaa, // mov x16, x0
|
||||
0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176]
|
||||
0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144]
|
||||
0xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112]
|
||||
0xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80]
|
||||
0xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64]
|
||||
0xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48]
|
||||
0xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32]
|
||||
0xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16]
|
||||
0xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #208
|
||||
0x00, 0x02, 0x1f, 0xd6, // br x16
|
||||
};
|
||||
|
||||
// A chunk for the delay import thunk.
|
||||
class ThunkChunkX64 : public Chunk {
|
||||
public:
|
||||
@ -298,11 +328,35 @@ public:
|
||||
Defined *Helper = nullptr;
|
||||
};
|
||||
|
||||
class ThunkChunkARM64 : public Chunk {
|
||||
public:
|
||||
ThunkChunkARM64(Defined *I, Chunk *D, Defined *H)
|
||||
: Imp(I), Desc(D), Helper(H) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(ThunkARM64); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
memcpy(Buf + OutputSectionOff, ThunkARM64, sizeof(ThunkARM64));
|
||||
applyArm64Addr(Buf + OutputSectionOff + 0, Imp->getRVA(), RVA + 0, 12);
|
||||
applyArm64Imm(Buf + OutputSectionOff + 4, Imp->getRVA() & 0xfff, 0);
|
||||
applyArm64Addr(Buf + OutputSectionOff + 52, Desc->getRVA(), RVA + 52, 12);
|
||||
applyArm64Imm(Buf + OutputSectionOff + 56, Desc->getRVA() & 0xfff, 0);
|
||||
applyArm64Branch26(Buf + OutputSectionOff + 60,
|
||||
Helper->getRVA() - RVA - 60);
|
||||
}
|
||||
|
||||
Defined *Imp = nullptr;
|
||||
Chunk *Desc = nullptr;
|
||||
Defined *Helper = nullptr;
|
||||
};
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class DelayAddressChunk : public Chunk {
|
||||
public:
|
||||
explicit DelayAddressChunk(Chunk *C) : Thunk(C) { Alignment = ptrSize(); }
|
||||
size_t getSize() const override { return ptrSize(); }
|
||||
explicit DelayAddressChunk(Chunk *C) : Thunk(C) {
|
||||
Alignment = Config->Wordsize;
|
||||
}
|
||||
size_t getSize() const override { return Config->Wordsize; }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
if (Config->is64()) {
|
||||
@ -362,6 +416,8 @@ public:
|
||||
size_t getSize() const override { return Size * 4; }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
memset(Buf + OutputSectionOff, 0, getSize());
|
||||
|
||||
for (const Export &E : Config->Exports) {
|
||||
uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4;
|
||||
uint32_t Bit = 0;
|
||||
@ -418,30 +474,6 @@ private:
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
uint64_t IdataContents::getDirSize() {
|
||||
return Dirs.size() * sizeof(ImportDirectoryTableEntry);
|
||||
}
|
||||
|
||||
uint64_t IdataContents::getIATSize() {
|
||||
return Addresses.size() * ptrSize();
|
||||
}
|
||||
|
||||
// Returns a list of .idata contents.
|
||||
// See Microsoft PE/COFF spec 5.4 for details.
|
||||
std::vector<Chunk *> IdataContents::getChunks() {
|
||||
create();
|
||||
|
||||
// The loader assumes a specific order of data.
|
||||
// Add each type in the correct order.
|
||||
std::vector<Chunk *> V;
|
||||
V.insert(V.end(), Dirs.begin(), Dirs.end());
|
||||
V.insert(V.end(), Lookups.begin(), Lookups.end());
|
||||
V.insert(V.end(), Addresses.begin(), Addresses.end());
|
||||
V.insert(V.end(), Hints.begin(), Hints.end());
|
||||
V.insert(V.end(), DLLNames.begin(), DLLNames.end());
|
||||
return V;
|
||||
}
|
||||
|
||||
void IdataContents::create() {
|
||||
std::vector<std::vector<DefinedImportData *>> V = binImports(Imports);
|
||||
|
||||
@ -465,8 +497,8 @@ void IdataContents::create() {
|
||||
Hints.push_back(C);
|
||||
}
|
||||
// Terminate with null values.
|
||||
Lookups.push_back(make<NullChunk>(ptrSize()));
|
||||
Addresses.push_back(make<NullChunk>(ptrSize()));
|
||||
Lookups.push_back(make<NullChunk>(Config->Wordsize));
|
||||
Addresses.push_back(make<NullChunk>(Config->Wordsize));
|
||||
|
||||
for (int I = 0, E = Syms.size(); I < E; ++I)
|
||||
Syms[I]->setLocation(Addresses[Base + I]);
|
||||
@ -555,6 +587,8 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) {
|
||||
return make<ThunkChunkX86>(S, Dir, Helper);
|
||||
case ARMNT:
|
||||
return make<ThunkChunkARM>(S, Dir, Helper);
|
||||
case ARM64:
|
||||
return make<ThunkChunkARM64>(S, Dir, Helper);
|
||||
default:
|
||||
llvm_unreachable("unsupported machine type");
|
||||
}
|
||||
|
@ -19,19 +19,12 @@ namespace coff {
|
||||
// Windows-specific.
|
||||
// IdataContents creates all chunks for the DLL import table.
|
||||
// You are supposed to call add() to add symbols and then
|
||||
// call getChunks() to get a list of chunks.
|
||||
// call create() to populate the chunk vectors.
|
||||
class IdataContents {
|
||||
public:
|
||||
void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
|
||||
bool empty() { return Imports.empty(); }
|
||||
std::vector<Chunk *> getChunks();
|
||||
|
||||
uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
|
||||
uint64_t getDirSize();
|
||||
uint64_t getIATRVA() { return Addresses[0]->getRVA(); }
|
||||
uint64_t getIATSize();
|
||||
|
||||
private:
|
||||
void create();
|
||||
|
||||
std::vector<DefinedImportData *> Imports;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Option/Option.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/LEB128.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/TarWriter.h"
|
||||
@ -56,7 +57,7 @@ Configuration *Config;
|
||||
LinkerDriver *Driver;
|
||||
|
||||
bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
|
||||
errorHandler().LogName = sys::path::filename(Args[0]);
|
||||
errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
|
||||
errorHandler().ErrorOS = &Diag;
|
||||
errorHandler().ColorDiagnostics = Diag.has_colors();
|
||||
errorHandler().ErrorLimitExceededMsg =
|
||||
@ -370,13 +371,30 @@ Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
|
||||
return Path;
|
||||
}
|
||||
|
||||
// MinGW specific. If an embedded directive specified to link to
|
||||
// foo.lib, but it isn't found, try libfoo.a instead.
|
||||
StringRef LinkerDriver::doFindLibMinGW(StringRef Filename) {
|
||||
if (Filename.contains('/') || Filename.contains('\\'))
|
||||
return Filename;
|
||||
|
||||
SmallString<128> S = Filename;
|
||||
sys::path::replace_extension(S, ".a");
|
||||
StringRef LibName = Saver.save("lib" + S.str());
|
||||
return doFindFile(LibName);
|
||||
}
|
||||
|
||||
// Find library file from search path.
|
||||
StringRef LinkerDriver::doFindLib(StringRef Filename) {
|
||||
// Add ".lib" to Filename if that has no file extension.
|
||||
bool HasExt = Filename.contains('.');
|
||||
if (!HasExt)
|
||||
Filename = Saver.save(Filename + ".lib");
|
||||
return doFindFile(Filename);
|
||||
StringRef Ret = doFindFile(Filename);
|
||||
// For MinGW, if the find above didn't turn up anything, try
|
||||
// looking for a MinGW formatted library name.
|
||||
if (Config->MinGW && Ret == Filename)
|
||||
return doFindLibMinGW(Filename);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
// Resolves a library path. /nodefaultlib options are taken into
|
||||
@ -429,29 +447,48 @@ StringRef LinkerDriver::findDefaultEntry() {
|
||||
assert(Config->Subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
|
||||
"must handle /subsystem before calling this");
|
||||
|
||||
// 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".
|
||||
bool FindMain = !Config->NoDefaultLibAll;
|
||||
if (Config->MinGW)
|
||||
return mangle(Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
|
||||
? "WinMainCRTStartup"
|
||||
: "mainCRTStartup");
|
||||
|
||||
if (Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
|
||||
if (findUnderscoreMangle(FindMain ? "WinMain" : "WinMainCRTStartup"))
|
||||
return mangle("WinMainCRTStartup");
|
||||
if (findUnderscoreMangle(FindMain ? "wWinMain" : "wWinMainCRTStartup"))
|
||||
return mangle("wWinMainCRTStartup");
|
||||
if (findUnderscoreMangle("wWinMain")) {
|
||||
if (!findUnderscoreMangle("WinMain"))
|
||||
return mangle("wWinMainCRTStartup");
|
||||
warn("found both wWinMain and WinMain; using latter");
|
||||
}
|
||||
return mangle("WinMainCRTStartup");
|
||||
}
|
||||
if (findUnderscoreMangle(FindMain ? "main" : "mainCRTStartup"))
|
||||
return mangle("mainCRTStartup");
|
||||
if (findUnderscoreMangle(FindMain ? "wmain" : "wmainCRTStartup"))
|
||||
return mangle("wmainCRTStartup");
|
||||
return "";
|
||||
if (findUnderscoreMangle("wmain")) {
|
||||
if (!findUnderscoreMangle("main"))
|
||||
return mangle("wmainCRTStartup");
|
||||
warn("found both wmain and main; using latter");
|
||||
}
|
||||
return mangle("mainCRTStartup");
|
||||
}
|
||||
|
||||
WindowsSubsystem LinkerDriver::inferSubsystem() {
|
||||
if (Config->DLL)
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
||||
if (findUnderscoreMangle("main") || findUnderscoreMangle("wmain"))
|
||||
if (Config->MinGW)
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
|
||||
if (findUnderscoreMangle("WinMain") || findUnderscoreMangle("wWinMain"))
|
||||
// Note that link.exe infers the subsystem from the presence of these
|
||||
// functions even if /entry: or /nodefaultlib are passed which causes them
|
||||
// to not be called.
|
||||
bool HaveMain = findUnderscoreMangle("main");
|
||||
bool HaveWMain = findUnderscoreMangle("wmain");
|
||||
bool HaveWinMain = findUnderscoreMangle("WinMain");
|
||||
bool HaveWWinMain = findUnderscoreMangle("wWinMain");
|
||||
if (HaveMain || HaveWMain) {
|
||||
if (HaveWinMain || HaveWWinMain) {
|
||||
warn(std::string("found ") + (HaveMain ? "main" : "wmain") + " and " +
|
||||
(HaveWinMain ? "WinMain" : "wWinMain") +
|
||||
"; defaulting to /subsystem:console");
|
||||
}
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
|
||||
}
|
||||
if (HaveWinMain || HaveWWinMain)
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
||||
return IMAGE_SUBSYSTEM_UNKNOWN;
|
||||
}
|
||||
@ -497,26 +534,65 @@ static std::string createResponseFile(const opt::InputArgList &Args,
|
||||
return Data.str();
|
||||
}
|
||||
|
||||
static unsigned getDefaultDebugType(const opt::InputArgList &Args) {
|
||||
unsigned DebugTypes = static_cast<unsigned>(DebugType::CV);
|
||||
enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab };
|
||||
|
||||
static DebugKind parseDebugKind(const opt::InputArgList &Args) {
|
||||
auto *A = Args.getLastArg(OPT_debug, OPT_debug_opt);
|
||||
if (!A)
|
||||
return DebugKind::None;
|
||||
if (A->getNumValues() == 0)
|
||||
return DebugKind::Full;
|
||||
|
||||
DebugKind Debug = StringSwitch<DebugKind>(A->getValue())
|
||||
.CaseLower("none", DebugKind::None)
|
||||
.CaseLower("full", DebugKind::Full)
|
||||
.CaseLower("fastlink", DebugKind::FastLink)
|
||||
// LLD extensions
|
||||
.CaseLower("ghash", DebugKind::GHash)
|
||||
.CaseLower("dwarf", DebugKind::Dwarf)
|
||||
.CaseLower("symtab", DebugKind::Symtab)
|
||||
.Default(DebugKind::Unknown);
|
||||
|
||||
if (Debug == DebugKind::FastLink) {
|
||||
warn("/debug:fastlink unsupported; using /debug:full");
|
||||
return DebugKind::Full;
|
||||
}
|
||||
if (Debug == DebugKind::Unknown) {
|
||||
error("/debug: unknown option: " + Twine(A->getValue()));
|
||||
return DebugKind::None;
|
||||
}
|
||||
return Debug;
|
||||
}
|
||||
|
||||
static unsigned parseDebugTypes(const opt::InputArgList &Args) {
|
||||
unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
|
||||
|
||||
if (auto *A = Args.getLastArg(OPT_debugtype)) {
|
||||
SmallVector<StringRef, 3> Types;
|
||||
A->getSpelling().split(Types, ',', /*KeepEmpty=*/false);
|
||||
|
||||
for (StringRef Type : Types) {
|
||||
unsigned V = StringSwitch<unsigned>(Type.lower())
|
||||
.Case("cv", static_cast<unsigned>(DebugType::CV))
|
||||
.Case("pdata", static_cast<unsigned>(DebugType::PData))
|
||||
.Case("fixup", static_cast<unsigned>(DebugType::Fixup))
|
||||
.Default(0);
|
||||
if (V == 0) {
|
||||
warn("/debugtype: unknown option: " + Twine(A->getValue()));
|
||||
continue;
|
||||
}
|
||||
DebugTypes |= V;
|
||||
}
|
||||
return DebugTypes;
|
||||
}
|
||||
|
||||
// Default debug types
|
||||
DebugTypes = static_cast<unsigned>(DebugType::CV);
|
||||
if (Args.hasArg(OPT_driver))
|
||||
DebugTypes |= static_cast<unsigned>(DebugType::PData);
|
||||
if (Args.hasArg(OPT_profile))
|
||||
DebugTypes |= static_cast<unsigned>(DebugType::Fixup);
|
||||
return DebugTypes;
|
||||
}
|
||||
|
||||
static unsigned parseDebugType(StringRef Arg) {
|
||||
SmallVector<StringRef, 3> Types;
|
||||
Arg.split(Types, ',', /*KeepEmpty=*/false);
|
||||
|
||||
unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
|
||||
for (StringRef Type : Types)
|
||||
DebugTypes |= StringSwitch<unsigned>(Type.lower())
|
||||
.Case("cv", static_cast<unsigned>(DebugType::CV))
|
||||
.Case("pdata", static_cast<unsigned>(DebugType::PData))
|
||||
.Case("fixup", static_cast<unsigned>(DebugType::Fixup))
|
||||
.Default(0);
|
||||
return DebugTypes;
|
||||
}
|
||||
|
||||
@ -676,131 +752,6 @@ static void parseModuleDefs(StringRef Path) {
|
||||
}
|
||||
}
|
||||
|
||||
// A helper function for filterBitcodeFiles.
|
||||
static bool needsRebuilding(MemoryBufferRef MB) {
|
||||
// The MSVC linker doesn't support thin archives, so if it's a thin
|
||||
// archive, we always need to rebuild it.
|
||||
std::unique_ptr<Archive> File =
|
||||
CHECK(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier());
|
||||
if (File->isThin())
|
||||
return true;
|
||||
|
||||
// Returns true if the archive contains at least one bitcode file.
|
||||
for (MemoryBufferRef Member : getArchiveMembers(File.get()))
|
||||
if (identify_magic(Member.getBuffer()) == file_magic::bitcode)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Opens a given path as an archive file and removes bitcode files
|
||||
// from them if exists. This function is to appease the MSVC linker as
|
||||
// their linker doesn't like archive files containing non-native
|
||||
// object files.
|
||||
//
|
||||
// If a given archive doesn't contain bitcode files, the archive path
|
||||
// is returned as-is. Otherwise, a new temporary file is created and
|
||||
// its path is returned.
|
||||
static Optional<std::string>
|
||||
filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
|
||||
std::unique_ptr<MemoryBuffer> MB = CHECK(
|
||||
MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
|
||||
MemoryBufferRef MBRef = MB->getMemBufferRef();
|
||||
file_magic Magic = identify_magic(MBRef.getBuffer());
|
||||
|
||||
if (Magic == file_magic::bitcode)
|
||||
return None;
|
||||
if (Magic != file_magic::archive)
|
||||
return Path.str();
|
||||
if (!needsRebuilding(MBRef))
|
||||
return Path.str();
|
||||
|
||||
std::unique_ptr<Archive> File =
|
||||
CHECK(Archive::create(MBRef),
|
||||
MBRef.getBufferIdentifier() + ": failed to parse archive");
|
||||
|
||||
std::vector<NewArchiveMember> New;
|
||||
for (MemoryBufferRef Member : getArchiveMembers(File.get()))
|
||||
if (identify_magic(Member.getBuffer()) != file_magic::bitcode)
|
||||
New.emplace_back(Member);
|
||||
|
||||
if (New.empty())
|
||||
return None;
|
||||
|
||||
log("Creating a temporary archive for " + Path + " to remove bitcode files");
|
||||
|
||||
SmallString<128> 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);
|
||||
|
||||
Error E =
|
||||
llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU,
|
||||
/*Deterministics=*/true,
|
||||
/*Thin=*/false);
|
||||
handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
|
||||
error("failed to create a new archive " + S.str() + ": " + EI.message());
|
||||
});
|
||||
return Temp;
|
||||
}
|
||||
|
||||
// Create response file contents and invoke the MSVC linker.
|
||||
void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
|
||||
std::string Rsp = "/nologo\n";
|
||||
std::vector<std::string> Temps;
|
||||
|
||||
// Write out archive members that we used in symbol resolution and pass these
|
||||
// to MSVC before any archives, so that MSVC uses the same objects to satisfy
|
||||
// references.
|
||||
for (ObjFile *Obj : ObjFile::Instances) {
|
||||
if (Obj->ParentName.empty())
|
||||
continue;
|
||||
SmallString<128> S;
|
||||
int Fd;
|
||||
if (auto EC = sys::fs::createTemporaryFile(
|
||||
"lld-" + sys::path::filename(Obj->ParentName), ".obj", Fd, S))
|
||||
fatal("cannot create a temporary file: " + EC.message());
|
||||
raw_fd_ostream OS(Fd, /*shouldClose*/ true);
|
||||
OS << Obj->MB.getBuffer();
|
||||
Temps.push_back(S.str());
|
||||
Rsp += quote(S) + "\n";
|
||||
}
|
||||
|
||||
for (auto *Arg : Args) {
|
||||
switch (Arg->getOption().getID()) {
|
||||
case OPT_linkrepro:
|
||||
case OPT_lldmap:
|
||||
case OPT_lldmap_file:
|
||||
case OPT_lldsavetemps:
|
||||
case OPT_msvclto:
|
||||
// LLD-specific options are stripped.
|
||||
break;
|
||||
case OPT_opt:
|
||||
if (!StringRef(Arg->getValue()).startswith("lld"))
|
||||
Rsp += toString(*Arg) + " ";
|
||||
break;
|
||||
case OPT_INPUT: {
|
||||
if (Optional<StringRef> Path = doFindFile(Arg->getValue())) {
|
||||
if (Optional<std::string> S = filterBitcodeFiles(*Path, Temps))
|
||||
Rsp += quote(*S) + "\n";
|
||||
continue;
|
||||
}
|
||||
Rsp += quote(Arg->getValue()) + "\n";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Rsp += toString(*Arg) + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<StringRef> ObjFiles = Symtab->compileBitcodeFiles();
|
||||
runMSVCLinker(Rsp, ObjFiles);
|
||||
|
||||
for (StringRef Path : Temps)
|
||||
sys::fs::remove(Path);
|
||||
}
|
||||
|
||||
void LinkerDriver::enqueueTask(std::function<void()> Task) {
|
||||
TaskQueue.push_back(std::move(Task));
|
||||
}
|
||||
@ -856,6 +807,97 @@ static void parseOrderFile(StringRef Arg) {
|
||||
}
|
||||
}
|
||||
|
||||
static void markAddrsig(Symbol *S) {
|
||||
if (auto *D = dyn_cast_or_null<Defined>(S))
|
||||
if (Chunk *C = D->getChunk())
|
||||
C->KeepUnique = true;
|
||||
}
|
||||
|
||||
static void findKeepUniqueSections() {
|
||||
// Exported symbols could be address-significant in other executables or DSOs,
|
||||
// so we conservatively mark them as address-significant.
|
||||
for (Export &R : Config->Exports)
|
||||
markAddrsig(R.Sym);
|
||||
|
||||
// Visit the address-significance table in each object file and mark each
|
||||
// referenced symbol as address-significant.
|
||||
for (ObjFile *Obj : ObjFile::Instances) {
|
||||
ArrayRef<Symbol *> Syms = Obj->getSymbols();
|
||||
if (Obj->AddrsigSec) {
|
||||
ArrayRef<uint8_t> Contents;
|
||||
Obj->getCOFFObj()->getSectionContents(Obj->AddrsigSec, Contents);
|
||||
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(Obj) + ": could not decode addrsig section: " + Err);
|
||||
if (SymIndex >= Syms.size())
|
||||
fatal(toString(Obj) + ": invalid symbol index in addrsig section");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// link.exe replaces each %foo% in AltPath with the contents of environment
|
||||
// variable foo, and adds the two magic env vars _PDB (expands to the basename
|
||||
// of pdb's output path) and _EXT (expands to the extension of the output
|
||||
// binary).
|
||||
// lld only supports %_PDB% and %_EXT% and warns on references to all other env
|
||||
// vars.
|
||||
static void parsePDBAltPath(StringRef AltPath) {
|
||||
SmallString<128> Buf;
|
||||
StringRef PDBBasename =
|
||||
sys::path::filename(Config->PDBPath, sys::path::Style::windows);
|
||||
StringRef BinaryExtension =
|
||||
sys::path::extension(Config->OutputFile, sys::path::Style::windows);
|
||||
if (!BinaryExtension.empty())
|
||||
BinaryExtension = BinaryExtension.substr(1); // %_EXT% does not include '.'.
|
||||
|
||||
// Invariant:
|
||||
// +--------- Cursor ('a...' might be the empty string).
|
||||
// | +----- FirstMark
|
||||
// | | +- SecondMark
|
||||
// v v v
|
||||
// a...%...%...
|
||||
size_t Cursor = 0;
|
||||
while (Cursor < AltPath.size()) {
|
||||
size_t FirstMark, SecondMark;
|
||||
if ((FirstMark = AltPath.find('%', Cursor)) == StringRef::npos ||
|
||||
(SecondMark = AltPath.find('%', FirstMark + 1)) == StringRef::npos) {
|
||||
// Didn't find another full fragment, treat rest of string as literal.
|
||||
Buf.append(AltPath.substr(Cursor));
|
||||
break;
|
||||
}
|
||||
|
||||
// Found a full fragment. Append text in front of first %, and interpret
|
||||
// text between first and second % as variable name.
|
||||
Buf.append(AltPath.substr(Cursor, FirstMark - Cursor));
|
||||
StringRef Var = AltPath.substr(FirstMark, SecondMark - FirstMark + 1);
|
||||
if (Var.equals_lower("%_pdb%"))
|
||||
Buf.append(PDBBasename);
|
||||
else if (Var.equals_lower("%_ext%"))
|
||||
Buf.append(BinaryExtension);
|
||||
else {
|
||||
warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " +
|
||||
Var + " as literal");
|
||||
Buf.append(Var);
|
||||
}
|
||||
|
||||
Cursor = SecondMark + 1;
|
||||
}
|
||||
|
||||
Config->PDBAltPath = Buf;
|
||||
}
|
||||
|
||||
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.
|
||||
@ -944,11 +986,17 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
|
||||
// Handle /ignore
|
||||
for (auto *Arg : Args.filtered(OPT_ignore)) {
|
||||
if (StringRef(Arg->getValue()) == "4037")
|
||||
Config->WarnMissingOrderSymbol = false;
|
||||
else if (StringRef(Arg->getValue()) == "4217")
|
||||
Config->WarnLocallyDefinedImported = false;
|
||||
// Other warning numbers are ignored.
|
||||
SmallVector<StringRef, 8> Vec;
|
||||
StringRef(Arg->getValue()).split(Vec, ',');
|
||||
for (StringRef S : Vec) {
|
||||
if (S == "4037")
|
||||
Config->WarnMissingOrderSymbol = false;
|
||||
else if (S == "4099")
|
||||
Config->WarnDebugInfoUnusable = false;
|
||||
else if (S == "4217")
|
||||
Config->WarnLocallyDefinedImported = false;
|
||||
// Other warning numbers are ignored.
|
||||
}
|
||||
}
|
||||
|
||||
// Handle /out
|
||||
@ -962,20 +1010,26 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
|
||||
// Handle /force or /force:unresolved
|
||||
if (Args.hasArg(OPT_force, OPT_force_unresolved))
|
||||
Config->Force = true;
|
||||
Config->ForceUnresolved = true;
|
||||
|
||||
// Handle /force or /force:multiple
|
||||
if (Args.hasArg(OPT_force, OPT_force_multiple))
|
||||
Config->ForceMultiple = true;
|
||||
|
||||
// Handle /debug
|
||||
if (Args.hasArg(OPT_debug, OPT_debug_dwarf, OPT_debug_ghash)) {
|
||||
DebugKind Debug = parseDebugKind(Args);
|
||||
if (Debug == DebugKind::Full || Debug == DebugKind::Dwarf ||
|
||||
Debug == DebugKind::GHash) {
|
||||
Config->Debug = true;
|
||||
Config->Incremental = true;
|
||||
if (auto *Arg = Args.getLastArg(OPT_debugtype))
|
||||
Config->DebugTypes = parseDebugType(Arg->getValue());
|
||||
else
|
||||
Config->DebugTypes = getDefaultDebugType(Args);
|
||||
}
|
||||
|
||||
// Handle /debugtype
|
||||
Config->DebugTypes = parseDebugTypes(Args);
|
||||
|
||||
// Handle /pdb
|
||||
bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash);
|
||||
bool ShouldCreatePDB =
|
||||
(Debug == DebugKind::Full || Debug == DebugKind::GHash);
|
||||
if (ShouldCreatePDB) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_pdb))
|
||||
Config->PDBPath = Arg->getValue();
|
||||
@ -1096,7 +1150,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
Config->Implib = Arg->getValue();
|
||||
|
||||
// Handle /opt.
|
||||
bool DoGC = !Args.hasArg(OPT_debug) || Args.hasArg(OPT_profile);
|
||||
bool DoGC = Debug == DebugKind::None || Args.hasArg(OPT_profile);
|
||||
unsigned ICFLevel =
|
||||
Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on
|
||||
unsigned TailMerge = 1;
|
||||
@ -1181,6 +1235,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
parseMerge(".xdata=.rdata");
|
||||
parseMerge(".bss=.data");
|
||||
|
||||
if (Config->MinGW) {
|
||||
parseMerge(".ctors=.rdata");
|
||||
parseMerge(".dtors=.rdata");
|
||||
parseMerge(".CRT=.rdata");
|
||||
}
|
||||
|
||||
// Handle /section
|
||||
for (auto *Arg : Args.filtered(OPT_section))
|
||||
parseSection(Arg->getValue());
|
||||
@ -1234,9 +1294,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_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->DebugDwarf = Debug == DebugKind::Dwarf;
|
||||
Config->DebugGHashes = Debug == DebugKind::GHash;
|
||||
Config->DebugSymtab = Debug == DebugKind::Symtab;
|
||||
|
||||
Config->MapFile = getMapFile(Args);
|
||||
|
||||
@ -1266,10 +1326,14 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
return;
|
||||
|
||||
std::set<sys::fs::UniqueID> WholeArchives;
|
||||
for (auto *Arg : Args.filtered(OPT_wholearchive_file))
|
||||
if (Optional<StringRef> Path = doFindFile(Arg->getValue()))
|
||||
AutoExporter Exporter;
|
||||
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);
|
||||
Exporter.addWholeArchive(*Path);
|
||||
}
|
||||
}
|
||||
|
||||
// A predicate returning true if a given path is an argument for
|
||||
// /wholearchive:, or /wholearchive is enabled globally.
|
||||
@ -1300,12 +1364,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
// Read all input files given via the command line.
|
||||
run();
|
||||
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// We should have inferred a machine type by now from the input files, but if
|
||||
// not we assume x64.
|
||||
if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
|
||||
warn("/machine is not specified. x64 is assumed");
|
||||
Config->Machine = AMD64;
|
||||
}
|
||||
Config->Wordsize = Config->is64() ? 8 : 4;
|
||||
|
||||
// Input files can be Windows resource files (.res files). We use
|
||||
// WindowsResource to convert resource files to a regular COFF file,
|
||||
@ -1418,6 +1486,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
// tools won't work correctly if these assumptions are not held.
|
||||
sys::fs::make_absolute(Config->PDBAltPath);
|
||||
sys::path::remove_dots(Config->PDBAltPath);
|
||||
} else {
|
||||
// Don't do this earlier, so that Config->OutputFile is ready.
|
||||
parsePDBAltPath(Config->PDBAltPath);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1441,6 +1512,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
// Needed for MSVC 2017 15.5 CRT.
|
||||
Symtab->addAbsolute(mangle("__enclave_config"), 0);
|
||||
|
||||
if (Config->MinGW) {
|
||||
Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
|
||||
Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
|
||||
Symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
|
||||
Symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
|
||||
}
|
||||
|
||||
// This code may add new undefined symbols to the link, which may enqueue more
|
||||
// symbol resolution tasks, so we need to continue executing tasks until we
|
||||
// converge.
|
||||
@ -1480,18 +1558,29 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// If /msvclto is given, we use the MSVC linker to link LTO output files.
|
||||
// This is useful because MSVC link.exe can generate complete PDBs.
|
||||
if (Args.hasArg(OPT_msvclto)) {
|
||||
invokeMSVC(Args);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
||||
// link those files.
|
||||
Symtab->addCombinedLTOObjects();
|
||||
run();
|
||||
|
||||
if (Config->MinGW) {
|
||||
// Load any further object files that might be needed for doing automatic
|
||||
// imports.
|
||||
//
|
||||
// For cases with no automatically imported symbols, this iterates once
|
||||
// over the symbol table and doesn't do anything.
|
||||
//
|
||||
// For the normal case with a few automatically imported symbols, this
|
||||
// should only need to be run once, since each new object file imported
|
||||
// is an import library and wouldn't add any new undefined references,
|
||||
// but there's nothing stopping the __imp_ symbols from coming from a
|
||||
// normal object file as well (although that won't be used for the
|
||||
// actual autoimport later on). If this pass adds new undefined references,
|
||||
// we won't iterate further to resolve them.
|
||||
Symtab->loadMinGWAutomaticImports();
|
||||
run();
|
||||
}
|
||||
|
||||
// Make sure we have resolved all symbols.
|
||||
Symtab->reportRemainingUndefines();
|
||||
if (errorCount())
|
||||
@ -1510,7 +1599,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
// are chosen to be exported.
|
||||
if (Config->DLL && ((Config->MinGW && Config->Exports.empty()) ||
|
||||
Args.hasArg(OPT_export_all_symbols))) {
|
||||
AutoExporter Exporter;
|
||||
Exporter.initSymbolExcludes();
|
||||
|
||||
Symtab->forEachSymbol([=](Symbol *S) {
|
||||
auto *Def = dyn_cast<Defined>(S);
|
||||
@ -1574,8 +1663,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
markLive(Symtab->getChunks());
|
||||
|
||||
// Identify identical COMDAT sections to merge them.
|
||||
if (Config->DoICF)
|
||||
if (Config->DoICF) {
|
||||
findKeepUniqueSections();
|
||||
doICF(Symtab->getChunks());
|
||||
}
|
||||
|
||||
// Write the result.
|
||||
writeResult();
|
||||
|
@ -89,6 +89,7 @@ private:
|
||||
Optional<StringRef> findLib(StringRef Filename);
|
||||
StringRef doFindFile(StringRef Filename);
|
||||
StringRef doFindLib(StringRef Filename);
|
||||
StringRef doFindLibMinGW(StringRef Filename);
|
||||
|
||||
// Parses LIB environment which contains a list of search paths.
|
||||
void addLibSearchPaths();
|
||||
@ -114,8 +115,6 @@ private:
|
||||
StringRef findDefaultEntry();
|
||||
WindowsSubsystem inferSubsystem();
|
||||
|
||||
void invokeMSVC(llvm::opt::InputArgList &Args);
|
||||
|
||||
void addBuffer(std::unique_ptr<MemoryBuffer> MB, bool WholeArchive);
|
||||
void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
|
||||
StringRef ParentName);
|
||||
|
@ -713,26 +713,6 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs) {
|
||||
return MBRef;
|
||||
}
|
||||
|
||||
// Run MSVC link.exe for given in-memory object files.
|
||||
// Command line options are copied from those given to LLD.
|
||||
// This is for the /msvclto option.
|
||||
void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) {
|
||||
// Write the in-memory object files to disk.
|
||||
std::vector<TemporaryFile> Temps;
|
||||
for (StringRef S : Objects) {
|
||||
Temps.emplace_back("lto", "obj", S);
|
||||
Rsp += quote(Temps.back().Path) + "\n";
|
||||
}
|
||||
|
||||
log("link.exe " + Rsp);
|
||||
|
||||
// Run MSVC link.exe.
|
||||
Temps.emplace_back("lto", "rsp", Rsp);
|
||||
Executor E("link.exe");
|
||||
E.add(Twine("@" + Temps.back().Path));
|
||||
E.run();
|
||||
}
|
||||
|
||||
// Create OptTable
|
||||
|
||||
// Create prefix string literals used in Options.td
|
||||
@ -883,7 +863,9 @@ std::vector<const char *> ArgParser::tokenize(StringRef S) {
|
||||
}
|
||||
|
||||
void printHelp(const char *Argv0) {
|
||||
COFFOptTable().PrintHelp(outs(), Argv0, "LLVM Linker", false);
|
||||
COFFOptTable().PrintHelp(outs(),
|
||||
(std::string(Argv0) + " [options] file...").c_str(),
|
||||
"LLVM Linker", false);
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "Chunks.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "lld/Common/Timer.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
@ -80,7 +81,7 @@ private:
|
||||
bool ICF::isEligible(SectionChunk *C) {
|
||||
// Non-comdat chunks, dead chunks, and writable chunks are not elegible.
|
||||
bool Writable = C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
|
||||
if (!C->isCOMDAT() || !C->isLive() || Writable)
|
||||
if (!C->isCOMDAT() || !C->Live || Writable)
|
||||
return false;
|
||||
|
||||
// Code sections are eligible.
|
||||
@ -93,7 +94,11 @@ bool ICF::isEligible(SectionChunk *C) {
|
||||
return true;
|
||||
|
||||
// So are vtables.
|
||||
return C->Sym && C->Sym->getName().startswith("??_7");
|
||||
if (C->Sym && C->Sym->getName().startswith("??_7"))
|
||||
return true;
|
||||
|
||||
// Anything else not in an address-significance table is eligible.
|
||||
return !C->KeepUnique;
|
||||
}
|
||||
|
||||
// Split an equivalence class into smaller classes.
|
||||
@ -222,10 +227,10 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> 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) {
|
||||
parallelForEachN(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) {
|
||||
parallelForEachN(1, NumShards + 1, [&](size_t I) {
|
||||
if (Boundaries[I - 1] < Boundaries[I]) {
|
||||
forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn);
|
||||
}
|
||||
@ -257,9 +262,19 @@ void ICF::run(ArrayRef<Chunk *> Vec) {
|
||||
SC->Class[0] = NextId++;
|
||||
|
||||
// Initially, we use hash values to partition sections.
|
||||
for_each(parallel::par, Chunks.begin(), Chunks.end(), [&](SectionChunk *SC) {
|
||||
parallelForEach(Chunks, [&](SectionChunk *SC) {
|
||||
SC->Class[1] = xxHash64(SC->getContents());
|
||||
});
|
||||
|
||||
// Combine the hashes of the sections referenced by each section into its
|
||||
// hash.
|
||||
parallelForEach(Chunks, [&](SectionChunk *SC) {
|
||||
uint32_t Hash = SC->Class[1];
|
||||
for (Symbol *B : SC->symbols())
|
||||
if (auto *Sym = dyn_cast_or_null<DefinedRegular>(B))
|
||||
Hash ^= Sym->getChunk()->Class[1];
|
||||
// Set MSB to 1 to avoid collisions with non-hash classs.
|
||||
SC->Class[0] = xxHash64(SC->getContents()) | (1 << 31);
|
||||
SC->Class[0] = Hash | (1U << 31);
|
||||
});
|
||||
|
||||
// From now on, sections in Chunks are ordered so that sections in
|
||||
|
@ -54,8 +54,16 @@ std::vector<BitcodeFile *> BitcodeFile::Instances;
|
||||
static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
|
||||
Symbol *Source, Symbol *Target) {
|
||||
if (auto *U = dyn_cast<Undefined>(Source)) {
|
||||
if (U->WeakAlias && U->WeakAlias != Target)
|
||||
if (U->WeakAlias && U->WeakAlias != Target) {
|
||||
// Weak aliases as produced by GCC are named in the form
|
||||
// .weak.<weaksymbol>.<othersymbol>, where <othersymbol> is the name
|
||||
// of another symbol emitted near the weak symbol.
|
||||
// Just use the definition from the first object file that defined
|
||||
// this weak symbol.
|
||||
if (Config->MinGW)
|
||||
return;
|
||||
Symtab->reportDuplicate(Source, F);
|
||||
}
|
||||
U->WeakAlias = Target;
|
||||
}
|
||||
}
|
||||
@ -147,9 +155,10 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
|
||||
const coff_aux_section_definition *Def,
|
||||
StringRef LeaderName) {
|
||||
const coff_section *Sec;
|
||||
StringRef Name;
|
||||
if (auto EC = COFFObj->getSection(SectionNumber, Sec))
|
||||
fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message());
|
||||
|
||||
StringRef Name;
|
||||
if (auto EC = COFFObj->getSectionName(Sec, Name))
|
||||
fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " +
|
||||
EC.message());
|
||||
@ -161,6 +170,11 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Name == ".llvm_addrsig") {
|
||||
AddrsigSec = Sec;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Object files may have DWARF debug info or MS CodeView debug info
|
||||
// (or both).
|
||||
//
|
||||
@ -168,8 +182,8 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
|
||||
// of the linker; they are just a data section containing relocations.
|
||||
// We can just link them to complete debug info.
|
||||
//
|
||||
// CodeView needs a linker support. We need to interpret and debug
|
||||
// info, and then write it to a separate .pdb file.
|
||||
// CodeView needs linker support. We need to interpret debug info,
|
||||
// and then write it to a separate .pdb file.
|
||||
|
||||
// Ignore DWARF debug info unless /debug is given.
|
||||
if (!Config->Debug && Name.startswith(".debug_"))
|
||||
@ -267,10 +281,17 @@ Symbol *ObjFile::createRegular(COFFSymbolRef Sym) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
if (SC)
|
||||
return Symtab->addRegular(this, Name, Sym.getGeneric(), SC);
|
||||
// For MinGW symbols named .weak.* that point to a discarded section,
|
||||
// don't create an Undefined symbol. If nothing ever refers to the symbol,
|
||||
// everything should be fine. If something actually refers to the symbol
|
||||
// (e.g. the undefined weak alias), linking will fail due to undefined
|
||||
// references at the end.
|
||||
if (Config->MinGW && Name.startswith(".weak."))
|
||||
return nullptr;
|
||||
return Symtab->addUndefined(Name, this, false);
|
||||
}
|
||||
if (SC)
|
||||
return make<DefinedRegular>(this, /*Name*/ "", false,
|
||||
return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
|
||||
/*IsExternal*/ false, Sym.getGeneric(), SC);
|
||||
return nullptr;
|
||||
}
|
||||
@ -318,7 +339,7 @@ void ObjFile::initializeSymbols() {
|
||||
|
||||
for (uint32_t I : PendingIndexes) {
|
||||
COFFSymbolRef Sym = check(COFFObj->getSymbol(I));
|
||||
if (auto *Def = Sym.getSectionDefinition()) {
|
||||
if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) {
|
||||
if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
|
||||
readAssociativeDefinition(Sym, Def);
|
||||
else if (Config->MinGW)
|
||||
@ -401,7 +422,7 @@ Optional<Symbol *> ObjFile::createDefined(
|
||||
std::tie(Leader, Prevailing) =
|
||||
Symtab->addComdat(this, GetName(), Sym.getGeneric());
|
||||
} else {
|
||||
Leader = make<DefinedRegular>(this, /*Name*/ "", false,
|
||||
Leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
|
||||
/*IsExternal*/ false, Sym.getGeneric());
|
||||
Prevailing = true;
|
||||
}
|
||||
@ -421,7 +442,7 @@ Optional<Symbol *> ObjFile::createDefined(
|
||||
// leader symbol by setting the section's ComdatDefs pointer if we encounter a
|
||||
// non-associative comdat.
|
||||
if (SparseChunks[SectionNumber] == PendingComdat) {
|
||||
if (auto *Def = Sym.getSectionDefinition()) {
|
||||
if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) {
|
||||
if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
|
||||
readAssociativeDefinition(Sym, Def);
|
||||
else
|
||||
@ -429,8 +450,10 @@ Optional<Symbol *> ObjFile::createDefined(
|
||||
}
|
||||
}
|
||||
|
||||
// readAssociativeDefinition() writes to SparseChunks, so need to check again.
|
||||
if (SparseChunks[SectionNumber] == PendingComdat)
|
||||
return None;
|
||||
|
||||
return createRegular(Sym);
|
||||
}
|
||||
|
||||
@ -481,6 +504,10 @@ void ImportFile::parse() {
|
||||
ExternalName = ExtName;
|
||||
|
||||
ImpSym = Symtab->addImportData(ImpName, this);
|
||||
// If this was a duplicate, we logged an error but may continue;
|
||||
// in this case, ImpSym is nullptr.
|
||||
if (!ImpSym)
|
||||
return;
|
||||
|
||||
if (Hdr->getType() == llvm::COFF::IMPORT_CONST)
|
||||
static_cast<void>(Symtab->addImportData(Name, this));
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
@ -122,9 +123,12 @@ public:
|
||||
return Symbols[SymbolIndex];
|
||||
}
|
||||
|
||||
// Returns the underying COFF file.
|
||||
// Returns the underlying COFF file.
|
||||
COFFObjectFile *getCOFFObj() { return COFFObj.get(); }
|
||||
|
||||
// Whether the object was already merged into the final PDB or not
|
||||
bool wasProcessedForPDB() const { return !!ModuleDBI; }
|
||||
|
||||
static std::vector<ObjFile *> Instances;
|
||||
|
||||
// Flags in the absolute @feat.00 symbol if it is present. These usually
|
||||
@ -145,6 +149,13 @@ public:
|
||||
// if we are not producing a PDB.
|
||||
llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr;
|
||||
|
||||
const coff_section *AddrsigSec = nullptr;
|
||||
|
||||
// When using Microsoft precompiled headers, this is the PCH's key.
|
||||
// The same key is used by both the precompiled object, and objects using the
|
||||
// precompiled object. Any difference indicates out-of-date objects.
|
||||
llvm::Optional<uint32_t> PCHSignature;
|
||||
|
||||
private:
|
||||
void initializeChunks();
|
||||
void initializeSymbols();
|
||||
|
@ -60,6 +60,9 @@ static std::unique_ptr<lto::LTO> createLTO() {
|
||||
C.DisableVerify = true;
|
||||
C.DiagHandler = diagnosticHandler;
|
||||
C.OptLevel = Config->LTOO;
|
||||
C.CPU = GetCPUStr();
|
||||
C.MAttrs = GetMAttrs();
|
||||
|
||||
if (Config->SaveTemps)
|
||||
checkError(C.addSaveTemps(std::string(Config->OutputFile) + ".",
|
||||
/*UseInputModulePath*/ true));
|
||||
|
@ -110,7 +110,7 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
|
||||
writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize);
|
||||
OS << Sec->Name << '\n';
|
||||
|
||||
for (Chunk *C : Sec->getChunks()) {
|
||||
for (Chunk *C : Sec->Chunks) {
|
||||
auto *SC = dyn_cast<SectionChunk>(C);
|
||||
if (!SC)
|
||||
continue;
|
||||
|
@ -32,13 +32,13 @@ void markLive(ArrayRef<Chunk *> Chunks) {
|
||||
// COMDAT section chunks are dead by default. Add non-COMDAT chunks.
|
||||
for (Chunk *C : Chunks)
|
||||
if (auto *SC = dyn_cast<SectionChunk>(C))
|
||||
if (SC->isLive())
|
||||
if (SC->Live)
|
||||
Worklist.push_back(SC);
|
||||
|
||||
auto Enqueue = [&](SectionChunk *C) {
|
||||
if (C->isLive())
|
||||
if (C->Live)
|
||||
return;
|
||||
C->markLive();
|
||||
C->Live = true;
|
||||
Worklist.push_back(C);
|
||||
};
|
||||
|
||||
@ -57,7 +57,7 @@ void markLive(ArrayRef<Chunk *> Chunks) {
|
||||
|
||||
while (!Worklist.empty()) {
|
||||
SectionChunk *SC = Worklist.pop_back_val();
|
||||
assert(SC->isLive() && "We mark as live when pushing onto the worklist!");
|
||||
assert(SC->Live && "We mark as live when pushing onto the worklist!");
|
||||
|
||||
// Mark all symbols listed in the relocation table for this section.
|
||||
for (Symbol *B : SC->symbols())
|
||||
|
@ -19,7 +19,23 @@ using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
using namespace llvm::COFF;
|
||||
|
||||
AutoExporter::AutoExporter() {
|
||||
void AutoExporter::initSymbolExcludes() {
|
||||
ExcludeSymbolPrefixes = {
|
||||
// Import symbols
|
||||
"__imp_",
|
||||
"__IMPORT_DESCRIPTOR_",
|
||||
// Extra import symbols from GNU import libraries
|
||||
"__nm_",
|
||||
// C++ symbols
|
||||
"__rtti_",
|
||||
"__builtin_",
|
||||
// Artifical symbols such as .refptr
|
||||
".",
|
||||
};
|
||||
ExcludeSymbolSuffixes = {
|
||||
"_iname",
|
||||
"_NULL_THUNK_DATA",
|
||||
};
|
||||
if (Config->Machine == I386) {
|
||||
ExcludeSymbols = {
|
||||
"__NULL_IMPORT_DESCRIPTOR",
|
||||
@ -36,9 +52,10 @@ AutoExporter::AutoExporter() {
|
||||
"_DllEntryPoint@12",
|
||||
"_DllMainCRTStartup@12",
|
||||
};
|
||||
ExcludeSymbolPrefixes.insert("__head_");
|
||||
} else {
|
||||
ExcludeSymbols = {
|
||||
"_NULL_IMPORT_DESCRIPTOR",
|
||||
"__NULL_IMPORT_DESCRIPTOR",
|
||||
"_pei386_runtime_relocator",
|
||||
"do_pseudo_reloc",
|
||||
"impure_ptr",
|
||||
@ -52,8 +69,11 @@ AutoExporter::AutoExporter() {
|
||||
"DllEntryPoint",
|
||||
"DllMainCRTStartup",
|
||||
};
|
||||
ExcludeSymbolPrefixes.insert("_head_");
|
||||
}
|
||||
}
|
||||
|
||||
AutoExporter::AutoExporter() {
|
||||
ExcludeLibs = {
|
||||
"libgcc",
|
||||
"libgcc_s",
|
||||
@ -64,6 +84,7 @@ AutoExporter::AutoExporter() {
|
||||
"libsupc++",
|
||||
"libobjc",
|
||||
"libgcj",
|
||||
"libclang_rt.builtins",
|
||||
"libclang_rt.builtins-aarch64",
|
||||
"libclang_rt.builtins-arm",
|
||||
"libclang_rt.builtins-i386",
|
||||
@ -90,6 +111,13 @@ AutoExporter::AutoExporter() {
|
||||
};
|
||||
}
|
||||
|
||||
void AutoExporter::addWholeArchive(StringRef Path) {
|
||||
StringRef LibName = sys::path::filename(Path);
|
||||
// Drop the file extension, to match the processing below.
|
||||
LibName = LibName.substr(0, LibName.rfind('.'));
|
||||
ExcludeLibs.erase(LibName);
|
||||
}
|
||||
|
||||
bool AutoExporter::shouldExport(Defined *Sym) const {
|
||||
if (!Sym || !Sym->isLive() || !Sym->getChunk())
|
||||
return false;
|
||||
@ -101,10 +129,12 @@ bool AutoExporter::shouldExport(Defined *Sym) const {
|
||||
if (ExcludeSymbols.count(Sym->getName()))
|
||||
return false;
|
||||
|
||||
// Don't export anything that looks like an import symbol (which also can be
|
||||
// a manually defined data symbol with such a name).
|
||||
if (Sym->getName().startswith("__imp_"))
|
||||
return false;
|
||||
for (StringRef Prefix : ExcludeSymbolPrefixes.keys())
|
||||
if (Sym->getName().startswith(Prefix))
|
||||
return false;
|
||||
for (StringRef Suffix : ExcludeSymbolSuffixes.keys())
|
||||
if (Sym->getName().endswith(Suffix))
|
||||
return false;
|
||||
|
||||
// If a corresponding __imp_ symbol exists and is defined, don't export it.
|
||||
if (Symtab->find(("__imp_" + Sym->getName()).str()))
|
||||
|
@ -23,7 +23,13 @@ class AutoExporter {
|
||||
public:
|
||||
AutoExporter();
|
||||
|
||||
void initSymbolExcludes();
|
||||
|
||||
void addWholeArchive(StringRef Path);
|
||||
|
||||
llvm::StringSet<> ExcludeSymbols;
|
||||
llvm::StringSet<> ExcludeSymbolPrefixes;
|
||||
llvm::StringSet<> ExcludeSymbolSuffixes;
|
||||
llvm::StringSet<> ExcludeLibs;
|
||||
llvm::StringSet<> ExcludeObjects;
|
||||
|
||||
|
@ -66,13 +66,18 @@ def wholearchive_file : P<"wholearchive", "Include all object files from this ar
|
||||
|
||||
def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
|
||||
|
||||
def manifest : F<"manifest">;
|
||||
def manifest_colon : P<"manifest", "Create manifest file">;
|
||||
def manifest : F<"manifest">, HelpText<"Create .manifest file">;
|
||||
def manifest_colon : P<
|
||||
"manifest",
|
||||
"NO disables manifest output; EMBED[,ID=#] embeds manifest as resource in the image">;
|
||||
def manifestuac : P<"manifestuac", "User access control">;
|
||||
def manifestfile : P<"manifestfile", "Manifest file path">;
|
||||
def manifestdependency : P<"manifestdependency",
|
||||
"Attributes for <dependency> in manifest file">;
|
||||
def manifestinput : P<"manifestinput", "Specify manifest file">;
|
||||
def manifestfile : P<"manifestfile", "Manifest output path, with /manifest">;
|
||||
def manifestdependency : P<
|
||||
"manifestdependency",
|
||||
"Attributes for <dependency> element in manifest file; implies /manifest">;
|
||||
def manifestinput : P<
|
||||
"manifestinput",
|
||||
"Additional manifest inputs; only valid with /manifest:embed">;
|
||||
|
||||
// We cannot use multiclass P because class name "incl" is different
|
||||
// from its command line option name. We do this because "include" is
|
||||
@ -85,22 +90,28 @@ 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 debug_opt : P<"debug", "Embed a symbol table in the image with option">;
|
||||
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 nodefaultlib_all : F<"nodefaultlib">,
|
||||
HelpText<"Remove all default libraries">;
|
||||
def noentry : F<"noentry">,
|
||||
HelpText<"Don't add reference to DllMainCRTStartup; only valid with /dll">;
|
||||
def profile : F<"profile">;
|
||||
def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">;
|
||||
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">;
|
||||
def wholearchive_flag : F<"wholearchive">;
|
||||
|
||||
def force : F<"force">,
|
||||
HelpText<"Allow undefined and multiply defined symbols when creating executables">;
|
||||
def force_unresolved : F<"force:unresolved">,
|
||||
HelpText<"Allow undefined symbols when creating executables">;
|
||||
def force_unresolved : F<"force:unresolved">;
|
||||
def force_multiple : F<"force:multiple">,
|
||||
HelpText<"Allow multiply defined symbols when creating executables">;
|
||||
defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">;
|
||||
|
||||
defm allowbind : B<"allowbind", "Enable DLL binding (default)",
|
||||
@ -139,13 +150,9 @@ def help : F<"help">;
|
||||
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">;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,7 @@ class SymbolTable;
|
||||
void createPDB(SymbolTable *Symtab,
|
||||
llvm::ArrayRef<OutputSection *> OutputSections,
|
||||
llvm::ArrayRef<uint8_t> SectionTable,
|
||||
const llvm::codeview::DebugInfo &BuildId);
|
||||
llvm::codeview::DebugInfo *BuildId);
|
||||
|
||||
std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *C,
|
||||
uint32_t Addr);
|
||||
|
@ -60,16 +60,16 @@ void SymbolTable::addFile(InputFile *File) {
|
||||
}
|
||||
|
||||
static void errorOrWarn(const Twine &S) {
|
||||
if (Config->Force)
|
||||
if (Config->ForceUnresolved)
|
||||
warn(S);
|
||||
else
|
||||
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) {
|
||||
// Returns the symbol in SC whose value is <= Addr that is closest to Addr.
|
||||
// This is generally the global variable or function whose definition contains
|
||||
// Addr.
|
||||
static Symbol *getSymbol(SectionChunk *SC, uint32_t Addr) {
|
||||
DefinedRegular *Candidate = nullptr;
|
||||
|
||||
for (Symbol *S : SC->File->getSymbols()) {
|
||||
@ -81,14 +81,12 @@ static StringRef getSymbolName(SectionChunk *SC, uint32_t Addr) {
|
||||
Candidate = D;
|
||||
}
|
||||
|
||||
if (!Candidate)
|
||||
return "";
|
||||
return Candidate->getName();
|
||||
return Candidate;
|
||||
}
|
||||
|
||||
static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
|
||||
std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
|
||||
struct Location {
|
||||
StringRef SymName;
|
||||
Symbol *Sym;
|
||||
std::pair<StringRef, uint32_t> FileLine;
|
||||
};
|
||||
std::vector<Location> Locations;
|
||||
@ -102,14 +100,14 @@ static std::string getSymbolLocations(ObjFile *File, uint32_t 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});
|
||||
Symbol *Sym = getSymbol(SC, R.VirtualAddress);
|
||||
if (!FileLine.first.empty() || Sym)
|
||||
Locations.push_back({Sym, FileLine});
|
||||
}
|
||||
}
|
||||
|
||||
if (Locations.empty())
|
||||
return "\n>>> referenced by " + toString(File) + "\n";
|
||||
return "\n>>> referenced by " + toString(File);
|
||||
|
||||
std::string Out;
|
||||
llvm::raw_string_ostream OS(Out);
|
||||
@ -119,13 +117,87 @@ static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
|
||||
OS << Loc.FileLine.first << ":" << Loc.FileLine.second
|
||||
<< "\n>>> ";
|
||||
OS << toString(File);
|
||||
if (!Loc.SymName.empty())
|
||||
OS << ":(" << Loc.SymName << ')';
|
||||
if (Loc.Sym)
|
||||
OS << ":(" << toString(*Loc.Sym) << ')';
|
||||
}
|
||||
OS << '\n';
|
||||
return OS.str();
|
||||
}
|
||||
|
||||
void SymbolTable::loadMinGWAutomaticImports() {
|
||||
for (auto &I : SymMap) {
|
||||
Symbol *Sym = I.second;
|
||||
auto *Undef = dyn_cast<Undefined>(Sym);
|
||||
if (!Undef)
|
||||
continue;
|
||||
if (!Sym->IsUsedInRegularObj)
|
||||
continue;
|
||||
|
||||
StringRef Name = Undef->getName();
|
||||
|
||||
if (Name.startswith("__imp_"))
|
||||
continue;
|
||||
// If we have an undefined symbol, but we have a Lazy representing a
|
||||
// symbol we could load from file, make sure to load that.
|
||||
Lazy *L = dyn_cast_or_null<Lazy>(find(("__imp_" + Name).str()));
|
||||
if (!L || L->PendingArchiveLoad)
|
||||
continue;
|
||||
|
||||
log("Loading lazy " + L->getName() + " from " + L->File->getName() +
|
||||
" for automatic import");
|
||||
L->PendingArchiveLoad = true;
|
||||
L->File->addMember(&L->Sym);
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolTable::handleMinGWAutomaticImport(Symbol *Sym, StringRef Name) {
|
||||
if (Name.startswith("__imp_"))
|
||||
return false;
|
||||
Defined *Imp = dyn_cast_or_null<Defined>(find(("__imp_" + Name).str()));
|
||||
if (!Imp)
|
||||
return false;
|
||||
|
||||
// Replace the reference directly to a variable with a reference
|
||||
// to the import address table instead. This obviously isn't right,
|
||||
// but we mark the symbol as IsRuntimePseudoReloc, and a later pass
|
||||
// will add runtime pseudo relocations for every relocation against
|
||||
// this Symbol. The runtime pseudo relocation framework expects the
|
||||
// reference itself to point at the IAT entry.
|
||||
size_t ImpSize = 0;
|
||||
if (isa<DefinedImportData>(Imp)) {
|
||||
log("Automatically importing " + Name + " from " +
|
||||
cast<DefinedImportData>(Imp)->getDLLName());
|
||||
ImpSize = sizeof(DefinedImportData);
|
||||
} else if (isa<DefinedRegular>(Imp)) {
|
||||
log("Automatically importing " + Name + " from " +
|
||||
toString(cast<DefinedRegular>(Imp)->File));
|
||||
ImpSize = sizeof(DefinedRegular);
|
||||
} else {
|
||||
warn("unable to automatically import " + Name + " from " + Imp->getName() +
|
||||
" from " + toString(cast<DefinedRegular>(Imp)->File) +
|
||||
"; unexpected symbol type");
|
||||
return false;
|
||||
}
|
||||
Sym->replaceKeepingName(Imp, ImpSize);
|
||||
Sym->IsRuntimePseudoReloc = true;
|
||||
|
||||
// There may exist symbols named .refptr.<name> which only consist
|
||||
// of a single pointer to <name>. If it turns out <name> is
|
||||
// automatically imported, we don't need to keep the .refptr.<name>
|
||||
// pointer at all, but redirect all accesses to it to the IAT entry
|
||||
// for __imp_<name> instead, and drop the whole .refptr.<name> chunk.
|
||||
DefinedRegular *Refptr =
|
||||
dyn_cast_or_null<DefinedRegular>(find((".refptr." + Name).str()));
|
||||
if (Refptr && Refptr->getChunk()->getSize() == Config->Wordsize) {
|
||||
SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Refptr->getChunk());
|
||||
if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) {
|
||||
log("Replacing .refptr." + Name + " with " + Imp->getName());
|
||||
Refptr->getChunk()->Live = false;
|
||||
Refptr->replaceKeepingName(Imp, ImpSize);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SymbolTable::reportRemainingUndefines() {
|
||||
SmallPtrSet<Symbol *, 8> Undefs;
|
||||
DenseMap<Symbol *, Symbol *> LocalImports;
|
||||
@ -169,9 +241,17 @@ void SymbolTable::reportRemainingUndefines() {
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to report missing Microsoft precompiled headers symbols.
|
||||
// A proper message will be emitted instead in PDBLinker::aquirePrecompObj
|
||||
if (Name.contains("_PchSym_"))
|
||||
continue;
|
||||
|
||||
if (Config->MinGW && handleMinGWAutomaticImport(Sym, Name))
|
||||
continue;
|
||||
|
||||
// Remaining undefined symbols are not fatal if /force is specified.
|
||||
// They are replaced with dummy defined symbols.
|
||||
if (Config->Force)
|
||||
if (Config->ForceUnresolved)
|
||||
replaceSymbol<DefinedAbsolute>(Sym, Name, 0);
|
||||
Undefs.insert(Sym);
|
||||
}
|
||||
@ -181,10 +261,10 @@ void SymbolTable::reportRemainingUndefines() {
|
||||
|
||||
for (Symbol *B : Config->GCRoot) {
|
||||
if (Undefs.count(B))
|
||||
errorOrWarn("<root>: undefined symbol: " + B->getName());
|
||||
errorOrWarn("<root>: undefined symbol: " + toString(*B));
|
||||
if (Config->WarnLocallyDefinedImported)
|
||||
if (Symbol *Imp = LocalImports.lookup(B))
|
||||
warn("<root>: locally defined symbol imported: " + Imp->getName() +
|
||||
warn("<root>: locally defined symbol imported: " + toString(*Imp) +
|
||||
" (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
|
||||
}
|
||||
|
||||
@ -195,34 +275,41 @@ void SymbolTable::reportRemainingUndefines() {
|
||||
if (!Sym)
|
||||
continue;
|
||||
if (Undefs.count(Sym))
|
||||
errorOrWarn("undefined symbol: " + Sym->getName() +
|
||||
errorOrWarn("undefined symbol: " + toString(*Sym) +
|
||||
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]");
|
||||
warn(toString(File) +
|
||||
": locally defined symbol imported: " + toString(*Imp) +
|
||||
" (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
|
||||
bool Inserted = false;
|
||||
Symbol *&Sym = SymMap[CachedHashStringRef(Name)];
|
||||
if (Sym)
|
||||
return {Sym, false};
|
||||
Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
|
||||
Sym->IsUsedInRegularObj = false;
|
||||
Sym->PendingArchiveLoad = false;
|
||||
return {Sym, true};
|
||||
if (!Sym) {
|
||||
Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
|
||||
Sym->IsUsedInRegularObj = false;
|
||||
Sym->PendingArchiveLoad = false;
|
||||
Inserted = true;
|
||||
}
|
||||
return {Sym, Inserted};
|
||||
}
|
||||
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, InputFile *File) {
|
||||
std::pair<Symbol *, bool> Result = insert(Name);
|
||||
if (!File || !isa<BitcodeFile>(File))
|
||||
Result.first->IsUsedInRegularObj = true;
|
||||
return Result;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F,
|
||||
bool IsWeakAlias) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
if (!F || !isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
std::tie(S, WasInserted) = insert(Name, F);
|
||||
if (WasInserted || (isa<Lazy>(S) && IsWeakAlias)) {
|
||||
replaceSymbol<Undefined>(S, Name);
|
||||
return S;
|
||||
@ -253,14 +340,20 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
|
||||
}
|
||||
|
||||
void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
|
||||
error("duplicate symbol: " + toString(*Existing) + " in " +
|
||||
toString(Existing->getFile()) + " and in " + toString(NewFile));
|
||||
std::string Msg = "duplicate symbol: " + toString(*Existing) + " in " +
|
||||
toString(Existing->getFile()) + " and in " +
|
||||
toString(NewFile);
|
||||
|
||||
if (Config->ForceMultiple)
|
||||
warn(Msg);
|
||||
else
|
||||
error(Msg);
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
std::tie(S, WasInserted) = insert(N, nullptr);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
|
||||
replaceSymbol<DefinedAbsolute>(S, N, Sym);
|
||||
@ -272,7 +365,7 @@ Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
|
||||
Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
std::tie(S, WasInserted) = insert(N, nullptr);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
|
||||
replaceSymbol<DefinedAbsolute>(S, N, VA);
|
||||
@ -284,7 +377,7 @@ Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
|
||||
Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
std::tie(S, WasInserted) = insert(N, nullptr);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
|
||||
replaceSymbol<DefinedSynthetic>(S, N, C);
|
||||
@ -298,9 +391,7 @@ Symbol *SymbolTable::addRegular(InputFile *F, StringRef N,
|
||||
SectionChunk *C) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
std::tie(S, WasInserted) = insert(N, F);
|
||||
if (WasInserted || !isa<DefinedRegular>(S))
|
||||
replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ false,
|
||||
/*IsExternal*/ true, Sym, C);
|
||||
@ -314,9 +405,7 @@ SymbolTable::addComdat(InputFile *F, StringRef N,
|
||||
const coff_symbol_generic *Sym) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
std::tie(S, WasInserted) = insert(N, F);
|
||||
if (WasInserted || !isa<DefinedRegular>(S)) {
|
||||
replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ true,
|
||||
/*IsExternal*/ true, Sym, nullptr);
|
||||
@ -331,9 +420,7 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const coff_symbol_generic *Sym, CommonChunk *C) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
std::tie(S, WasInserted) = insert(N, F);
|
||||
if (WasInserted || !isa<DefinedCOFF>(S))
|
||||
replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
else if (auto *DC = dyn_cast<DefinedCommon>(S))
|
||||
@ -345,7 +432,7 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
std::tie(S, WasInserted) = insert(N, nullptr);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
|
||||
replaceSymbol<DefinedImportData>(S, N, F);
|
||||
@ -360,7 +447,7 @@ Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
|
||||
uint16_t Machine) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
std::tie(S, WasInserted) = insert(Name, nullptr);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
|
||||
replaceSymbol<DefinedImportThunk>(S, Name, ID, Machine);
|
||||
|
@ -54,6 +54,9 @@ public:
|
||||
// symbols.
|
||||
void reportRemainingUndefines();
|
||||
|
||||
void loadMinGWAutomaticImports();
|
||||
bool handleMinGWAutomaticImport(Symbol *Sym, StringRef Name);
|
||||
|
||||
// Returns a list of chunks of selected symbols.
|
||||
std::vector<Chunk *> getChunks();
|
||||
|
||||
@ -108,7 +111,10 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
/// Inserts symbol if not already present.
|
||||
std::pair<Symbol *, bool> insert(StringRef Name);
|
||||
/// Same as insert(Name), but also sets IsUsedInRegularObj.
|
||||
std::pair<Symbol *, bool> insert(StringRef Name, InputFile *F);
|
||||
StringRef findByPrefix(StringRef Prefix);
|
||||
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap;
|
||||
@ -117,6 +123,8 @@ private:
|
||||
|
||||
extern SymbolTable *Symtab;
|
||||
|
||||
std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
|
@ -54,7 +54,7 @@ InputFile *Symbol::getFile() {
|
||||
|
||||
bool Symbol::isLive() const {
|
||||
if (auto *R = dyn_cast<DefinedRegular>(this))
|
||||
return R->getChunk()->isLive();
|
||||
return R->getChunk()->Live;
|
||||
if (auto *Imp = dyn_cast<DefinedImportData>(this))
|
||||
return Imp->File->Live;
|
||||
if (auto *Imp = dyn_cast<DefinedImportThunk>(this))
|
||||
@ -63,6 +63,13 @@ bool Symbol::isLive() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
void Symbol::replaceKeepingName(Symbol *Other, size_t Size) {
|
||||
StringRef OrigName = Name;
|
||||
memcpy(this, Other, Size);
|
||||
Name = OrigName;
|
||||
}
|
||||
|
||||
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
|
||||
size_t SymSize = cast<ObjFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
|
||||
if (SymSize == sizeof(coff_symbol16))
|
||||
|
@ -39,9 +39,9 @@ class Symbol {
|
||||
public:
|
||||
enum Kind {
|
||||
// The order of these is significant. We start with the regular defined
|
||||
// symbols as those are the most prevelant and the zero tag is the cheapest
|
||||
// symbols as those are the most prevalent and the zero tag is the cheapest
|
||||
// to set. Among the defined kinds, the lower the kind is preferred over
|
||||
// the higher kind when testing wether one symbol should take precedence
|
||||
// the higher kind when testing whether one symbol should take precedence
|
||||
// over another.
|
||||
DefinedRegularKind = 0,
|
||||
DefinedCommonKind,
|
||||
@ -66,6 +66,8 @@ public:
|
||||
// Returns the symbol name.
|
||||
StringRef getName();
|
||||
|
||||
void replaceKeepingName(Symbol *Other, size_t Size);
|
||||
|
||||
// Returns the file from which this symbol was created.
|
||||
InputFile *getFile();
|
||||
|
||||
@ -78,7 +80,7 @@ protected:
|
||||
explicit Symbol(Kind K, StringRef N = "")
|
||||
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
|
||||
WrittenToSymtab(false), PendingArchiveLoad(false), IsGCRoot(false),
|
||||
Name(N) {}
|
||||
IsRuntimePseudoReloc(false), Name(N) {}
|
||||
|
||||
const unsigned SymbolKind : 8;
|
||||
unsigned IsExternal : 1;
|
||||
@ -102,6 +104,8 @@ public:
|
||||
/// True if we've already added this symbol to the list of GC roots.
|
||||
unsigned IsGCRoot : 1;
|
||||
|
||||
unsigned IsRuntimePseudoReloc : 1;
|
||||
|
||||
protected:
|
||||
StringRef Name;
|
||||
};
|
||||
@ -331,8 +335,8 @@ private:
|
||||
Chunk *Data;
|
||||
};
|
||||
|
||||
// If you have a symbol "__imp_foo" in your object file, a symbol name
|
||||
// "foo" becomes automatically available as a pointer to "__imp_foo".
|
||||
// If you have a symbol "foo" in your object file, a symbol name
|
||||
// "__imp_foo" becomes automatically available as a pointer to "foo".
|
||||
// This class is for such automatically-created symbols.
|
||||
// Yes, this is an odd feature. We didn't intend to implement that.
|
||||
// This is here just for compatibility with MSVC.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -34,8 +34,8 @@ public:
|
||||
Header.Characteristics = Chars;
|
||||
}
|
||||
void addChunk(Chunk *C);
|
||||
void insertChunkAtStart(Chunk *C);
|
||||
void merge(OutputSection *Other);
|
||||
ArrayRef<Chunk *> getChunks() { return Chunks; }
|
||||
void addPermissions(uint32_t C);
|
||||
void setPermissions(uint32_t C);
|
||||
uint64_t getRVA() { return Header.VirtualAddress; }
|
||||
@ -62,9 +62,11 @@ public:
|
||||
llvm::StringRef Name;
|
||||
llvm::object::coff_section Header = {};
|
||||
|
||||
std::vector<Chunk *> Chunks;
|
||||
std::vector<Chunk *> OrigChunks;
|
||||
|
||||
private:
|
||||
uint32_t StringTableOff = 0;
|
||||
std::vector<Chunk *> Chunks;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
@ -40,7 +41,7 @@ std::vector<StringRef> lld::args::getStrings(opt::InputArgList &Args, int Id) {
|
||||
|
||||
uint64_t lld::args::getZOptionValue(opt::InputArgList &Args, int Id,
|
||||
StringRef Key, uint64_t Default) {
|
||||
for (auto *Arg : Args.filtered(Id)) {
|
||||
for (auto *Arg : Args.filtered_reverse(Id)) {
|
||||
std::pair<StringRef, StringRef> KV = StringRef(Arg->getValue()).split('=');
|
||||
if (KV.first == Key) {
|
||||
uint64_t Result = Default;
|
||||
@ -64,3 +65,9 @@ std::vector<StringRef> lld::args::getLines(MemoryBufferRef MB) {
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
StringRef lld::args::getFilenameWithoutExe(StringRef Path) {
|
||||
if (Path.endswith_lower(".exe"))
|
||||
return sys::path::stem(Path);
|
||||
return sys::path::filename(Path);
|
||||
}
|
||||
|
@ -47,8 +47,9 @@ ErrorHandler &lld::errorHandler() {
|
||||
}
|
||||
|
||||
void lld::exitLld(int Val) {
|
||||
// Delete the output buffer so that any tempory file is deleted.
|
||||
errorHandler().OutputBuffer.reset();
|
||||
// Delete any temporary file, while keeping the memory mapping open.
|
||||
if (errorHandler().OutputBuffer)
|
||||
errorHandler().OutputBuffer->discard();
|
||||
|
||||
// Dealloc/destroy ManagedStatic variables before calling
|
||||
// _exit(). In a non-LTO build, this is a nop. In an LTO
|
||||
|
@ -16,14 +16,6 @@
|
||||
#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;
|
||||
|
||||
@ -45,18 +37,21 @@ Optional<std::string> lld::demangleItanium(StringRef Name) {
|
||||
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);
|
||||
Optional<std::string> lld::demangleMSVC(StringRef Name) {
|
||||
std::string Prefix;
|
||||
if (Name.consume_front("__imp_"))
|
||||
Prefix = "__declspec(dllimport) ";
|
||||
|
||||
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;
|
||||
// Demangle only C++ names.
|
||||
if (!Name.startswith("?"))
|
||||
return None;
|
||||
|
||||
char *Buf = microsoftDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
|
||||
if (!Buf)
|
||||
return None;
|
||||
std::string S(Buf);
|
||||
free(Buf);
|
||||
return Prefix + S;
|
||||
}
|
||||
|
||||
StringMatcher::StringMatcher(ArrayRef<StringRef> Pat) {
|
||||
|
@ -32,3 +32,4 @@ llvm::Optional<llvm::CodeModel::Model> lld::GetCodeModelFromCMModel() {
|
||||
}
|
||||
|
||||
std::string lld::GetCPUStr() { return ::getCPUStr(); }
|
||||
std::vector<std::string> lld::GetMAttrs() { return ::MAttrs; }
|
||||
|
@ -356,7 +356,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
|
||||
}
|
||||
|
||||
uint64_t PatchOff = 0;
|
||||
const uint8_t *Buf = IS->Data.begin();
|
||||
const uint8_t *Buf = IS->data().begin();
|
||||
const ulittle32_t *InstBuf = reinterpret_cast<const ulittle32_t *>(Buf + Off);
|
||||
uint32_t Instr1 = *InstBuf++;
|
||||
uint32_t Instr2 = *InstBuf++;
|
||||
@ -411,7 +411,7 @@ uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
|
||||
void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) {
|
||||
// Copy the instruction that we will be replacing with a branch in the
|
||||
// Patchee Section.
|
||||
write32le(Buf, read32le(Patchee->Data.begin() + PatcheeOffset));
|
||||
write32le(Buf, read32le(Patchee->data().begin() + PatcheeOffset));
|
||||
|
||||
// Apply any relocation transferred from the original PatcheeSection.
|
||||
// For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc
|
||||
@ -451,7 +451,7 @@ void AArch64Err843419Patcher::init() {
|
||||
continue;
|
||||
if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def))
|
||||
continue;
|
||||
if (auto *Sec = dyn_cast<InputSection>(Def->Section))
|
||||
if (auto *Sec = dyn_cast_or_null<InputSection>(Def->Section))
|
||||
if (Sec->Flags & SHF_EXECINSTR)
|
||||
SectionMap[Sec].push_back(Def);
|
||||
}
|
||||
@ -487,7 +487,8 @@ void AArch64Err843419Patcher::insertPatches(
|
||||
InputSectionDescription &ISD, std::vector<Patch843419Section *> &Patches) {
|
||||
uint64_t ISLimit;
|
||||
uint64_t PrevISLimit = ISD.Sections.front()->OutSecOff;
|
||||
uint64_t PatchUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
|
||||
uint64_t PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing();
|
||||
uint64_t OutSecAddr = ISD.Sections.front()->getParent()->Addr;
|
||||
|
||||
// Set the OutSecOff of patches to the place where we want to insert them.
|
||||
// We use a similar strategy to Thunk placement. Place patches roughly
|
||||
@ -498,12 +499,12 @@ void AArch64Err843419Patcher::insertPatches(
|
||||
ISLimit = IS->OutSecOff + IS->getSize();
|
||||
if (ISLimit > PatchUpperBound) {
|
||||
while (PatchIt != PatchEnd) {
|
||||
if ((*PatchIt)->getLDSTAddr() >= PrevISLimit)
|
||||
if ((*PatchIt)->getLDSTAddr() - OutSecAddr >= PrevISLimit)
|
||||
break;
|
||||
(*PatchIt)->OutSecOff = PrevISLimit;
|
||||
++PatchIt;
|
||||
}
|
||||
PatchUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
|
||||
PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing();
|
||||
}
|
||||
PrevISLimit = ISLimit;
|
||||
}
|
||||
@ -538,20 +539,24 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
|
||||
InputSection *IS,
|
||||
std::vector<Patch843419Section *> &Patches) {
|
||||
// There may be a relocation at the same offset that we are patching. There
|
||||
// are three cases that we need to consider.
|
||||
// are four cases that we need to consider.
|
||||
// Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this
|
||||
// instance of the erratum on a previous patch and altered the relocation. We
|
||||
// have nothing more to do.
|
||||
// Case 2: A load/store register (unsigned immediate) class relocation. There
|
||||
// Case 2: A TLS Relaxation R_RELAX_TLS_IE_TO_LE. In this case the ADRP that
|
||||
// we read will be transformed into a MOVZ later so we actually don't match
|
||||
// the sequence and have nothing more to do.
|
||||
// Case 3: A load/store register (unsigned immediate) class relocation. There
|
||||
// are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and
|
||||
// they are both absolute. We need to add the same relocation to the patch,
|
||||
// and replace the relocation with a R_AARCH_JUMP26 branch relocation.
|
||||
// Case 3: No relocation. We must create a new R_AARCH64_JUMP26 branch
|
||||
// Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch
|
||||
// relocation at the offset.
|
||||
auto RelIt = std::find_if(
|
||||
IS->Relocations.begin(), IS->Relocations.end(),
|
||||
[=](const Relocation &R) { return R.Offset == PatcheeOffset; });
|
||||
if (RelIt != IS->Relocations.end() && RelIt->Type == R_AARCH64_JUMP26)
|
||||
if (RelIt != IS->Relocations.end() &&
|
||||
(RelIt->Type == R_AARCH64_JUMP26 || RelIt->Expr == R_RELAX_TLS_IE_TO_LE))
|
||||
return;
|
||||
|
||||
log("detected cortex-a53-843419 erratum sequence starting at " +
|
||||
@ -598,7 +603,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
|
||||
auto DataSym = std::next(CodeSym);
|
||||
uint64_t Off = (*CodeSym)->Value;
|
||||
uint64_t Limit =
|
||||
(DataSym == MapSyms.end()) ? IS->Data.size() : (*DataSym)->Value;
|
||||
(DataSym == MapSyms.end()) ? IS->data().size() : (*DataSym)->Value;
|
||||
|
||||
while (Off < Limit) {
|
||||
uint64_t StartAddr = IS->getVA(Off);
|
||||
|
@ -41,6 +41,7 @@ public:
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
uint64_t BranchAddr, const Symbol &S) const override;
|
||||
uint32_t getThunkSectionSpacing() const override;
|
||||
bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
|
||||
bool usesOnlyLowPageBits(RelType Type) const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
@ -57,6 +58,7 @@ AArch64::AArch64() {
|
||||
RelativeRel = R_AARCH64_RELATIVE;
|
||||
IRelativeRel = R_AARCH64_IRELATIVE;
|
||||
GotRel = R_AARCH64_GLOB_DAT;
|
||||
NoneRel = R_AARCH64_NONE;
|
||||
PltRel = R_AARCH64_JUMP_SLOT;
|
||||
TlsDescRel = R_AARCH64_TLSDESC;
|
||||
TlsGotRel = R_AARCH64_TLS_TPREL64;
|
||||
@ -70,22 +72,14 @@ AArch64::AArch64() {
|
||||
// FreeBSD automatically promotes 2 MiB-aligned allocations.
|
||||
DefaultImageBase = 0x200000;
|
||||
|
||||
// It doesn't seem to be documented anywhere, but tls on aarch64 uses variant
|
||||
// 1 of the tls structures and the tcb size is 16.
|
||||
TcbSize = 16;
|
||||
NeedsThunks = true;
|
||||
|
||||
// See comment in Arch/ARM.cpp for a more detailed explanation of
|
||||
// ThunkSectionSpacing. For AArch64 the only branches we are permitted to
|
||||
// Thunk have a range of +/- 128 MiB
|
||||
ThunkSectionSpacing = (128 * 1024 * 1024) - 0x30000;
|
||||
}
|
||||
|
||||
RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_AARCH64_TLSDESC_ADR_PAGE21:
|
||||
return R_TLSDESC_PAGE;
|
||||
return R_AARCH64_TLSDESC_PAGE;
|
||||
case R_AARCH64_TLSDESC_LD64_LO12:
|
||||
case R_AARCH64_TLSDESC_ADD_LO12:
|
||||
return R_TLSDESC;
|
||||
@ -111,13 +105,13 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
|
||||
case R_AARCH64_LD_PREL_LO19:
|
||||
return R_PC;
|
||||
case R_AARCH64_ADR_PREL_PG_HI21:
|
||||
return R_PAGE_PC;
|
||||
return R_AARCH64_PAGE_PC;
|
||||
case R_AARCH64_LD64_GOT_LO12_NC:
|
||||
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
|
||||
return R_GOT;
|
||||
case R_AARCH64_ADR_GOT_PAGE:
|
||||
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
|
||||
return R_GOT_PAGE_PC;
|
||||
return R_AARCH64_GOT_PAGE_PC;
|
||||
case R_AARCH64_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
@ -129,7 +123,7 @@ RelExpr AArch64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
RelExpr Expr) const {
|
||||
if (Expr == R_RELAX_TLS_GD_TO_IE) {
|
||||
if (Type == R_AARCH64_TLSDESC_ADR_PAGE21)
|
||||
return R_RELAX_TLS_GD_TO_IE_PAGE_PC;
|
||||
return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC;
|
||||
return R_RELAX_TLS_GD_TO_IE_ABS;
|
||||
}
|
||||
return Expr;
|
||||
@ -160,7 +154,7 @@ RelType AArch64::getDynRel(RelType Type) const {
|
||||
}
|
||||
|
||||
void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const {
|
||||
write64le(Buf, InX::Plt->getVA());
|
||||
write64le(Buf, In.Plt->getVA());
|
||||
}
|
||||
|
||||
void AArch64::writePltHeader(uint8_t *Buf) const {
|
||||
@ -176,8 +170,8 @@ void AArch64::writePltHeader(uint8_t *Buf) const {
|
||||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
|
||||
uint64_t Got = InX::GotPlt->getVA();
|
||||
uint64_t Plt = InX::Plt->getVA();
|
||||
uint64_t Got = In.GotPlt->getVA();
|
||||
uint64_t Plt = In.Plt->getVA();
|
||||
relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
|
||||
getAArch64Page(Got + 16) - getAArch64Page(Plt + 4));
|
||||
relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16);
|
||||
@ -212,6 +206,13 @@ bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
return !inBranchRange(Type, BranchAddr, Dst);
|
||||
}
|
||||
|
||||
uint32_t AArch64::getThunkSectionSpacing() const {
|
||||
// See comment in Arch/ARM.cpp for a more detailed explanation of
|
||||
// getThunkSectionSpacing(). For AArch64 the only branches we are permitted to
|
||||
// Thunk have a range of +/- 128 MiB
|
||||
return (128 * 1024 * 1024) - 0x30000;
|
||||
}
|
||||
|
||||
bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
|
||||
if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
|
||||
return true;
|
||||
@ -342,7 +343,7 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
or32le(Loc, (Val & 0xFFFC) << 3);
|
||||
break;
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
|
||||
checkInt(Loc, Val, 24, Type);
|
||||
checkUInt(Loc, Val, 24, Type);
|
||||
or32AArch64Imm(Loc, Val >> 12);
|
||||
break;
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
|
||||
|
@ -35,6 +35,7 @@ public:
|
||||
AMDGPU::AMDGPU() {
|
||||
RelativeRel = R_AMDGPU_RELATIVE64;
|
||||
GotRel = R_AMDGPU_ABS64;
|
||||
NoneRel = R_AMDGPU_NONE;
|
||||
GotEntrySize = 8;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ public:
|
||||
void addPltHeaderSymbols(InputSection &ISD) const override;
|
||||
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
uint64_t BranchAddr, const Symbol &S) const override;
|
||||
uint32_t getThunkSectionSpacing() const override;
|
||||
bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
};
|
||||
@ -50,6 +51,7 @@ ARM::ARM() {
|
||||
RelativeRel = R_ARM_RELATIVE;
|
||||
IRelativeRel = R_ARM_IRELATIVE;
|
||||
GotRel = R_ARM_GLOB_DAT;
|
||||
NoneRel = R_ARM_NONE;
|
||||
PltRel = R_ARM_JUMP_SLOT;
|
||||
TlsGotRel = R_ARM_TLS_TPOFF32;
|
||||
TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
|
||||
@ -59,41 +61,8 @@ ARM::ARM() {
|
||||
GotPltEntrySize = 4;
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 32;
|
||||
TrapInstr = 0xd4d4d4d4;
|
||||
// ARM uses Variant 1 TLS
|
||||
TcbSize = 8;
|
||||
TrapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
|
||||
NeedsThunks = true;
|
||||
|
||||
// The placing of pre-created ThunkSections is controlled by the
|
||||
// ThunkSectionSpacing parameter. The aim is to place the
|
||||
// ThunkSection such that all branches from the InputSections prior to the
|
||||
// ThunkSection can reach a Thunk placed at the end of the ThunkSection.
|
||||
// Graphically:
|
||||
// | up to ThunkSectionSpacing .text input sections |
|
||||
// | ThunkSection |
|
||||
// | up to ThunkSectionSpacing .text input sections |
|
||||
// | ThunkSection |
|
||||
|
||||
// Pre-created ThunkSections are spaced roughly 16MiB apart on ARM. This is to
|
||||
// match the most common expected case of a Thumb 2 encoded BL, BLX or B.W
|
||||
// ARM B, BL, BLX range +/- 32MiB
|
||||
// Thumb B.W, BL, BLX range +/- 16MiB
|
||||
// Thumb B<cc>.W range +/- 1MiB
|
||||
// If a branch cannot reach a pre-created ThunkSection a new one will be
|
||||
// created so we can handle the rare cases of a Thumb 2 conditional branch.
|
||||
// We intentionally use a lower size for ThunkSectionSpacing than the maximum
|
||||
// branch range so the end of the ThunkSection is more likely to be within
|
||||
// range of the branch instruction that is furthest away. The value we shorten
|
||||
// ThunkSectionSpacing by is set conservatively to allow us to create 16,384
|
||||
// 12 byte Thunks at any offset in a ThunkSection without risk of a branch to
|
||||
// one of the Thunks going out of range.
|
||||
|
||||
// FIXME: lld assumes that the Thumb BL and BLX encoding permits the J1 and
|
||||
// J2 bits to be used to extend the branch range. On earlier Architectures
|
||||
// such as ARMv4, ARMv5 and ARMv6 (except ARMv6T2) the range is +/- 4MiB. If
|
||||
// support for the earlier encodings is added then when they are used the
|
||||
// ThunkSectionSpacing will need lowering.
|
||||
ThunkSectionSpacing = 0x1000000 - 0x30000;
|
||||
}
|
||||
|
||||
uint32_t ARM::calcEFlags() const {
|
||||
@ -165,6 +134,12 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
|
||||
return R_NONE;
|
||||
case R_ARM_TLS_LE32:
|
||||
return R_TLS;
|
||||
case R_ARM_V4BX:
|
||||
// V4BX is just a marker to indicate there's a "bx rN" instruction at the
|
||||
// given address. It can be used to implement a special linker mode which
|
||||
// rewrites ARMv4T inputs to ARMv4. Since we support only ARMv4 input and
|
||||
// not ARMv4 output, we can just ignore it.
|
||||
return R_HINT;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
@ -177,7 +152,7 @@ RelType ARM::getDynRel(RelType Type) const {
|
||||
}
|
||||
|
||||
void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const {
|
||||
write32le(Buf, InX::Plt->getVA());
|
||||
write32le(Buf, In.Plt->getVA());
|
||||
}
|
||||
|
||||
void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
|
||||
@ -198,8 +173,8 @@ static void writePltHeaderLong(uint8_t *Buf) {
|
||||
0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
|
||||
0xd4, 0xd4, 0xd4, 0xd4};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
uint64_t GotPlt = InX::GotPlt->getVA();
|
||||
uint64_t L1 = InX::Plt->getVA() + 8;
|
||||
uint64_t GotPlt = In.GotPlt->getVA();
|
||||
uint64_t L1 = In.Plt->getVA() + 8;
|
||||
write32le(Buf + 16, GotPlt - L1 - 8);
|
||||
}
|
||||
|
||||
@ -217,7 +192,7 @@ void ARM::writePltHeader(uint8_t *Buf) const {
|
||||
0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4)
|
||||
};
|
||||
|
||||
uint64_t Offset = InX::GotPlt->getVA() - InX::Plt->getVA() - 4;
|
||||
uint64_t Offset = In.GotPlt->getVA() - In.Plt->getVA() - 4;
|
||||
if (!llvm::isUInt<27>(Offset)) {
|
||||
// We cannot encode the Offset, use the long form.
|
||||
writePltHeaderLong(Buf);
|
||||
@ -227,10 +202,10 @@ void ARM::writePltHeader(uint8_t *Buf) const {
|
||||
write32le(Buf + 4, PltData[1] | ((Offset >> 20) & 0xff));
|
||||
write32le(Buf + 8, PltData[2] | ((Offset >> 12) & 0xff));
|
||||
write32le(Buf + 12, PltData[3] | (Offset & 0xfff));
|
||||
write32le(Buf + 16, TrapInstr); // Pad to 32-byte boundary
|
||||
write32le(Buf + 20, TrapInstr);
|
||||
write32le(Buf + 24, TrapInstr);
|
||||
write32le(Buf + 28, TrapInstr);
|
||||
memcpy(Buf + 16, TrapInstr.data(), 4); // Pad to 32-byte boundary
|
||||
memcpy(Buf + 20, TrapInstr.data(), 4);
|
||||
memcpy(Buf + 24, TrapInstr.data(), 4);
|
||||
memcpy(Buf + 28, TrapInstr.data(), 4);
|
||||
}
|
||||
|
||||
void ARM::addPltHeaderSymbols(InputSection &IS) const {
|
||||
@ -279,7 +254,7 @@ void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
write32le(Buf + 0, PltData[0] | ((Offset >> 20) & 0xff));
|
||||
write32le(Buf + 4, PltData[1] | ((Offset >> 12) & 0xff));
|
||||
write32le(Buf + 8, PltData[2] | (Offset & 0xfff));
|
||||
write32le(Buf + 12, TrapInstr); // Pad to 16-byte boundary
|
||||
memcpy(Buf + 12, TrapInstr.data(), 4); // Pad to 16-byte boundary
|
||||
}
|
||||
|
||||
void ARM::addPltSymbols(InputSection &IS, uint64_t Off) const {
|
||||
@ -324,6 +299,40 @@ bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t ARM::getThunkSectionSpacing() const {
|
||||
// The placing of pre-created ThunkSections is controlled by the value
|
||||
// ThunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to
|
||||
// place the ThunkSection such that all branches from the InputSections
|
||||
// prior to the ThunkSection can reach a Thunk placed at the end of the
|
||||
// ThunkSection. Graphically:
|
||||
// | up to ThunkSectionSpacing .text input sections |
|
||||
// | ThunkSection |
|
||||
// | up to ThunkSectionSpacing .text input sections |
|
||||
// | ThunkSection |
|
||||
|
||||
// Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This
|
||||
// is to match the most common expected case of a Thumb 2 encoded BL, BLX or
|
||||
// B.W:
|
||||
// ARM B, BL, BLX range +/- 32MiB
|
||||
// Thumb B.W, BL, BLX range +/- 16MiB
|
||||
// Thumb B<cc>.W range +/- 1MiB
|
||||
// If a branch cannot reach a pre-created ThunkSection a new one will be
|
||||
// created so we can handle the rare cases of a Thumb 2 conditional branch.
|
||||
// We intentionally use a lower size for ThunkSectionSpacing than the maximum
|
||||
// branch range so the end of the ThunkSection is more likely to be within
|
||||
// range of the branch instruction that is furthest away. The value we shorten
|
||||
// ThunkSectionSpacing by is set conservatively to allow us to create 16,384
|
||||
// 12 byte Thunks at any offset in a ThunkSection without risk of a branch to
|
||||
// one of the Thunks going out of range.
|
||||
|
||||
// On Arm the ThunkSectionSpacing depends on the range of the Thumb Branch
|
||||
// range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except
|
||||
// ARMv6T2) the range is +/- 4MiB.
|
||||
|
||||
return (Config->ARMJ1J2BranchEncoding) ? 0x1000000 - 0x30000
|
||||
: 0x400000 - 0x7500;
|
||||
}
|
||||
|
||||
bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
|
||||
uint64_t Range;
|
||||
uint64_t InstrSize;
|
||||
@ -342,7 +351,7 @@ bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
|
||||
break;
|
||||
case R_ARM_THM_JUMP24:
|
||||
case R_ARM_THM_CALL:
|
||||
Range = 0x1000000;
|
||||
Range = Config->ARMJ1J2BranchEncoding ? 0x1000000 : 0x400000;
|
||||
InstrSize = 2;
|
||||
break;
|
||||
default:
|
||||
@ -447,11 +456,23 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
}
|
||||
// Bit 12 is 0 for BLX, 1 for BL
|
||||
write16le(Loc + 2, (read16le(Loc + 2) & ~0x1000) | (Val & 1) << 12);
|
||||
if (!Config->ARMJ1J2BranchEncoding) {
|
||||
// Older Arm architectures do not support R_ARM_THM_JUMP24 and have
|
||||
// different encoding rules and range due to J1 and J2 always being 1.
|
||||
checkInt(Loc, Val, 23, Type);
|
||||
write16le(Loc,
|
||||
0xf000 | // opcode
|
||||
((Val >> 12) & 0x07ff)); // imm11
|
||||
write16le(Loc + 2,
|
||||
(read16le(Loc + 2) & 0xd000) | // opcode
|
||||
0x2800 | // J1 == J2 == 1
|
||||
((Val >> 1) & 0x07ff)); // imm11
|
||||
break;
|
||||
}
|
||||
// Fall through as rest of encoding is the same as B.W
|
||||
LLVM_FALLTHROUGH;
|
||||
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(Loc, Val, 25, Type);
|
||||
write16le(Loc,
|
||||
0xf000 | // opcode
|
||||
@ -470,14 +491,12 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
break;
|
||||
case R_ARM_MOVT_ABS:
|
||||
case R_ARM_MOVT_PREL:
|
||||
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(Loc, Val, 32, Type);
|
||||
write16le(Loc,
|
||||
0xf2c0 | // opcode
|
||||
((Val >> 17) & 0x0400) | // i
|
||||
@ -542,10 +561,19 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
|
||||
((Lo & 0x07ff) << 1)); // imm11:0
|
||||
}
|
||||
case R_ARM_THM_CALL:
|
||||
if (!Config->ARMJ1J2BranchEncoding) {
|
||||
// Older Arm architectures do not support R_ARM_THM_JUMP24 and have
|
||||
// different encoding rules and range due to J1 and J2 always being 1.
|
||||
uint16_t Hi = read16le(Buf);
|
||||
uint16_t Lo = read16le(Buf + 2);
|
||||
return SignExtend64<22>(((Hi & 0x7ff) << 12) | // imm11
|
||||
((Lo & 0x7ff) << 1)); // imm11:0
|
||||
break;
|
||||
}
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_ARM_THM_JUMP24: {
|
||||
// Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0
|
||||
// I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S)
|
||||
// FIXME: I1 and I2 require v6T2ops
|
||||
uint16_t Hi = read16le(Buf);
|
||||
uint16_t Lo = read16le(Buf + 2);
|
||||
return SignExtend64<24>(((Hi & 0x0400) << 14) | // S
|
||||
|
@ -43,12 +43,15 @@ using namespace lld::elf;
|
||||
namespace {
|
||||
class AVR final : public TargetInfo {
|
||||
public:
|
||||
AVR();
|
||||
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
|
||||
|
||||
AVR::AVR() { NoneRel = R_AVR_NONE; }
|
||||
|
||||
RelExpr AVR::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
return R_ABS;
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
@ -25,15 +26,48 @@ using namespace lld::elf;
|
||||
namespace {
|
||||
class Hexagon final : public TargetInfo {
|
||||
public:
|
||||
Hexagon();
|
||||
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;
|
||||
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
|
||||
|
||||
// Support V60 only at the moment.
|
||||
uint32_t Hexagon::calcEFlags() const { return 0x60; }
|
||||
Hexagon::Hexagon() {
|
||||
PltRel = R_HEX_JMP_SLOT;
|
||||
RelativeRel = R_HEX_RELATIVE;
|
||||
GotRel = R_HEX_GLOB_DAT;
|
||||
GotEntrySize = 4;
|
||||
// The zero'th GOT entry is reserved for the address of _DYNAMIC. The
|
||||
// next 3 are reserved for the dynamic loader.
|
||||
GotPltHeaderEntriesNum = 4;
|
||||
GotPltEntrySize = 4;
|
||||
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 32;
|
||||
|
||||
// Hexagon Linux uses 64K pages by default.
|
||||
DefaultMaxPageSize = 0x10000;
|
||||
NoneRel = R_HEX_NONE;
|
||||
}
|
||||
|
||||
uint32_t Hexagon::calcEFlags() const {
|
||||
assert(!ObjectFiles.empty());
|
||||
|
||||
// The architecture revision must always be equal to or greater than
|
||||
// greatest revision in the list of inputs.
|
||||
uint32_t Ret = 0;
|
||||
for (InputFile *F : ObjectFiles) {
|
||||
uint32_t EFlags = cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags;
|
||||
if (EFlags > Ret)
|
||||
Ret = EFlags;
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
static uint32_t applyMask(uint32_t Mask, uint32_t Data) {
|
||||
uint32_t Result = 0;
|
||||
@ -53,29 +87,143 @@ static uint32_t applyMask(uint32_t Mask, uint32_t Data) {
|
||||
RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_HEX_B9_PCREL:
|
||||
case R_HEX_B9_PCREL_X:
|
||||
case R_HEX_B13_PCREL:
|
||||
case R_HEX_B15_PCREL:
|
||||
case R_HEX_B15_PCREL_X:
|
||||
case R_HEX_6_PCREL_X:
|
||||
case R_HEX_32_PCREL:
|
||||
return R_PC;
|
||||
case R_HEX_B22_PCREL:
|
||||
case R_HEX_PLT_B22_PCREL:
|
||||
case R_HEX_B22_PCREL_X:
|
||||
case R_HEX_B32_PCREL_X:
|
||||
return R_PC;
|
||||
return R_PLT_PC;
|
||||
case R_HEX_GOT_11_X:
|
||||
case R_HEX_GOT_16_X:
|
||||
case R_HEX_GOT_32_6_X:
|
||||
return R_HEXAGON_GOT;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t findMaskR6(uint32_t Insn) {
|
||||
// There are (arguably too) many relocation masks for the DSP's
|
||||
// R_HEX_6_X type. The table below is used to select the correct mask
|
||||
// for the given instruction.
|
||||
struct InstructionMask {
|
||||
uint32_t CmpMask;
|
||||
uint32_t RelocMask;
|
||||
};
|
||||
|
||||
static const InstructionMask R6[] = {
|
||||
{0x38000000, 0x0000201f}, {0x39000000, 0x0000201f},
|
||||
{0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80},
|
||||
{0x40000000, 0x000020f8}, {0x41000000, 0x000007e0},
|
||||
{0x42000000, 0x000020f8}, {0x43000000, 0x000007e0},
|
||||
{0x44000000, 0x000020f8}, {0x45000000, 0x000007e0},
|
||||
{0x46000000, 0x000020f8}, {0x47000000, 0x000007e0},
|
||||
{0x6a000000, 0x00001f80}, {0x7c000000, 0x001f2000},
|
||||
{0x9a000000, 0x00000f60}, {0x9b000000, 0x00000f60},
|
||||
{0x9c000000, 0x00000f60}, {0x9d000000, 0x00000f60},
|
||||
{0x9f000000, 0x001f0100}, {0xab000000, 0x0000003f},
|
||||
{0xad000000, 0x0000003f}, {0xaf000000, 0x00030078},
|
||||
{0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0},
|
||||
{0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}};
|
||||
|
||||
// Duplex forms have a fixed mask and parse bits 15:14 are always
|
||||
// zero. Non-duplex insns will always have at least one bit set in the
|
||||
// parse field.
|
||||
if ((0xC000 & Insn) == 0x0)
|
||||
return 0x03f00000;
|
||||
|
||||
for (InstructionMask I : R6)
|
||||
if ((0xff000000 & Insn) == I.CmpMask)
|
||||
return I.RelocMask;
|
||||
|
||||
error("unrecognized instruction for R_HEX_6 relocation: 0x" +
|
||||
utohexstr(Insn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t findMaskR8(uint32_t Insn) {
|
||||
if ((0xff000000 & Insn) == 0xde000000)
|
||||
return 0x00e020e8;
|
||||
if ((0xff000000 & Insn) == 0x3c000000)
|
||||
return 0x0000207f;
|
||||
return 0x00001fe0;
|
||||
}
|
||||
|
||||
static uint32_t findMaskR11(uint32_t Insn) {
|
||||
if ((0xff000000 & Insn) == 0xa1000000)
|
||||
return 0x060020ff;
|
||||
return 0x06003fe0;
|
||||
}
|
||||
|
||||
static uint32_t findMaskR16(uint32_t Insn) {
|
||||
if ((0xff000000 & Insn) == 0x48000000)
|
||||
return 0x061f20ff;
|
||||
if ((0xff000000 & Insn) == 0x49000000)
|
||||
return 0x061f3fe0;
|
||||
if ((0xff000000 & Insn) == 0x78000000)
|
||||
return 0x00df3fe0;
|
||||
if ((0xff000000 & Insn) == 0xb0000000)
|
||||
return 0x0fe03fe0;
|
||||
|
||||
error("unrecognized instruction for R_HEX_16_X relocation: 0x" +
|
||||
utohexstr(Insn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_6_PCREL_X:
|
||||
case R_HEX_6_X:
|
||||
or32le(Loc, applyMask(findMaskR6(read32le(Loc)), Val));
|
||||
break;
|
||||
case R_HEX_8_X:
|
||||
or32le(Loc, applyMask(findMaskR8(read32le(Loc)), Val));
|
||||
break;
|
||||
case R_HEX_9_X:
|
||||
or32le(Loc, applyMask(0x00003fe0, Val & 0x3f));
|
||||
break;
|
||||
case R_HEX_10_X:
|
||||
or32le(Loc, applyMask(0x00203fe0, Val & 0x3f));
|
||||
break;
|
||||
case R_HEX_11_X:
|
||||
case R_HEX_GOT_11_X:
|
||||
or32le(Loc, applyMask(findMaskR11(read32le(Loc)), Val & 0x3f));
|
||||
break;
|
||||
case R_HEX_12_X:
|
||||
or32le(Loc, applyMask(0x000007e0, Val));
|
||||
break;
|
||||
case R_HEX_16_X: // These relocs only have 6 effective bits.
|
||||
case R_HEX_GOT_16_X:
|
||||
or32le(Loc, applyMask(findMaskR16(read32le(Loc)), Val & 0x3f));
|
||||
break;
|
||||
case R_HEX_32:
|
||||
case R_HEX_32_PCREL:
|
||||
or32le(Loc, Val);
|
||||
break;
|
||||
case R_HEX_32_6_X:
|
||||
case R_HEX_GOT_32_6_X:
|
||||
or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
|
||||
break;
|
||||
case R_HEX_B9_PCREL:
|
||||
or32le(Loc, applyMask(0x003000fe, Val >> 2));
|
||||
break;
|
||||
case R_HEX_B9_PCREL_X:
|
||||
or32le(Loc, applyMask(0x003000fe, Val & 0x3f));
|
||||
break;
|
||||
case R_HEX_B13_PCREL:
|
||||
or32le(Loc, applyMask(0x00202ffe, Val >> 2));
|
||||
break;
|
||||
case R_HEX_B15_PCREL:
|
||||
or32le(Loc, applyMask(0x00df20fe, Val >> 2));
|
||||
break;
|
||||
@ -83,6 +231,7 @@ void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
or32le(Loc, applyMask(0x00df20fe, Val & 0x3f));
|
||||
break;
|
||||
case R_HEX_B22_PCREL:
|
||||
case R_HEX_PLT_B22_PCREL:
|
||||
or32le(Loc, applyMask(0x1ff3ffe, Val >> 2));
|
||||
break;
|
||||
case R_HEX_B22_PCREL_X:
|
||||
@ -91,12 +240,52 @@ void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
case R_HEX_B32_PCREL_X:
|
||||
or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
|
||||
break;
|
||||
case R_HEX_HI16:
|
||||
or32le(Loc, applyMask(0x00c03fff, Val >> 16));
|
||||
break;
|
||||
case R_HEX_LO16:
|
||||
or32le(Loc, applyMask(0x00c03fff, Val));
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Hexagon::writePltHeader(uint8_t *Buf) const {
|
||||
const uint8_t PltData[] = {
|
||||
0x00, 0x40, 0x00, 0x00, // { immext (#0)
|
||||
0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0
|
||||
0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn
|
||||
0x4f, 0x40, 0x9c, 0x91, // r15 = memw (r28 + #8) # object ID at GOT2
|
||||
0x3c, 0xc0, 0x9c, 0x91, // r28 = memw (r28 + #4) }# dynamic link at GOT1
|
||||
0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2) # index of PLTn
|
||||
0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker
|
||||
0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment
|
||||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
|
||||
// Offset from PLT0 to the GOT.
|
||||
uint64_t Off = In.GotPlt->getVA() - In.Plt->getVA();
|
||||
relocateOne(Buf, R_HEX_B32_PCREL_X, Off);
|
||||
relocateOne(Buf + 4, R_HEX_6_PCREL_X, Off);
|
||||
}
|
||||
|
||||
void Hexagon::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
const uint8_t Inst[] = {
|
||||
0x00, 0x40, 0x00, 0x00, // { immext (#0)
|
||||
0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) }
|
||||
0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14)
|
||||
0x00, 0xc0, 0x9c, 0x52, // jumpr r28
|
||||
};
|
||||
memcpy(Buf, Inst, sizeof(Inst));
|
||||
|
||||
relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr);
|
||||
relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr);
|
||||
}
|
||||
|
||||
TargetInfo *elf::getHexagonTargetInfo() {
|
||||
static Hexagon Target;
|
||||
return &Target;
|
||||
|
94
contrib/llvm/tools/lld/ELF/Arch/MSP430.cpp
Normal file
94
contrib/llvm/tools/lld/ELF/Arch/MSP430.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
//===- MSP430.cpp ---------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The MSP430 is a 16-bit microcontroller RISC architecture. The instruction set
|
||||
// has only 27 core instructions orthogonally augmented with a variety
|
||||
// of addressing modes for source and destination operands. Entire address space
|
||||
// of MSP430 is 64KB (the extended MSP430X architecture is not considered here).
|
||||
// A typical MSP430 MCU has several kilobytes of RAM and ROM, plenty
|
||||
// of peripherals and is generally optimized for a low power consumption.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.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 MSP430 final : public TargetInfo {
|
||||
public:
|
||||
MSP430();
|
||||
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
|
||||
|
||||
MSP430::MSP430() {
|
||||
// mov.b #0, r3
|
||||
TrapInstr = {0x43, 0x43, 0x43, 0x43};
|
||||
}
|
||||
|
||||
RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_MSP430_10_PCREL:
|
||||
case R_MSP430_16_PCREL:
|
||||
case R_MSP430_16_PCREL_BYTE:
|
||||
case R_MSP430_2X_PCREL:
|
||||
case R_MSP430_RL_PCREL:
|
||||
case R_MSP430_SYM_DIFF:
|
||||
return R_PC;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
void MSP430::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_MSP430_8:
|
||||
checkIntUInt(Loc, Val, 8, Type);
|
||||
*Loc = Val;
|
||||
break;
|
||||
case R_MSP430_16:
|
||||
case R_MSP430_16_PCREL:
|
||||
case R_MSP430_16_BYTE:
|
||||
case R_MSP430_16_PCREL_BYTE:
|
||||
checkIntUInt(Loc, Val, 16, Type);
|
||||
write16le(Loc, Val);
|
||||
break;
|
||||
case R_MSP430_32:
|
||||
checkIntUInt(Loc, Val, 32, Type);
|
||||
write32le(Loc, Val);
|
||||
break;
|
||||
case R_MSP430_10_PCREL: {
|
||||
int16_t Offset = ((int16_t)Val >> 1) - 1;
|
||||
checkInt(Loc, Offset, 10, Type);
|
||||
write16le(Loc, (read16le(Loc) & 0xFC00) | (Offset & 0x3FF));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getMSP430TargetInfo() {
|
||||
static MSP430 Target;
|
||||
return &Target;
|
||||
}
|
@ -53,9 +53,12 @@ template <class ELFT> MIPS<ELFT>::MIPS() {
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 32;
|
||||
CopyRel = R_MIPS_COPY;
|
||||
NoneRel = R_MIPS_NONE;
|
||||
PltRel = R_MIPS_JUMP_SLOT;
|
||||
NeedsThunks = true;
|
||||
TrapInstr = 0xefefefef;
|
||||
|
||||
// Set `sigrie 1` as a trap instruction.
|
||||
write32(TrapInstr.data(), 0x04170001);
|
||||
|
||||
if (ELFT::Is64Bits) {
|
||||
RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
|
||||
@ -185,7 +188,7 @@ template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const {
|
||||
|
||||
template <class ELFT>
|
||||
void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &) const {
|
||||
uint64_t VA = InX::Plt->getVA();
|
||||
uint64_t VA = In.Plt->getVA();
|
||||
if (isMicroMips())
|
||||
VA |= 1;
|
||||
write32<ELFT::TargetEndianness>(Buf, VA);
|
||||
@ -239,8 +242,8 @@ static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
|
||||
template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
if (isMicroMips()) {
|
||||
uint64_t GotPlt = InX::GotPlt->getVA();
|
||||
uint64_t Plt = InX::Plt->getVA();
|
||||
uint64_t GotPlt = In.GotPlt->getVA();
|
||||
uint64_t Plt = In.Plt->getVA();
|
||||
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
||||
memset(Buf, 0, PltHeaderSize);
|
||||
|
||||
@ -292,7 +295,7 @@ template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
|
||||
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();
|
||||
uint64_t GotPlt = In.GotPlt->getVA();
|
||||
writeValue<E>(Buf, GotPlt + 0x8000, 16, 16);
|
||||
writeValue<E>(Buf + 4, GotPlt, 16, 0);
|
||||
writeValue<E>(Buf + 8, GotPlt, 16, 0);
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
} // namespace
|
||||
|
||||
PPC::PPC() {
|
||||
NoneRel = R_PPC_NONE;
|
||||
GotBaseSymOff = 0x8000;
|
||||
GotBaseSymInGotPlt = false;
|
||||
}
|
||||
@ -36,6 +37,7 @@ PPC::PPC() {
|
||||
RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_PPC_REL14:
|
||||
case R_PPC_REL24:
|
||||
case R_PPC_REL32:
|
||||
return R_PC;
|
||||
@ -61,6 +63,9 @@ void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
case R_PPC_REL32:
|
||||
write32be(Loc, Val);
|
||||
break;
|
||||
case R_PPC_REL14:
|
||||
write32be(Loc, read32be(Loc) | (Val & 0xFFFC));
|
||||
break;
|
||||
case R_PPC_PLTREL24:
|
||||
case R_PPC_REL24:
|
||||
write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC));
|
||||
|
@ -23,12 +23,49 @@ using namespace lld::elf;
|
||||
static uint64_t PPC64TocOffset = 0x8000;
|
||||
static uint64_t DynamicThreadPointerOffset = 0x8000;
|
||||
|
||||
// The instruction encoding of bits 21-30 from the ISA for the Xform and Dform
|
||||
// instructions that can be used as part of the initial exec TLS sequence.
|
||||
enum XFormOpcd {
|
||||
LBZX = 87,
|
||||
LHZX = 279,
|
||||
LWZX = 23,
|
||||
LDX = 21,
|
||||
STBX = 215,
|
||||
STHX = 407,
|
||||
STWX = 151,
|
||||
STDX = 149,
|
||||
ADD = 266,
|
||||
};
|
||||
|
||||
enum DFormOpcd {
|
||||
LBZ = 34,
|
||||
LBZU = 35,
|
||||
LHZ = 40,
|
||||
LHZU = 41,
|
||||
LHAU = 43,
|
||||
LWZ = 32,
|
||||
LWZU = 33,
|
||||
LFSU = 49,
|
||||
LD = 58,
|
||||
LFDU = 51,
|
||||
STB = 38,
|
||||
STBU = 39,
|
||||
STH = 44,
|
||||
STHU = 45,
|
||||
STW = 36,
|
||||
STWU = 37,
|
||||
STFSU = 53,
|
||||
STFDU = 55,
|
||||
STD = 62,
|
||||
ADDI = 14
|
||||
};
|
||||
|
||||
uint64_t elf::getPPC64TocBase() {
|
||||
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
|
||||
// TOC starts where the first of these sections starts. We always create a
|
||||
// .got when we see a relocation that uses it, so for us the start is always
|
||||
// the .got.
|
||||
uint64_t TocVA = InX::Got->getVA();
|
||||
uint64_t TocVA = In.Got->getVA();
|
||||
|
||||
// Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000
|
||||
// thus permitting a full 64 Kbytes segment. Note that the glibc startup
|
||||
@ -37,6 +74,31 @@ uint64_t elf::getPPC64TocBase() {
|
||||
return TocVA + PPC64TocOffset;
|
||||
}
|
||||
|
||||
unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) {
|
||||
// The offset is encoded into the 3 most significant bits of the st_other
|
||||
// field, with some special values described in section 3.4.1 of the ABI:
|
||||
// 0 --> Zero offset between the GEP and LEP, and the function does NOT use
|
||||
// the TOC pointer (r2). r2 will hold the same value on returning from
|
||||
// the function as it did on entering the function.
|
||||
// 1 --> Zero offset between the GEP and LEP, and r2 should be treated as a
|
||||
// caller-saved register for all callers.
|
||||
// 2-6 --> The binary logarithm of the offset eg:
|
||||
// 2 --> 2^2 = 4 bytes --> 1 instruction.
|
||||
// 6 --> 2^6 = 64 bytes --> 16 instructions.
|
||||
// 7 --> Reserved.
|
||||
uint8_t GepToLep = (StOther >> 5) & 7;
|
||||
if (GepToLep < 2)
|
||||
return 0;
|
||||
|
||||
// The value encoded in the st_other bits is the
|
||||
// log-base-2(offset).
|
||||
if (GepToLep < 7)
|
||||
return 1 << GepToLep;
|
||||
|
||||
error("reserved value of 7 in the 3 most-significant-bits of st_other");
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class PPC64 final : public TargetInfo {
|
||||
public:
|
||||
@ -51,11 +113,16 @@ public:
|
||||
void writeGotHeader(uint8_t *Buf) const override;
|
||||
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
uint64_t BranchAddr, const Symbol &S) const override;
|
||||
bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) 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;
|
||||
void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
|
||||
bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
|
||||
uint8_t StOther) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -71,8 +138,64 @@ 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; }
|
||||
|
||||
// Extracts the 'PO' field of an instruction encoding.
|
||||
static uint8_t getPrimaryOpCode(uint32_t Encoding) { return (Encoding >> 26); }
|
||||
|
||||
static bool isDQFormInstruction(uint32_t Encoding) {
|
||||
switch (getPrimaryOpCode(Encoding)) {
|
||||
default:
|
||||
return false;
|
||||
case 56:
|
||||
// The only instruction with a primary opcode of 56 is `lq`.
|
||||
return true;
|
||||
case 61:
|
||||
// There are both DS and DQ instruction forms with this primary opcode.
|
||||
// Namely `lxv` and `stxv` are the DQ-forms that use it.
|
||||
// The DS 'XO' bits being set to 01 is restricted to DQ form.
|
||||
return (Encoding & 3) == 0x1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isInstructionUpdateForm(uint32_t Encoding) {
|
||||
switch (getPrimaryOpCode(Encoding)) {
|
||||
default:
|
||||
return false;
|
||||
case LBZU:
|
||||
case LHAU:
|
||||
case LHZU:
|
||||
case LWZU:
|
||||
case LFSU:
|
||||
case LFDU:
|
||||
case STBU:
|
||||
case STHU:
|
||||
case STWU:
|
||||
case STFSU:
|
||||
case STFDU:
|
||||
return true;
|
||||
// LWA has the same opcode as LD, and the DS bits is what differentiates
|
||||
// between LD/LDU/LWA
|
||||
case LD:
|
||||
case STD:
|
||||
return (Encoding & 3) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
// There are a number of places when we either want to read or write an
|
||||
// instruction when handling a half16 relocation type. On big-endian the buffer
|
||||
// pointer is pointing into the middle of the word we want to extract, and on
|
||||
// little-endian it is pointing to the start of the word. These 2 helpers are to
|
||||
// simplify reading and writing in that context.
|
||||
static void writeInstrFromHalf16(uint8_t *Loc, uint32_t Instr) {
|
||||
write32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0), Instr);
|
||||
}
|
||||
|
||||
static uint32_t readInstrFromHalf16(const uint8_t *Loc) {
|
||||
return read32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0));
|
||||
}
|
||||
|
||||
PPC64::PPC64() {
|
||||
GotRel = R_PPC64_GLOB_DAT;
|
||||
NoneRel = R_PPC64_NONE;
|
||||
PltRel = R_PPC64_JMP_SLOT;
|
||||
RelativeRel = R_PPC64_RELATIVE;
|
||||
IRelativeRel = R_PPC64_IRELATIVE;
|
||||
@ -85,14 +208,14 @@ PPC64::PPC64() {
|
||||
GotPltHeaderEntriesNum = 2;
|
||||
PltHeaderSize = 60;
|
||||
NeedsThunks = true;
|
||||
TcbSize = 8;
|
||||
TlsTpOffset = 0x7000;
|
||||
|
||||
TlsModuleIndexRel = R_PPC64_DTPMOD64;
|
||||
TlsOffsetRel = R_PPC64_DTPREL64;
|
||||
|
||||
TlsGotRel = R_PPC64_TPREL64;
|
||||
|
||||
NeedsMoreStackNonSplit = false;
|
||||
|
||||
// We need 64K pages (at least under glibc/Linux, the loader won't
|
||||
// set different permissions on a finer granularity than that).
|
||||
DefaultMaxPageSize = 65536;
|
||||
@ -107,8 +230,7 @@ PPC64::PPC64() {
|
||||
// use 0x10000000 as the starting address.
|
||||
DefaultImageBase = 0x10000000;
|
||||
|
||||
TrapInstr =
|
||||
(Config->IsLE == sys::IsLittleEndianHost) ? 0x7fe00008 : 0x0800e07f;
|
||||
write32(TrapInstr.data(), 0x7fe00008);
|
||||
}
|
||||
|
||||
static uint32_t getEFlags(InputFile *File) {
|
||||
@ -146,27 +268,29 @@ void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
// 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
|
||||
writeInstrFromHalf16(Loc, 0x60000000); // nop
|
||||
break;
|
||||
case R_PPC64_GOT_TLSGD16:
|
||||
case R_PPC64_GOT_TLSGD16_LO:
|
||||
write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13
|
||||
writeInstrFromHalf16(Loc, 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);
|
||||
// Since we are relocating a half16 type relocation and Loc + 4 points to
|
||||
// the start of an instruction we need to advance the buffer by an extra
|
||||
// 2 bytes on BE.
|
||||
relocateOne(Loc + 4 + (Config->EKind == ELF64BEKind ? 2 : 0),
|
||||
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:
|
||||
@ -183,13 +307,12 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
// 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
|
||||
writeInstrFromHalf16(Loc, 0x60000000); // nop
|
||||
break;
|
||||
case R_PPC64_GOT_TLSLD16_LO:
|
||||
write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13, 0
|
||||
writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13, 0
|
||||
break;
|
||||
case R_PPC64_TLSLD:
|
||||
write32(Loc, 0x60000000); // nop
|
||||
@ -212,9 +335,90 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned getDFormOp(unsigned SecondaryOp) {
|
||||
switch (SecondaryOp) {
|
||||
case LBZX:
|
||||
return LBZ;
|
||||
case LHZX:
|
||||
return LHZ;
|
||||
case LWZX:
|
||||
return LWZ;
|
||||
case LDX:
|
||||
return LD;
|
||||
case STBX:
|
||||
return STB;
|
||||
case STHX:
|
||||
return STH;
|
||||
case STWX:
|
||||
return STW;
|
||||
case STDX:
|
||||
return STD;
|
||||
case ADD:
|
||||
return ADDI;
|
||||
default:
|
||||
error("unrecognized instruction for IE to LE R_PPC64_TLS");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
// The initial exec code sequence for a global `x` will look like:
|
||||
// Instruction Relocation Symbol
|
||||
// addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x
|
||||
// ld r9, x@got@tprel@l(r9) R_PPC64_GOT_TPREL16_LO_DS x
|
||||
// add r9, r9, x@tls R_PPC64_TLS x
|
||||
|
||||
// Relaxing to local exec entails converting:
|
||||
// addis r9, r2, x@got@tprel@ha into nop
|
||||
// ld r9, x@got@tprel@l(r9) into addis r9, r13, x@tprel@ha
|
||||
// add r9, r9, x@tls into addi r9, r9, x@tprel@l
|
||||
|
||||
// x@tls R_PPC64_TLS is a relocation which does not compute anything,
|
||||
// it is replaced with r13 (thread pointer).
|
||||
|
||||
// The add instruction in the initial exec sequence has multiple variations
|
||||
// that need to be handled. If we are building an address it will use an add
|
||||
// instruction, if we are accessing memory it will use any of the X-form
|
||||
// indexed load or store instructions.
|
||||
|
||||
unsigned Offset = (Config->EKind == ELF64BEKind) ? 2 : 0;
|
||||
switch (Type) {
|
||||
case R_PPC64_GOT_TPREL16_HA:
|
||||
write32(Loc - Offset, 0x60000000); // nop
|
||||
break;
|
||||
case R_PPC64_GOT_TPREL16_LO_DS:
|
||||
case R_PPC64_GOT_TPREL16_DS: {
|
||||
uint32_t RegNo = read32(Loc - Offset) & 0x03E00000; // bits 6-10
|
||||
write32(Loc - Offset, 0x3C0D0000 | RegNo); // addis RegNo, r13
|
||||
relocateOne(Loc, R_PPC64_TPREL16_HA, Val);
|
||||
break;
|
||||
}
|
||||
case R_PPC64_TLS: {
|
||||
uint32_t PrimaryOp = getPrimaryOpCode(read32(Loc));
|
||||
if (PrimaryOp != 31)
|
||||
error("unrecognized instruction for IE to LE R_PPC64_TLS");
|
||||
uint32_t SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30
|
||||
uint32_t DFormOp = getDFormOp(SecondaryOp);
|
||||
write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF)));
|
||||
relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("unknown relocation for IE to LE");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_PPC64_GOT16:
|
||||
case R_PPC64_GOT16_DS:
|
||||
case R_PPC64_GOT16_HA:
|
||||
case R_PPC64_GOT16_HI:
|
||||
case R_PPC64_GOT16_LO:
|
||||
case R_PPC64_GOT16_LO_DS:
|
||||
return R_GOT_OFF;
|
||||
case R_PPC64_TOC16:
|
||||
case R_PPC64_TOC16_DS:
|
||||
case R_PPC64_TOC16_HA:
|
||||
@ -224,6 +428,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
|
||||
return R_GOTREL;
|
||||
case R_PPC64_TOC:
|
||||
return R_PPC_TOC;
|
||||
case R_PPC64_REL14:
|
||||
case R_PPC64_REL24:
|
||||
return R_PPC_CALL_PLT;
|
||||
case R_PPC64_REL16_LO:
|
||||
@ -279,7 +484,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
|
||||
case R_PPC64_TLSLD:
|
||||
return R_TLSLD_HINT;
|
||||
case R_PPC64_TLS:
|
||||
return R_HINT;
|
||||
return R_TLSIE_HINT;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
@ -308,16 +513,16 @@ void PPC64::writePltHeader(uint8_t *Buf) const {
|
||||
// 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);
|
||||
int64_t GotPltOffset = In.GotPlt->getVA() - (In.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 {
|
||||
int32_t Offset = PltHeaderSize + Index * PltEntrySize;
|
||||
// bl __glink_PLTresolve
|
||||
write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc));
|
||||
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) {
|
||||
@ -328,30 +533,36 @@ static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
|
||||
|
||||
switch (Type) {
|
||||
// TOC biased relocation.
|
||||
case R_PPC64_GOT16:
|
||||
case R_PPC64_GOT_TLSGD16:
|
||||
case R_PPC64_GOT_TLSLD16:
|
||||
case R_PPC64_TOC16:
|
||||
return {R_PPC64_ADDR16, TocBiasedVal};
|
||||
case R_PPC64_GOT16_DS:
|
||||
case R_PPC64_TOC16_DS:
|
||||
case R_PPC64_GOT_TPREL16_DS:
|
||||
case R_PPC64_GOT_DTPREL16_DS:
|
||||
return {R_PPC64_ADDR16_DS, TocBiasedVal};
|
||||
case R_PPC64_GOT16_HA:
|
||||
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, TocBiasedVal};
|
||||
case R_PPC64_GOT16_HI:
|
||||
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, TocBiasedVal};
|
||||
case R_PPC64_GOT16_LO:
|
||||
case R_PPC64_GOT_TLSGD16_LO:
|
||||
case R_PPC64_GOT_TLSLD16_LO:
|
||||
case R_PPC64_TOC16_LO:
|
||||
return {R_PPC64_ADDR16_LO, TocBiasedVal};
|
||||
case R_PPC64_GOT16_LO_DS:
|
||||
case R_PPC64_TOC16_LO_DS:
|
||||
case R_PPC64_GOT_TPREL16_LO_DS:
|
||||
case R_PPC64_GOT_DTPREL16_LO_DS:
|
||||
@ -386,9 +597,27 @@ static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool isTocOptType(RelType Type) {
|
||||
switch (Type) {
|
||||
case R_PPC64_GOT16_HA:
|
||||
case R_PPC64_GOT16_LO_DS:
|
||||
case R_PPC64_TOC16_HA:
|
||||
case R_PPC64_TOC16_LO_DS:
|
||||
case R_PPC64_TOC16_LO:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
// For a TOC-relative relocation, proceed in terms of the corresponding
|
||||
// ADDR16 relocation type.
|
||||
// We need to save the original relocation type to use in diagnostics, and
|
||||
// use the original type to determine if we should toc-optimize the
|
||||
// instructions being relocated.
|
||||
RelType OriginalType = Type;
|
||||
bool ShouldTocOptimize = isTocOptType(Type);
|
||||
// For dynamic thread pointer relative, toc-relative, and got-indirect
|
||||
// relocations, proceed in terms of the corresponding ADDR16 relocation type.
|
||||
std::tie(Type, Val) = toAddr16Rel(Type, Val);
|
||||
|
||||
switch (Type) {
|
||||
@ -401,18 +630,25 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
}
|
||||
case R_PPC64_ADDR16:
|
||||
case R_PPC64_TPREL16:
|
||||
checkInt(Loc, Val, 16, Type);
|
||||
checkInt(Loc, Val, 16, OriginalType);
|
||||
write16(Loc, Val);
|
||||
break;
|
||||
case R_PPC64_ADDR16_DS:
|
||||
case R_PPC64_TPREL16_DS:
|
||||
checkInt(Loc, Val, 16, Type);
|
||||
write16(Loc, (read16(Loc) & 3) | (Val & ~3));
|
||||
break;
|
||||
case R_PPC64_TPREL16_DS: {
|
||||
checkInt(Loc, Val, 16, OriginalType);
|
||||
// DQ-form instructions use bits 28-31 as part of the instruction encoding
|
||||
// DS-form instructions only use bits 30-31.
|
||||
uint16_t Mask = isDQFormInstruction(readInstrFromHalf16(Loc)) ? 0xF : 0x3;
|
||||
checkAlignment(Loc, lo(Val), Mask + 1, OriginalType);
|
||||
write16(Loc, (read16(Loc) & Mask) | lo(Val));
|
||||
} break;
|
||||
case R_PPC64_ADDR16_HA:
|
||||
case R_PPC64_REL16_HA:
|
||||
case R_PPC64_TPREL16_HA:
|
||||
write16(Loc, ha(Val));
|
||||
if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0)
|
||||
writeInstrFromHalf16(Loc, 0x60000000);
|
||||
else
|
||||
write16(Loc, ha(Val));
|
||||
break;
|
||||
case R_PPC64_ADDR16_HI:
|
||||
case R_PPC64_REL16_HI:
|
||||
@ -438,12 +674,40 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
case R_PPC64_ADDR16_LO:
|
||||
case R_PPC64_REL16_LO:
|
||||
case R_PPC64_TPREL16_LO:
|
||||
// When the high-adjusted part of a toc relocation evalutes to 0, it is
|
||||
// changed into a nop. The lo part then needs to be updated to use the
|
||||
// toc-pointer register r2, as the base register.
|
||||
if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) {
|
||||
uint32_t Instr = readInstrFromHalf16(Loc);
|
||||
if (isInstructionUpdateForm(Instr))
|
||||
error(getErrorLocation(Loc) +
|
||||
"can't toc-optimize an update instruction: 0x" +
|
||||
utohexstr(Instr));
|
||||
Instr = (Instr & 0xFFE00000) | 0x00020000;
|
||||
writeInstrFromHalf16(Loc, Instr);
|
||||
}
|
||||
write16(Loc, lo(Val));
|
||||
break;
|
||||
case R_PPC64_ADDR16_LO_DS:
|
||||
case R_PPC64_TPREL16_LO_DS:
|
||||
write16(Loc, (read16(Loc) & 3) | (lo(Val) & ~3));
|
||||
break;
|
||||
case R_PPC64_TPREL16_LO_DS: {
|
||||
// DQ-form instructions use bits 28-31 as part of the instruction encoding
|
||||
// DS-form instructions only use bits 30-31.
|
||||
uint32_t Inst = readInstrFromHalf16(Loc);
|
||||
uint16_t Mask = isDQFormInstruction(Inst) ? 0xF : 0x3;
|
||||
checkAlignment(Loc, lo(Val), Mask + 1, OriginalType);
|
||||
if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) {
|
||||
// When the high-adjusted part of a toc relocation evalutes to 0, it is
|
||||
// changed into a nop. The lo part then needs to be updated to use the toc
|
||||
// pointer register r2, as the base register.
|
||||
if (isInstructionUpdateForm(Inst))
|
||||
error(getErrorLocation(Loc) +
|
||||
"Can't toc-optimize an update instruction: 0x" +
|
||||
Twine::utohexstr(Inst));
|
||||
Inst = (Inst & 0xFFE0000F) | 0x00020000;
|
||||
writeInstrFromHalf16(Loc, Inst);
|
||||
}
|
||||
write16(Loc, (read16(Loc) & Mask) | lo(Val));
|
||||
} break;
|
||||
case R_PPC64_ADDR32:
|
||||
case R_PPC64_REL32:
|
||||
checkInt(Loc, Val, 32, Type);
|
||||
@ -454,9 +718,17 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
case R_PPC64_TOC:
|
||||
write64(Loc, Val);
|
||||
break;
|
||||
case R_PPC64_REL14: {
|
||||
uint32_t Mask = 0x0000FFFC;
|
||||
checkInt(Loc, Val, 16, Type);
|
||||
checkAlignment(Loc, Val, 4, Type);
|
||||
write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
|
||||
break;
|
||||
}
|
||||
case R_PPC64_REL24: {
|
||||
uint32_t Mask = 0x03FFFFFC;
|
||||
checkInt(Loc, Val, 24, Type);
|
||||
checkInt(Loc, Val, 26, Type);
|
||||
checkAlignment(Loc, Val, 4, Type);
|
||||
write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
|
||||
break;
|
||||
}
|
||||
@ -470,9 +742,30 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
|
||||
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();
|
||||
if (Type != R_PPC64_REL14 && Type != R_PPC64_REL24)
|
||||
return false;
|
||||
|
||||
// If a function is in the Plt it needs to be called with a call-stub.
|
||||
if (S.isInPlt())
|
||||
return true;
|
||||
|
||||
// If a symbol is a weak undefined and we are compiling an executable
|
||||
// it doesn't need a range-extending thunk since it can't be called.
|
||||
if (S.isUndefWeak() && !Config->Shared)
|
||||
return false;
|
||||
|
||||
// If the offset exceeds the range of the branch type then it will need
|
||||
// a range-extending thunk.
|
||||
return !inBranchRange(Type, BranchAddr, S.getVA());
|
||||
}
|
||||
|
||||
bool PPC64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
|
||||
int64_t Offset = Dst - Src;
|
||||
if (Type == R_PPC64_REL14)
|
||||
return isInt<16>(Offset);
|
||||
if (Type == R_PPC64_REL24)
|
||||
return isInt<26>(Offset);
|
||||
llvm_unreachable("unsupported relocation type used in branch");
|
||||
}
|
||||
|
||||
RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
@ -511,9 +804,8 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
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);
|
||||
uint32_t InputRegister = (readInstrFromHalf16(Loc) & (0x1f << 16));
|
||||
writeInstrFromHalf16(Loc, 0xE8600000 | InputRegister);
|
||||
relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val);
|
||||
return;
|
||||
}
|
||||
@ -526,6 +818,113 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
}
|
||||
}
|
||||
|
||||
// The prologue for a split-stack function is expected to look roughly
|
||||
// like this:
|
||||
// .Lglobal_entry_point:
|
||||
// # TOC pointer initalization.
|
||||
// ...
|
||||
// .Llocal_entry_point:
|
||||
// # load the __private_ss member of the threads tcbhead.
|
||||
// ld r0,-0x7000-64(r13)
|
||||
// # subtract the functions stack size from the stack pointer.
|
||||
// addis r12, r1, ha(-stack-frame size)
|
||||
// addi r12, r12, l(-stack-frame size)
|
||||
// # compare needed to actual and branch to allocate_more_stack if more
|
||||
// # space is needed, otherwise fallthrough to 'normal' function body.
|
||||
// cmpld cr7,r12,r0
|
||||
// blt- cr7, .Lallocate_more_stack
|
||||
//
|
||||
// -) The allocate_more_stack block might be placed after the split-stack
|
||||
// prologue and the `blt-` replaced with a `bge+ .Lnormal_func_body`
|
||||
// instead.
|
||||
// -) If either the addis or addi is not needed due to the stack size being
|
||||
// smaller then 32K or a multiple of 64K they will be replaced with a nop,
|
||||
// but there will always be 2 instructions the linker can overwrite for the
|
||||
// adjusted stack size.
|
||||
//
|
||||
// The linkers job here is to increase the stack size used in the addis/addi
|
||||
// pair by split-stack-size-adjust.
|
||||
// addis r12, r1, ha(-stack-frame size - split-stack-adjust-size)
|
||||
// addi r12, r12, l(-stack-frame size - split-stack-adjust-size)
|
||||
bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
|
||||
uint8_t StOther) const {
|
||||
// If the caller has a global entry point adjust the buffer past it. The start
|
||||
// of the split-stack prologue will be at the local entry point.
|
||||
Loc += getPPC64GlobalEntryToLocalEntryOffset(StOther);
|
||||
|
||||
// At the very least we expect to see a load of some split-stack data from the
|
||||
// tcb, and 2 instructions that calculate the ending stack address this
|
||||
// function will require. If there is not enough room for at least 3
|
||||
// instructions it can't be a split-stack prologue.
|
||||
if (Loc + 12 >= End)
|
||||
return false;
|
||||
|
||||
// First instruction must be `ld r0, -0x7000-64(r13)`
|
||||
if (read32(Loc) != 0xe80d8fc0)
|
||||
return false;
|
||||
|
||||
int16_t HiImm = 0;
|
||||
int16_t LoImm = 0;
|
||||
// First instruction can be either an addis if the frame size is larger then
|
||||
// 32K, or an addi if the size is less then 32K.
|
||||
int32_t FirstInstr = read32(Loc + 4);
|
||||
if (getPrimaryOpCode(FirstInstr) == 15) {
|
||||
HiImm = FirstInstr & 0xFFFF;
|
||||
} else if (getPrimaryOpCode(FirstInstr) == 14) {
|
||||
LoImm = FirstInstr & 0xFFFF;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Second instruction is either an addi or a nop. If the first instruction was
|
||||
// an addi then LoImm is set and the second instruction must be a nop.
|
||||
uint32_t SecondInstr = read32(Loc + 8);
|
||||
if (!LoImm && getPrimaryOpCode(SecondInstr) == 14) {
|
||||
LoImm = SecondInstr & 0xFFFF;
|
||||
} else if (SecondInstr != 0x60000000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The register operands of the first instruction should be the stack-pointer
|
||||
// (r1) as the input (RA) and r12 as the output (RT). If the second
|
||||
// instruction is not a nop, then it should use r12 as both input and output.
|
||||
auto CheckRegOperands = [](uint32_t Instr, uint8_t ExpectedRT,
|
||||
uint8_t ExpectedRA) {
|
||||
return ((Instr & 0x3E00000) >> 21 == ExpectedRT) &&
|
||||
((Instr & 0x1F0000) >> 16 == ExpectedRA);
|
||||
};
|
||||
if (!CheckRegOperands(FirstInstr, 12, 1))
|
||||
return false;
|
||||
if (SecondInstr != 0x60000000 && !CheckRegOperands(SecondInstr, 12, 12))
|
||||
return false;
|
||||
|
||||
int32_t StackFrameSize = (HiImm * 65536) + LoImm;
|
||||
// Check that the adjusted size doesn't overflow what we can represent with 2
|
||||
// instructions.
|
||||
if (StackFrameSize < Config->SplitStackAdjustSize + INT32_MIN) {
|
||||
error(getErrorLocation(Loc) + "split-stack prologue adjustment overflows");
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t AdjustedStackFrameSize =
|
||||
StackFrameSize - Config->SplitStackAdjustSize;
|
||||
|
||||
LoImm = AdjustedStackFrameSize & 0xFFFF;
|
||||
HiImm = (AdjustedStackFrameSize + 0x8000) >> 16;
|
||||
if (HiImm) {
|
||||
write32(Loc + 4, 0x3D810000 | (uint16_t)HiImm);
|
||||
// If the low immediate is zero the second instruction will be a nop.
|
||||
SecondInstr = LoImm ? 0x398C0000 | (uint16_t)LoImm : 0x60000000;
|
||||
write32(Loc + 8, SecondInstr);
|
||||
} else {
|
||||
// addi r12, r1, imm
|
||||
write32(Loc + 4, (0x39810000) | (uint16_t)LoImm);
|
||||
write32(Loc + 8, 0x60000000);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getPPC64TargetInfo() {
|
||||
static PPC64 Target;
|
||||
return &Target;
|
||||
|
279
contrib/llvm/tools/lld/ELF/Arch/RISCV.cpp
Normal file
279
contrib/llvm/tools/lld/ELF/Arch/RISCV.cpp
Normal file
@ -0,0 +1,279 @@
|
||||
//===- RISCV.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 "Target.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 RISCV final : public TargetInfo {
|
||||
public:
|
||||
RISCV();
|
||||
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;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
RISCV::RISCV() { NoneRel = R_RISCV_NONE; }
|
||||
|
||||
static uint32_t getEFlags(InputFile *F) {
|
||||
if (Config->Is64)
|
||||
return cast<ObjFile<ELF64LE>>(F)->getObj().getHeader()->e_flags;
|
||||
return cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags;
|
||||
}
|
||||
|
||||
uint32_t RISCV::calcEFlags() const {
|
||||
assert(!ObjectFiles.empty());
|
||||
|
||||
uint32_t Target = getEFlags(ObjectFiles.front());
|
||||
|
||||
for (InputFile *F : ObjectFiles) {
|
||||
uint32_t EFlags = getEFlags(F);
|
||||
if (EFlags & EF_RISCV_RVC)
|
||||
Target |= EF_RISCV_RVC;
|
||||
|
||||
if ((EFlags & EF_RISCV_FLOAT_ABI) != (Target & EF_RISCV_FLOAT_ABI))
|
||||
error(toString(F) +
|
||||
": cannot link object files with different floating-point ABI");
|
||||
|
||||
if ((EFlags & EF_RISCV_RVE) != (Target & EF_RISCV_RVE))
|
||||
error(toString(F) +
|
||||
": cannot link object files with different EF_RISCV_RVE");
|
||||
}
|
||||
|
||||
return Target;
|
||||
}
|
||||
|
||||
RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_RISCV_JAL:
|
||||
case R_RISCV_BRANCH:
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_PCREL_HI20:
|
||||
case R_RISCV_RVC_BRANCH:
|
||||
case R_RISCV_RVC_JUMP:
|
||||
case R_RISCV_32_PCREL:
|
||||
return R_PC;
|
||||
case R_RISCV_PCREL_LO12_I:
|
||||
case R_RISCV_PCREL_LO12_S:
|
||||
return R_RISCV_PC_INDIRECT;
|
||||
case R_RISCV_RELAX:
|
||||
case R_RISCV_ALIGN:
|
||||
return R_HINT;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63.
|
||||
static uint32_t extractBits(uint64_t V, uint32_t Begin, uint32_t End) {
|
||||
return (V & ((1ULL << (Begin + 1)) - 1)) >> End;
|
||||
}
|
||||
|
||||
void RISCV::relocateOne(uint8_t *Loc, const RelType Type,
|
||||
const uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_RISCV_32:
|
||||
write32le(Loc, Val);
|
||||
return;
|
||||
case R_RISCV_64:
|
||||
write64le(Loc, Val);
|
||||
return;
|
||||
|
||||
case R_RISCV_RVC_BRANCH: {
|
||||
checkInt(Loc, static_cast<int64_t>(Val) >> 1, 8, Type);
|
||||
checkAlignment(Loc, Val, 2, Type);
|
||||
uint16_t Insn = read16le(Loc) & 0xE383;
|
||||
uint16_t Imm8 = extractBits(Val, 8, 8) << 12;
|
||||
uint16_t Imm4_3 = extractBits(Val, 4, 3) << 10;
|
||||
uint16_t Imm7_6 = extractBits(Val, 7, 6) << 5;
|
||||
uint16_t Imm2_1 = extractBits(Val, 2, 1) << 3;
|
||||
uint16_t Imm5 = extractBits(Val, 5, 5) << 2;
|
||||
Insn |= Imm8 | Imm4_3 | Imm7_6 | Imm2_1 | Imm5;
|
||||
|
||||
write16le(Loc, Insn);
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_RVC_JUMP: {
|
||||
checkInt(Loc, static_cast<int64_t>(Val) >> 1, 11, Type);
|
||||
checkAlignment(Loc, Val, 2, Type);
|
||||
uint16_t Insn = read16le(Loc) & 0xE003;
|
||||
uint16_t Imm11 = extractBits(Val, 11, 11) << 12;
|
||||
uint16_t Imm4 = extractBits(Val, 4, 4) << 11;
|
||||
uint16_t Imm9_8 = extractBits(Val, 9, 8) << 9;
|
||||
uint16_t Imm10 = extractBits(Val, 10, 10) << 8;
|
||||
uint16_t Imm6 = extractBits(Val, 6, 6) << 7;
|
||||
uint16_t Imm7 = extractBits(Val, 7, 7) << 6;
|
||||
uint16_t Imm3_1 = extractBits(Val, 3, 1) << 3;
|
||||
uint16_t Imm5 = extractBits(Val, 5, 5) << 2;
|
||||
Insn |= Imm11 | Imm4 | Imm9_8 | Imm10 | Imm6 | Imm7 | Imm3_1 | Imm5;
|
||||
|
||||
write16le(Loc, Insn);
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_RVC_LUI: {
|
||||
int32_t Imm = ((Val + 0x800) >> 12);
|
||||
checkUInt(Loc, Imm, 6, Type);
|
||||
if (Imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0`
|
||||
write16le(Loc, (read16le(Loc) & 0x0F83) | 0x4000);
|
||||
} else {
|
||||
uint16_t Imm17 = extractBits(Val + 0x800, 17, 17) << 12;
|
||||
uint16_t Imm16_12 = extractBits(Val + 0x800, 16, 12) << 2;
|
||||
write16le(Loc, (read16le(Loc) & 0xEF83) | Imm17 | Imm16_12);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_JAL: {
|
||||
checkInt(Loc, static_cast<int64_t>(Val) >> 1, 20, Type);
|
||||
checkAlignment(Loc, Val, 2, Type);
|
||||
|
||||
uint32_t Insn = read32le(Loc) & 0xFFF;
|
||||
uint32_t Imm20 = extractBits(Val, 20, 20) << 31;
|
||||
uint32_t Imm10_1 = extractBits(Val, 10, 1) << 21;
|
||||
uint32_t Imm11 = extractBits(Val, 11, 11) << 20;
|
||||
uint32_t Imm19_12 = extractBits(Val, 19, 12) << 12;
|
||||
Insn |= Imm20 | Imm10_1 | Imm11 | Imm19_12;
|
||||
|
||||
write32le(Loc, Insn);
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_BRANCH: {
|
||||
checkInt(Loc, static_cast<int64_t>(Val) >> 1, 12, Type);
|
||||
checkAlignment(Loc, Val, 2, Type);
|
||||
|
||||
uint32_t Insn = read32le(Loc) & 0x1FFF07F;
|
||||
uint32_t Imm12 = extractBits(Val, 12, 12) << 31;
|
||||
uint32_t Imm10_5 = extractBits(Val, 10, 5) << 25;
|
||||
uint32_t Imm4_1 = extractBits(Val, 4, 1) << 8;
|
||||
uint32_t Imm11 = extractBits(Val, 11, 11) << 7;
|
||||
Insn |= Imm12 | Imm10_5 | Imm4_1 | Imm11;
|
||||
|
||||
write32le(Loc, Insn);
|
||||
return;
|
||||
}
|
||||
|
||||
// auipc + jalr pair
|
||||
case R_RISCV_CALL: {
|
||||
checkInt(Loc, Val, 32, Type);
|
||||
if (isInt<32>(Val)) {
|
||||
relocateOne(Loc, R_RISCV_PCREL_HI20, Val);
|
||||
relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_PCREL_HI20:
|
||||
case R_RISCV_HI20: {
|
||||
checkInt(Loc, Val, 32, Type);
|
||||
uint32_t Hi = Val + 0x800;
|
||||
write32le(Loc, (read32le(Loc) & 0xFFF) | (Hi & 0xFFFFF000));
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_PCREL_LO12_I:
|
||||
case R_RISCV_LO12_I: {
|
||||
checkInt(Loc, Val, 32, Type);
|
||||
uint32_t Hi = Val + 0x800;
|
||||
uint32_t Lo = Val - (Hi & 0xFFFFF000);
|
||||
write32le(Loc, (read32le(Loc) & 0xFFFFF) | ((Lo & 0xFFF) << 20));
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_PCREL_LO12_S:
|
||||
case R_RISCV_LO12_S: {
|
||||
checkInt(Loc, Val, 32, Type);
|
||||
uint32_t Hi = Val + 0x800;
|
||||
uint32_t Lo = Val - (Hi & 0xFFFFF000);
|
||||
uint32_t Imm11_5 = extractBits(Lo, 11, 5) << 25;
|
||||
uint32_t Imm4_0 = extractBits(Lo, 4, 0) << 7;
|
||||
write32le(Loc, (read32le(Loc) & 0x1FFF07F) | Imm11_5 | Imm4_0);
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_ADD8:
|
||||
*Loc += Val;
|
||||
return;
|
||||
case R_RISCV_ADD16:
|
||||
write16le(Loc, read16le(Loc) + Val);
|
||||
return;
|
||||
case R_RISCV_ADD32:
|
||||
write32le(Loc, read32le(Loc) + Val);
|
||||
return;
|
||||
case R_RISCV_ADD64:
|
||||
write64le(Loc, read64le(Loc) + Val);
|
||||
return;
|
||||
case R_RISCV_SUB6:
|
||||
*Loc = (*Loc & 0xc0) | (((*Loc & 0x3f) - Val) & 0x3f);
|
||||
return;
|
||||
case R_RISCV_SUB8:
|
||||
*Loc -= Val;
|
||||
return;
|
||||
case R_RISCV_SUB16:
|
||||
write16le(Loc, read16le(Loc) - Val);
|
||||
return;
|
||||
case R_RISCV_SUB32:
|
||||
write32le(Loc, read32le(Loc) - Val);
|
||||
return;
|
||||
case R_RISCV_SUB64:
|
||||
write64le(Loc, read64le(Loc) - Val);
|
||||
return;
|
||||
case R_RISCV_SET6:
|
||||
*Loc = (*Loc & 0xc0) | (Val & 0x3f);
|
||||
return;
|
||||
case R_RISCV_SET8:
|
||||
*Loc = Val;
|
||||
return;
|
||||
case R_RISCV_SET16:
|
||||
write16le(Loc, Val);
|
||||
return;
|
||||
case R_RISCV_SET32:
|
||||
case R_RISCV_32_PCREL:
|
||||
write32le(Loc, Val);
|
||||
return;
|
||||
|
||||
case R_RISCV_ALIGN:
|
||||
case R_RISCV_RELAX:
|
||||
return; // Ignored (for now)
|
||||
case R_RISCV_NONE:
|
||||
return; // Do nothing
|
||||
|
||||
// These are handled by the dynamic linker
|
||||
case R_RISCV_RELATIVE:
|
||||
case R_RISCV_COPY:
|
||||
case R_RISCV_JUMP_SLOT:
|
||||
// GP-relative relocations are only produced after relaxation, which
|
||||
// we don't support for now
|
||||
case R_RISCV_GPREL_I:
|
||||
case R_RISCV_GPREL_S:
|
||||
default:
|
||||
error(getErrorLocation(Loc) +
|
||||
"unimplemented relocation: " + toString(Type));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getRISCVTargetInfo() {
|
||||
static RISCV Target;
|
||||
return &Target;
|
||||
}
|
@ -35,6 +35,7 @@ public:
|
||||
SPARCV9::SPARCV9() {
|
||||
CopyRel = R_SPARC_COPY;
|
||||
GotRel = R_SPARC_GLOB_DAT;
|
||||
NoneRel = R_SPARC_NONE;
|
||||
PltRel = R_SPARC_JMP_SLOT;
|
||||
RelativeRel = R_SPARC_RELATIVE;
|
||||
GotEntrySize = 8;
|
||||
|
@ -48,6 +48,7 @@ public:
|
||||
X86::X86() {
|
||||
CopyRel = R_386_COPY;
|
||||
GotRel = R_386_GLOB_DAT;
|
||||
NoneRel = R_386_NONE;
|
||||
PltRel = R_386_JUMP_SLOT;
|
||||
IRelativeRel = R_386_IRELATIVE;
|
||||
RelativeRel = R_386_RELATIVE;
|
||||
@ -59,7 +60,7 @@ X86::X86() {
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 16;
|
||||
TlsGdRelaxSkip = 2;
|
||||
TrapInstr = 0xcccccccc; // 0xcc = INT3
|
||||
TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
|
||||
|
||||
// Align to the non-PAE large page size (known as a superpage or huge page).
|
||||
// FreeBSD automatically promotes large, superpage-aligned allocations.
|
||||
@ -156,7 +157,7 @@ RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
}
|
||||
|
||||
void X86::writeGotPltHeader(uint8_t *Buf) const {
|
||||
write32le(Buf, InX::Dynamic->getVA());
|
||||
write32le(Buf, In.Dynamic->getVA());
|
||||
}
|
||||
|
||||
void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
|
||||
@ -187,8 +188,8 @@ void X86::writePltHeader(uint8_t *Buf) const {
|
||||
};
|
||||
memcpy(Buf, V, sizeof(V));
|
||||
|
||||
uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
|
||||
uint32_t GotPlt = InX::GotPlt->getVA() - Ebx;
|
||||
uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
|
||||
uint32_t GotPlt = In.GotPlt->getVA() - Ebx;
|
||||
write32le(Buf + 2, GotPlt + 4);
|
||||
write32le(Buf + 8, GotPlt + 8);
|
||||
return;
|
||||
@ -200,7 +201,7 @@ void X86::writePltHeader(uint8_t *Buf) const {
|
||||
0x90, 0x90, 0x90, 0x90, // nop
|
||||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
uint32_t GotPlt = InX::GotPlt->getVA();
|
||||
uint32_t GotPlt = In.GotPlt->getVA();
|
||||
write32le(Buf + 2, GotPlt + 4);
|
||||
write32le(Buf + 8, GotPlt + 8);
|
||||
}
|
||||
@ -217,7 +218,7 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
|
||||
if (Config->Pic) {
|
||||
// jmp *foo@GOT(%ebx)
|
||||
uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
|
||||
uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
|
||||
Buf[1] = 0xa3;
|
||||
write32le(Buf + 2, GotPltEntryAddr - Ebx);
|
||||
} else {
|
||||
@ -451,8 +452,8 @@ void RetpolinePic::writePltHeader(uint8_t *Buf) const {
|
||||
};
|
||||
memcpy(Buf, Insn, sizeof(Insn));
|
||||
|
||||
uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
|
||||
uint32_t GotPlt = InX::GotPlt->getVA() - Ebx;
|
||||
uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
|
||||
uint32_t GotPlt = In.GotPlt->getVA() - Ebx;
|
||||
write32le(Buf + 2, GotPlt + 4);
|
||||
write32le(Buf + 9, GotPlt + 8);
|
||||
}
|
||||
@ -471,7 +472,7 @@ void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
};
|
||||
memcpy(Buf, Insn, sizeof(Insn));
|
||||
|
||||
uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
|
||||
uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
|
||||
unsigned Off = getPltEntryOffset(Index);
|
||||
write32le(Buf + 3, GotPltEntryAddr - Ebx);
|
||||
write32le(Buf + 8, -Off - 12 + 32);
|
||||
@ -510,7 +511,7 @@ void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
|
||||
};
|
||||
memcpy(Buf, Insn, sizeof(Insn));
|
||||
|
||||
uint32_t GotPlt = InX::GotPlt->getVA();
|
||||
uint32_t GotPlt = In.GotPlt->getVA();
|
||||
write32le(Buf + 2, GotPlt + 4);
|
||||
write32le(Buf + 8, GotPlt + 8);
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ public:
|
||||
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;
|
||||
bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
|
||||
uint8_t StOther) const override;
|
||||
|
||||
private:
|
||||
void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
|
||||
@ -55,6 +55,7 @@ private:
|
||||
template <class ELFT> X86_64<ELFT>::X86_64() {
|
||||
CopyRel = R_X86_64_COPY;
|
||||
GotRel = R_X86_64_GLOB_DAT;
|
||||
NoneRel = R_X86_64_NONE;
|
||||
PltRel = R_X86_64_JUMP_SLOT;
|
||||
RelativeRel = R_X86_64_RELATIVE;
|
||||
IRelativeRel = R_X86_64_IRELATIVE;
|
||||
@ -66,7 +67,7 @@ template <class ELFT> X86_64<ELFT>::X86_64() {
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 16;
|
||||
TlsGdRelaxSkip = 2;
|
||||
TrapInstr = 0xcccccccc; // 0xcc = INT3
|
||||
TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
|
||||
|
||||
// Align to the large page size (known as a superpage or huge page).
|
||||
// FreeBSD automatically promotes large, superpage-aligned allocations.
|
||||
@ -124,7 +125,7 @@ template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const {
|
||||
// required, but it is documented in the psabi and the glibc dynamic linker
|
||||
// seems to use it (note that this is relevant for linking ld.so, not any
|
||||
// other program).
|
||||
write64le(Buf, InX::Dynamic->getVA());
|
||||
write64le(Buf, In.Dynamic->getVA());
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
@ -140,8 +141,8 @@ template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const {
|
||||
0x0f, 0x1f, 0x40, 0x00, // nop
|
||||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
uint64_t GotPlt = InX::GotPlt->getVA();
|
||||
uint64_t Plt = InX::Plt->getVA();
|
||||
uint64_t GotPlt = In.GotPlt->getVA();
|
||||
uint64_t Plt = In.Plt->getVA();
|
||||
write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8
|
||||
write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16
|
||||
}
|
||||
@ -481,23 +482,27 @@ namespace {
|
||||
// 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 {
|
||||
uint8_t *End,
|
||||
uint8_t StOther) const {
|
||||
if (Loc + 8 >= End)
|
||||
return false;
|
||||
|
||||
// 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) {
|
||||
if (memcmp(Loc, "\x64\x48\x3b\x24\x25", 5) == 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);
|
||||
// Adjust "lea X(%rsp),%rYY" to lea "(X - 0x4000)(%rsp),%rYY" where rYY could
|
||||
// be r10 or r11. The lea instruction feeds a subsequent compare which checks
|
||||
// if there is X available stack space. Making X larger effectively reserves
|
||||
// that much additional space. The stack grows downward so subtract the value.
|
||||
if (memcmp(Loc, "\x4c\x8d\x94\x24", 4) == 0 ||
|
||||
memcmp(Loc, "\x4c\x8d\x9c\x24", 4) == 0) {
|
||||
// The offset bytes are encoded four bytes after the start of the
|
||||
// instruction.
|
||||
write32le(Loc + 4, read32le(Loc + 4) - 0x4000);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -505,7 +510,8 @@ bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
|
||||
|
||||
template <>
|
||||
bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
|
||||
uint8_t *End) const {
|
||||
uint8_t *End,
|
||||
uint8_t StOther) const {
|
||||
llvm_unreachable("Target doesn't support split stacks.");
|
||||
}
|
||||
|
||||
@ -566,8 +572,8 @@ template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const {
|
||||
};
|
||||
memcpy(Buf, Insn, sizeof(Insn));
|
||||
|
||||
uint64_t GotPlt = InX::GotPlt->getVA();
|
||||
uint64_t Plt = InX::Plt->getVA();
|
||||
uint64_t GotPlt = In.GotPlt->getVA();
|
||||
uint64_t Plt = In.Plt->getVA();
|
||||
write32le(Buf + 2, GotPlt - Plt - 6 + 8);
|
||||
write32le(Buf + 9, GotPlt - Plt - 13 + 16);
|
||||
}
|
||||
@ -586,7 +592,7 @@ void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
};
|
||||
memcpy(Buf, Insn, sizeof(Insn));
|
||||
|
||||
uint64_t Off = TargetInfo::getPltEntryOffset(Index);
|
||||
uint64_t Off = getPltEntryOffset(Index);
|
||||
|
||||
write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
|
||||
write32le(Buf + 8, -Off - 12 + 32);
|
||||
@ -629,7 +635,7 @@ void RetpolineZNow<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
memcpy(Buf, Insn, sizeof(Insn));
|
||||
|
||||
write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
|
||||
write32le(Buf + 8, -TargetInfo::getPltEntryOffset(Index) - 12);
|
||||
write32le(Buf + 8, -getPltEntryOffset(Index) - 12);
|
||||
}
|
||||
|
||||
template <class ELFT> static TargetInfo *getTargetInfo() {
|
||||
|
@ -15,17 +15,19 @@ add_lld_library(lldELF
|
||||
Arch/Hexagon.cpp
|
||||
Arch/Mips.cpp
|
||||
Arch/MipsArchTree.cpp
|
||||
Arch/MSP430.cpp
|
||||
Arch/PPC.cpp
|
||||
Arch/PPC64.cpp
|
||||
Arch/RISCV.cpp
|
||||
Arch/SPARCV9.cpp
|
||||
Arch/X86.cpp
|
||||
Arch/X86_64.cpp
|
||||
CallGraphSort.cpp
|
||||
DWARF.cpp
|
||||
Driver.cpp
|
||||
DriverUtils.cpp
|
||||
EhFrame.cpp
|
||||
Filesystem.cpp
|
||||
GdbIndex.cpp
|
||||
ICF.cpp
|
||||
InputFiles.cpp
|
||||
InputSection.cpp
|
||||
|
@ -57,10 +57,7 @@ struct Edge {
|
||||
};
|
||||
|
||||
struct Cluster {
|
||||
Cluster(int Sec, size_t S) {
|
||||
Sections.push_back(Sec);
|
||||
Size = S;
|
||||
}
|
||||
Cluster(int Sec, size_t S) : Sections{Sec}, Size(S) {}
|
||||
|
||||
double getDensity() const {
|
||||
if (Size == 0)
|
||||
@ -72,7 +69,7 @@ struct Cluster {
|
||||
size_t Size = 0;
|
||||
uint64_t Weight = 0;
|
||||
uint64_t InitialWeight = 0;
|
||||
std::vector<Edge> Preds;
|
||||
Edge BestPred = {-1, 0};
|
||||
};
|
||||
|
||||
class CallGraphSort {
|
||||
@ -96,12 +93,14 @@ constexpr int MAX_DENSITY_DEGRADATION = 8;
|
||||
constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024;
|
||||
} // end anonymous namespace
|
||||
|
||||
typedef std::pair<const InputSectionBase *, const InputSectionBase *>
|
||||
SectionPair;
|
||||
|
||||
// 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;
|
||||
MapVector<SectionPair, uint64_t> &Profile = Config->CallGraphProfile;
|
||||
DenseMap<const InputSectionBase *, int> SecToCluster;
|
||||
|
||||
auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int {
|
||||
@ -114,7 +113,7 @@ CallGraphSort::CallGraphSort() {
|
||||
};
|
||||
|
||||
// Create the graph.
|
||||
for (const auto &C : Profile) {
|
||||
for (std::pair<SectionPair, uint64_t> &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;
|
||||
@ -136,8 +135,12 @@ CallGraphSort::CallGraphSort() {
|
||||
if (From == To)
|
||||
continue;
|
||||
|
||||
// Add an edge
|
||||
Clusters[To].Preds.push_back({From, Weight});
|
||||
// Remember the best edge.
|
||||
Cluster &ToC = Clusters[To];
|
||||
if (ToC.BestPred.From == -1 || ToC.BestPred.Weight < Weight) {
|
||||
ToC.BestPred.From = From;
|
||||
ToC.BestPred.Weight = Weight;
|
||||
}
|
||||
}
|
||||
for (Cluster &C : Clusters)
|
||||
C.InitialWeight = C.Weight;
|
||||
@ -146,9 +149,7 @@ CallGraphSort::CallGraphSort() {
|
||||
// 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;
|
||||
return NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION;
|
||||
}
|
||||
|
||||
static void mergeClusters(Cluster &Into, Cluster &From) {
|
||||
@ -167,9 +168,9 @@ 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];
|
||||
for (size_t I = 0; I < Clusters.size(); ++I) {
|
||||
SortedSecs[I] = I;
|
||||
SecToCluster[I] = &Clusters[I];
|
||||
}
|
||||
|
||||
std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) {
|
||||
@ -181,21 +182,11 @@ void CallGraphSort::groupClusters() {
|
||||
// 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)
|
||||
// Don't consider merging if the edge is unlikely.
|
||||
if (C.BestPred.From == -1 || C.BestPred.Weight * 10 <= C.InitialWeight)
|
||||
continue;
|
||||
|
||||
Cluster *PredC = SecToCluster[BestPred];
|
||||
Cluster *PredC = SecToCluster[C.BestPred.From];
|
||||
if (PredC == &C)
|
||||
continue;
|
||||
|
||||
@ -229,7 +220,7 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
|
||||
groupClusters();
|
||||
|
||||
// Generate order.
|
||||
llvm::DenseMap<const InputSectionBase *, int> OrderMap;
|
||||
DenseMap<const InputSectionBase *, int> OrderMap;
|
||||
ssize_t CurOrder = 1;
|
||||
|
||||
for (const Cluster &C : Clusters)
|
||||
|
@ -47,7 +47,7 @@ enum class ICFLevel { None, Safe, All };
|
||||
enum class StripPolicy { None, All, Debug };
|
||||
|
||||
// For --unresolved-symbols.
|
||||
enum class UnresolvedPolicy { ReportError, Warn, Ignore, IgnoreAll };
|
||||
enum class UnresolvedPolicy { ReportError, Warn, Ignore };
|
||||
|
||||
// For --orphan-handling.
|
||||
enum class OrphanHandlingPolicy { Place, Warn, Error };
|
||||
@ -127,6 +127,7 @@ struct Configuration {
|
||||
bool AsNeeded = false;
|
||||
bool Bsymbolic;
|
||||
bool BsymbolicFunctions;
|
||||
bool CallGraphProfileSort;
|
||||
bool CheckSections;
|
||||
bool CompressDebugSections;
|
||||
bool Cref;
|
||||
@ -134,11 +135,13 @@ struct Configuration {
|
||||
bool Demangle = true;
|
||||
bool DisableVerify;
|
||||
bool EhFrameHdr;
|
||||
bool EmitLLVM;
|
||||
bool EmitRelocs;
|
||||
bool EnableNewDtags;
|
||||
bool ExecuteOnly;
|
||||
bool ExportDynamic;
|
||||
bool FixCortexA53Errata843419;
|
||||
bool FormatBinary = false;
|
||||
bool GcSections;
|
||||
bool GdbIndex;
|
||||
bool GnuHash = false;
|
||||
@ -170,21 +173,25 @@ struct Configuration {
|
||||
bool Trace;
|
||||
bool ThinLTOEmitImportsFiles;
|
||||
bool ThinLTOIndexOnly;
|
||||
bool TocOptimize;
|
||||
bool UndefinedVersion;
|
||||
bool UseAndroidRelrTags = false;
|
||||
bool WarnBackrefs;
|
||||
bool WarnCommon;
|
||||
bool WarnIfuncTextrel;
|
||||
bool WarnMissingEntry;
|
||||
bool WarnSymbolOrdering;
|
||||
bool WriteAddends;
|
||||
bool ZCombreloc;
|
||||
bool ZCopyreloc;
|
||||
bool ZExecstack;
|
||||
bool ZGlobal;
|
||||
bool ZHazardplt;
|
||||
bool ZIfuncnoplt;
|
||||
bool ZInitfirst;
|
||||
bool ZInterpose;
|
||||
bool ZKeepTextSectionPrefix;
|
||||
bool ZNodefaultlib;
|
||||
bool ZNodelete;
|
||||
bool ZNodlopen;
|
||||
bool ZNow;
|
||||
@ -214,6 +221,7 @@ struct Configuration {
|
||||
unsigned LTOO;
|
||||
unsigned Optimize;
|
||||
unsigned ThinLTOJobs;
|
||||
int32_t SplitStackAdjustSize;
|
||||
|
||||
// The following config options do not directly correspond to any
|
||||
// particualr command line options.
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===- GdbIndex.cpp -------------------------------------------------------===//
|
||||
//===- DWARF.cpp ----------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
@ -14,8 +14,9 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "GdbIndex.h"
|
||||
#include "DWARF.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
@ -29,24 +30,28 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) {
|
||||
for (InputSectionBase *Sec : Obj->getSections()) {
|
||||
if (!Sec)
|
||||
continue;
|
||||
if (LLDDWARFSection *M = StringSwitch<LLDDWARFSection *>(Sec->Name)
|
||||
.Case(".debug_info", &InfoSection)
|
||||
.Case(".debug_ranges", &RangeSection)
|
||||
.Case(".debug_line", &LineSection)
|
||||
.Default(nullptr)) {
|
||||
Sec->maybeDecompress();
|
||||
M->Data = toStringRef(Sec->Data);
|
||||
|
||||
if (LLDDWARFSection *M =
|
||||
StringSwitch<LLDDWARFSection *>(Sec->Name)
|
||||
.Case(".debug_addr", &AddrSection)
|
||||
.Case(".debug_gnu_pubnames", &GnuPubNamesSection)
|
||||
.Case(".debug_gnu_pubtypes", &GnuPubTypesSection)
|
||||
.Case(".debug_info", &InfoSection)
|
||||
.Case(".debug_ranges", &RangeSection)
|
||||
.Case(".debug_rnglists", &RngListsSection)
|
||||
.Case(".debug_line", &LineSection)
|
||||
.Default(nullptr)) {
|
||||
M->Data = toStringRef(Sec->data());
|
||||
M->Sec = Sec;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Sec->Name == ".debug_abbrev")
|
||||
AbbrevSection = toStringRef(Sec->Data);
|
||||
else if (Sec->Name == ".debug_gnu_pubnames")
|
||||
GnuPubNamesSection = toStringRef(Sec->Data);
|
||||
else if (Sec->Name == ".debug_gnu_pubtypes")
|
||||
GnuPubTypesSection = toStringRef(Sec->Data);
|
||||
AbbrevSection = toStringRef(Sec->data());
|
||||
else if (Sec->Name == ".debug_str")
|
||||
StrSection = toStringRef(Sec->Data);
|
||||
StrSection = toStringRef(Sec->data());
|
||||
else if (Sec->Name == ".debug_line_str")
|
||||
LineStringSection = toStringRef(Sec->data());
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +78,10 @@ LLDDwarfObj<ELFT>::findAux(const InputSectionBase &Sec, uint64_t Pos,
|
||||
// Broken debug info can point to a non-Defined symbol.
|
||||
auto *DR = dyn_cast<Defined>(&File->getRelocTargetSym(Rel));
|
||||
if (!DR) {
|
||||
error("unsupported relocation target while parsing debug info");
|
||||
RelType Type = Rel.getType(Config->IsMips64EL);
|
||||
if (Type != Target->NoneRel)
|
||||
error(toString(File) + ": relocation " + lld::toString(Type) + " at 0x" +
|
||||
llvm::utohexstr(Rel.r_offset) + " has unsupported target");
|
||||
return None;
|
||||
}
|
||||
uint64_t Val = DR->Value + getAddend<ELFT>(Rel);
|
@ -1,4 +1,4 @@
|
||||
//===- GdbIndex.h --------------------------------------------*- C++ -*-===//
|
||||
//===- DWARF.h -----------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
@ -7,10 +7,11 @@
|
||||
//
|
||||
//===-------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_GDB_INDEX_H
|
||||
#define LLD_ELF_GDB_INDEX_H
|
||||
#ifndef LLD_ELF_DWARF_H
|
||||
#define LLD_ELF_DWARF_H
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
@ -24,44 +25,66 @@ struct LLDDWARFSection final : public llvm::DWARFSection {
|
||||
};
|
||||
|
||||
template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
|
||||
LLDDWARFSection InfoSection;
|
||||
LLDDWARFSection RangeSection;
|
||||
LLDDWARFSection LineSection;
|
||||
StringRef AbbrevSection;
|
||||
StringRef GnuPubNamesSection;
|
||||
StringRef GnuPubTypesSection;
|
||||
StringRef StrSection;
|
||||
public:
|
||||
explicit LLDDwarfObj(ObjFile<ELFT> *Obj);
|
||||
|
||||
void forEachInfoSections(
|
||||
llvm::function_ref<void(const llvm::DWARFSection &)> F) const override {
|
||||
F(InfoSection);
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getRangeSection() const override {
|
||||
return RangeSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getRnglistsSection() const override {
|
||||
return RngListsSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getLineSection() const override {
|
||||
return LineSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getAddrSection() const override {
|
||||
return AddrSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getGnuPubNamesSection() const override {
|
||||
return GnuPubNamesSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getGnuPubTypesSection() const override {
|
||||
return GnuPubTypesSection;
|
||||
}
|
||||
|
||||
StringRef getFileName() const override { return ""; }
|
||||
StringRef getAbbrevSection() const override { return AbbrevSection; }
|
||||
StringRef getStringSection() const override { return StrSection; }
|
||||
StringRef getLineStringSection() const override { return LineStringSection; }
|
||||
|
||||
bool isLittleEndian() const override {
|
||||
return ELFT::TargetEndianness == llvm::support::little;
|
||||
}
|
||||
|
||||
llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &Sec,
|
||||
uint64_t Pos) const override;
|
||||
|
||||
private:
|
||||
template <class RelTy>
|
||||
llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec,
|
||||
uint64_t Pos,
|
||||
ArrayRef<RelTy> Rels) const;
|
||||
|
||||
public:
|
||||
explicit LLDDwarfObj(ObjFile<ELFT> *Obj);
|
||||
const llvm::DWARFSection &getInfoSection() const override {
|
||||
return InfoSection;
|
||||
}
|
||||
const llvm::DWARFSection &getRangeSection() const override {
|
||||
return RangeSection;
|
||||
}
|
||||
const llvm::DWARFSection &getLineSection() const override {
|
||||
return LineSection;
|
||||
}
|
||||
StringRef getFileName() const override { return ""; }
|
||||
StringRef getAbbrevSection() const override { return AbbrevSection; }
|
||||
StringRef getStringSection() const override { return StrSection; }
|
||||
StringRef getGnuPubNamesSection() const override {
|
||||
return GnuPubNamesSection;
|
||||
}
|
||||
StringRef getGnuPubTypesSection() const override {
|
||||
return GnuPubTypesSection;
|
||||
}
|
||||
bool isLittleEndian() const override {
|
||||
return ELFT::TargetEndianness == llvm::support::little;
|
||||
}
|
||||
llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &Sec,
|
||||
uint64_t Pos) const override;
|
||||
LLDDWARFSection GnuPubNamesSection;
|
||||
LLDDWARFSection GnuPubTypesSection;
|
||||
LLDDWARFSection InfoSection;
|
||||
LLDDWARFSection RangeSection;
|
||||
LLDDWARFSection RngListsSection;
|
||||
LLDDWARFSection LineSection;
|
||||
LLDDWARFSection AddrSection;
|
||||
StringRef AbbrevSection;
|
||||
StringRef StrSection;
|
||||
StringRef LineStringSection;
|
||||
};
|
||||
|
||||
} // namespace elf
|
@ -63,6 +63,7 @@ using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::sys;
|
||||
using namespace llvm::support;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
@ -74,7 +75,7 @@ static void setConfigs(opt::InputArgList &Args);
|
||||
|
||||
bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
|
||||
raw_ostream &Error) {
|
||||
errorHandler().LogName = sys::path::filename(Args[0]);
|
||||
errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
|
||||
errorHandler().ErrorLimitExceededMsg =
|
||||
"too many errors emitted, stopping now (use "
|
||||
"-error-limit=0 to see all errors)";
|
||||
@ -84,7 +85,6 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
|
||||
|
||||
InputSections.clear();
|
||||
OutputSections.clear();
|
||||
Tar = nullptr;
|
||||
BinaryFiles.clear();
|
||||
BitcodeFiles.clear();
|
||||
ObjectFiles.clear();
|
||||
@ -94,6 +94,10 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
|
||||
Driver = make<LinkerDriver>();
|
||||
Script = make<LinkerScript>();
|
||||
Symtab = make<SymbolTable>();
|
||||
|
||||
Tar = nullptr;
|
||||
memset(&In, 0, sizeof(In));
|
||||
|
||||
Config->ProgName = Args[0];
|
||||
|
||||
Driver->main(Args);
|
||||
@ -125,9 +129,11 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
|
||||
.Case("elf32_x86_64", {ELF32LEKind, EM_X86_64})
|
||||
.Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS})
|
||||
.Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
|
||||
.Case("elf32lriscv", {ELF32LEKind, EM_RISCV})
|
||||
.Case("elf32ppc", {ELF32BEKind, EM_PPC})
|
||||
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
|
||||
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
|
||||
.Case("elf64lriscv", {ELF64LEKind, EM_RISCV})
|
||||
.Case("elf64ppc", {ELF64BEKind, EM_PPC64})
|
||||
.Case("elf64lppc", {ELF64LEKind, EM_PPC64})
|
||||
.Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64})
|
||||
@ -183,7 +189,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
|
||||
return;
|
||||
MemoryBufferRef MBRef = *Buffer;
|
||||
|
||||
if (InBinary) {
|
||||
if (Config->FormatBinary) {
|
||||
Files.push_back(make<BinaryFile>(MBRef));
|
||||
return;
|
||||
}
|
||||
@ -218,7 +224,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
|
||||
return;
|
||||
}
|
||||
case file_magic::elf_shared_object:
|
||||
if (Config->Relocatable) {
|
||||
if (Config->Static || Config->Relocatable) {
|
||||
error("attempted static link of dynamic object " + Path);
|
||||
return;
|
||||
}
|
||||
@ -269,14 +275,17 @@ static void initLLVM() {
|
||||
|
||||
// Some command line options or some combinations of them are not allowed.
|
||||
// This function checks for such errors.
|
||||
static void checkOptions(opt::InputArgList &Args) {
|
||||
static void checkOptions() {
|
||||
// The MIPS ABI as of 2016 does not support the GNU-style symbol lookup
|
||||
// table which is a relatively new feature.
|
||||
if (Config->EMachine == EM_MIPS && Config->GnuHash)
|
||||
error("the .gnu.hash section is not compatible with the MIPS target.");
|
||||
error("the .gnu.hash section is not compatible with the MIPS target");
|
||||
|
||||
if (Config->FixCortexA53Errata843419 && Config->EMachine != EM_AARCH64)
|
||||
error("--fix-cortex-a53-843419 is only supported on AArch64 targets.");
|
||||
error("--fix-cortex-a53-843419 is only supported on AArch64 targets");
|
||||
|
||||
if (Config->TocOptimize && Config->EMachine != EM_PPC64)
|
||||
error("--toc-optimize is only supported on the PowerPC64 target");
|
||||
|
||||
if (Config->Pie && Config->Shared)
|
||||
error("-shared and -pie may not be used together");
|
||||
@ -336,13 +345,14 @@ static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2,
|
||||
return Default;
|
||||
}
|
||||
|
||||
static bool isKnown(StringRef S) {
|
||||
static bool isKnownZFlag(StringRef S) {
|
||||
return S == "combreloc" || S == "copyreloc" || S == "defs" ||
|
||||
S == "execstack" || S == "hazardplt" || S == "ifunc-noplt" ||
|
||||
S == "execstack" || S == "global" || S == "hazardplt" ||
|
||||
S == "ifunc-noplt" ||
|
||||
S == "initfirst" || S == "interpose" ||
|
||||
S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" ||
|
||||
S == "nocombreloc" || S == "nocopyreloc" || S == "nodelete" ||
|
||||
S == "nodlopen" || S == "noexecstack" ||
|
||||
S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" ||
|
||||
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" ||
|
||||
@ -352,7 +362,7 @@ static bool isKnown(StringRef S) {
|
||||
// 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()))
|
||||
if (!isKnownZFlag(Arg->getValue()))
|
||||
error("unknown -z value: " + StringRef(Arg->getValue()));
|
||||
}
|
||||
|
||||
@ -387,6 +397,23 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
|
||||
if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version))
|
||||
message(getLLDVersion() + " (compatible with GNU linkers)");
|
||||
|
||||
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.
|
||||
Expected<std::unique_ptr<TarWriter>> ErrOrWriter =
|
||||
TarWriter::create(Path, path::stem(Path));
|
||||
if (ErrOrWriter) {
|
||||
Tar = std::move(*ErrOrWriter);
|
||||
Tar->append("response.txt", createResponseFile(Args));
|
||||
Tar->append("version.txt", getLLDVersion() + "\n");
|
||||
} else {
|
||||
error("--reproduce: " + toString(ErrOrWriter.takeError()));
|
||||
}
|
||||
}
|
||||
|
||||
readConfigs(Args);
|
||||
checkZOptions(Args);
|
||||
|
||||
// The behavior of -v or --version is a bit strange, but this is
|
||||
// needed for compatibility with GNU linkers.
|
||||
if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT))
|
||||
@ -394,24 +421,6 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
|
||||
if (Args.hasArg(OPT_version))
|
||||
return;
|
||||
|
||||
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.
|
||||
Expected<std::unique_ptr<TarWriter>> ErrOrWriter =
|
||||
TarWriter::create(Path, path::stem(Path));
|
||||
if (ErrOrWriter) {
|
||||
Tar = ErrOrWriter->get();
|
||||
Tar->append("response.txt", createResponseFile(Args));
|
||||
Tar->append("version.txt", getLLDVersion() + "\n");
|
||||
make<std::unique_ptr<TarWriter>>(std::move(*ErrOrWriter));
|
||||
} else {
|
||||
error(Twine("--reproduce: failed to open ") + Path + ": " +
|
||||
toString(ErrOrWriter.takeError()));
|
||||
}
|
||||
}
|
||||
|
||||
readConfigs(Args);
|
||||
checkZOptions(Args);
|
||||
initLLVM();
|
||||
createFiles(Args);
|
||||
if (errorCount())
|
||||
@ -419,7 +428,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
|
||||
|
||||
inferMachineType();
|
||||
setConfigs(Args);
|
||||
checkOptions(Args);
|
||||
checkOptions();
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
@ -449,9 +458,6 @@ static std::string getRpath(opt::InputArgList &Args) {
|
||||
// Determines what we should do if there are remaining unresolved
|
||||
// symbols after the name resolution.
|
||||
static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) {
|
||||
if (Args.hasArg(OPT_relocatable))
|
||||
return UnresolvedPolicy::IgnoreAll;
|
||||
|
||||
UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols,
|
||||
OPT_warn_unresolved_symbols, true)
|
||||
? UnresolvedPolicy::ReportError
|
||||
@ -498,14 +504,11 @@ static Target2Policy getTarget2(opt::InputArgList &Args) {
|
||||
}
|
||||
|
||||
static bool isOutputFormatBinary(opt::InputArgList &Args) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_oformat)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "binary")
|
||||
return true;
|
||||
if (S.startswith("elf"))
|
||||
return false;
|
||||
StringRef S = Args.getLastArgValue(OPT_oformat, "elf");
|
||||
if (S == "binary")
|
||||
return true;
|
||||
if (!S.startswith("elf"))
|
||||
error("unknown --oformat value: " + S);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -646,38 +649,56 @@ static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &Args) {
|
||||
|
||||
static void readCallGraph(MemoryBufferRef MB) {
|
||||
// Build a map from symbol name to section
|
||||
DenseMap<StringRef, const Symbol *> SymbolNameToSymbol;
|
||||
DenseMap<StringRef, Symbol *> Map;
|
||||
for (InputFile *File : ObjectFiles)
|
||||
for (Symbol *Sym : File->getSymbols())
|
||||
SymbolNameToSymbol[Sym->getName()] = Sym;
|
||||
Map[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]);
|
||||
auto FindSection = [&](StringRef Name) -> InputSectionBase * {
|
||||
Symbol *Sym = Map.lookup(Name);
|
||||
if (!Sym) {
|
||||
if (Config->WarnSymbolOrdering)
|
||||
warn(MB.getBufferIdentifier() + ": no such symbol: " + Name);
|
||||
return nullptr;
|
||||
}
|
||||
maybeWarnUnorderableSymbol(Sym);
|
||||
|
||||
if (Defined *DR = dyn_cast_or_null<Defined>(Sym))
|
||||
return dyn_cast_or_null<InputSectionBase>(DR->Section);
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
for (StringRef Line : args::getLines(MB)) {
|
||||
SmallVector<StringRef, 3> Fields;
|
||||
Line.split(Fields, ' ');
|
||||
uint64_t Count;
|
||||
|
||||
if (Fields.size() != 3 || !to_integer(Fields[2], Count)) {
|
||||
error(MB.getBufferIdentifier() + ": parse error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (InputSectionBase *From = FindSection(Fields[0]))
|
||||
if (InputSectionBase *To = FindSection(Fields[1]))
|
||||
Config->CallGraphProfile[std::make_pair(From, To)] += Count;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> static void readCallGraphsFromObjectFiles() {
|
||||
for (auto File : ObjectFiles) {
|
||||
auto *Obj = cast<ObjFile<ELFT>>(File);
|
||||
|
||||
for (const Elf_CGProfile_Impl<ELFT> &CGPE : Obj->CGProfile) {
|
||||
auto *FromSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_from));
|
||||
auto *ToSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_to));
|
||||
if (!FromSym || !ToSym)
|
||||
continue;
|
||||
|
||||
auto *From = dyn_cast_or_null<InputSectionBase>(FromSym->Section);
|
||||
auto *To = dyn_cast_or_null<InputSectionBase>(ToSym->Section);
|
||||
if (From && To)
|
||||
Config->CallGraphProfile[{From, To}] += CGPE.cgp_weight;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -754,7 +775,10 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
Config->DynamicLinker = getDynamicLinker(Args);
|
||||
Config->EhFrameHdr =
|
||||
Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false);
|
||||
Config->EmitLLVM = Args.hasArg(OPT_plugin_opt_emit_llvm, false);
|
||||
Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
|
||||
Config->CallGraphProfileSort = Args.hasFlag(
|
||||
OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true);
|
||||
Config->EnableNewDtags =
|
||||
Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
|
||||
Config->Entry = Args.getLastArgValue(OPT_entry);
|
||||
@ -809,6 +833,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
|
||||
Config->SoName = Args.getLastArgValue(OPT_soname);
|
||||
Config->SortSection = getSortSection(Args);
|
||||
Config->SplitStackAdjustSize = args::getInteger(Args, OPT_split_stack_adjust_size, 16384);
|
||||
Config->Strip = getStrip(Args);
|
||||
Config->Sysroot = Args.getLastArgValue(OPT_sysroot);
|
||||
Config->Target1Rel = Args.hasFlag(OPT_target1_rel, OPT_target1_abs, false);
|
||||
@ -838,17 +863,21 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
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->WarnIfuncTextrel =
|
||||
Args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, 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->ZGlobal = hasZOption(Args, "global");
|
||||
Config->ZHazardplt = hasZOption(Args, "hazardplt");
|
||||
Config->ZIfuncnoplt = hasZOption(Args, "ifunc-noplt");
|
||||
Config->ZInitfirst = hasZOption(Args, "initfirst");
|
||||
Config->ZInterpose = hasZOption(Args, "interpose");
|
||||
Config->ZKeepTextSectionPrefix = getZFlag(
|
||||
Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
|
||||
Config->ZNodefaultlib = hasZOption(Args, "nodefaultlib");
|
||||
Config->ZNodelete = hasZOption(Args, "nodelete");
|
||||
Config->ZNodlopen = hasZOption(Args, "nodlopen");
|
||||
Config->ZNow = getZFlag(Args, "now", "lazy", false);
|
||||
@ -879,6 +908,9 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
if (Config->ThinLTOJobs == 0)
|
||||
error("--thinlto-jobs: number of threads must be > 0");
|
||||
|
||||
if (Config->SplitStackAdjustSize < 0)
|
||||
error("--split-stack-adjust-size: size must be >= 0");
|
||||
|
||||
// Parse ELF{32,64}{LE,BE} and CPU type.
|
||||
if (auto *Arg = Args.getLastArg(OPT_m)) {
|
||||
StringRef S = Arg->getValue();
|
||||
@ -967,22 +999,17 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
// This function initialize such members. See Config.h for the details
|
||||
// of these values.
|
||||
static void setConfigs(opt::InputArgList &Args) {
|
||||
ELFKind Kind = Config->EKind;
|
||||
uint16_t Machine = Config->EMachine;
|
||||
ELFKind K = Config->EKind;
|
||||
uint16_t M = Config->EMachine;
|
||||
|
||||
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->Is64 = (K == ELF64LEKind || K == ELF64BEKind);
|
||||
Config->IsLE = (K == ELF32LEKind || K == ELF64LEKind);
|
||||
Config->Endianness = Config->IsLE ? endianness::little : endianness::big;
|
||||
Config->IsMips64EL = (K == ELF64LEKind && M == EM_MIPS);
|
||||
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.
|
||||
@ -996,8 +1023,9 @@ static void setConfigs(opt::InputArgList &Args) {
|
||||
// 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;
|
||||
Config->IsRela = M == EM_AARCH64 || M == EM_AMDGPU || M == EM_HEXAGON ||
|
||||
M == EM_PPC || M == EM_PPC64 || M == EM_RISCV ||
|
||||
M == EM_X86_64;
|
||||
|
||||
// If the output uses REL relocations we must store the dynamic relocation
|
||||
// addends to the output sections. We also store addends for RELA relocations
|
||||
@ -1007,10 +1035,13 @@ static void setConfigs(opt::InputArgList &Args) {
|
||||
Config->WriteAddends = Args.hasFlag(OPT_apply_dynamic_relocs,
|
||||
OPT_no_apply_dynamic_relocs, false) ||
|
||||
!Config->IsRela;
|
||||
|
||||
Config->TocOptimize =
|
||||
Args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, M == EM_PPC64);
|
||||
}
|
||||
|
||||
// Returns a value of "-format" option.
|
||||
static bool getBinaryOption(StringRef S) {
|
||||
static bool isFormatBinary(StringRef S) {
|
||||
if (S == "binary")
|
||||
return true;
|
||||
if (S == "elf" || S == "default")
|
||||
@ -1037,7 +1068,10 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
|
||||
StringRef From;
|
||||
StringRef To;
|
||||
std::tie(From, To) = StringRef(Arg->getValue()).split('=');
|
||||
readDefsym(From, MemoryBufferRef(To, "-defsym"));
|
||||
if (From.empty() || To.empty())
|
||||
error("-defsym: syntax error: " + StringRef(Arg->getValue()));
|
||||
else
|
||||
readDefsym(From, MemoryBufferRef(To, "-defsym"));
|
||||
break;
|
||||
}
|
||||
case OPT_script:
|
||||
@ -1052,7 +1086,7 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
|
||||
Config->AsNeeded = true;
|
||||
break;
|
||||
case OPT_format:
|
||||
InBinary = getBinaryOption(Arg->getValue());
|
||||
Config->FormatBinary = isFormatBinary(Arg->getValue());
|
||||
break;
|
||||
case OPT_no_as_needed:
|
||||
Config->AsNeeded = false;
|
||||
@ -1223,33 +1257,34 @@ template <class ELFT> static void handleUndefined(StringRef Name) {
|
||||
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;
|
||||
template <class ELFT> static void handleLibcall(StringRef Name) {
|
||||
Symbol *Sym = Symtab->find(Name);
|
||||
if (!Sym || !Sym->isLazy())
|
||||
return;
|
||||
|
||||
// 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;
|
||||
MemoryBufferRef MB;
|
||||
if (auto *LO = dyn_cast<LazyObject>(Sym))
|
||||
MB = LO->File->MB;
|
||||
else
|
||||
MB = cast<LazyArchive>(Sym)->getMemberBuffer();
|
||||
|
||||
if (isBitcode(MB))
|
||||
Symtab->fetchLazy<ELFT>(Sym);
|
||||
}
|
||||
|
||||
// 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() {
|
||||
// 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.
|
||||
template <class ELFT> static void demoteSharedSymbols() {
|
||||
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;
|
||||
if (auto *S = dyn_cast<SharedSymbol>(Sym)) {
|
||||
if (!S->getFile<ELFT>().IsNeeded) {
|
||||
bool Used = S->Used;
|
||||
replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther,
|
||||
S->Type);
|
||||
S->Used = Used;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1318,6 +1353,85 @@ static void findKeepUniqueSections(opt::InputArgList &Args) {
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> static Symbol *addUndefined(StringRef Name) {
|
||||
return Symtab->addUndefined<ELFT>(Name, STB_GLOBAL, STV_DEFAULT, 0, false,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// The --wrap option is a feature to rename symbols so that you can write
|
||||
// wrappers for existing functions. If you pass `-wrap=foo`, all
|
||||
// occurrences of symbol `foo` are resolved to `wrap_foo` (so, you are
|
||||
// expected to write `wrap_foo` function as a wrapper). The original
|
||||
// symbol becomes accessible as `real_foo`, so you can call that from your
|
||||
// wrapper.
|
||||
//
|
||||
// This data structure is instantiated for each -wrap option.
|
||||
struct WrappedSymbol {
|
||||
Symbol *Sym;
|
||||
Symbol *Real;
|
||||
Symbol *Wrap;
|
||||
};
|
||||
|
||||
// Handles -wrap option.
|
||||
//
|
||||
// This function instantiates wrapper symbols. At this point, they seem
|
||||
// like they are not being used at all, so we explicitly set some flags so
|
||||
// that LTO won't eliminate them.
|
||||
template <class ELFT>
|
||||
static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) {
|
||||
std::vector<WrappedSymbol> V;
|
||||
DenseSet<StringRef> Seen;
|
||||
|
||||
for (auto *Arg : Args.filtered(OPT_wrap)) {
|
||||
StringRef Name = Arg->getValue();
|
||||
if (!Seen.insert(Name).second)
|
||||
continue;
|
||||
|
||||
Symbol *Sym = Symtab->find(Name);
|
||||
if (!Sym)
|
||||
continue;
|
||||
|
||||
Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name));
|
||||
Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name));
|
||||
V.push_back({Sym, Real, Wrap});
|
||||
|
||||
// We want to tell LTO not to inline symbols to be overwritten
|
||||
// because LTO doesn't know the final symbol contents after renaming.
|
||||
Real->CanInline = false;
|
||||
Sym->CanInline = false;
|
||||
|
||||
// Tell LTO not to eliminate these symbols.
|
||||
Sym->IsUsedInRegularObj = true;
|
||||
Wrap->IsUsedInRegularObj = true;
|
||||
}
|
||||
return V;
|
||||
}
|
||||
|
||||
// Do renaming for -wrap by updating pointers to symbols.
|
||||
//
|
||||
// When this function is executed, only InputFiles and symbol table
|
||||
// contain pointers to symbol objects. We visit them to replace pointers,
|
||||
// so that wrapped symbols are swapped as instructed by the command line.
|
||||
template <class ELFT> static void wrapSymbols(ArrayRef<WrappedSymbol> Wrapped) {
|
||||
DenseMap<Symbol *, Symbol *> Map;
|
||||
for (const WrappedSymbol &W : Wrapped) {
|
||||
Map[W.Sym] = W.Wrap;
|
||||
Map[W.Real] = W.Sym;
|
||||
}
|
||||
|
||||
// Update pointers in input files.
|
||||
parallelForEach(ObjectFiles, [&](InputFile *File) {
|
||||
std::vector<Symbol *> &Syms = File->getMutableSymbols();
|
||||
for (size_t I = 0, E = Syms.size(); I != E; ++I)
|
||||
if (Symbol *S = Map.lookup(Syms[I]))
|
||||
Syms[I] = S;
|
||||
});
|
||||
|
||||
// Update pointers in the symbol table.
|
||||
for (const WrappedSymbol &W : Wrapped)
|
||||
Symtab->wrap(W.Sym, W.Real, W.Wrap);
|
||||
}
|
||||
|
||||
static const char *LibcallRoutineNames[] = {
|
||||
#define HANDLE_LIBCALL(code, name) name,
|
||||
#include "llvm/IR/RuntimeLibcalls.def"
|
||||
@ -1328,6 +1442,8 @@ static const char *LibcallRoutineNames[] = {
|
||||
// all linker scripts have already been parsed.
|
||||
template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
Target = getTarget();
|
||||
InX<ELFT>::VerSym = nullptr;
|
||||
InX<ELFT>::VerNeed = nullptr;
|
||||
|
||||
Config->MaxPageSize = getMaxPageSize(Args);
|
||||
Config->ImageBase = getImageBase(Args);
|
||||
@ -1383,8 +1499,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
|
||||
// Some symbols (such as __ehdr_start) are defined lazily only when there
|
||||
// are undefined symbols for them, so we add these to trigger that logic.
|
||||
for (StringRef Sym : Script->ReferencedSymbols)
|
||||
Symtab->addUndefined<ELFT>(Sym);
|
||||
for (StringRef Name : Script->ReferencedSymbols)
|
||||
addUndefined<ELFT>(Name);
|
||||
|
||||
// Handle the `--undefined <sym>` options.
|
||||
for (StringRef S : Config->Undefined)
|
||||
@ -1399,11 +1515,20 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
// in a bitcode file in an archive member, we need to arrange to use LTO to
|
||||
// compile those archive members by adding them to the link beforehand.
|
||||
//
|
||||
// With this the symbol table should be complete. After this, no new names
|
||||
// except a few linker-synthesized ones will be added to the symbol table.
|
||||
// However, adding all libcall symbols to the link can have undesired
|
||||
// consequences. For example, the libgcc implementation of
|
||||
// __sync_val_compare_and_swap_8 on 32-bit ARM pulls in an .init_array entry
|
||||
// that aborts the program if the Linux kernel does not support 64-bit
|
||||
// atomics, which would prevent the program from running even if it does not
|
||||
// use 64-bit atomics.
|
||||
//
|
||||
// Therefore, we only add libcall symbols to the link before LTO if we have
|
||||
// to, i.e. if the symbol's definition is in bitcode. Any other required
|
||||
// libcall symbols will be added to the link after LTO when we add the LTO
|
||||
// object file to the link.
|
||||
if (!BitcodeFiles.empty())
|
||||
for (const char *S : LibcallRoutineNames)
|
||||
handleUndefined<ELFT>(S);
|
||||
handleLibcall<ELFT>(S);
|
||||
|
||||
// Return if there were name resolution errors.
|
||||
if (errorCount())
|
||||
@ -1427,6 +1552,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
Out::ElfHeader = make<OutputSection>("", 0, SHF_ALLOC);
|
||||
Out::ElfHeader->Size = sizeof(typename ELFT::Ehdr);
|
||||
|
||||
// Create wrapped symbols for -wrap option.
|
||||
std::vector<WrappedSymbol> Wrapped = addWrappedSymbols<ELFT>(Args);
|
||||
|
||||
// We need to create some reserved symbols such as _end. Create them.
|
||||
if (!Config->Relocatable)
|
||||
addReservedSymbols();
|
||||
@ -1439,12 +1567,11 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
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.
|
||||
//
|
||||
// With this the symbol table should be complete. After this, no new names
|
||||
// except a few linker-synthesized ones will be added to the symbol table.
|
||||
Symtab->addCombinedLTOObject<ELFT>();
|
||||
if (errorCount())
|
||||
return;
|
||||
@ -1455,8 +1582,15 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
if (Config->ThinLTOIndexOnly)
|
||||
return;
|
||||
|
||||
// Likewise, --plugin-opt=emit-llvm is an option to make LTO create
|
||||
// an output file in bitcode and exit, so that you can just get a
|
||||
// combined bitcode file.
|
||||
if (Config->EmitLLVM)
|
||||
return;
|
||||
|
||||
// Apply symbol renames for -wrap.
|
||||
Symtab->applySymbolWrap();
|
||||
if (!Wrapped.empty())
|
||||
wrapSymbols<ELFT>(Wrapped);
|
||||
|
||||
// Now that we have a complete list of input files.
|
||||
// Beyond this point, no new files are added.
|
||||
@ -1484,27 +1618,19 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
// supports them.
|
||||
if (Config->ARMHasBlx == false)
|
||||
warn("lld uses blx instruction, no object with architecture supporting "
|
||||
"feature detected.");
|
||||
if (Config->ARMJ1J2BranchEncoding == false)
|
||||
warn("lld uses extended branch encoding, no object with architecture "
|
||||
"supporting feature detected.");
|
||||
if (Config->ARMHasMovtMovw == false)
|
||||
warn("lld may use movt/movw, no object with architecture supporting "
|
||||
"feature detected.");
|
||||
"feature detected");
|
||||
}
|
||||
|
||||
// This adds a .comment section containing a version string. We have to add it
|
||||
// before decompressAndMergeSections because the .comment section is a
|
||||
// mergeable section.
|
||||
// before mergeSections because the .comment section is a mergeable section.
|
||||
if (!Config->Relocatable)
|
||||
InputSections.push_back(createCommentSection());
|
||||
|
||||
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
|
||||
// and identical code folding.
|
||||
decompressSections();
|
||||
splitSections<ELFT>();
|
||||
markLive<ELFT>();
|
||||
demoteSymbols<ELFT>();
|
||||
demoteSharedSymbols<ELFT>();
|
||||
mergeSections();
|
||||
if (Config->ICF != ICFLevel::None) {
|
||||
findKeepUniqueSections<ELFT>(Args);
|
||||
@ -1512,9 +1638,12 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (Config->CallGraphProfileSort) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file))
|
||||
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
|
||||
readCallGraph(*Buffer);
|
||||
readCallGraphsFromObjectFiles<ELFT>();
|
||||
}
|
||||
|
||||
// Write the result to the file.
|
||||
writeResult<ELFT>();
|
||||
|
@ -42,9 +42,6 @@ private:
|
||||
// True if we are in --start-lib and --end-lib.
|
||||
bool InLib = false;
|
||||
|
||||
// True if we are in -format=binary and -format=elf.
|
||||
bool InBinary = false;
|
||||
|
||||
std::vector<InputFile *> Files;
|
||||
};
|
||||
|
||||
|
@ -139,8 +139,9 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
|
||||
}
|
||||
|
||||
void elf::printHelp() {
|
||||
ELFOptTable().PrintHelp(outs(), Config->ProgName.data(), "lld",
|
||||
false /*ShowHidden*/, true /*ShowAllAliases*/);
|
||||
ELFOptTable().PrintHelp(
|
||||
outs(), (Config->ProgName + " [options] file...").str().c_str(), "lld",
|
||||
false /*ShowHidden*/, true /*ShowAllAliases*/);
|
||||
outs() << "\n";
|
||||
|
||||
// Scripts generated by Libtool versions up to at least 2.4.6 (the most
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
private:
|
||||
template <class P> void failOn(const P *Loc, const Twine &Msg) {
|
||||
fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " +
|
||||
IS->getObjMsg((const uint8_t *)Loc - IS->Data.data()));
|
||||
IS->getObjMsg((const uint8_t *)Loc - IS->data().data()));
|
||||
}
|
||||
|
||||
uint8_t readByte();
|
||||
@ -59,7 +59,7 @@ private:
|
||||
}
|
||||
|
||||
size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) {
|
||||
return EhReader(S, S->Data.slice(Off)).readEhRecordSize();
|
||||
return EhReader(S, S->data().slice(Off)).readEhRecordSize();
|
||||
}
|
||||
|
||||
// .eh_frame section is a sequence of records. Each record starts with
|
||||
|
@ -252,7 +252,10 @@ bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
|
||||
|
||||
auto *DA = dyn_cast<Defined>(&SA);
|
||||
auto *DB = dyn_cast<Defined>(&SB);
|
||||
if (!DA || !DB)
|
||||
|
||||
// Placeholder symbols generated by linker scripts look the same now but
|
||||
// may have different values later.
|
||||
if (!DA || !DB || DA->ScriptDefined || DB->ScriptDefined)
|
||||
return false;
|
||||
|
||||
// Relocations referring to absolute symbols are constant-equal if their
|
||||
@ -298,7 +301,7 @@ bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
|
||||
template <class ELFT>
|
||||
bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) {
|
||||
if (A->NumRelocations != B->NumRelocations || A->Flags != B->Flags ||
|
||||
A->getSize() != B->getSize() || A->Data != B->Data)
|
||||
A->getSize() != B->getSize() || A->data() != B->data())
|
||||
return false;
|
||||
|
||||
// If two sections have different output sections, we cannot merge them.
|
||||
@ -420,6 +423,21 @@ void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> Fn) {
|
||||
++Cnt;
|
||||
}
|
||||
|
||||
// Combine the hashes of the sections referenced by the given section into its
|
||||
// hash.
|
||||
template <class ELFT, class RelTy>
|
||||
static void combineRelocHashes(InputSection *IS, ArrayRef<RelTy> Rels) {
|
||||
uint32_t Hash = IS->Class[1];
|
||||
for (RelTy Rel : Rels) {
|
||||
Symbol &S = IS->template getFile<ELFT>()->getRelocTargetSym(Rel);
|
||||
if (auto *D = dyn_cast<Defined>(&S))
|
||||
if (auto *RelSec = dyn_cast_or_null<InputSection>(D->Section))
|
||||
Hash ^= RelSec->Class[1];
|
||||
}
|
||||
// Set MSB to 1 to avoid collisions with non-hash IDs.
|
||||
IS->Class[0] = Hash | (1U << 31);
|
||||
}
|
||||
|
||||
static void print(const Twine &S) {
|
||||
if (Config->PrintIcfSections)
|
||||
message(S);
|
||||
@ -435,8 +453,14 @@ 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] = xxHash64(S->Data) | (1U << 31);
|
||||
S->Class[1] = xxHash64(S->data());
|
||||
});
|
||||
|
||||
parallelForEach(Sections, [&](InputSection *S) {
|
||||
if (S->AreRelocsRela)
|
||||
combineRelocHashes<ELFT>(S, S->template relas<ELFT>());
|
||||
else
|
||||
combineRelocHashes<ELFT>(S, S->template rels<ELFT>());
|
||||
});
|
||||
|
||||
// From now on, sections in Sections vector are ordered so that sections
|
||||
|
@ -46,7 +46,7 @@ std::vector<LazyObjFile *> elf::LazyObjFiles;
|
||||
std::vector<InputFile *> elf::ObjectFiles;
|
||||
std::vector<InputFile *> elf::SharedFiles;
|
||||
|
||||
TarWriter *elf::Tar;
|
||||
std::unique_ptr<TarWriter> elf::Tar;
|
||||
|
||||
InputFile::InputFile(Kind K, MemoryBufferRef M)
|
||||
: MB(M), GroupId(NextGroupId), FileKind(K) {
|
||||
@ -125,11 +125,7 @@ std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
|
||||
|
||||
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
|
||||
Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
|
||||
const DWARFObject &Obj = Dwarf->getDWARFObj();
|
||||
DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE,
|
||||
Config->Wordsize);
|
||||
|
||||
for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf->compile_units()) {
|
||||
for (std::unique_ptr<DWARFUnit> &CU : Dwarf->compile_units()) {
|
||||
auto Report = [](Error Err) {
|
||||
handleAllErrors(std::move(Err),
|
||||
[](ErrorInfoBase &Info) { warn(Info.message()); });
|
||||
@ -416,6 +412,11 @@ void ObjFile<ELFT>::initializeSections(
|
||||
continue;
|
||||
const Elf_Shdr &Sec = ObjSections[I];
|
||||
|
||||
if (Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE)
|
||||
CGProfile = check(
|
||||
this->getObj().template getSectionContentsAsArray<Elf_CGProfile>(
|
||||
&Sec));
|
||||
|
||||
// SHF_EXCLUDE'ed sections are discarded by the linker. However,
|
||||
// if -r is given, we'll let the final link discard such sections.
|
||||
// This is compatible with GNU.
|
||||
@ -442,6 +443,10 @@ void ObjFile<ELFT>::initializeSections(
|
||||
bool IsNew = ComdatGroups.insert(CachedHashStringRef(Signature)).second;
|
||||
this->Sections[I] = &InputSection::Discarded;
|
||||
|
||||
// We only support GRP_COMDAT type of group. Get the all entries of the
|
||||
// section here to let getShtGroupEntries to check the type early for us.
|
||||
ArrayRef<Elf_Word> Entries = getShtGroupEntries(Sec);
|
||||
|
||||
// If it is a new section group, we want to keep group members.
|
||||
// Group leader sections, which contain indices of group members, are
|
||||
// discarded because they are useless beyond this point. The only
|
||||
@ -454,7 +459,7 @@ void ObjFile<ELFT>::initializeSections(
|
||||
}
|
||||
|
||||
// Otherwise, discard group members.
|
||||
for (uint32_t SecIndex : getShtGroupEntries(Sec)) {
|
||||
for (uint32_t SecIndex : Entries) {
|
||||
if (SecIndex >= Size)
|
||||
fatal(toString(this) +
|
||||
": invalid section index in group: " + Twine(SecIndex));
|
||||
@ -478,11 +483,13 @@ void ObjFile<ELFT>::initializeSections(
|
||||
// .ARM.exidx sections have a reverse dependency on the InputSection they
|
||||
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
|
||||
if (Sec.sh_flags & SHF_LINK_ORDER) {
|
||||
if (Sec.sh_link >= this->Sections.size())
|
||||
InputSectionBase *LinkSec = nullptr;
|
||||
if (Sec.sh_link < this->Sections.size())
|
||||
LinkSec = this->Sections[Sec.sh_link];
|
||||
if (!LinkSec)
|
||||
fatal(toString(this) +
|
||||
": invalid sh_link index: " + Twine(Sec.sh_link));
|
||||
|
||||
InputSectionBase *LinkSec = this->Sections[Sec.sh_link];
|
||||
InputSection *IS = cast<InputSection>(this->Sections[I]);
|
||||
LinkSec->DependentSections.push_back(IS);
|
||||
if (!isa<InputSection>(LinkSec))
|
||||
@ -598,7 +605,7 @@ InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
|
||||
// as a given section.
|
||||
static InputSection *toRegularSection(MergeInputSection *Sec) {
|
||||
return make<InputSection>(Sec->File, Sec->Flags, Sec->Type, Sec->Alignment,
|
||||
Sec->Data, Sec->Name);
|
||||
Sec->data(), Sec->Name);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
@ -618,9 +625,9 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
|
||||
// FIXME: Retain the first attribute section we see. The eglibc ARM
|
||||
// dynamic loaders require the presence of an attribute section for dlopen
|
||||
// to work. In a full implementation we would merge all attribute sections.
|
||||
if (InX::ARMAttributes == nullptr) {
|
||||
InX::ARMAttributes = make<InputSection>(*this, Sec, Name);
|
||||
return InX::ARMAttributes;
|
||||
if (In.ARMAttributes == nullptr) {
|
||||
In.ARMAttributes = make<InputSection>(*this, Sec, Name);
|
||||
return In.ARMAttributes;
|
||||
}
|
||||
return &InputSection::Discarded;
|
||||
}
|
||||
@ -638,8 +645,16 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
|
||||
// This section contains relocation information.
|
||||
// If -r is given, we do not interpret or apply relocation
|
||||
// but just copy relocation sections to output.
|
||||
if (Config->Relocatable)
|
||||
return make<InputSection>(*this, Sec, Name);
|
||||
if (Config->Relocatable) {
|
||||
InputSection *RelocSec = make<InputSection>(*this, Sec, Name);
|
||||
// We want to add a dependency to target, similar like we do for
|
||||
// -emit-relocs below. This is useful for the case when linker script
|
||||
// contains the "/DISCARD/". It is perhaps uncommon to use a script with
|
||||
// -r, but we faced it in the Linux kernel and have to handle such case
|
||||
// and not to crash.
|
||||
Target->DependentSections.push_back(RelocSec);
|
||||
return RelocSec;
|
||||
}
|
||||
|
||||
if (Target->FirstRelocation)
|
||||
fatal(toString(this) +
|
||||
@ -704,7 +719,7 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
|
||||
// for split stack will include a .note.GNU-split-stack section.
|
||||
if (Name == ".note.GNU-split-stack") {
|
||||
if (Config->Relocatable) {
|
||||
error("Cannot mix split-stack and non-split-stack in a relocatable link");
|
||||
error("cannot mix split-stack and non-split-stack in a relocatable link");
|
||||
return &InputSection::Discarded;
|
||||
}
|
||||
this->SplitStack = true;
|
||||
@ -806,7 +821,7 @@ template <class ELFT> Symbol *ObjFile<ELFT>::createSymbol(const Elf_Sym *Sym) {
|
||||
if (Sec == &InputSection::Discarded)
|
||||
return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
|
||||
/*CanOmitFromDynSym=*/false, this);
|
||||
return Symtab->addRegular(Name, StOther, Type, Value, Size, Binding, Sec,
|
||||
return Symtab->addDefined(Name, StOther, Type, Value, Size, Binding, Sec,
|
||||
this);
|
||||
}
|
||||
}
|
||||
@ -940,8 +955,7 @@ std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() {
|
||||
auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef);
|
||||
Verdef += CurVerdef->vd_next;
|
||||
unsigned VerdefIndex = CurVerdef->vd_ndx;
|
||||
if (Verdefs.size() <= VerdefIndex)
|
||||
Verdefs.resize(VerdefIndex + 1);
|
||||
Verdefs.resize(VerdefIndex + 1);
|
||||
Verdefs[VerdefIndex] = CurVerdef;
|
||||
}
|
||||
|
||||
@ -993,7 +1007,17 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
|
||||
for (size_t I = 0; I < Syms.size(); ++I) {
|
||||
const Elf_Sym &Sym = Syms[I];
|
||||
|
||||
// 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.
|
||||
StringRef Name = CHECK(Sym.getName(this->StringTable), this);
|
||||
if (Sym.getBinding() == STB_LOCAL) {
|
||||
warn("found local symbol '" + Name +
|
||||
"' in global part of symbol table in file " + toString(this));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Sym.isUndefined()) {
|
||||
Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(),
|
||||
Sym.st_other, Sym.getType(),
|
||||
@ -1002,16 +1026,6 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -1054,6 +1068,9 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
|
||||
switch (T.getArch()) {
|
||||
case Triple::aarch64:
|
||||
return EM_AARCH64;
|
||||
case Triple::amdgcn:
|
||||
case Triple::r600:
|
||||
return EM_AMDGPU;
|
||||
case Triple::arm:
|
||||
case Triple::thumb:
|
||||
return EM_ARM;
|
||||
@ -1064,9 +1081,12 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
|
||||
case Triple::mips64:
|
||||
case Triple::mips64el:
|
||||
return EM_MIPS;
|
||||
case Triple::msp430:
|
||||
return EM_MSP430;
|
||||
case Triple::ppc:
|
||||
return EM_PPC;
|
||||
case Triple::ppc64:
|
||||
case Triple::ppc64le:
|
||||
return EM_PPC64;
|
||||
case Triple::x86:
|
||||
return T.isOSIAMCU() ? EM_IAMCU : EM_386;
|
||||
@ -1178,7 +1198,7 @@ static ELFKind getELFKind(MemoryBufferRef MB) {
|
||||
}
|
||||
|
||||
void BinaryFile::parse() {
|
||||
ArrayRef<uint8_t> Data = toArrayRef(MB.getBuffer());
|
||||
ArrayRef<uint8_t> Data = arrayRefFromStringRef(MB.getBuffer());
|
||||
auto *Section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
|
||||
8, Data, ".data");
|
||||
Sections.push_back(Section);
|
||||
@ -1192,11 +1212,11 @@ void BinaryFile::parse() {
|
||||
if (!isAlnum(S[I]))
|
||||
S[I] = '_';
|
||||
|
||||
Symtab->addRegular(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0,
|
||||
Symtab->addDefined(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0,
|
||||
STB_GLOBAL, Section, nullptr);
|
||||
Symtab->addRegular(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT,
|
||||
Symtab->addDefined(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT,
|
||||
Data.size(), 0, STB_GLOBAL, Section, nullptr);
|
||||
Symtab->addRegular(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT,
|
||||
Symtab->addDefined(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT,
|
||||
Data.size(), 0, STB_GLOBAL, nullptr, nullptr);
|
||||
}
|
||||
|
||||
@ -1262,25 +1282,11 @@ template <class ELFT> void LazyObjFile::parse() {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (getELFKind(this->MB)) {
|
||||
case ELF32LEKind:
|
||||
addElfSymbols<ELF32LE>();
|
||||
if (getELFKind(this->MB) != Config->EKind) {
|
||||
error("incompatible file: " + this->MB.getBufferIdentifier());
|
||||
return;
|
||||
case ELF32BEKind:
|
||||
addElfSymbols<ELF32BE>();
|
||||
return;
|
||||
case ELF64LEKind:
|
||||
addElfSymbols<ELF64LE>();
|
||||
return;
|
||||
case ELF64BEKind:
|
||||
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);
|
||||
|
||||
@ -1305,12 +1311,9 @@ 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();
|
||||
if (Path.consume_back(Suffix))
|
||||
return (Path + Repl).str();
|
||||
return Path;
|
||||
}
|
||||
|
||||
template void ArchiveFile::parse<ELF32LE>();
|
||||
|
@ -50,7 +50,7 @@ class Symbol;
|
||||
|
||||
// If -reproduce option is given, all input files are written
|
||||
// to this tar archive.
|
||||
extern llvm::TarWriter *Tar;
|
||||
extern std::unique_ptr<llvm::TarWriter> Tar;
|
||||
|
||||
// Opens a given file.
|
||||
llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
|
||||
@ -86,7 +86,9 @@ public:
|
||||
|
||||
// Returns object file symbols. It is a runtime error to call this
|
||||
// function on files of other types.
|
||||
ArrayRef<Symbol *> getSymbols() {
|
||||
ArrayRef<Symbol *> getSymbols() { return getMutableSymbols(); }
|
||||
|
||||
std::vector<Symbol *> &getMutableSymbols() {
|
||||
assert(FileKind == BinaryKind || FileKind == ObjKind ||
|
||||
FileKind == BitcodeKind);
|
||||
return Symbols;
|
||||
@ -169,6 +171,7 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Word Elf_Word;
|
||||
typedef typename ELFT::CGProfile Elf_CGProfile;
|
||||
|
||||
StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
|
||||
const Elf_Shdr &Sec);
|
||||
@ -218,6 +221,9 @@ public:
|
||||
// Pointer to this input file's .llvm_addrsig section, if it has one.
|
||||
const Elf_Shdr *AddrsigSec = nullptr;
|
||||
|
||||
// SHT_LLVM_CALL_GRAPH_PROFILE table
|
||||
ArrayRef<Elf_CGProfile> CGProfile;
|
||||
|
||||
private:
|
||||
void
|
||||
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
@ -272,8 +278,6 @@ public:
|
||||
bool AddedToLink = false;
|
||||
|
||||
private:
|
||||
template <class ELFT> void addElfSymbols();
|
||||
|
||||
uint64_t OffsetInArchive;
|
||||
};
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/Object/Decompressor.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/Compression.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
@ -64,11 +63,11 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
|
||||
StringRef Name, Kind SectionKind)
|
||||
: SectionBase(SectionKind, Name, Flags, Entsize, Alignment, Type, Info,
|
||||
Link),
|
||||
File(File), Data(Data) {
|
||||
File(File), RawData(Data) {
|
||||
// In order to reduce memory allocation, we assume that mergeable
|
||||
// sections are smaller than 4 GiB, which is not an unreasonable
|
||||
// assumption as of 2017.
|
||||
if (SectionKind == SectionBase::Merge && Data.size() > UINT32_MAX)
|
||||
if (SectionKind == SectionBase::Merge && RawData.size() > UINT32_MAX)
|
||||
error(toString(this) + ": section too large");
|
||||
|
||||
NumRelocations = 0;
|
||||
@ -80,6 +79,17 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
|
||||
if (!isPowerOf2_64(V))
|
||||
fatal(toString(File) + ": section sh_addralign is not a power of 2");
|
||||
this->Alignment = V;
|
||||
|
||||
// In ELF, each section can be compressed by zlib, and if compressed,
|
||||
// section name may be mangled by appending "z" (e.g. ".zdebug_info").
|
||||
// If that's the case, demangle section name so that we can handle a
|
||||
// section as if it weren't compressed.
|
||||
if ((Flags & SHF_COMPRESSED) || Name.startswith(".zdebug")) {
|
||||
if (!zlib::isAvailable())
|
||||
error(toString(File) + ": contains a compressed section, " +
|
||||
"but zlib is not available");
|
||||
parseCompressedHeader();
|
||||
}
|
||||
}
|
||||
|
||||
// Drop SHF_GROUP bit unless we are producing a re-linkable object file.
|
||||
@ -128,13 +138,25 @@ InputSectionBase::InputSectionBase(ObjFile<ELFT> &File,
|
||||
size_t InputSectionBase::getSize() const {
|
||||
if (auto *S = dyn_cast<SyntheticSection>(this))
|
||||
return S->getSize();
|
||||
if (UncompressedSize >= 0)
|
||||
return UncompressedSize;
|
||||
return RawData.size();
|
||||
}
|
||||
|
||||
return Data.size();
|
||||
void InputSectionBase::uncompress() const {
|
||||
size_t Size = UncompressedSize;
|
||||
UncompressedBuf.reset(new char[Size]);
|
||||
|
||||
if (Error E =
|
||||
zlib::uncompress(toStringRef(RawData), UncompressedBuf.get(), Size))
|
||||
fatal(toString(this) +
|
||||
": uncompress failed: " + llvm::toString(std::move(E)));
|
||||
RawData = makeArrayRef((uint8_t *)UncompressedBuf.get(), Size);
|
||||
}
|
||||
|
||||
uint64_t InputSectionBase::getOffsetInFile() const {
|
||||
const uint8_t *FileStart = (const uint8_t *)File->MB.getBufferStart();
|
||||
const uint8_t *SecStart = Data.begin();
|
||||
const uint8_t *SecStart = data().begin();
|
||||
return SecStart - FileStart;
|
||||
}
|
||||
|
||||
@ -180,34 +202,70 @@ OutputSection *SectionBase::getOutputSection() {
|
||||
return Sec ? Sec->getParent() : nullptr;
|
||||
}
|
||||
|
||||
// Decompress section contents if required. Note that this function
|
||||
// is called from parallelForEach, so it must be thread-safe.
|
||||
void InputSectionBase::maybeDecompress() {
|
||||
if (DecompressBuf)
|
||||
return;
|
||||
if (!(Flags & SHF_COMPRESSED) && !Name.startswith(".zdebug"))
|
||||
// When a section is compressed, `RawData` consists with a header followed
|
||||
// by zlib-compressed data. This function parses a header to initialize
|
||||
// `UncompressedSize` member and remove the header from `RawData`.
|
||||
void InputSectionBase::parseCompressedHeader() {
|
||||
typedef typename ELF64LE::Chdr Chdr64;
|
||||
typedef typename ELF32LE::Chdr Chdr32;
|
||||
|
||||
// Old-style header
|
||||
if (Name.startswith(".zdebug")) {
|
||||
if (!toStringRef(RawData).startswith("ZLIB")) {
|
||||
error(toString(this) + ": corrupted compressed section header");
|
||||
return;
|
||||
}
|
||||
RawData = RawData.slice(4);
|
||||
|
||||
if (RawData.size() < 8) {
|
||||
error(toString(this) + ": corrupted compressed section header");
|
||||
return;
|
||||
}
|
||||
|
||||
UncompressedSize = read64be(RawData.data());
|
||||
RawData = RawData.slice(8);
|
||||
|
||||
// Restore the original section name.
|
||||
// (e.g. ".zdebug_info" -> ".debug_info")
|
||||
Name = Saver.save("." + Name.substr(2));
|
||||
return;
|
||||
}
|
||||
|
||||
// Decompress a section.
|
||||
Decompressor Dec = check(Decompressor::create(Name, toStringRef(Data),
|
||||
Config->IsLE, Config->Is64));
|
||||
|
||||
size_t Size = Dec.getDecompressedSize();
|
||||
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 *)DecompressBuf.get(), Size);
|
||||
assert(Flags & SHF_COMPRESSED);
|
||||
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);
|
||||
// New-style 64-bit header
|
||||
if (Config->Is64) {
|
||||
if (RawData.size() < sizeof(Chdr64)) {
|
||||
error(toString(this) + ": corrupted compressed section");
|
||||
return;
|
||||
}
|
||||
|
||||
auto *Hdr = reinterpret_cast<const Chdr64 *>(RawData.data());
|
||||
if (Hdr->ch_type != ELFCOMPRESS_ZLIB) {
|
||||
error(toString(this) + ": unsupported compression type");
|
||||
return;
|
||||
}
|
||||
|
||||
UncompressedSize = Hdr->ch_size;
|
||||
RawData = RawData.slice(sizeof(*Hdr));
|
||||
return;
|
||||
}
|
||||
|
||||
// New-style 32-bit header
|
||||
if (RawData.size() < sizeof(Chdr32)) {
|
||||
error(toString(this) + ": corrupted compressed section");
|
||||
return;
|
||||
}
|
||||
|
||||
auto *Hdr = reinterpret_cast<const Chdr32 *>(RawData.data());
|
||||
if (Hdr->ch_type != ELFCOMPRESS_ZLIB) {
|
||||
error(toString(this) + ": unsupported compression type");
|
||||
return;
|
||||
}
|
||||
|
||||
UncompressedSize = Hdr->ch_size;
|
||||
RawData = RawData.slice(sizeof(*Hdr));
|
||||
}
|
||||
|
||||
InputSection *InputSectionBase::getLinkOrderDep() const {
|
||||
@ -230,14 +288,17 @@ Defined *InputSectionBase::getEnclosingFunction(uint64_t Offset) {
|
||||
// Returns a source location string. Used to construct an error message.
|
||||
template <class ELFT>
|
||||
std::string InputSectionBase::getLocation(uint64_t Offset) {
|
||||
std::string SecAndOffset = (Name + "+0x" + utohexstr(Offset)).str();
|
||||
|
||||
// We don't have file for synthetic sections.
|
||||
if (getFile<ELFT>() == nullptr)
|
||||
return (Config->OutputFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")")
|
||||
return (Config->OutputFile + ":(" + SecAndOffset + ")")
|
||||
.str();
|
||||
|
||||
// First check if we can get desired values from debugging information.
|
||||
if (Optional<DILineInfo> Info = getFile<ELFT>()->getDILineInfo(this, Offset))
|
||||
return Info->FileName + ":" + std::to_string(Info->Line);
|
||||
return Info->FileName + ":" + std::to_string(Info->Line) + ":(" +
|
||||
SecAndOffset + ")";
|
||||
|
||||
// File->SourceFile contains STT_FILE symbol that contains a
|
||||
// source file name. If it's missing, we use an object file name.
|
||||
@ -246,10 +307,10 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
|
||||
SrcFile = toString(File);
|
||||
|
||||
if (Defined *D = getEnclosingFunction<ELFT>(Offset))
|
||||
return SrcFile + ":(function " + toString(*D) + ")";
|
||||
return SrcFile + ":(function " + toString(*D) + ": " + SecAndOffset + ")";
|
||||
|
||||
// If there's no symbol, print out the offset in the section.
|
||||
return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str();
|
||||
return (SrcFile + ":(" + SecAndOffset + ")");
|
||||
}
|
||||
|
||||
// This function is intended to be used for constructing an error message.
|
||||
@ -259,9 +320,6 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
|
||||
//
|
||||
// Returns an empty string if there's no way to get line info.
|
||||
std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
|
||||
// Synthetic sections don't have input files.
|
||||
if (!File)
|
||||
return "";
|
||||
return File->getSrcMsg(Sym, *this, Offset);
|
||||
}
|
||||
|
||||
@ -275,9 +333,6 @@ std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
|
||||
//
|
||||
// path/to/foo.o:(function bar) in archive path/to/bar.a
|
||||
std::string InputSectionBase::getObjMsg(uint64_t Off) {
|
||||
// Synthetic sections don't have input files.
|
||||
if (!File)
|
||||
return ("<internal>:(" + Name + "+0x" + utohexstr(Off) + ")").str();
|
||||
std::string Filename = File->getName();
|
||||
|
||||
std::string Archive;
|
||||
@ -362,7 +417,7 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
|
||||
// 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->getVA(Rel.r_offset);
|
||||
P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type,
|
||||
P->setSymbolAndType(In.SymTab->getSymbolIndex(&Sym), Type,
|
||||
Config->IsMips64EL);
|
||||
|
||||
if (Sym.Type == STT_SECTION) {
|
||||
@ -380,14 +435,14 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
|
||||
error("STT_SECTION symbol should be defined");
|
||||
continue;
|
||||
}
|
||||
SectionBase *Section = D->Section;
|
||||
if (Section == &InputSection::Discarded) {
|
||||
SectionBase *Section = D->Section->Repl;
|
||||
if (!Section->Live) {
|
||||
P->setSymbolAndType(0, 0, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t Addend = getAddend<ELFT>(Rel);
|
||||
const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset;
|
||||
const uint8_t *BufLoc = Sec->data().begin() + Rel.r_offset;
|
||||
if (!RelTy::IsRela)
|
||||
Addend = Target->getImplicitAddend(BufLoc, Type);
|
||||
|
||||
@ -487,6 +542,62 @@ static uint64_t getARMStaticBase(const Symbol &Sym) {
|
||||
return OS->PtLoad->FirstSec->Addr;
|
||||
}
|
||||
|
||||
// For R_RISCV_PC_INDIRECT (R_RISCV_PCREL_LO12_{I,S}), the symbol actually
|
||||
// points the corresponding R_RISCV_PCREL_HI20 relocation, and the target VA
|
||||
// is calculated using PCREL_HI20's symbol.
|
||||
//
|
||||
// This function returns the R_RISCV_PCREL_HI20 relocation from
|
||||
// R_RISCV_PCREL_LO12's symbol and addend.
|
||||
static Relocation *getRISCVPCRelHi20(const Symbol *Sym, uint64_t Addend) {
|
||||
const Defined *D = cast<Defined>(Sym);
|
||||
InputSection *IS = cast<InputSection>(D->Section);
|
||||
|
||||
if (Addend != 0)
|
||||
warn("Non-zero addend in R_RISCV_PCREL_LO12 relocation to " +
|
||||
IS->getObjMsg(D->Value) + " is ignored");
|
||||
|
||||
// Relocations are sorted by offset, so we can use std::equal_range to do
|
||||
// binary search.
|
||||
auto Range = std::equal_range(IS->Relocations.begin(), IS->Relocations.end(),
|
||||
D->Value, RelocationOffsetComparator{});
|
||||
for (auto It = std::get<0>(Range); It != std::get<1>(Range); ++It)
|
||||
if (isRelExprOneOf<R_PC>(It->Expr))
|
||||
return &*It;
|
||||
|
||||
error("R_RISCV_PCREL_LO12 relocation points to " + IS->getObjMsg(D->Value) +
|
||||
" without an associated R_RISCV_PCREL_HI20 relocation");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// A TLS symbol's virtual address is relative to the TLS segment. Add a
|
||||
// target-specific adjustment to produce a thread-pointer-relative offset.
|
||||
static int64_t getTlsTpOffset() {
|
||||
switch (Config->EMachine) {
|
||||
case EM_ARM:
|
||||
case EM_AARCH64:
|
||||
// Variant 1. The thread pointer points to a TCB with a fixed 2-word size,
|
||||
// followed by a variable amount of alignment padding, followed by the TLS
|
||||
// segment.
|
||||
//
|
||||
// NB: While the ARM/AArch64 ABI formally has a 2-word TCB size, lld
|
||||
// effectively increases the TCB size to 8 words for Android compatibility.
|
||||
// It accomplishes this by increasing the segment's alignment.
|
||||
return alignTo(Config->Wordsize * 2, Out::TlsPhdr->p_align);
|
||||
case EM_386:
|
||||
case EM_X86_64:
|
||||
// Variant 2. The TLS segment is located just before the thread pointer.
|
||||
return -Out::TlsPhdr->p_memsz;
|
||||
case EM_PPC64:
|
||||
// The thread pointer points to a fixed offset from the start of the
|
||||
// executable's TLS segment. An offset of 0x7000 allows a signed 16-bit
|
||||
// offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the
|
||||
// program's TLS segment.
|
||||
return -0x7000;
|
||||
default:
|
||||
llvm_unreachable("unhandled Config->EMachine");
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
|
||||
uint64_t P, const Symbol &Sym, RelExpr Expr) {
|
||||
switch (Expr) {
|
||||
@ -501,38 +612,37 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
|
||||
case R_ARM_SBREL:
|
||||
return Sym.getVA(A) - getARMStaticBase(Sym);
|
||||
case R_GOT:
|
||||
case R_GOT_PLT:
|
||||
case R_RELAX_TLS_GD_TO_IE_ABS:
|
||||
return Sym.getGotVA() + A;
|
||||
case R_GOTONLY_PC:
|
||||
return InX::Got->getVA() + A - P;
|
||||
return In.Got->getVA() + A - P;
|
||||
case R_GOTONLY_PC_FROM_END:
|
||||
return InX::Got->getVA() + A - P + InX::Got->getSize();
|
||||
return In.Got->getVA() + A - P + In.Got->getSize();
|
||||
case R_GOTREL:
|
||||
return Sym.getVA(A) - InX::Got->getVA();
|
||||
return Sym.getVA(A) - In.Got->getVA();
|
||||
case R_GOTREL_FROM_END:
|
||||
return Sym.getVA(A) - InX::Got->getVA() - InX::Got->getSize();
|
||||
return Sym.getVA(A) - In.Got->getVA() - In.Got->getSize();
|
||||
case R_GOT_FROM_END:
|
||||
case R_RELAX_TLS_GD_TO_IE_END:
|
||||
return Sym.getGotOffset() + A - InX::Got->getSize();
|
||||
return Sym.getGotOffset() + A - In.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:
|
||||
case R_AARCH64_GOT_PAGE_PC:
|
||||
case R_AARCH64_GOT_PAGE_PC_PLT:
|
||||
case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
|
||||
return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P);
|
||||
case R_GOT_PC:
|
||||
case R_RELAX_TLS_GD_TO_IE:
|
||||
return Sym.getGotVA() + A - P;
|
||||
case R_HINT:
|
||||
case R_NONE:
|
||||
case R_TLSDESC_CALL:
|
||||
case R_TLSLD_HINT:
|
||||
llvm_unreachable("cannot relocate hint relocs");
|
||||
case R_HEXAGON_GOT:
|
||||
return Sym.getGotVA() - In.GotPlt->getVA();
|
||||
case R_MIPS_GOTREL:
|
||||
return Sym.getVA(A) - InX::MipsGot->getGp(File);
|
||||
return Sym.getVA(A) - In.MipsGot->getGp(File);
|
||||
case R_MIPS_GOT_GP:
|
||||
return InX::MipsGot->getGp(File) + A;
|
||||
return In.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
|
||||
@ -541,7 +651,7 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
|
||||
// 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(File) + A - P;
|
||||
uint64_t V = In.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)
|
||||
@ -552,31 +662,34 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
|
||||
// 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(File, Sym, A) -
|
||||
InX::MipsGot->getGp(File);
|
||||
return In.MipsGot->getVA() + In.MipsGot->getPageEntryOffset(File, Sym, A) -
|
||||
In.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(File, Sym, A) -
|
||||
InX::MipsGot->getGp(File);
|
||||
return In.MipsGot->getVA() + In.MipsGot->getSymEntryOffset(File, Sym, A) -
|
||||
In.MipsGot->getGp(File);
|
||||
case R_MIPS_TLSGD:
|
||||
return InX::MipsGot->getVA() + InX::MipsGot->getGlobalDynOffset(File, Sym) -
|
||||
InX::MipsGot->getGp(File);
|
||||
return In.MipsGot->getVA() + In.MipsGot->getGlobalDynOffset(File, Sym) -
|
||||
In.MipsGot->getGp(File);
|
||||
case R_MIPS_TLSLD:
|
||||
return InX::MipsGot->getVA() + InX::MipsGot->getTlsIndexOffset(File) -
|
||||
InX::MipsGot->getGp(File);
|
||||
case R_PAGE_PC:
|
||||
case R_PLT_PAGE_PC: {
|
||||
uint64_t Dest;
|
||||
if (Sym.isUndefWeak())
|
||||
Dest = getAArch64Page(A);
|
||||
else
|
||||
Dest = getAArch64Page(Sym.getVA(A));
|
||||
return Dest - getAArch64Page(P);
|
||||
return In.MipsGot->getVA() + In.MipsGot->getTlsIndexOffset(File) -
|
||||
In.MipsGot->getGp(File);
|
||||
case R_AARCH64_PAGE_PC: {
|
||||
uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getVA(A);
|
||||
return getAArch64Page(Val) - getAArch64Page(P);
|
||||
}
|
||||
case R_AARCH64_PLT_PAGE_PC: {
|
||||
uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getPltVA() + A;
|
||||
return getAArch64Page(Val) - getAArch64Page(P);
|
||||
}
|
||||
case R_RISCV_PC_INDIRECT: {
|
||||
if (const Relocation *HiRel = getRISCVPCRelHi20(&Sym, A))
|
||||
return getRelocTargetVA(File, HiRel->Type, HiRel->Addend, Sym.getVA(),
|
||||
*HiRel->Sym, HiRel->Expr);
|
||||
return 0;
|
||||
}
|
||||
case R_PC: {
|
||||
uint64_t Dest;
|
||||
@ -608,16 +721,12 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
|
||||
return 0;
|
||||
|
||||
// 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);
|
||||
// point is used for calls where the caller and callee (may) have different
|
||||
// TOC base pointers and r2 needs to be modified to hold the TOC base for
|
||||
// the callee. For local calls the caller and callee share the same
|
||||
// TOC base and so the TOC pointer initialization code should be skipped by
|
||||
// branching to the local entry point.
|
||||
return SymVA - P + getPPC64GlobalEntryToLocalEntryOffset(Sym.StOther);
|
||||
}
|
||||
case R_PPC_TOC:
|
||||
return getPPC64TocBase() + A;
|
||||
@ -634,48 +743,32 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
|
||||
// statically to zero.
|
||||
if (Sym.isTls() && Sym.isUndefWeak())
|
||||
return 0;
|
||||
|
||||
// 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;
|
||||
return Sym.getVA(A) + getTlsTpOffset();
|
||||
case R_RELAX_TLS_GD_TO_LE_NEG:
|
||||
case R_NEG_TLS:
|
||||
return Out::TlsPhdr->p_memsz - Sym.getVA(A);
|
||||
case R_SIZE:
|
||||
return Sym.getSize() + A;
|
||||
case R_TLSDESC:
|
||||
return InX::Got->getGlobalDynAddr(Sym) + A;
|
||||
case R_TLSDESC_PAGE:
|
||||
return getAArch64Page(InX::Got->getGlobalDynAddr(Sym) + A) -
|
||||
return In.Got->getGlobalDynAddr(Sym) + A;
|
||||
case R_AARCH64_TLSDESC_PAGE:
|
||||
return getAArch64Page(In.Got->getGlobalDynAddr(Sym) + A) -
|
||||
getAArch64Page(P);
|
||||
case R_TLSGD_GOT:
|
||||
return InX::Got->getGlobalDynOffset(Sym) + A;
|
||||
return In.Got->getGlobalDynOffset(Sym) + A;
|
||||
case R_TLSGD_GOT_FROM_END:
|
||||
return InX::Got->getGlobalDynOffset(Sym) + A - InX::Got->getSize();
|
||||
return In.Got->getGlobalDynOffset(Sym) + A - In.Got->getSize();
|
||||
case R_TLSGD_PC:
|
||||
return InX::Got->getGlobalDynAddr(Sym) + A - P;
|
||||
return In.Got->getGlobalDynAddr(Sym) + A - P;
|
||||
case R_TLSLD_GOT_FROM_END:
|
||||
return InX::Got->getTlsIndexOff() + A - InX::Got->getSize();
|
||||
return In.Got->getTlsIndexOff() + A - In.Got->getSize();
|
||||
case R_TLSLD_GOT:
|
||||
return InX::Got->getTlsIndexOff() + A;
|
||||
return In.Got->getTlsIndexOff() + A;
|
||||
case R_TLSLD_PC:
|
||||
return InX::Got->getTlsIndexVA() + A - P;
|
||||
return In.Got->getTlsIndexVA() + A - P;
|
||||
default:
|
||||
llvm_unreachable("invalid expression");
|
||||
}
|
||||
llvm_unreachable("Invalid expression");
|
||||
}
|
||||
|
||||
// This function applies relocations to sections without SHF_ALLOC bit.
|
||||
@ -808,10 +901,10 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
|
||||
case R_RELAX_TLS_GD_TO_LE_NEG:
|
||||
Target->relaxTlsGdToLe(BufLoc, Type, TargetVA);
|
||||
break;
|
||||
case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
|
||||
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;
|
||||
@ -848,16 +941,20 @@ static void switchMorestackCallsToMorestackNonSplit(
|
||||
// __morestack inside that function should be switched to
|
||||
// __morestack_non_split.
|
||||
Symbol *MoreStackNonSplit = Symtab->find("__morestack_non_split");
|
||||
if (!MoreStackNonSplit) {
|
||||
error("Mixing split-stack objects requires a definition of "
|
||||
"__morestack_non_split");
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort both collections to compare addresses efficiently.
|
||||
llvm::sort(MorestackCalls.begin(), MorestackCalls.end(),
|
||||
[](const Relocation *L, const Relocation *R) {
|
||||
return L->Offset < R->Offset;
|
||||
});
|
||||
llvm::sort(MorestackCalls, [](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; });
|
||||
llvm::sort(Functions, [](const Defined *L, const Defined *R) {
|
||||
return L->Value < R->Value;
|
||||
});
|
||||
|
||||
auto It = MorestackCalls.begin();
|
||||
for (Defined *F : Functions) {
|
||||
@ -872,8 +969,8 @@ static void switchMorestackCallsToMorestackNonSplit(
|
||||
}
|
||||
}
|
||||
|
||||
static bool enclosingPrologueAdjusted(uint64_t Offset,
|
||||
const DenseSet<Defined *> &Prologues) {
|
||||
static bool enclosingPrologueAttempted(uint64_t Offset,
|
||||
const DenseSet<Defined *> &Prologues) {
|
||||
for (Defined *F : Prologues)
|
||||
if (F->Value <= Offset && Offset < F->Value + F->Size)
|
||||
return true;
|
||||
@ -889,7 +986,7 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
|
||||
uint8_t *End) {
|
||||
if (!getFile<ELFT>()->SplitStack)
|
||||
return;
|
||||
DenseSet<Defined *> AdjustedPrologues;
|
||||
DenseSet<Defined *> Prologues;
|
||||
std::vector<Relocation *> MorestackCalls;
|
||||
|
||||
for (Relocation &Rel : Relocations) {
|
||||
@ -898,15 +995,9 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
|
||||
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"))
|
||||
if (Rel.Sym->getName().startswith("__morestack")) {
|
||||
if (Rel.Sym->getName().equals("__morestack"))
|
||||
MorestackCalls.push_back(&Rel);
|
||||
continue;
|
||||
}
|
||||
@ -914,24 +1005,36 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
|
||||
// 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)
|
||||
if (Rel.Sym->Type != STT_FUNC)
|
||||
continue;
|
||||
|
||||
if (enclosingPrologueAdjusted(Rel.Offset, AdjustedPrologues))
|
||||
// If the callee's-file was compiled with split stack, nothing to do. In
|
||||
// this context, a "Defined" symbol is one "defined by the binary currently
|
||||
// being produced". So an "undefined" symbol might be provided by a shared
|
||||
// library. It is not possible to tell how such symbols were compiled, so be
|
||||
// conservative.
|
||||
if (Defined *D = dyn_cast<Defined>(Rel.Sym))
|
||||
if (InputSection *IS = cast_or_null<InputSection>(D->Section))
|
||||
if (!IS || !IS->getFile<ELFT>() || IS->getFile<ELFT>()->SplitStack)
|
||||
continue;
|
||||
|
||||
if (enclosingPrologueAttempted(Rel.Offset, Prologues))
|
||||
continue;
|
||||
|
||||
if (Defined *F = getEnclosingFunction<ELFT>(Rel.Offset)) {
|
||||
if (Target->adjustPrologueForCrossSplitStack(Buf + F->Value, End)) {
|
||||
AdjustedPrologues.insert(F);
|
||||
Prologues.insert(F);
|
||||
if (Target->adjustPrologueForCrossSplitStack(Buf + getOffset(F->Value),
|
||||
End, F->StOther))
|
||||
continue;
|
||||
}
|
||||
if (!getFile<ELFT>()->SomeNoSplitStack)
|
||||
error(lld::toString(this) + ": " + F->getName() +
|
||||
" (with -fsplit-stack) calls " + Rel.Sym->getName() +
|
||||
" (without -fsplit-stack), but couldn't adjust its prologue");
|
||||
}
|
||||
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);
|
||||
|
||||
if (Target->NeedsMoreStackNonSplit)
|
||||
switchMorestackCallsToMorestackNonSplit(Prologues, MorestackCalls);
|
||||
}
|
||||
|
||||
template <class ELFT> void InputSection::writeTo(uint8_t *Buf) {
|
||||
@ -960,10 +1063,23 @@ template <class ELFT> void InputSection::writeTo(uint8_t *Buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a compressed section, uncompress section contents directly
|
||||
// to the buffer.
|
||||
if (UncompressedSize >= 0 && !UncompressedBuf) {
|
||||
size_t Size = UncompressedSize;
|
||||
if (Error E = zlib::uncompress(toStringRef(RawData),
|
||||
(char *)(Buf + OutSecOff), Size))
|
||||
fatal(toString(this) +
|
||||
": uncompress failed: " + llvm::toString(std::move(E)));
|
||||
uint8_t *BufEnd = Buf + OutSecOff + Size;
|
||||
relocate<ELFT>(Buf, BufEnd);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy section contents from source object file to output file
|
||||
// and then apply relocations.
|
||||
memcpy(Buf + OutSecOff, Data.data(), Data.size());
|
||||
uint8_t *BufEnd = Buf + OutSecOff + Data.size();
|
||||
memcpy(Buf + OutSecOff, data().data(), data().size());
|
||||
uint8_t *BufEnd = Buf + OutSecOff + data().size();
|
||||
relocate<ELFT>(Buf, BufEnd);
|
||||
}
|
||||
|
||||
@ -1014,7 +1130,7 @@ template <class ELFT> void EhInputSection::split() {
|
||||
template <class ELFT, class RelTy>
|
||||
void EhInputSection::split(ArrayRef<RelTy> Rels) {
|
||||
unsigned RelI = 0;
|
||||
for (size_t Off = 0, End = Data.size(); Off != End;) {
|
||||
for (size_t Off = 0, End = data().size(); Off != End;) {
|
||||
size_t Size = readEhRecordSize(this, Off);
|
||||
Pieces.emplace_back(Off, this, Size, getReloc(Off, Size, Rels, RelI));
|
||||
// The empty record is the end marker.
|
||||
@ -1094,65 +1210,32 @@ void MergeInputSection::splitIntoPieces() {
|
||||
assert(Pieces.empty());
|
||||
|
||||
if (Flags & SHF_STRINGS)
|
||||
splitStrings(Data, Entsize);
|
||||
splitStrings(data(), Entsize);
|
||||
else
|
||||
splitNonStrings(Data, Entsize);
|
||||
|
||||
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>
|
||||
static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) {
|
||||
size_t Size = std::distance(First, Last);
|
||||
assert(Size != 0);
|
||||
while (Size != 1) {
|
||||
size_t H = Size / 2;
|
||||
const It MI = First + H;
|
||||
Size -= H;
|
||||
First = Comp(Value, *MI) ? First : First + H;
|
||||
}
|
||||
return Comp(Value, *First) ? First : First + 1;
|
||||
}
|
||||
|
||||
// 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(
|
||||
Sec->Pieces.begin(), Sec->Pieces.end(), Offset,
|
||||
[](const uint64_t &A, const SectionPiece &B) { return A < B.InputOff; });
|
||||
--I;
|
||||
return &*I;
|
||||
splitNonStrings(data(), Entsize);
|
||||
}
|
||||
|
||||
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 (this->data().size() <= Offset)
|
||||
fatal(toString(this) + ": offset is outside the section");
|
||||
|
||||
// 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);
|
||||
// In that case we need to do a binary search of the original section piece vector.
|
||||
auto It2 =
|
||||
llvm::upper_bound(Pieces, Offset, [](uint64_t Offset, SectionPiece P) {
|
||||
return Offset < P.InputOff;
|
||||
});
|
||||
return &It2[-1];
|
||||
}
|
||||
|
||||
// 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::getParentOffset(uint64_t Offset) const {
|
||||
// Find a string starting at a given offset.
|
||||
auto It = OffsetMap.find(Offset);
|
||||
if (It != OffsetMap.end())
|
||||
return Pieces[It->second].OutputOff;
|
||||
|
||||
// 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 =
|
||||
*findSectionPiece(const_cast<MergeInputSection *>(this), Offset);
|
||||
*(const_cast<MergeInputSection *>(this)->getSectionPiece (Offset));
|
||||
uint64_t Addend = Offset - Piece.InputOff;
|
||||
return Piece.OutputOff + Addend;
|
||||
}
|
||||
|
@ -115,7 +115,12 @@ public:
|
||||
return cast_or_null<ObjFile<ELFT>>(File);
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> Data;
|
||||
ArrayRef<uint8_t> data() const {
|
||||
if (UncompressedSize >= 0 && !UncompressedBuf)
|
||||
uncompress();
|
||||
return RawData;
|
||||
}
|
||||
|
||||
uint64_t getOffsetInFile() const;
|
||||
|
||||
// True if this section has already been placed to a linker script
|
||||
@ -169,11 +174,6 @@ public:
|
||||
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 maybeDecompress();
|
||||
|
||||
// Returns a source location string. Used to construct an error message.
|
||||
template <class ELFT> std::string getLocation(uint64_t Offset);
|
||||
std::string getSrcMsg(const Symbol &Sym, uint64_t Offset);
|
||||
@ -200,15 +200,21 @@ public:
|
||||
|
||||
|
||||
template <typename T> llvm::ArrayRef<T> getDataAs() const {
|
||||
size_t S = Data.size();
|
||||
size_t S = data().size();
|
||||
assert(S % sizeof(T) == 0);
|
||||
return llvm::makeArrayRef<T>((const T *)Data.data(), S / sizeof(T));
|
||||
return llvm::makeArrayRef<T>((const T *)data().data(), S / sizeof(T));
|
||||
}
|
||||
|
||||
private:
|
||||
// A pointer that owns decompressed data if a section is compressed by zlib.
|
||||
protected:
|
||||
void parseCompressedHeader();
|
||||
void uncompress() const;
|
||||
|
||||
mutable ArrayRef<uint8_t> RawData;
|
||||
|
||||
// A pointer that owns uncompressed data if a section is compressed by zlib.
|
||||
// Since the feature is not used often, this is usually a nullptr.
|
||||
std::unique_ptr<char[]> DecompressBuf;
|
||||
mutable std::unique_ptr<char[]> UncompressedBuf;
|
||||
int64_t UncompressedSize = -1;
|
||||
};
|
||||
|
||||
// SectionPiece represents a piece of splittable section contents.
|
||||
@ -247,7 +253,6 @@ public:
|
||||
// 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.
|
||||
@ -255,8 +260,8 @@ public:
|
||||
llvm::CachedHashStringRef getData(size_t I) const {
|
||||
size_t Begin = Pieces[I].InputOff;
|
||||
size_t End =
|
||||
(Pieces.size() - 1 == I) ? Data.size() : Pieces[I + 1].InputOff;
|
||||
return {toStringRef(Data.slice(Begin, End - Begin)), Pieces[I].Hash};
|
||||
(Pieces.size() - 1 == I) ? data().size() : Pieces[I + 1].InputOff;
|
||||
return {toStringRef(data().slice(Begin, End - Begin)), Pieces[I].Hash};
|
||||
}
|
||||
|
||||
// Returns the SectionPiece at a given input section offset.
|
||||
@ -277,7 +282,9 @@ struct EhSectionPiece {
|
||||
unsigned FirstRelocation)
|
||||
: InputOff(Off), Sec(Sec), Size(Size), FirstRelocation(FirstRelocation) {}
|
||||
|
||||
ArrayRef<uint8_t> data() { return {Sec->Data.data() + this->InputOff, Size}; }
|
||||
ArrayRef<uint8_t> data() {
|
||||
return {Sec->data().data() + this->InputOff, Size};
|
||||
}
|
||||
|
||||
size_t InputOff;
|
||||
ssize_t OutputOff = -1;
|
||||
@ -353,6 +360,7 @@ private:
|
||||
|
||||
// The list of all input sections.
|
||||
extern std::vector<InputSectionBase *> InputSections;
|
||||
|
||||
} // namespace elf
|
||||
|
||||
std::string toString(const elf::InputSectionBase *);
|
||||
|
@ -67,9 +67,10 @@ static std::string getThinLTOOutputFile(StringRef ModulePath) {
|
||||
static lto::Config createConfig() {
|
||||
lto::Config C;
|
||||
|
||||
// LLD supports the new relocations.
|
||||
// LLD supports the new relocations and address-significance tables.
|
||||
C.Options = InitTargetOptionsFromCodeGenFlags();
|
||||
C.Options.RelaxELFRelocations = true;
|
||||
C.Options.EmitAddrsig = true;
|
||||
|
||||
// Always emit a section per function/datum with LTO.
|
||||
C.Options.FunctionSections = true;
|
||||
@ -87,6 +88,7 @@ static lto::Config createConfig() {
|
||||
C.DiagHandler = diagnosticHandler;
|
||||
C.OptLevel = Config->LTOO;
|
||||
C.CPU = GetCPUStr();
|
||||
C.MAttrs = GetMAttrs();
|
||||
|
||||
// Set up a custom pipeline if we've been asked to.
|
||||
C.OptPipeline = Config->LTONewPmPasses;
|
||||
@ -101,6 +103,14 @@ static lto::Config createConfig() {
|
||||
C.DebugPassManager = Config->LTODebugPassManager;
|
||||
C.DwoDir = Config->DwoDir;
|
||||
|
||||
if (Config->EmitLLVM) {
|
||||
C.PostInternalizeModuleHook = [](size_t Task, const Module &M) {
|
||||
if (std::unique_ptr<raw_fd_ostream> OS = openFile(Config->OutputFile))
|
||||
WriteBitcodeToFile(M, *OS, false);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
if (Config->SaveTemps)
|
||||
checkError(C.addSaveTemps(Config->OutputFile.str() + ".",
|
||||
/*UseInputModulePath*/ true));
|
||||
@ -108,18 +118,14 @@ static lto::Config createConfig() {
|
||||
}
|
||||
|
||||
BitcodeCompiler::BitcodeCompiler() {
|
||||
// Initialize IndexFile.
|
||||
if (!Config->ThinLTOIndexOnlyArg.empty())
|
||||
IndexFile = openFile(Config->ThinLTOIndexOnlyArg);
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
auto OnIndexWrite = [&](StringRef S) { ThinIndices.erase(S); };
|
||||
Backend = lto::createWriteIndexesThinBackend(
|
||||
Config->ThinLTOPrefixReplace.first, Config->ThinLTOPrefixReplace.second,
|
||||
Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite);
|
||||
@ -132,10 +138,10 @@ BitcodeCompiler::BitcodeCompiler() {
|
||||
|
||||
// Initialize UsedStartStop.
|
||||
for (Symbol *Sym : Symtab->getSymbols()) {
|
||||
StringRef Name = Sym->getName();
|
||||
StringRef S = Sym->getName();
|
||||
for (StringRef Prefix : {"__start_", "__stop_"})
|
||||
if (Name.startswith(Prefix))
|
||||
UsedStartStop.insert(Name.substr(Prefix.size()));
|
||||
if (S.startswith(Prefix))
|
||||
UsedStartStop.insert(S.substr(Prefix.size()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +157,7 @@ void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
bool IsExec = !Config->Shared && !Config->Relocatable;
|
||||
|
||||
if (Config->ThinLTOIndexOnly)
|
||||
ObjectToIndexFileState.insert({Obj.getName(), false});
|
||||
ThinIndices.insert(Obj.getName());
|
||||
|
||||
ArrayRef<Symbol *> Syms = F.getSymbols();
|
||||
ArrayRef<lto::InputFile::Symbol> ObjSyms = Obj.symbols();
|
||||
@ -240,15 +246,11 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||
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");
|
||||
}
|
||||
for (StringRef S : ThinIndices) {
|
||||
std::string Path = getThinLTOOutputFile(S);
|
||||
openFile(Path + ".thinlto.bc");
|
||||
if (Config->ThinLTOEmitImportsFiles)
|
||||
openFile(Path + ".imports");
|
||||
}
|
||||
|
||||
// If LazyObjFile has not been added to link, emit empty index files.
|
||||
|
@ -55,7 +55,7 @@ private:
|
||||
std::vector<std::unique_ptr<MemoryBuffer>> Files;
|
||||
llvm::DenseSet<StringRef> UsedStartStop;
|
||||
std::unique_ptr<llvm::raw_fd_ostream> IndexFile;
|
||||
llvm::StringMap<bool> ObjectToIndexFileState;
|
||||
llvm::DenseSet<StringRef> ThinIndices;
|
||||
};
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -169,7 +169,7 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
|
||||
// Define a symbol.
|
||||
Symbol *Sym;
|
||||
uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
|
||||
std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, /*Type*/ 0, Visibility,
|
||||
std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility,
|
||||
/*CanOmitFromDynSym*/ false,
|
||||
/*File*/ nullptr);
|
||||
ExprValue Value = Cmd->Expression();
|
||||
@ -202,13 +202,14 @@ static void declareSymbol(SymbolAssignment *Cmd) {
|
||||
// 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,
|
||||
std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, 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;
|
||||
Sym->ScriptDefined = true;
|
||||
}
|
||||
|
||||
// This method is used to handle INSERT AFTER statement. Here we rebuild
|
||||
@ -414,18 +415,16 @@ 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::RelaPlt || S == InX::RelaDyn ||
|
||||
S == InX::RelrDyn)
|
||||
if (S == In.ShStrTab || S == In.RelaDyn || S == In.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;
|
||||
if (S == In.GnuHashTab)
|
||||
In.GnuHashTab = nullptr;
|
||||
if (S == In.HashTab)
|
||||
In.HashTab = nullptr;
|
||||
|
||||
S->Assigned = false;
|
||||
S->Live = false;
|
||||
@ -701,6 +700,7 @@ uint64_t LinkerScript::advance(uint64_t Size, unsigned Alignment) {
|
||||
}
|
||||
|
||||
void LinkerScript::output(InputSection *S) {
|
||||
assert(Ctx->OutSec == S->getParent());
|
||||
uint64_t Before = advance(0, 1);
|
||||
uint64_t Pos = advance(S->getSize(), S->Alignment);
|
||||
S->OutSecOff = Pos - S->getSize() - Ctx->OutSec->Addr;
|
||||
@ -816,21 +816,8 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
|
||||
// Handle a single input section description command.
|
||||
// It calculates and assigns the offsets for each section and also
|
||||
// updates the output section size.
|
||||
auto *Cmd = cast<InputSectionDescription>(Base);
|
||||
for (InputSection *Sec : Cmd->Sections) {
|
||||
// We tentatively added all synthetic sections at the beginning and
|
||||
// removed empty ones afterwards (because there is no way to know
|
||||
// whether they were going be empty or not other than actually running
|
||||
// linker scripts.) We need to ignore remains of empty sections.
|
||||
if (auto *S = dyn_cast<SyntheticSection>(Sec))
|
||||
if (S->empty())
|
||||
continue;
|
||||
|
||||
if (!Sec->Live)
|
||||
continue;
|
||||
assert(Ctx->OutSec == Sec->getParent());
|
||||
for (InputSection *Sec : cast<InputSectionDescription>(Base)->Sections)
|
||||
output(Sec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,12 +30,13 @@ namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class Defined;
|
||||
class Symbol;
|
||||
class InputSectionBase;
|
||||
class InputSection;
|
||||
class OutputSection;
|
||||
class InputSectionBase;
|
||||
class InputSectionBase;
|
||||
class OutputSection;
|
||||
class SectionBase;
|
||||
class Symbol;
|
||||
class ThunkSection;
|
||||
|
||||
// This represents an r-value in the linker script.
|
||||
struct ExprValue {
|
||||
@ -145,7 +146,9 @@ struct MemoryRegion {
|
||||
// Also it may be surrounded with SORT() command, so contains sorting rules.
|
||||
struct SectionPattern {
|
||||
SectionPattern(StringMatcher &&Pat1, StringMatcher &&Pat2)
|
||||
: ExcludedFilePat(Pat1), SectionPat(Pat2) {}
|
||||
: ExcludedFilePat(Pat1), SectionPat(Pat2),
|
||||
SortOuter(SortSectionPolicy::Default),
|
||||
SortInner(SortSectionPolicy::Default) {}
|
||||
|
||||
StringMatcher ExcludedFilePat;
|
||||
StringMatcher SectionPat;
|
||||
@ -153,7 +156,6 @@ struct SectionPattern {
|
||||
SortSectionPolicy SortInner;
|
||||
};
|
||||
|
||||
class ThunkSection;
|
||||
struct InputSectionDescription : BaseCommand {
|
||||
InputSectionDescription(StringRef FilePattern)
|
||||
: BaseCommand(InputSectionKind), FilePat(FilePattern) {}
|
||||
|
@ -126,7 +126,7 @@ static void printEhFrame(raw_ostream &OS, OutputSection *OSec) {
|
||||
};
|
||||
|
||||
// Gather section pieces.
|
||||
for (const CieRecord *Rec : InX::EhFrame->getCieRecords()) {
|
||||
for (const CieRecord *Rec : In.EhFrame->getCieRecords()) {
|
||||
Add(*Rec->Cie);
|
||||
for (const EhSectionPiece *Fde : Rec->Fdes)
|
||||
Add(*Fde);
|
||||
@ -163,17 +163,18 @@ void elf::writeMapFile() {
|
||||
OS << right_justify("VMA", W) << ' ' << right_justify("LMA", W)
|
||||
<< " Size Align Out In Symbol\n";
|
||||
|
||||
OutputSection* OSec = nullptr;
|
||||
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);
|
||||
uint64_t LMA = OSec ? OSec->getLMA() + Cmd->Addr - OSec->getVA(0) : 0;
|
||||
writeHeader(OS, Cmd->Addr, LMA, Cmd->Size, 1);
|
||||
OS << Cmd->CommandString << '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *OSec = cast<OutputSection>(Base);
|
||||
OSec = cast<OutputSection>(Base);
|
||||
writeHeader(OS, OSec->Addr, OSec->getLMA(), OSec->Size, OSec->Alignment);
|
||||
OS << OSec->Name << '\n';
|
||||
|
||||
@ -181,7 +182,7 @@ void elf::writeMapFile() {
|
||||
for (BaseCommand *Base : OSec->SectionCommands) {
|
||||
if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
|
||||
for (InputSection *IS : ISD->Sections) {
|
||||
if (IS == InX::EhFrame) {
|
||||
if (IS == In.EhFrame) {
|
||||
printEhFrame(OS, OSec);
|
||||
continue;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ using namespace lld::elf;
|
||||
template <class ELFT>
|
||||
static typename ELFT::uint getAddend(InputSectionBase &Sec,
|
||||
const typename ELFT::Rel &Rel) {
|
||||
return Target->getImplicitAddend(Sec.Data.begin() + Rel.r_offset,
|
||||
return Target->getImplicitAddend(Sec.data().begin() + Rel.r_offset,
|
||||
Rel.getType(Config->IsMips64EL));
|
||||
}
|
||||
|
||||
@ -250,9 +250,10 @@ template <class ELFT> static void doGcSections() {
|
||||
|
||||
if (Sec->Flags & SHF_LINK_ORDER)
|
||||
continue;
|
||||
if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec))
|
||||
|
||||
if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec)) {
|
||||
Enqueue(Sec, 0);
|
||||
else if (isValidCIdentifier(Sec->Name)) {
|
||||
} else if (isValidCIdentifier(Sec->Name)) {
|
||||
CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec);
|
||||
CNamedSections[Saver.save("__stop_" + Sec->Name)].push_back(Sec);
|
||||
}
|
||||
@ -267,10 +268,16 @@ template <class ELFT> static void doGcSections() {
|
||||
// input sections. This function make some or all of them on
|
||||
// so that they are emitted to the output file.
|
||||
template <class ELFT> void elf::markLive() {
|
||||
// If -gc-sections is missing, no sections are removed.
|
||||
if (!Config->GcSections) {
|
||||
// If -gc-sections is missing, no sections are removed.
|
||||
for (InputSectionBase *Sec : InputSections)
|
||||
Sec->Live = true;
|
||||
|
||||
// If a DSO defines a symbol referenced in a regular object, it is needed.
|
||||
for (Symbol *Sym : Symtab->getSymbols())
|
||||
if (auto *S = dyn_cast<SharedSymbol>(Sym))
|
||||
if (S->IsUsedInRegularObj && !S->isWeak())
|
||||
S->getFile<ELFT>().IsNeeded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,12 @@ defm compress_debug_sections:
|
||||
|
||||
defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">;
|
||||
|
||||
defm split_stack_adjust_size
|
||||
: Eq<"split-stack-adjust-size",
|
||||
"Specify adjustment to stack size when a split-stack function calls a "
|
||||
"non-split-stack function">,
|
||||
MetaVarName<"<value>">;
|
||||
|
||||
defm library_path:
|
||||
Eq<"library-path", "Add a directory to the library search path">, MetaVarName<"<dir>">;
|
||||
|
||||
@ -68,6 +74,10 @@ defm as_needed: B<"as-needed",
|
||||
defm call_graph_ordering_file:
|
||||
Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">;
|
||||
|
||||
defm call_graph_profile_sort: B<"call-graph-profile-sort",
|
||||
"Reorder sections with call graph profile (default)",
|
||||
"Do not reorder sections with call graph profile">;
|
||||
|
||||
// -chroot doesn't have a help text because it is an internal option.
|
||||
def chroot: Separate<["--", "-"], "chroot">;
|
||||
|
||||
@ -132,7 +142,7 @@ def error_unresolved_symbols: F<"error-unresolved-symbols">,
|
||||
defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">;
|
||||
|
||||
defm execute_only: B<"execute-only",
|
||||
"Do not mark executable sections readable",
|
||||
"Mark executable sections unreadable",
|
||||
"Mark executable sections readable (default)">;
|
||||
|
||||
defm export_dynamic: B<"export-dynamic",
|
||||
@ -315,6 +325,10 @@ defm threads: B<"threads",
|
||||
"Run the linker multi-threaded (default)",
|
||||
"Do not run the linker multi-threaded">;
|
||||
|
||||
defm toc_optimize : B<"toc-optimize",
|
||||
"(PowerPC64) Enable TOC related optimizations (default)",
|
||||
"(PowerPC64) Disable TOC related optimizations">;
|
||||
|
||||
def trace: F<"trace">, HelpText<"Print the names of the input files">;
|
||||
|
||||
defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">;
|
||||
@ -348,6 +362,10 @@ defm warn_common: B<"warn-common",
|
||||
"Warn about duplicate common symbols",
|
||||
"Do not warn about duplicate common symbols (default)">;
|
||||
|
||||
defm warn_ifunc_textrel: B<"warn-ifunc-textrel",
|
||||
"Warn about using ifunc symbols with text relocations",
|
||||
"Do not warn about using ifunc symbols with text relocations (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">;
|
||||
@ -440,6 +458,7 @@ def: F<"plugin-opt=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 plugin_opt_emit_llvm: F<"plugin-opt=emit-llvm">;
|
||||
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=">;
|
||||
|
@ -25,6 +25,7 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::dwarf;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
@ -32,7 +33,6 @@ using namespace lld::elf;
|
||||
|
||||
uint8_t Out::First;
|
||||
PhdrEntry *Out::TlsPhdr;
|
||||
OutputSection *Out::DebugInfo;
|
||||
OutputSection *Out::ElfHeader;
|
||||
OutputSection *Out::ProgramHeaders;
|
||||
OutputSection *Out::PreinitArray;
|
||||
@ -95,7 +95,7 @@ void OutputSection::addSection(InputSection *IS) {
|
||||
Flags = IS->Flags;
|
||||
} else {
|
||||
// Otherwise, check if new type or flags are compatible with existing ones.
|
||||
unsigned Mask = SHF_ALLOC | SHF_TLS | SHF_LINK_ORDER;
|
||||
unsigned Mask = 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 +
|
||||
@ -171,11 +171,12 @@ void OutputSection::sort(llvm::function_ref<int(InputSectionBase *S)> Order) {
|
||||
|
||||
// Fill [Buf, Buf + Size) with Filler.
|
||||
// This is used for linker script "=fillexp" command.
|
||||
static void fill(uint8_t *Buf, size_t Size, uint32_t Filler) {
|
||||
static void fill(uint8_t *Buf, size_t Size,
|
||||
const std::array<uint8_t, 4> &Filler) {
|
||||
size_t I = 0;
|
||||
for (; I + 4 < Size; I += 4)
|
||||
memcpy(Buf + I, &Filler, 4);
|
||||
memcpy(Buf + I, &Filler, Size - I);
|
||||
memcpy(Buf + I, Filler.data(), 4);
|
||||
memcpy(Buf + I, Filler.data(), Size - I);
|
||||
}
|
||||
|
||||
// Compress section contents if this section contains debug info.
|
||||
@ -236,8 +237,9 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
|
||||
|
||||
// Write leading padding.
|
||||
std::vector<InputSection *> Sections = getInputSections(this);
|
||||
uint32_t Filler = getFiller();
|
||||
if (Filler)
|
||||
std::array<uint8_t, 4> Filler = getFiller();
|
||||
bool NonZeroFiller = read32(Filler.data()) != 0;
|
||||
if (NonZeroFiller)
|
||||
fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler);
|
||||
|
||||
parallelForEachN(0, Sections.size(), [&](size_t I) {
|
||||
@ -245,7 +247,7 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
|
||||
IS->writeTo<ELFT>(Buf);
|
||||
|
||||
// Fill gaps between sections.
|
||||
if (Filler) {
|
||||
if (NonZeroFiller) {
|
||||
uint8_t *Start = Buf + IS->OutSecOff + IS->getSize();
|
||||
uint8_t *End;
|
||||
if (I + 1 == Sections.size())
|
||||
@ -270,13 +272,13 @@ static void finalizeShtGroup(OutputSection *OS,
|
||||
|
||||
// sh_link field for SHT_GROUP sections should contain the section index of
|
||||
// the symbol table.
|
||||
OS->Link = InX::SymTab->getParent()->SectionIndex;
|
||||
OS->Link = In.SymTab->getParent()->SectionIndex;
|
||||
|
||||
// sh_info then contain index of an entry in symbol table section which
|
||||
// provides signature of the section group.
|
||||
ObjFile<ELFT> *Obj = Section->getFile<ELFT>();
|
||||
ArrayRef<Symbol *> Symbols = Obj->getSymbols();
|
||||
OS->Info = InX::SymTab->getSymbolIndex(Symbols[Section->Info]);
|
||||
OS->Info = In.SymTab->getSymbolIndex(Symbols[Section->Info]);
|
||||
}
|
||||
|
||||
template <class ELFT> void OutputSection::finalize() {
|
||||
@ -308,7 +310,7 @@ template <class ELFT> void OutputSection::finalize() {
|
||||
if (isa<SyntheticSection>(First))
|
||||
return;
|
||||
|
||||
Link = InX::SymTab->getParent()->SectionIndex;
|
||||
Link = In.SymTab->getParent()->SectionIndex;
|
||||
// sh_info for SHT_REL[A] sections should contain the section header index of
|
||||
// the section to which the relocation applies.
|
||||
InputSectionBase *S = First->getRelocatedSection();
|
||||
@ -406,12 +408,12 @@ void OutputSection::sortInitFini() {
|
||||
sort([](InputSectionBase *S) { return getPriority(S->Name); });
|
||||
}
|
||||
|
||||
uint32_t OutputSection::getFiller() {
|
||||
std::array<uint8_t, 4> OutputSection::getFiller() {
|
||||
if (Filler)
|
||||
return *Filler;
|
||||
if (Flags & SHF_EXECINSTR)
|
||||
return Target->TrapInstr;
|
||||
return 0;
|
||||
return {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
template void OutputSection::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include <array>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
@ -94,7 +95,7 @@ public:
|
||||
Expr SubalignExpr;
|
||||
std::vector<BaseCommand *> SectionCommands;
|
||||
std::vector<StringRef> Phdrs;
|
||||
llvm::Optional<uint32_t> Filler;
|
||||
llvm::Optional<std::array<uint8_t, 4>> Filler;
|
||||
ConstraintKind Constraint = ConstraintKind::NoConstraint;
|
||||
std::string Location;
|
||||
std::string MemoryRegionName;
|
||||
@ -117,7 +118,7 @@ private:
|
||||
std::vector<uint8_t> ZDebugHeader;
|
||||
llvm::SmallVector<char, 1> CompressedData;
|
||||
|
||||
uint32_t getFiller();
|
||||
std::array<uint8_t, 4> getFiller();
|
||||
};
|
||||
|
||||
int getPriority(StringRef S);
|
||||
@ -130,7 +131,6 @@ std::vector<InputSection *> getInputSections(OutputSection* OS);
|
||||
struct Out {
|
||||
static uint8_t First;
|
||||
static PhdrEntry *TlsPhdr;
|
||||
static OutputSection *DebugInfo;
|
||||
static OutputSection *ElfHeader;
|
||||
static OutputSection *ProgramHeaders;
|
||||
static OutputSection *PreinitArray;
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
@ -65,6 +66,14 @@ using namespace llvm::support::endian;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
static Optional<std::string> getLinkerScriptLocation(const Symbol &Sym) {
|
||||
for (BaseCommand *Base : Script->SectionCommands)
|
||||
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base))
|
||||
if (Cmd->Sym == &Sym)
|
||||
return Cmd->Location;
|
||||
return None;
|
||||
}
|
||||
|
||||
// Construct a message in the following format.
|
||||
//
|
||||
// >>> defined in /home/alice/src/foo.o
|
||||
@ -72,8 +81,13 @@ using namespace lld::elf;
|
||||
// >>> /home/alice/src/bar.o:(.text+0x1)
|
||||
static std::string getLocation(InputSectionBase &S, const Symbol &Sym,
|
||||
uint64_t Off) {
|
||||
std::string Msg =
|
||||
"\n>>> defined in " + toString(Sym.File) + "\n>>> referenced by ";
|
||||
std::string Msg = "\n>>> defined in ";
|
||||
if (Sym.File)
|
||||
Msg += toString(Sym.File);
|
||||
else if (Optional<std::string> Loc = getLinkerScriptLocation(Sym))
|
||||
Msg += *Loc;
|
||||
|
||||
Msg += "\n>>> referenced by ";
|
||||
std::string Src = S.getSrcMsg(Sym, Off);
|
||||
if (!Src.empty())
|
||||
Msg += Src + "\n>>> ";
|
||||
@ -90,12 +104,12 @@ static unsigned handleMipsTlsRelocation(RelType Type, Symbol &Sym,
|
||||
InputSectionBase &C, uint64_t Offset,
|
||||
int64_t Addend, RelExpr Expr) {
|
||||
if (Expr == R_MIPS_TLSLD) {
|
||||
InX::MipsGot->addTlsIndex(*C.File);
|
||||
In.MipsGot->addTlsIndex(*C.File);
|
||||
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
|
||||
return 1;
|
||||
}
|
||||
if (Expr == R_MIPS_TLSGD) {
|
||||
InX::MipsGot->addDynTlsEntry(*C.File, Sym);
|
||||
In.MipsGot->addDynTlsEntry(*C.File, Sym);
|
||||
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
|
||||
return 1;
|
||||
}
|
||||
@ -128,17 +142,17 @@ static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym,
|
||||
|
||||
auto AddTlsReloc = [&](uint64_t Off, RelType Type, Symbol *Dest, bool Dyn) {
|
||||
if (Dyn)
|
||||
InX::RelaDyn->addReloc(Type, InX::Got, Off, Dest);
|
||||
In.RelaDyn->addReloc(Type, In.Got, Off, Dest);
|
||||
else
|
||||
InX::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest});
|
||||
In.Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest});
|
||||
};
|
||||
|
||||
// Local Dynamic is for access to module local TLS variables, while still
|
||||
// being suitable for being dynamically loaded via dlopen.
|
||||
// GOT[e0] is the module index, with a special value of 0 for the current
|
||||
// module. GOT[e1] is unused. There only needs to be one module index entry.
|
||||
if (Expr == R_TLSLD_PC && InX::Got->addTlsIndex()) {
|
||||
AddTlsReloc(InX::Got->getTlsIndexOff(), Target->TlsModuleIndexRel,
|
||||
if (Expr == R_TLSLD_PC && In.Got->addTlsIndex()) {
|
||||
AddTlsReloc(In.Got->getTlsIndexOff(), Target->TlsModuleIndexRel,
|
||||
NeedDynId ? nullptr : &Sym, NeedDynId);
|
||||
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
|
||||
return 1;
|
||||
@ -148,8 +162,8 @@ static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym,
|
||||
// the module index and offset of symbol in TLS block we can fill these in
|
||||
// using static GOT relocations.
|
||||
if (Expr == R_TLSGD_PC) {
|
||||
if (InX::Got->addDynTlsEntry(Sym)) {
|
||||
uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
|
||||
if (In.Got->addDynTlsEntry(Sym)) {
|
||||
uint64_t Off = In.Got->getGlobalDynOffset(Sym);
|
||||
AddTlsReloc(Off, Target->TlsModuleIndexRel, &Sym, NeedDynId);
|
||||
AddTlsReloc(Off + Config->Wordsize, Target->TlsOffsetRel, &Sym,
|
||||
NeedDynOff);
|
||||
@ -165,9 +179,6 @@ template <class ELFT>
|
||||
static unsigned
|
||||
handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
|
||||
typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) {
|
||||
if (!(C.Flags & SHF_ALLOC))
|
||||
return 0;
|
||||
|
||||
if (!Sym.isTls())
|
||||
return 0;
|
||||
|
||||
@ -176,12 +187,12 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
|
||||
if (Config->EMachine == EM_MIPS)
|
||||
return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr);
|
||||
|
||||
if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) &&
|
||||
if (isRelExprOneOf<R_TLSDESC, R_AARCH64_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) &&
|
||||
Config->Shared) {
|
||||
if (InX::Got->addDynTlsEntry(Sym)) {
|
||||
uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
|
||||
InX::RelaDyn->addReloc(
|
||||
{Target->TlsDescRel, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0});
|
||||
if (In.Got->addDynTlsEntry(Sym)) {
|
||||
uint64_t Off = In.Got->getGlobalDynOffset(Sym);
|
||||
In.RelaDyn->addReloc(
|
||||
{Target->TlsDescRel, In.Got, Off, !Sym.IsPreemptible, &Sym, 0});
|
||||
}
|
||||
if (Expr != R_TLSDESC_CALL)
|
||||
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
|
||||
@ -199,9 +210,9 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
|
||||
}
|
||||
if (Expr == R_TLSLD_HINT)
|
||||
return 1;
|
||||
if (InX::Got->addTlsIndex())
|
||||
InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::Got,
|
||||
InX::Got->getTlsIndexOff(), nullptr);
|
||||
if (In.Got->addTlsIndex())
|
||||
In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got,
|
||||
In.Got->getTlsIndexOff(), nullptr);
|
||||
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
|
||||
return 1;
|
||||
}
|
||||
@ -223,29 +234,29 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
|
||||
return 1;
|
||||
}
|
||||
if (!Sym.isInGot()) {
|
||||
InX::Got->addEntry(Sym);
|
||||
In.Got->addEntry(Sym);
|
||||
uint64_t Off = Sym.getGotOffset();
|
||||
InX::Got->Relocations.push_back({R_ABS, Target->TlsOffsetRel, Off, 0, &Sym});
|
||||
In.Got->Relocations.push_back(
|
||||
{R_ABS, Target->TlsOffsetRel, Off, 0, &Sym});
|
||||
}
|
||||
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL, R_TLSGD_GOT,
|
||||
R_TLSGD_GOT_FROM_END, R_TLSGD_PC>(Expr)) {
|
||||
if (isRelExprOneOf<R_TLSDESC, R_AARCH64_TLSDESC_PAGE, R_TLSDESC_CALL,
|
||||
R_TLSGD_GOT, R_TLSGD_GOT_FROM_END, R_TLSGD_PC>(Expr)) {
|
||||
if (Config->Shared) {
|
||||
if (InX::Got->addDynTlsEntry(Sym)) {
|
||||
uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
|
||||
InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::Got, Off, &Sym);
|
||||
if (In.Got->addDynTlsEntry(Sym)) {
|
||||
uint64_t Off = In.Got->getGlobalDynOffset(Sym);
|
||||
In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got, Off, &Sym);
|
||||
|
||||
// If the symbol is preemptible we need the dynamic linker to write
|
||||
// the offset too.
|
||||
uint64_t OffsetOff = Off + Config->Wordsize;
|
||||
if (Sym.IsPreemptible)
|
||||
InX::RelaDyn->addReloc(Target->TlsOffsetRel, InX::Got, OffsetOff,
|
||||
&Sym);
|
||||
In.RelaDyn->addReloc(Target->TlsOffsetRel, In.Got, OffsetOff, &Sym);
|
||||
else
|
||||
InX::Got->Relocations.push_back(
|
||||
In.Got->Relocations.push_back(
|
||||
{R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym});
|
||||
}
|
||||
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
|
||||
@ -259,9 +270,9 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
|
||||
{Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type,
|
||||
Offset, Addend, &Sym});
|
||||
if (!Sym.isInGot()) {
|
||||
InX::Got->addEntry(Sym);
|
||||
InX::RelaDyn->addReloc(Target->TlsGotRel, InX::Got, Sym.getGotOffset(),
|
||||
&Sym);
|
||||
In.Got->addEntry(Sym);
|
||||
In.RelaDyn->addReloc(Target->TlsGotRel, In.Got, Sym.getGotOffset(),
|
||||
&Sym);
|
||||
}
|
||||
} else {
|
||||
C.Relocations.push_back(
|
||||
@ -273,13 +284,14 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
|
||||
|
||||
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
|
||||
// defined.
|
||||
if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_GOT_PAGE_PC>(Expr) &&
|
||||
if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_AARCH64_GOT_PAGE_PC,
|
||||
R_GOT_OFF, R_TLSIE_HINT>(Expr) &&
|
||||
!Config->Shared && !Sym.IsPreemptible) {
|
||||
C.Relocations.push_back({R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Sym});
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (Expr == R_TLSDESC_CALL)
|
||||
if (Expr == R_TLSIE_HINT)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@ -325,23 +337,25 @@ static bool isAbsoluteValue(const Symbol &Sym) {
|
||||
|
||||
// Returns true if Expr refers a PLT entry.
|
||||
static bool needsPlt(RelExpr Expr) {
|
||||
return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_PLT_PAGE_PC>(Expr);
|
||||
return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_AARCH64_PLT_PAGE_PC,
|
||||
R_GOT_PLT, R_AARCH64_GOT_PAGE_PC_PLT>(Expr);
|
||||
}
|
||||
|
||||
// Returns true if Expr refers a GOT entry. Note that this function
|
||||
// returns false for TLS variables even though they need GOT, because
|
||||
// TLS variables uses GOT differently than the regular variables.
|
||||
static bool needsGot(RelExpr Expr) {
|
||||
return isRelExprOneOf<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF,
|
||||
R_MIPS_GOT_OFF32, R_GOT_PAGE_PC, R_GOT_PC,
|
||||
R_GOT_FROM_END>(Expr);
|
||||
return isRelExprOneOf<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE,
|
||||
R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC,
|
||||
R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC, R_GOT_FROM_END,
|
||||
R_GOT_PLT>(Expr);
|
||||
}
|
||||
|
||||
// True if this expression is of the form Sym - X, where X is a position in the
|
||||
// file (PC, or GOT for example).
|
||||
static bool isRelExpr(RelExpr Expr) {
|
||||
return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL,
|
||||
R_PPC_CALL, R_PPC_CALL_PLT, R_PAGE_PC,
|
||||
R_PPC_CALL, R_PPC_CALL_PLT, R_AARCH64_PAGE_PC,
|
||||
R_RELAX_GOT_PC>(Expr);
|
||||
}
|
||||
|
||||
@ -357,13 +371,14 @@ static bool isRelExpr(RelExpr Expr) {
|
||||
static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
|
||||
InputSectionBase &S, uint64_t RelOff) {
|
||||
// These expressions always compute a constant
|
||||
if (isRelExprOneOf<
|
||||
R_GOT_FROM_END, R_GOT_OFF, R_TLSLD_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE,
|
||||
R_MIPS_GOTREL, R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC,
|
||||
R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC,
|
||||
R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT, R_TLSGD_GOT_FROM_END,
|
||||
R_TLSGD_PC, R_PPC_CALL_PLT, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT,
|
||||
R_TLSLD_HINT>(E))
|
||||
if (isRelExprOneOf<R_GOT_FROM_END, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
|
||||
R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
|
||||
R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
|
||||
R_AARCH64_GOT_PAGE_PC, R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC,
|
||||
R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT,
|
||||
R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT,
|
||||
R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT,
|
||||
R_TLSLD_HINT, R_TLSIE_HINT>(E))
|
||||
return true;
|
||||
|
||||
// The computation involves output from the ifunc resolver.
|
||||
@ -372,7 +387,7 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
|
||||
|
||||
// These never do, except if the entire file is position dependent or if
|
||||
// only the low bits are used.
|
||||
if (E == R_GOT || E == R_PLT || E == R_TLSDESC)
|
||||
if (E == R_GOT || E == R_GOT_PLT || E == R_PLT || E == R_TLSDESC)
|
||||
return Target->usesOnlyLowPageBits(Type) || !Config->Pic;
|
||||
|
||||
if (Sym.IsPreemptible)
|
||||
@ -418,10 +433,14 @@ static RelExpr toPlt(RelExpr Expr) {
|
||||
return R_PPC_CALL_PLT;
|
||||
case R_PC:
|
||||
return R_PLT_PC;
|
||||
case R_PAGE_PC:
|
||||
return R_PLT_PAGE_PC;
|
||||
case R_AARCH64_PAGE_PC:
|
||||
return R_AARCH64_PLT_PAGE_PC;
|
||||
case R_AARCH64_GOT_PAGE_PC:
|
||||
return R_AARCH64_GOT_PAGE_PC_PLT;
|
||||
case R_ABS:
|
||||
return R_PLT;
|
||||
case R_GOT:
|
||||
return R_GOT_PLT;
|
||||
default:
|
||||
return Expr;
|
||||
}
|
||||
@ -470,7 +489,7 @@ static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &SS) {
|
||||
SmallSet<SharedSymbol *, 4> Ret;
|
||||
for (const Elf_Sym &S : File.getGlobalELFSyms()) {
|
||||
if (S.st_shndx == SHN_UNDEF || S.st_shndx == SHN_ABS ||
|
||||
S.st_value != SS.Value)
|
||||
S.getType() == STT_TLS || S.st_value != SS.Value)
|
||||
continue;
|
||||
StringRef Name = check(S.getName(File.getStringTable()));
|
||||
Symbol *Sym = Symtab->find(Name);
|
||||
@ -493,6 +512,7 @@ static void replaceWithDefined(Symbol &Sym, SectionBase *Sec, uint64_t Value,
|
||||
Sym.PltIndex = Old.PltIndex;
|
||||
Sym.GotIndex = Old.GotIndex;
|
||||
Sym.VerdefIndex = Old.VerdefIndex;
|
||||
Sym.PPC64BranchltIndex = Old.PPC64BranchltIndex;
|
||||
Sym.IsPreemptible = true;
|
||||
Sym.ExportDynamic = true;
|
||||
Sym.IsUsedInRegularObj = true;
|
||||
@ -553,9 +573,9 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) {
|
||||
BssSection *Sec = make<BssSection>(IsReadOnly ? ".bss.rel.ro" : ".bss",
|
||||
SymSize, SS.Alignment);
|
||||
if (IsReadOnly)
|
||||
InX::BssRelRo->getParent()->addSection(Sec);
|
||||
In.BssRelRo->getParent()->addSection(Sec);
|
||||
else
|
||||
InX::Bss->getParent()->addSection(Sec);
|
||||
In.Bss->getParent()->addSection(Sec);
|
||||
|
||||
// Look through the DSO's dynamic symbol table for aliases and create a
|
||||
// dynamic symbol for each one. This causes the copy relocation to correctly
|
||||
@ -563,7 +583,7 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) {
|
||||
for (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS))
|
||||
replaceWithDefined(*Sym, Sec, 0, Sym->Size);
|
||||
|
||||
InX::RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS);
|
||||
In.RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS);
|
||||
}
|
||||
|
||||
// MIPS has an odd notion of "paired" relocations to calculate addends.
|
||||
@ -587,7 +607,7 @@ static int64_t computeMipsAddend(const RelTy &Rel, const RelTy *End,
|
||||
if (PairTy == R_MIPS_NONE)
|
||||
return 0;
|
||||
|
||||
const uint8_t *Buf = Sec.Data.data();
|
||||
const uint8_t *Buf = Sec.data().data();
|
||||
uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
|
||||
|
||||
// To make things worse, paired relocations might not be contiguous in
|
||||
@ -615,7 +635,7 @@ static int64_t computeAddend(const RelTy &Rel, const RelTy *End,
|
||||
if (RelTy::IsRela) {
|
||||
Addend = getAddend<ELFT>(Rel);
|
||||
} else {
|
||||
const uint8_t *Buf = Sec.Data.data();
|
||||
const uint8_t *Buf = Sec.data().data();
|
||||
Addend = Target->getImplicitAddend(Buf + Rel.r_offset, Type);
|
||||
}
|
||||
|
||||
@ -631,9 +651,6 @@ static int64_t computeAddend(const RelTy &Rel, const RelTy *End,
|
||||
// Returns true if this function printed out an error message.
|
||||
static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
|
||||
uint64_t Offset) {
|
||||
if (Config->UnresolvedSymbols == UnresolvedPolicy::IgnoreAll)
|
||||
return false;
|
||||
|
||||
if (Sym.isLocal() || !Sym.isUndefined() || Sym.isWeak())
|
||||
return false;
|
||||
|
||||
@ -650,6 +667,10 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
|
||||
Msg += Src + "\n>>> ";
|
||||
Msg += Sec.getObjMsg(Offset);
|
||||
|
||||
if (Sym.getName().startswith("_ZTV"))
|
||||
Msg += "\nthe vtable symbol may be undefined because the class is missing "
|
||||
"its key function (see https://lld.llvm.org/missingkeyfunction)";
|
||||
|
||||
if ((Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) ||
|
||||
Config->NoinhibitExec) {
|
||||
warn(Msg);
|
||||
@ -704,7 +725,7 @@ public:
|
||||
while (I != Pieces.size() && Pieces[I].InputOff + Pieces[I].Size <= Off)
|
||||
++I;
|
||||
if (I == Pieces.size())
|
||||
return Off;
|
||||
fatal(".eh_frame: relocation is not in any piece");
|
||||
|
||||
// Pieces must be contiguous, so there must be no holes in between.
|
||||
assert(Pieces[I].InputOff <= Off && "Relocation not in any piece");
|
||||
@ -730,13 +751,13 @@ static void addRelativeReloc(InputSectionBase *IS, uint64_t OffsetInSec,
|
||||
// RelrDyn sections don't support odd offsets. Also, RelrDyn sections
|
||||
// don't store the addend values, so we must write it to the relocated
|
||||
// address.
|
||||
if (InX::RelrDyn && IS->Alignment >= 2 && OffsetInSec % 2 == 0) {
|
||||
if (In.RelrDyn && IS->Alignment >= 2 && OffsetInSec % 2 == 0) {
|
||||
IS->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym});
|
||||
InX::RelrDyn->Relocs.push_back({IS, OffsetInSec});
|
||||
In.RelrDyn->Relocs.push_back({IS, OffsetInSec});
|
||||
return;
|
||||
}
|
||||
InX::RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend,
|
||||
Expr, Type);
|
||||
In.RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend, Expr,
|
||||
Type);
|
||||
}
|
||||
|
||||
template <class ELFT, class GotPltSection>
|
||||
@ -749,9 +770,16 @@ static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt,
|
||||
}
|
||||
|
||||
template <class ELFT> static void addGotEntry(Symbol &Sym) {
|
||||
InX::Got->addEntry(Sym);
|
||||
In.Got->addEntry(Sym);
|
||||
|
||||
RelExpr Expr;
|
||||
if (Sym.isTls())
|
||||
Expr = R_TLS;
|
||||
else if (Sym.isGnuIFunc())
|
||||
Expr = R_PLT;
|
||||
else
|
||||
Expr = R_ABS;
|
||||
|
||||
RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS;
|
||||
uint64_t Off = Sym.getGotOffset();
|
||||
|
||||
// If a GOT slot value can be calculated at link-time, which is now,
|
||||
@ -764,19 +792,19 @@ template <class ELFT> static void addGotEntry(Symbol &Sym) {
|
||||
bool IsLinkTimeConstant =
|
||||
!Sym.IsPreemptible && (!Config->Pic || isAbsolute(Sym));
|
||||
if (IsLinkTimeConstant) {
|
||||
InX::Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym});
|
||||
In.Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym});
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we emit a dynamic relocation to .rel[a].dyn so that
|
||||
// the GOT slot will be fixed at load-time.
|
||||
if (!Sym.isTls() && !Sym.IsPreemptible && Config->Pic && !isAbsolute(Sym)) {
|
||||
addRelativeReloc(InX::Got, Off, &Sym, 0, R_ABS, Target->GotRel);
|
||||
addRelativeReloc(In.Got, Off, &Sym, 0, R_ABS, Target->GotRel);
|
||||
return;
|
||||
}
|
||||
InX::RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel,
|
||||
InX::Got, Off, &Sym, 0,
|
||||
Sym.IsPreemptible ? R_ADDEND : R_ABS, Target->GotRel);
|
||||
In.RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel, In.Got,
|
||||
Off, &Sym, 0, Sym.IsPreemptible ? R_ADDEND : R_ABS,
|
||||
Target->GotRel);
|
||||
}
|
||||
|
||||
// Return true if we can define a symbol in the executable that
|
||||
@ -833,7 +861,7 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
|
||||
addRelativeReloc(&Sec, Offset, &Sym, Addend, Expr, Type);
|
||||
return;
|
||||
} else if (RelType Rel = Target->getDynRel(Type)) {
|
||||
InX::RelaDyn->addReloc(Rel, &Sec, Offset, &Sym, Addend, R_ADDEND, Type);
|
||||
In.RelaDyn->addReloc(Rel, &Sec, Offset, &Sym, Addend, R_ADDEND, Type);
|
||||
|
||||
// MIPS ABI turns using of GOT and dynamic relocations inside out.
|
||||
// While regular ABI uses dynamic relocations to fill up GOT entries
|
||||
@ -851,7 +879,7 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
|
||||
// a dynamic relocation.
|
||||
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19
|
||||
if (Config->EMachine == EM_MIPS)
|
||||
InX::MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
|
||||
In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -938,10 +966,9 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
|
||||
"' cannot be preempted; recompile with -fPIE" +
|
||||
getLocation(Sec, Sym, Offset));
|
||||
if (!Sym.isInPlt())
|
||||
addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel,
|
||||
Sym);
|
||||
addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
|
||||
if (!Sym.isDefined())
|
||||
replaceWithDefined(Sym, InX::Plt, Sym.getPltOffset(), 0);
|
||||
replaceWithDefined(Sym, In.Plt, getPltEntryOffset(Sym.PltIndex), 0);
|
||||
Sym.NeedsPltAddr = true;
|
||||
Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
|
||||
return;
|
||||
@ -975,7 +1002,7 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
|
||||
if (maybeReportUndefined(Sym, Sec, Rel.r_offset))
|
||||
return;
|
||||
|
||||
const uint8_t *RelocatedAddr = Sec.Data.begin() + Rel.r_offset;
|
||||
const uint8_t *RelocatedAddr = Sec.data().begin() + Rel.r_offset;
|
||||
RelExpr Expr = Target->getRelExpr(Type, Sym, RelocatedAddr);
|
||||
|
||||
// Ignore "hint" relocations because they are only markers for relaxation.
|
||||
@ -993,18 +1020,28 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
|
||||
// all dynamic symbols that can be resolved within the executable will
|
||||
// actually be resolved that way at runtime, because the main exectuable
|
||||
// is always at the beginning of a search list. We can leverage that fact.
|
||||
if (Sym.isGnuIFunc() && !Config->ZIfuncnoplt)
|
||||
if (Sym.isGnuIFunc() && !Config->ZIfuncnoplt) {
|
||||
if (!Config->ZText && Config->WarnIfuncTextrel) {
|
||||
warn("using ifunc symbols when text relocations are allowed may produce "
|
||||
"a binary that will segfault, if the object file is linked with "
|
||||
"old version of glibc (glibc 2.28 and earlier). If this applies to "
|
||||
"you, consider recompiling the object files without -fPIC and "
|
||||
"without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
|
||||
"turn off this warning." +
|
||||
getLocation(Sec, Sym, Offset));
|
||||
}
|
||||
Expr = toPlt(Expr);
|
||||
else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym))
|
||||
} else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) {
|
||||
Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr);
|
||||
else if (!Sym.IsPreemptible)
|
||||
} else if (!Sym.IsPreemptible) {
|
||||
Expr = fromPlt(Expr);
|
||||
}
|
||||
|
||||
// This relocation does not require got entry, but it is relative to got and
|
||||
// needs it to be created. Here we request for that.
|
||||
if (isRelExprOneOf<R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL,
|
||||
R_GOTREL_FROM_END, R_PPC_TOC>(Expr))
|
||||
InX::Got->HasGotOffRel = true;
|
||||
In.Got->HasGotOffRel = true;
|
||||
|
||||
// Read an addend.
|
||||
int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal());
|
||||
@ -1020,11 +1057,10 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
|
||||
// If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
|
||||
if (needsPlt(Expr) && !Sym.isInPlt()) {
|
||||
if (Sym.isGnuIFunc() && !Sym.IsPreemptible)
|
||||
addPltEntry<ELFT>(InX::Iplt, InX::IgotPlt, InX::RelaIplt,
|
||||
Target->IRelativeRel, Sym);
|
||||
else
|
||||
addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel,
|
||||
addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel,
|
||||
Sym);
|
||||
else
|
||||
addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
|
||||
}
|
||||
|
||||
// Create a GOT slot if a relocation needs GOT.
|
||||
@ -1037,7 +1073,7 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
|
||||
// See "Global Offset Table" in Chapter 5 in the following document
|
||||
// for detailed description:
|
||||
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
|
||||
InX::MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
|
||||
In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
|
||||
} else if (!Sym.isInGot()) {
|
||||
addGotEntry<ELFT>(Sym);
|
||||
}
|
||||
@ -1055,6 +1091,11 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
|
||||
|
||||
for (auto I = Rels.begin(), End = Rels.end(); I != End;)
|
||||
scanReloc<ELFT>(Sec, GetOffset, I, End);
|
||||
|
||||
// Sort relocations by offset to binary search for R_RISCV_PCREL_HI20
|
||||
if (Config->EMachine == EM_RISCV)
|
||||
std::stable_sort(Sec.Relocations.begin(), Sec.Relocations.end(),
|
||||
RelocationOffsetComparator{});
|
||||
}
|
||||
|
||||
template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
|
||||
@ -1064,6 +1105,43 @@ template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
|
||||
scanRelocs<ELFT>(S, S.rels<ELFT>());
|
||||
}
|
||||
|
||||
static bool mergeCmp(const InputSection *A, const InputSection *B) {
|
||||
// std::merge requires a strict weak ordering.
|
||||
if (A->OutSecOff < B->OutSecOff)
|
||||
return true;
|
||||
|
||||
if (A->OutSecOff == B->OutSecOff) {
|
||||
auto *TA = dyn_cast<ThunkSection>(A);
|
||||
auto *TB = dyn_cast<ThunkSection>(B);
|
||||
|
||||
// Check if Thunk is immediately before any specific Target
|
||||
// InputSection for example Mips LA25 Thunks.
|
||||
if (TA && TA->getTargetInputSection() == B)
|
||||
return true;
|
||||
|
||||
// Place Thunk Sections without specific targets before
|
||||
// non-Thunk Sections.
|
||||
if (TA && !TB && !TA->getTargetInputSection())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call Fn on every executable InputSection accessed via the linker script
|
||||
// InputSectionDescription::Sections.
|
||||
static void forEachInputSectionDescription(
|
||||
ArrayRef<OutputSection *> OutputSections,
|
||||
llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) {
|
||||
for (OutputSection *OS : OutputSections) {
|
||||
if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
|
||||
continue;
|
||||
for (BaseCommand *BC : OS->SectionCommands)
|
||||
if (auto *ISD = dyn_cast<InputSectionDescription>(BC))
|
||||
Fn(OS, ISD);
|
||||
}
|
||||
}
|
||||
|
||||
// Thunk Implementation
|
||||
//
|
||||
// Thunks (sometimes called stubs, veneers or branch islands) are small pieces
|
||||
@ -1166,6 +1244,7 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) {
|
||||
[](const std::pair<ThunkSection *, uint32_t> &TS) {
|
||||
return TS.first->getSize() == 0;
|
||||
});
|
||||
|
||||
// ISD->ThunkSections contains all created ThunkSections, including
|
||||
// those inserted in previous passes. Extract the Thunks created this
|
||||
// pass and order them in ascending OutSecOff.
|
||||
@ -1181,27 +1260,11 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) {
|
||||
// Merge sorted vectors of Thunks and InputSections by OutSecOff
|
||||
std::vector<InputSection *> Tmp;
|
||||
Tmp.reserve(ISD->Sections.size() + NewThunks.size());
|
||||
auto MergeCmp = [](const InputSection *A, const InputSection *B) {
|
||||
// std::merge requires a strict weak ordering.
|
||||
if (A->OutSecOff < B->OutSecOff)
|
||||
return true;
|
||||
if (A->OutSecOff == B->OutSecOff) {
|
||||
auto *TA = dyn_cast<ThunkSection>(A);
|
||||
auto *TB = dyn_cast<ThunkSection>(B);
|
||||
// Check if Thunk is immediately before any specific Target
|
||||
// InputSection for example Mips LA25 Thunks.
|
||||
if (TA && TA->getTargetInputSection() == B)
|
||||
return true;
|
||||
if (TA && !TB && !TA->getTargetInputSection())
|
||||
// Place Thunk Sections without specific targets before
|
||||
// non-Thunk Sections.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
std::merge(ISD->Sections.begin(), ISD->Sections.end(),
|
||||
NewThunks.begin(), NewThunks.end(), std::back_inserter(Tmp),
|
||||
MergeCmp);
|
||||
mergeCmp);
|
||||
|
||||
ISD->Sections = std::move(Tmp);
|
||||
});
|
||||
}
|
||||
@ -1245,20 +1308,23 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
|
||||
// Find InputSectionRange within Target Output Section (TOS) that the
|
||||
// InputSection (IS) that we need to precede is in.
|
||||
OutputSection *TOS = IS->getParent();
|
||||
for (BaseCommand *BC : TOS->SectionCommands)
|
||||
if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
|
||||
if (ISD->Sections.empty())
|
||||
continue;
|
||||
InputSection *first = ISD->Sections.front();
|
||||
InputSection *last = ISD->Sections.back();
|
||||
if (IS->OutSecOff >= first->OutSecOff &&
|
||||
IS->OutSecOff <= last->OutSecOff) {
|
||||
TS = addThunkSection(TOS, ISD, IS->OutSecOff);
|
||||
ThunkedSections[IS] = TS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return TS;
|
||||
for (BaseCommand *BC : TOS->SectionCommands) {
|
||||
auto *ISD = dyn_cast<InputSectionDescription>(BC);
|
||||
if (!ISD || ISD->Sections.empty())
|
||||
continue;
|
||||
|
||||
InputSection *First = ISD->Sections.front();
|
||||
InputSection *Last = ISD->Sections.back();
|
||||
|
||||
if (IS->OutSecOff < First->OutSecOff || Last->OutSecOff < IS->OutSecOff)
|
||||
continue;
|
||||
|
||||
TS = addThunkSection(TOS, ISD, IS->OutSecOff);
|
||||
ThunkedSections[IS] = TS;
|
||||
return TS;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create one or more ThunkSections per OS that can be used to place Thunks.
|
||||
@ -1279,26 +1345,29 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
|
||||
// allow for the creation of a short thunk.
|
||||
void ThunkCreator::createInitialThunkSections(
|
||||
ArrayRef<OutputSection *> OutputSections) {
|
||||
uint32_t ThunkSectionSpacing = Target->getThunkSectionSpacing();
|
||||
|
||||
forEachInputSectionDescription(
|
||||
OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
|
||||
if (ISD->Sections.empty())
|
||||
return;
|
||||
|
||||
uint32_t ISDBegin = ISD->Sections.front()->OutSecOff;
|
||||
uint32_t ISDEnd =
|
||||
ISD->Sections.back()->OutSecOff + ISD->Sections.back()->getSize();
|
||||
uint32_t LastThunkLowerBound = -1;
|
||||
if (ISDEnd - ISDBegin > Target->ThunkSectionSpacing * 2)
|
||||
LastThunkLowerBound = ISDEnd - Target->ThunkSectionSpacing;
|
||||
if (ISDEnd - ISDBegin > ThunkSectionSpacing * 2)
|
||||
LastThunkLowerBound = ISDEnd - ThunkSectionSpacing;
|
||||
|
||||
uint32_t ISLimit;
|
||||
uint32_t PrevISLimit = ISDBegin;
|
||||
uint32_t ThunkUpperBound = ISDBegin + Target->ThunkSectionSpacing;
|
||||
uint32_t ThunkUpperBound = ISDBegin + ThunkSectionSpacing;
|
||||
|
||||
for (const InputSection *IS : ISD->Sections) {
|
||||
ISLimit = IS->OutSecOff + IS->getSize();
|
||||
if (ISLimit > ThunkUpperBound) {
|
||||
addThunkSection(OS, ISD, PrevISLimit);
|
||||
ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
|
||||
ThunkUpperBound = PrevISLimit + ThunkSectionSpacing;
|
||||
}
|
||||
if (ISLimit > LastThunkLowerBound)
|
||||
break;
|
||||
@ -1312,13 +1381,14 @@ ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS,
|
||||
InputSectionDescription *ISD,
|
||||
uint64_t Off) {
|
||||
auto *TS = make<ThunkSection>(OS, Off);
|
||||
ISD->ThunkSections.push_back(std::make_pair(TS, Pass));
|
||||
ISD->ThunkSections.push_back({TS, Pass});
|
||||
return TS;
|
||||
}
|
||||
|
||||
std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
|
||||
uint64_t Src) {
|
||||
std::vector<Thunk *> *ThunkVec = nullptr;
|
||||
|
||||
// We use (section, offset) pair to find the thunk position if possible so
|
||||
// that we create only one thunk for aliased symbols or ICFed sections.
|
||||
if (auto *D = dyn_cast<Defined>(&Sym))
|
||||
@ -1326,40 +1396,28 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
|
||||
ThunkVec = &ThunkedSymbolsBySection[{D->Section->Repl, D->Value}];
|
||||
if (!ThunkVec)
|
||||
ThunkVec = &ThunkedSymbols[&Sym];
|
||||
|
||||
// Check existing Thunks for Sym to see if they can be reused
|
||||
for (Thunk *ET : *ThunkVec)
|
||||
if (ET->isCompatibleWith(Type) &&
|
||||
Target->inBranchRange(Type, Src, ET->getThunkTargetSym()->getVA()))
|
||||
return std::make_pair(ET, false);
|
||||
for (Thunk *T : *ThunkVec)
|
||||
if (T->isCompatibleWith(Type) &&
|
||||
Target->inBranchRange(Type, Src, T->getThunkTargetSym()->getVA()))
|
||||
return std::make_pair(T, false);
|
||||
|
||||
// No existing compatible Thunk in range, create a new one
|
||||
Thunk *T = addThunk(Type, Sym);
|
||||
ThunkVec->push_back(T);
|
||||
return std::make_pair(T, true);
|
||||
}
|
||||
|
||||
// Call Fn on every executable InputSection accessed via the linker script
|
||||
// InputSectionDescription::Sections.
|
||||
void ThunkCreator::forEachInputSectionDescription(
|
||||
ArrayRef<OutputSection *> OutputSections,
|
||||
llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) {
|
||||
for (OutputSection *OS : OutputSections) {
|
||||
if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
|
||||
continue;
|
||||
for (BaseCommand *BC : OS->SectionCommands)
|
||||
if (auto *ISD = dyn_cast<InputSectionDescription>(BC))
|
||||
Fn(OS, ISD);
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if the relocation target is an in range Thunk.
|
||||
// Return false if the relocation is not to a Thunk. If the relocation target
|
||||
// was originally to a Thunk, but is no longer in range we revert the
|
||||
// relocation back to its original non-Thunk target.
|
||||
bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) {
|
||||
if (Thunk *ET = Thunks.lookup(Rel.Sym)) {
|
||||
if (Thunk *T = Thunks.lookup(Rel.Sym)) {
|
||||
if (Target->inBranchRange(Rel.Type, Src, Rel.Sym->getVA()))
|
||||
return true;
|
||||
Rel.Sym = &ET->Destination;
|
||||
Rel.Sym = &T->Destination;
|
||||
if (Rel.Sym->isInPlt())
|
||||
Rel.Expr = toPlt(Rel.Expr);
|
||||
}
|
||||
@ -1393,11 +1451,13 @@ bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) {
|
||||
// relocation out of range error.
|
||||
bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
|
||||
bool AddressesChanged = false;
|
||||
if (Pass == 0 && Target->ThunkSectionSpacing)
|
||||
|
||||
if (Pass == 0 && Target->getThunkSectionSpacing())
|
||||
createInitialThunkSections(OutputSections);
|
||||
else if (Pass == 10)
|
||||
// With Thunk Size much smaller than branch range we expect to
|
||||
// converge quickly; if we get to 10 something has gone wrong.
|
||||
|
||||
// With Thunk Size much smaller than branch range we expect to
|
||||
// converge quickly; if we get to 10 something has gone wrong.
|
||||
if (Pass == 10)
|
||||
fatal("thunk creation not converged");
|
||||
|
||||
// Create all the Thunks and insert them into synthetic ThunkSections. The
|
||||
@ -1420,9 +1480,11 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
|
||||
if (!Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Src,
|
||||
*Rel.Sym))
|
||||
continue;
|
||||
|
||||
Thunk *T;
|
||||
bool IsNew;
|
||||
std::tie(T, IsNew) = getThunk(*Rel.Sym, Rel.Type, Src);
|
||||
|
||||
if (IsNew) {
|
||||
// Find or create a ThunkSection for the new Thunk
|
||||
ThunkSection *TS;
|
||||
@ -1433,13 +1495,16 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
|
||||
TS->addThunk(T);
|
||||
Thunks[T->getThunkTargetSym()] = T;
|
||||
}
|
||||
|
||||
// Redirect relocation to Thunk, we never go via the PLT to a Thunk
|
||||
Rel.Sym = T->getThunkTargetSym();
|
||||
Rel.Expr = fromPlt(Rel.Expr);
|
||||
}
|
||||
|
||||
for (auto &P : ISD->ThunkSections)
|
||||
AddressesChanged |= P.first->assignOffsets();
|
||||
});
|
||||
|
||||
for (auto &P : ThunkedSections)
|
||||
AddressesChanged |= P.second->assignOffsets();
|
||||
|
||||
|
@ -33,16 +33,28 @@ enum RelExpr {
|
||||
R_INVALID,
|
||||
R_ABS,
|
||||
R_ADDEND,
|
||||
R_AARCH64_GOT_PAGE_PC,
|
||||
// The expression is used for IFUNC support. Describes PC-relative
|
||||
// address of the memory page of GOT entry. This entry is used for
|
||||
// a redirection to IPLT.
|
||||
R_AARCH64_GOT_PAGE_PC_PLT,
|
||||
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
|
||||
R_AARCH64_PAGE_PC,
|
||||
R_AARCH64_PLT_PAGE_PC,
|
||||
R_AARCH64_TLSDESC_PAGE,
|
||||
R_ARM_SBREL,
|
||||
R_GOT,
|
||||
// The expression is used for IFUNC support. Evaluates to GOT entry,
|
||||
// containing redirection to the IPLT.
|
||||
R_GOT_PLT,
|
||||
R_GOTONLY_PC,
|
||||
R_GOTONLY_PC_FROM_END,
|
||||
R_GOTREL,
|
||||
R_GOTREL_FROM_END,
|
||||
R_GOT_FROM_END,
|
||||
R_GOT_OFF,
|
||||
R_GOT_PAGE_PC,
|
||||
R_GOT_PC,
|
||||
R_HEXAGON_GOT,
|
||||
R_HINT,
|
||||
R_MIPS_GOTREL,
|
||||
R_MIPS_GOT_GP,
|
||||
@ -54,10 +66,8 @@ enum RelExpr {
|
||||
R_MIPS_TLSLD,
|
||||
R_NEG_TLS,
|
||||
R_NONE,
|
||||
R_PAGE_PC,
|
||||
R_PC,
|
||||
R_PLT,
|
||||
R_PLT_PAGE_PC,
|
||||
R_PLT_PC,
|
||||
R_PPC_CALL,
|
||||
R_PPC_CALL_PLT,
|
||||
@ -68,20 +78,20 @@ enum RelExpr {
|
||||
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_RISCV_PC_INDIRECT,
|
||||
R_SIZE,
|
||||
R_TLS,
|
||||
R_TLSDESC,
|
||||
R_TLSDESC_CALL,
|
||||
R_TLSDESC_PAGE,
|
||||
R_TLSGD_GOT,
|
||||
R_TLSGD_GOT_FROM_END,
|
||||
R_TLSGD_PC,
|
||||
R_TLSIE_HINT,
|
||||
R_TLSLD_GOT,
|
||||
R_TLSLD_GOT_FROM_END,
|
||||
R_TLSLD_GOT_OFF,
|
||||
@ -128,6 +138,21 @@ struct Relocation {
|
||||
Symbol *Sym;
|
||||
};
|
||||
|
||||
struct RelocationOffsetComparator {
|
||||
bool operator()(const Relocation &Lhs, const Relocation &Rhs) {
|
||||
return Lhs.Offset < Rhs.Offset;
|
||||
}
|
||||
|
||||
// For std::lower_bound, std::upper_bound, std::equal_range.
|
||||
bool operator()(const Relocation &Rel, uint64_t Val) {
|
||||
return Rel.Offset < Val;
|
||||
}
|
||||
|
||||
bool operator()(uint64_t Val, const Relocation &Rel) {
|
||||
return Val < Rel.Offset;
|
||||
}
|
||||
};
|
||||
|
||||
template <class ELFT> void scanRelocations(InputSectionBase &);
|
||||
|
||||
class ThunkSection;
|
||||
@ -155,10 +180,6 @@ private:
|
||||
|
||||
void createInitialThunkSections(ArrayRef<OutputSection *> OutputSections);
|
||||
|
||||
void forEachInputSectionDescription(
|
||||
ArrayRef<OutputSection *> OutputSections,
|
||||
llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn);
|
||||
|
||||
std::pair<Thunk *, bool> getThunk(Symbol &Sym, RelType Type, uint64_t Src);
|
||||
|
||||
ThunkSection *addThunkSection(OutputSection *OS, InputSectionDescription *,
|
||||
|
@ -244,6 +244,15 @@ StringRef ScriptLexer::peek() {
|
||||
return Tok;
|
||||
}
|
||||
|
||||
StringRef ScriptLexer::peek2() {
|
||||
skip();
|
||||
StringRef Tok = next();
|
||||
if (errorCount())
|
||||
return "";
|
||||
Pos = Pos - 2;
|
||||
return Tok;
|
||||
}
|
||||
|
||||
bool ScriptLexer::consume(StringRef Tok) {
|
||||
if (peek() == Tok) {
|
||||
skip();
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
bool atEOF();
|
||||
StringRef next();
|
||||
StringRef peek();
|
||||
StringRef peek2();
|
||||
void skip();
|
||||
bool consume(StringRef Tok);
|
||||
void expect(StringRef Expect);
|
||||
|
@ -72,13 +72,15 @@ private:
|
||||
void readRegionAlias();
|
||||
void readSearchDir();
|
||||
void readSections();
|
||||
void readTarget();
|
||||
void readVersion();
|
||||
void readVersionScriptCommand();
|
||||
|
||||
SymbolAssignment *readSymbolAssignment(StringRef Name);
|
||||
ByteCommand *readByteCommand(StringRef Tok);
|
||||
uint32_t readFill();
|
||||
uint32_t parseFill(StringRef Tok);
|
||||
std::array<uint8_t, 4> readFill();
|
||||
std::array<uint8_t, 4> parseFill(StringRef Tok);
|
||||
bool readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2);
|
||||
void readSectionAddressType(OutputSection *Cmd);
|
||||
OutputSection *readOverlaySectionDescription();
|
||||
OutputSection *readOutputSectionDescription(StringRef OutSec);
|
||||
@ -92,6 +94,7 @@ private:
|
||||
SortSectionPolicy readSortKind();
|
||||
SymbolAssignment *readProvideHidden(bool Provide, bool Hidden);
|
||||
SymbolAssignment *readAssignment(StringRef Tok);
|
||||
std::tuple<ELFKind, uint16_t, bool> readBfdName();
|
||||
void readSort();
|
||||
Expr readAssert();
|
||||
Expr readConstant();
|
||||
@ -255,6 +258,8 @@ void ScriptParser::readLinkerScript() {
|
||||
readSearchDir();
|
||||
} else if (Tok == "SECTIONS") {
|
||||
readSections();
|
||||
} else if (Tok == "TARGET") {
|
||||
readTarget();
|
||||
} else if (Tok == "VERSION") {
|
||||
readVersion();
|
||||
} else if (SymbolAssignment *Cmd = readAssignment(Tok)) {
|
||||
@ -266,6 +271,8 @@ void ScriptParser::readLinkerScript() {
|
||||
}
|
||||
|
||||
void ScriptParser::readDefsym(StringRef Name) {
|
||||
if (errorCount())
|
||||
return;
|
||||
Expr E = readExpr();
|
||||
if (!atEOF())
|
||||
setError("EOF expected, but got " + next());
|
||||
@ -378,10 +385,50 @@ void ScriptParser::readOutputArch() {
|
||||
skip();
|
||||
}
|
||||
|
||||
std::tuple<ELFKind, uint16_t, bool> ScriptParser::readBfdName() {
|
||||
StringRef S = unquote(next());
|
||||
if (S == "elf32-i386")
|
||||
return std::make_tuple(ELF32LEKind, EM_386, false);
|
||||
if (S == "elf32-iamcu")
|
||||
return std::make_tuple(ELF32LEKind, EM_IAMCU, false);
|
||||
if (S == "elf32-littlearm")
|
||||
return std::make_tuple(ELF32LEKind, EM_ARM, false);
|
||||
if (S == "elf32-x86-64")
|
||||
return std::make_tuple(ELF32LEKind, EM_X86_64, false);
|
||||
if (S == "elf64-littleaarch64")
|
||||
return std::make_tuple(ELF64LEKind, EM_AARCH64, false);
|
||||
if (S == "elf64-powerpc")
|
||||
return std::make_tuple(ELF64BEKind, EM_PPC64, false);
|
||||
if (S == "elf64-powerpcle")
|
||||
return std::make_tuple(ELF64LEKind, EM_PPC64, false);
|
||||
if (S == "elf64-x86-64")
|
||||
return std::make_tuple(ELF64LEKind, EM_X86_64, false);
|
||||
if (S == "elf32-tradbigmips")
|
||||
return std::make_tuple(ELF32BEKind, EM_MIPS, false);
|
||||
if (S == "elf32-ntradbigmips")
|
||||
return std::make_tuple(ELF32BEKind, EM_MIPS, true);
|
||||
if (S == "elf32-tradlittlemips")
|
||||
return std::make_tuple(ELF32LEKind, EM_MIPS, false);
|
||||
if (S == "elf32-ntradlittlemips")
|
||||
return std::make_tuple(ELF32LEKind, EM_MIPS, true);
|
||||
if (S == "elf64-tradbigmips")
|
||||
return std::make_tuple(ELF64BEKind, EM_MIPS, false);
|
||||
if (S == "elf64-tradlittlemips")
|
||||
return std::make_tuple(ELF64LEKind, EM_MIPS, false);
|
||||
|
||||
setError("unknown output format name: " + S);
|
||||
return std::make_tuple(ELFNoneKind, EM_NONE, false);
|
||||
}
|
||||
|
||||
// Parse OUTPUT_FORMAT(bfdname) or OUTPUT_FORMAT(bfdname, big, little).
|
||||
// Currently we ignore big and little parameters.
|
||||
void ScriptParser::readOutputFormat() {
|
||||
// Error checking only for now.
|
||||
expect("(");
|
||||
skip();
|
||||
|
||||
std::tuple<ELFKind, uint16_t, bool> BfdTuple = readBfdName();
|
||||
if (Config->EKind == ELFNoneKind)
|
||||
std::tie(Config->EKind, Config->EMachine, Config->MipsN32Abi) = BfdTuple;
|
||||
|
||||
if (consume(")"))
|
||||
return;
|
||||
expect(",");
|
||||
@ -525,6 +572,23 @@ void ScriptParser::readSections() {
|
||||
V.end());
|
||||
}
|
||||
|
||||
void ScriptParser::readTarget() {
|
||||
// TARGET(foo) is an alias for "--format foo". Unlike GNU linkers,
|
||||
// we accept only a limited set of BFD names (i.e. "elf" or "binary")
|
||||
// for --format. We recognize only /^elf/ and "binary" in the linker
|
||||
// script as well.
|
||||
expect("(");
|
||||
StringRef Tok = next();
|
||||
expect(")");
|
||||
|
||||
if (Tok.startswith("elf"))
|
||||
Config->FormatBinary = false;
|
||||
else if (Tok == "binary")
|
||||
Config->FormatBinary = true;
|
||||
else
|
||||
setError("unknown target: " + Tok);
|
||||
}
|
||||
|
||||
static int precedence(StringRef Op) {
|
||||
return StringSwitch<int>(Op)
|
||||
.Cases("*", "/", "%", 8)
|
||||
@ -675,13 +739,33 @@ Expr ScriptParser::readAssert() {
|
||||
// alias for =fillexp section attribute, which is different from
|
||||
// what GNU linkers do.
|
||||
// https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
|
||||
uint32_t ScriptParser::readFill() {
|
||||
std::array<uint8_t, 4> ScriptParser::readFill() {
|
||||
expect("(");
|
||||
uint32_t V = parseFill(next());
|
||||
std::array<uint8_t, 4> V = parseFill(next());
|
||||
expect(")");
|
||||
return V;
|
||||
}
|
||||
|
||||
// Tries to read the special directive for an output section definition which
|
||||
// can be one of following: "(NOLOAD)", "(COPY)", "(INFO)" or "(OVERLAY)".
|
||||
// Tok1 and Tok2 are next 2 tokens peeked. See comment for readSectionAddressType below.
|
||||
bool ScriptParser::readSectionDirective(OutputSection *Cmd, StringRef Tok1, StringRef Tok2) {
|
||||
if (Tok1 != "(")
|
||||
return false;
|
||||
if (Tok2 != "NOLOAD" && Tok2 != "COPY" && Tok2 != "INFO" && Tok2 != "OVERLAY")
|
||||
return false;
|
||||
|
||||
expect("(");
|
||||
if (consume("NOLOAD")) {
|
||||
Cmd->Noload = true;
|
||||
} else {
|
||||
skip(); // This is "COPY", "INFO" or "OVERLAY".
|
||||
Cmd->NonAlloc = true;
|
||||
}
|
||||
expect(")");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reads an expression and/or the special directive for an output
|
||||
// section definition. Directive is one of following: "(NOLOAD)",
|
||||
// "(COPY)", "(INFO)" or "(OVERLAY)".
|
||||
@ -694,28 +778,12 @@ uint32_t ScriptParser::readFill() {
|
||||
// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
|
||||
// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html
|
||||
void ScriptParser::readSectionAddressType(OutputSection *Cmd) {
|
||||
if (consume("(")) {
|
||||
if (consume("NOLOAD")) {
|
||||
expect(")");
|
||||
Cmd->Noload = true;
|
||||
return;
|
||||
}
|
||||
if (consume("COPY") || consume("INFO") || consume("OVERLAY")) {
|
||||
expect(")");
|
||||
Cmd->NonAlloc = true;
|
||||
return;
|
||||
}
|
||||
Cmd->AddrExpr = readExpr();
|
||||
expect(")");
|
||||
} else {
|
||||
Cmd->AddrExpr = readExpr();
|
||||
}
|
||||
if (readSectionDirective(Cmd, peek(), peek2()))
|
||||
return;
|
||||
|
||||
if (consume("(")) {
|
||||
expect("NOLOAD");
|
||||
expect(")");
|
||||
Cmd->Noload = true;
|
||||
}
|
||||
Cmd->AddrExpr = readExpr();
|
||||
if (peek() == "(" && !readSectionDirective(Cmd, "(", peek2()))
|
||||
setError("unknown section directive: " + peek2());
|
||||
}
|
||||
|
||||
static Expr checkAlignment(Expr E, std::string &Loc) {
|
||||
@ -786,7 +854,12 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
|
||||
} else if (peek() == "(") {
|
||||
Cmd->SectionCommands.push_back(readInputSectionDescription(Tok));
|
||||
} else {
|
||||
setError("unknown command " + Tok);
|
||||
// We have a file name and no input sections description. It is not a
|
||||
// commonly used syntax, but still acceptable. In that case, all sections
|
||||
// from the file will be included.
|
||||
auto *ISD = make<InputSectionDescription>(Tok);
|
||||
ISD->SectionPatterns.push_back({{}, StringMatcher({"*"})});
|
||||
Cmd->SectionCommands.push_back(ISD);
|
||||
}
|
||||
}
|
||||
|
||||
@ -823,13 +896,13 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
|
||||
// When reading a hexstring, ld.bfd handles it as a blob of arbitrary
|
||||
// size, while ld.gold always handles it as a 32-bit big-endian number.
|
||||
// We are compatible with ld.gold because it's easier to implement.
|
||||
uint32_t ScriptParser::parseFill(StringRef Tok) {
|
||||
std::array<uint8_t, 4> ScriptParser::parseFill(StringRef Tok) {
|
||||
uint32_t V = 0;
|
||||
if (!to_integer(Tok, V))
|
||||
setError("invalid filler expression: " + Tok);
|
||||
|
||||
uint32_t Buf;
|
||||
write32be(&Buf, V);
|
||||
std::array<uint8_t, 4> Buf;
|
||||
write32be(Buf.data(), V);
|
||||
return Buf;
|
||||
}
|
||||
|
||||
|
@ -94,8 +94,20 @@ template <class ELFT> void SymbolTable::addFile(InputFile *File) {
|
||||
if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) {
|
||||
// DSOs are uniquified not by filename but by soname.
|
||||
F->parseSoName();
|
||||
if (errorCount() || !SoNames.insert(F->SoName).second)
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// If a DSO appears more than once on the command line with and without
|
||||
// --as-needed, --no-as-needed takes precedence over --as-needed because a
|
||||
// user can add an extra DSO with --no-as-needed to force it to be added to
|
||||
// the dependency list.
|
||||
DenseMap<StringRef, InputFile *>::iterator It;
|
||||
bool WasInserted;
|
||||
std::tie(It, WasInserted) = SoNames.try_emplace(F->SoName, F);
|
||||
cast<SharedFile<ELFT>>(It->second)->IsNeeded |= F->IsNeeded;
|
||||
if (!WasInserted)
|
||||
return;
|
||||
|
||||
SharedFiles.push_back(F);
|
||||
F->parseRest();
|
||||
return;
|
||||
@ -139,77 +151,27 @@ template <class ELFT> void SymbolTable::addCombinedLTOObject() {
|
||||
}
|
||||
}
|
||||
|
||||
Defined *SymbolTable::addAbsolute(StringRef Name, uint8_t Visibility,
|
||||
uint8_t Binding) {
|
||||
Symbol *Sym =
|
||||
addRegular(Name, Visibility, STT_NOTYPE, 0, 0, Binding, nullptr, nullptr);
|
||||
return cast<Defined>(Sym);
|
||||
}
|
||||
|
||||
// Set a flag for --trace-symbol so that we can print out a log message
|
||||
// if a new symbol with the same name is inserted into the symbol table.
|
||||
void SymbolTable::trace(StringRef Name) {
|
||||
SymMap.insert({CachedHashStringRef(Name), -1});
|
||||
}
|
||||
|
||||
// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM.
|
||||
// Used to implement --wrap.
|
||||
template <class ELFT> void SymbolTable::addSymbolWrap(StringRef Name) {
|
||||
Symbol *Sym = find(Name);
|
||||
if (!Sym)
|
||||
return;
|
||||
void SymbolTable::wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap) {
|
||||
// Swap symbols as instructed by -wrap.
|
||||
int &Idx1 = SymMap[CachedHashStringRef(Sym->getName())];
|
||||
int &Idx2 = SymMap[CachedHashStringRef(Real->getName())];
|
||||
int &Idx3 = SymMap[CachedHashStringRef(Wrap->getName())];
|
||||
|
||||
// Do not wrap the same symbol twice.
|
||||
for (const WrappedSymbol &S : WrappedSymbols)
|
||||
if (S.Sym == Sym)
|
||||
return;
|
||||
Idx2 = Idx1;
|
||||
Idx1 = Idx3;
|
||||
|
||||
Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name));
|
||||
Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name));
|
||||
WrappedSymbols.push_back({Sym, Real, Wrap});
|
||||
|
||||
// We want to tell LTO not to inline symbols to be overwritten
|
||||
// because LTO doesn't know the final symbol contents after renaming.
|
||||
Real->CanInline = false;
|
||||
Sym->CanInline = false;
|
||||
|
||||
// Tell LTO not to eliminate these symbols.
|
||||
Sym->IsUsedInRegularObj = true;
|
||||
Wrap->IsUsedInRegularObj = true;
|
||||
}
|
||||
|
||||
// Apply symbol renames created by -wrap. The renames are created
|
||||
// before LTO in addSymbolWrap() to have a chance to inform LTO (if
|
||||
// LTO is running) not to include these symbols in IPO. Now that the
|
||||
// symbols are finalized, we can perform the replacement.
|
||||
void SymbolTable::applySymbolWrap() {
|
||||
// This function rotates 3 symbols:
|
||||
//
|
||||
// __real_sym becomes sym
|
||||
// sym becomes __wrap_sym
|
||||
// __wrap_sym becomes __real_sym
|
||||
//
|
||||
// The last part is special in that we don't want to change what references to
|
||||
// __wrap_sym point to, we just want have __real_sym in the symbol table.
|
||||
|
||||
for (WrappedSymbol &W : WrappedSymbols) {
|
||||
// First, make a copy of __real_sym.
|
||||
Symbol *Real = nullptr;
|
||||
if (W.Real->isDefined()) {
|
||||
Real = reinterpret_cast<Symbol *>(make<SymbolUnion>());
|
||||
memcpy(Real, W.Real, sizeof(SymbolUnion));
|
||||
}
|
||||
|
||||
// Replace __real_sym with sym and sym with __wrap_sym.
|
||||
memcpy(W.Real, W.Sym, sizeof(SymbolUnion));
|
||||
memcpy(W.Sym, W.Wrap, sizeof(SymbolUnion));
|
||||
|
||||
// We now have two copies of __wrap_sym. Drop one.
|
||||
W.Wrap->IsUsedInRegularObj = false;
|
||||
|
||||
if (Real)
|
||||
SymVector.push_back(Real);
|
||||
}
|
||||
// Now renaming is complete. No one refers Real symbol. We could leave
|
||||
// Real as-is, but if Real is written to the symbol table, that may
|
||||
// contain irrelevant values. So, we copy all values from Sym to Real.
|
||||
StringRef S = Real->getName();
|
||||
memcpy(Real, Sym, sizeof(SymbolUnion));
|
||||
Real->setName(S);
|
||||
}
|
||||
|
||||
static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
|
||||
@ -221,7 +183,7 @@ static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
|
||||
}
|
||||
|
||||
// Find an existing symbol or create and insert a new one.
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
|
||||
std::pair<Symbol *, bool> SymbolTable::insertName(StringRef Name) {
|
||||
// <name>@@<version> means the symbol is the default version. In that
|
||||
// case <name>@@<version> will be used to resolve references to <name>.
|
||||
//
|
||||
@ -239,34 +201,34 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
|
||||
|
||||
if (SymIndex == -1) {
|
||||
SymIndex = SymVector.size();
|
||||
IsNew = Traced = true;
|
||||
IsNew = true;
|
||||
Traced = true;
|
||||
}
|
||||
|
||||
Symbol *Sym;
|
||||
if (IsNew) {
|
||||
Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
|
||||
Sym->Visibility = STV_DEFAULT;
|
||||
Sym->IsUsedInRegularObj = false;
|
||||
Sym->ExportDynamic = false;
|
||||
Sym->CanInline = true;
|
||||
Sym->Traced = Traced;
|
||||
Sym->VersionId = Config->DefaultSymbolVersion;
|
||||
SymVector.push_back(Sym);
|
||||
} else {
|
||||
Sym = SymVector[SymIndex];
|
||||
}
|
||||
return {Sym, IsNew};
|
||||
if (!IsNew)
|
||||
return {SymVector[SymIndex], false};
|
||||
|
||||
auto *Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
|
||||
Sym->SymbolKind = Symbol::PlaceholderKind;
|
||||
Sym->Visibility = STV_DEFAULT;
|
||||
Sym->IsUsedInRegularObj = false;
|
||||
Sym->ExportDynamic = false;
|
||||
Sym->CanInline = true;
|
||||
Sym->Traced = Traced;
|
||||
Sym->VersionId = Config->DefaultSymbolVersion;
|
||||
SymVector.push_back(Sym);
|
||||
return {Sym, true};
|
||||
}
|
||||
|
||||
// Find an existing symbol or create and insert a new one, then apply the given
|
||||
// attributes.
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, uint8_t Type,
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name,
|
||||
uint8_t Visibility,
|
||||
bool CanOmitFromDynSym,
|
||||
InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
std::tie(S, WasInserted) = insertName(Name);
|
||||
|
||||
// Merge in the new symbol's visibility.
|
||||
S->Visibility = getMinVisibility(S->Visibility, Visibility);
|
||||
@ -277,21 +239,9 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, uint8_t Type,
|
||||
if (!File || File->kind() == InputFile::ObjKind)
|
||||
S->IsUsedInRegularObj = true;
|
||||
|
||||
if (!WasInserted && S->Type != Symbol::UnknownType &&
|
||||
((Type == STT_TLS) != S->isTls())) {
|
||||
error("TLS attribute mismatch: " + toString(*S) + "\n>>> defined in " +
|
||||
toString(S->File) + "\n>>> defined in " + toString(File));
|
||||
}
|
||||
|
||||
return {S, WasInserted};
|
||||
}
|
||||
|
||||
template <class ELFT> Symbol *SymbolTable::addUndefined(StringRef Name) {
|
||||
return addUndefined<ELFT>(Name, STB_GLOBAL, STV_DEFAULT,
|
||||
/*Type*/ 0,
|
||||
/*CanOmitFromDynSym*/ false, /*File*/ nullptr);
|
||||
}
|
||||
|
||||
static uint8_t getVisibility(uint8_t StOther) { return StOther & 3; }
|
||||
|
||||
template <class ELFT>
|
||||
@ -301,8 +251,7 @@ Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding,
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
uint8_t Visibility = getVisibility(StOther);
|
||||
std::tie(S, WasInserted) =
|
||||
insert(Name, Type, Visibility, CanOmitFromDynSym, File);
|
||||
std::tie(S, WasInserted) = insert(Name, Visibility, CanOmitFromDynSym, File);
|
||||
|
||||
// An undefined symbol with non default visibility must be satisfied
|
||||
// in the same DSO.
|
||||
@ -314,10 +263,6 @@ Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding,
|
||||
if (S->isShared() || S->isLazy() || (S->isUndefined() && Binding != STB_WEAK))
|
||||
S->Binding = Binding;
|
||||
|
||||
if (!Config->GcSections && Binding != STB_WEAK)
|
||||
if (auto *SS = dyn_cast<SharedSymbol>(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.
|
||||
@ -450,7 +395,7 @@ Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
|
||||
InputFile &File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N, Type, getVisibility(StOther),
|
||||
std::tie(S, WasInserted) = insert(N, getVisibility(StOther),
|
||||
/*CanOmitFromDynSym*/ false, &File);
|
||||
|
||||
int Cmp = compareDefined(S, WasInserted, Binding, N);
|
||||
@ -487,12 +432,6 @@ Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
|
||||
return S;
|
||||
}
|
||||
|
||||
static void reportDuplicate(Symbol *Sym, InputFile *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, InputFile *NewFile,
|
||||
InputSectionBase *ErrSec, uint64_t ErrOffset) {
|
||||
if (Config->AllowMultipleDefinition)
|
||||
@ -500,7 +439,8 @@ static void reportDuplicate(Symbol *Sym, InputFile *NewFile,
|
||||
|
||||
Defined *D = cast<Defined>(Sym);
|
||||
if (!D->Section || !ErrSec) {
|
||||
reportDuplicate(Sym, NewFile);
|
||||
error("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
|
||||
toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -527,12 +467,12 @@ static void reportDuplicate(Symbol *Sym, InputFile *NewFile,
|
||||
error(Msg);
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
uint64_t Value, uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File) {
|
||||
Defined *SymbolTable::addDefined(StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
uint64_t Value, uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name, Type, getVisibility(StOther),
|
||||
std::tie(S, WasInserted) = insert(Name, getVisibility(StOther),
|
||||
/*CanOmitFromDynSym*/ false, File);
|
||||
int Cmp = compareDefinedNonCommon(S, WasInserted, Binding, Section == nullptr,
|
||||
Value, Name);
|
||||
@ -542,7 +482,7 @@ Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
else if (Cmp == 0)
|
||||
reportDuplicate(S, File, dyn_cast_or_null<InputSectionBase>(Section),
|
||||
Value);
|
||||
return S;
|
||||
return cast<Defined>(S);
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
@ -554,7 +494,7 @@ void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> &File,
|
||||
// unchanged.
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name, Sym.getType(), STV_DEFAULT,
|
||||
std::tie(S, WasInserted) = insert(Name, STV_DEFAULT,
|
||||
/*CanOmitFromDynSym*/ true, &File);
|
||||
// Make sure we preempt DSO symbols with default visibility.
|
||||
if (Sym.getVisibility() == STV_DEFAULT)
|
||||
@ -562,19 +502,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->Visibility == STV_DEFAULT)) {
|
||||
uint8_t Binding = S->Binding;
|
||||
bool WasUndefined = S->isUndefined();
|
||||
replaceSymbol<SharedSymbol>(S, File, Name, Sym.getBinding(), Sym.st_other,
|
||||
auto Replace = [&](uint8_t Binding) {
|
||||
replaceSymbol<SharedSymbol>(S, File, Name, Binding, Sym.st_other,
|
||||
Sym.getType(), Sym.st_value, Sym.st_size,
|
||||
Alignment, VerdefIndex);
|
||||
if (!WasInserted) {
|
||||
S->Binding = Binding;
|
||||
if (!S->isWeak() && !Config->GcSections && WasUndefined)
|
||||
File.IsNeeded = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (WasInserted)
|
||||
Replace(Sym.getBinding());
|
||||
else if (S->Visibility == STV_DEFAULT && (S->isUndefined() || S->isLazy()))
|
||||
Replace(S->Binding);
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addBitcode(StringRef Name, uint8_t Binding,
|
||||
@ -583,13 +520,13 @@ Symbol *SymbolTable::addBitcode(StringRef Name, uint8_t Binding,
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) =
|
||||
insert(Name, Type, getVisibility(StOther), CanOmitFromDynSym, &F);
|
||||
insert(Name, getVisibility(StOther), CanOmitFromDynSym, &F);
|
||||
int Cmp = compareDefinedNonCommon(S, WasInserted, Binding,
|
||||
/*IsAbs*/ false, /*Value*/ 0, Name);
|
||||
if (Cmp > 0)
|
||||
replaceSymbol<Defined>(S, &F, Name, Binding, StOther, Type, 0, 0, nullptr);
|
||||
else if (Cmp == 0)
|
||||
reportDuplicate(S, &F);
|
||||
reportDuplicate(S, &F, nullptr, 0);
|
||||
return S;
|
||||
}
|
||||
|
||||
@ -602,18 +539,14 @@ Symbol *SymbolTable::find(StringRef Name) {
|
||||
return SymVector[It->second];
|
||||
}
|
||||
|
||||
// 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) {
|
||||
template <class ELFT>
|
||||
void SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &File,
|
||||
const object::Archive::Symbol Sym) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = Symtab->insert(Name);
|
||||
std::tie(S, WasInserted) = insertName(Name);
|
||||
if (WasInserted) {
|
||||
replaceSymbol<LazyT>(S, File, Symbol::UnknownType,
|
||||
std::forward<ArgT>(Arg)...);
|
||||
replaceSymbol<LazyArchive>(S, File, STT_NOTYPE, Sym);
|
||||
return;
|
||||
}
|
||||
if (!S->isUndefined())
|
||||
@ -622,26 +555,37 @@ static void replaceOrFetchLazy(StringRef Name, InputFile &File,
|
||||
// An undefined weak will not fetch archive members. See comment on Lazy in
|
||||
// Symbols.h for the details.
|
||||
if (S->isWeak()) {
|
||||
replaceSymbol<LazyT>(S, File, S->Type, std::forward<ArgT>(Arg)...);
|
||||
replaceSymbol<LazyArchive>(S, File, S->Type, Sym);
|
||||
S->Binding = STB_WEAK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (InputFile *F = Fetch())
|
||||
Symtab->addFile<ELFT>(F);
|
||||
if (InputFile *F = File.fetch(Sym))
|
||||
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);
|
||||
}
|
||||
void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insertName(Name);
|
||||
if (WasInserted) {
|
||||
replaceSymbol<LazyObject>(S, File, STT_NOTYPE, Name);
|
||||
return;
|
||||
}
|
||||
if (!S->isUndefined())
|
||||
return;
|
||||
|
||||
template <class ELFT>
|
||||
void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &Obj) {
|
||||
replaceOrFetchLazy<ELFT, LazyObject>(Name, Obj, [&]() { return Obj.fetch(); },
|
||||
Name);
|
||||
// An undefined weak will not fetch archive members. See comment on Lazy in
|
||||
// Symbols.h for the details.
|
||||
if (S->isWeak()) {
|
||||
replaceSymbol<LazyObject>(S, File, S->Type, Name);
|
||||
S->Binding = STB_WEAK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (InputFile *F = File.fetch())
|
||||
addFile<ELFT>(F);
|
||||
}
|
||||
|
||||
template <class ELFT> void SymbolTable::fetchLazy(Symbol *Sym) {
|
||||
@ -822,16 +766,6 @@ 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);
|
||||
template void SymbolTable::addSymbolWrap<ELF64BE>(StringRef);
|
||||
|
||||
template Symbol *SymbolTable::addUndefined<ELF32LE>(StringRef);
|
||||
template Symbol *SymbolTable::addUndefined<ELF32BE>(StringRef);
|
||||
template Symbol *SymbolTable::addUndefined<ELF64LE>(StringRef);
|
||||
template Symbol *SymbolTable::addUndefined<ELF64BE>(StringRef);
|
||||
|
||||
template Symbol *SymbolTable::addUndefined<ELF32LE>(StringRef, uint8_t, uint8_t,
|
||||
uint8_t, bool, InputFile *);
|
||||
template Symbol *SymbolTable::addUndefined<ELF32BE>(StringRef, uint8_t, uint8_t,
|
||||
|
@ -37,22 +37,17 @@ class SymbolTable {
|
||||
public:
|
||||
template <class ELFT> void addFile(InputFile *File);
|
||||
template <class ELFT> void addCombinedLTOObject();
|
||||
template <class ELFT> void addSymbolWrap(StringRef Name);
|
||||
void applySymbolWrap();
|
||||
void wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap);
|
||||
|
||||
ArrayRef<Symbol *> getSymbols() const { return SymVector; }
|
||||
|
||||
Defined *addAbsolute(StringRef Name,
|
||||
uint8_t Visibility = llvm::ELF::STV_HIDDEN,
|
||||
uint8_t Binding = llvm::ELF::STB_GLOBAL);
|
||||
|
||||
template <class ELFT> Symbol *addUndefined(StringRef Name);
|
||||
template <class ELFT>
|
||||
Symbol *addUndefined(StringRef Name, uint8_t Binding, uint8_t StOther,
|
||||
uint8_t Type, bool CanOmitFromDynSym, InputFile *File);
|
||||
Symbol *addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
uint64_t Value, uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File);
|
||||
|
||||
Defined *addDefined(StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
uint64_t Value, uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File);
|
||||
|
||||
template <class ELFT>
|
||||
void addShared(StringRef Name, SharedFile<ELFT> &F,
|
||||
@ -72,10 +67,8 @@ public:
|
||||
uint8_t Binding, uint8_t StOther, uint8_t Type,
|
||||
InputFile &File);
|
||||
|
||||
std::pair<Symbol *, bool> insert(StringRef Name);
|
||||
std::pair<Symbol *, bool> insert(StringRef Name, uint8_t Type,
|
||||
uint8_t Visibility, bool CanOmitFromDynSym,
|
||||
InputFile *File);
|
||||
std::pair<Symbol *, bool> insert(StringRef Name, uint8_t Visibility,
|
||||
bool CanOmitFromDynSym, InputFile *File);
|
||||
|
||||
template <class ELFT> void fetchLazy(Symbol *Sym);
|
||||
|
||||
@ -88,6 +81,8 @@ public:
|
||||
void handleDynamicList();
|
||||
|
||||
private:
|
||||
std::pair<Symbol *, bool> insertName(StringRef Name);
|
||||
|
||||
std::vector<Symbol *> findByVersion(SymbolVersion Ver);
|
||||
std::vector<Symbol *> findAllByVersion(SymbolVersion Ver);
|
||||
|
||||
@ -113,7 +108,7 @@ private:
|
||||
llvm::DenseSet<llvm::CachedHashStringRef> ComdatGroups;
|
||||
|
||||
// Set of .so files to not link the same shared object file more than once.
|
||||
llvm::DenseSet<StringRef> SoNames;
|
||||
llvm::DenseMap<StringRef, InputFile *> SoNames;
|
||||
|
||||
// A map from demangled symbol names to their symbol objects.
|
||||
// This mapping is 1:N because two symbols with different versions
|
||||
@ -121,15 +116,6 @@ private:
|
||||
// directive in version scripts.
|
||||
llvm::Optional<llvm::StringMap<std::vector<Symbol *>>> DemangledSyms;
|
||||
|
||||
struct WrappedSymbol {
|
||||
Symbol *Sym;
|
||||
Symbol *Real;
|
||||
Symbol *Wrap;
|
||||
};
|
||||
|
||||
// For -wrap.
|
||||
std::vector<WrappedSymbol> WrappedSymbols;
|
||||
|
||||
// For LTO.
|
||||
std::unique_ptr<BitcodeCompiler> LTO;
|
||||
};
|
||||
|
@ -38,6 +38,7 @@ Defined *ElfSym::GlobalOffsetTable;
|
||||
Defined *ElfSym::MipsGp;
|
||||
Defined *ElfSym::MipsGpDisp;
|
||||
Defined *ElfSym::MipsLocalGp;
|
||||
Defined *ElfSym::RelaIpltStart;
|
||||
Defined *ElfSym::RelaIpltEnd;
|
||||
|
||||
static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
|
||||
@ -90,10 +91,15 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
|
||||
uint64_t VA = IS->getVA(Offset);
|
||||
|
||||
if (D.isTls() && !Config->Relocatable) {
|
||||
if (!Out::TlsPhdr)
|
||||
// Use the address of the TLS segment's first section rather than the
|
||||
// segment's address, because segment addresses aren't initialized until
|
||||
// after sections are finalized. (e.g. Measuring the size of .rela.dyn
|
||||
// for Android relocation packing requires knowing TLS symbol addresses
|
||||
// during section finalization.)
|
||||
if (!Out::TlsPhdr || !Out::TlsPhdr->FirstSec)
|
||||
fatal(toString(D.File) +
|
||||
" has an STT_TLS symbol but doesn't have an SHF_TLS section");
|
||||
return VA - Out::TlsPhdr->p_vaddr;
|
||||
return VA - Out::TlsPhdr->FirstSec->Addr;
|
||||
}
|
||||
return VA;
|
||||
}
|
||||
@ -102,7 +108,10 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
|
||||
return 0;
|
||||
case Symbol::LazyArchiveKind:
|
||||
case Symbol::LazyObjectKind:
|
||||
llvm_unreachable("lazy symbol reached writer");
|
||||
assert(Sym.IsUsedInRegularObj && "lazy symbol reached writer");
|
||||
return 0;
|
||||
case Symbol::PlaceholderKind:
|
||||
llvm_unreachable("placeholder symbol reached writer");
|
||||
}
|
||||
llvm_unreachable("invalid symbol kind");
|
||||
}
|
||||
@ -112,7 +121,7 @@ uint64_t Symbol::getVA(int64_t Addend) const {
|
||||
return OutVA + Addend;
|
||||
}
|
||||
|
||||
uint64_t Symbol::getGotVA() const { return InX::Got->getVA() + getGotOffset(); }
|
||||
uint64_t Symbol::getGotVA() const { return In.Got->getVA() + getGotOffset(); }
|
||||
|
||||
uint64_t Symbol::getGotOffset() const {
|
||||
return GotIndex * Target->GotEntrySize;
|
||||
@ -120,8 +129,8 @@ uint64_t Symbol::getGotOffset() const {
|
||||
|
||||
uint64_t Symbol::getGotPltVA() const {
|
||||
if (this->IsInIgot)
|
||||
return InX::IgotPlt->getVA() + getGotPltOffset();
|
||||
return InX::GotPlt->getVA() + getGotPltOffset();
|
||||
return In.IgotPlt->getVA() + getGotPltOffset();
|
||||
return In.GotPlt->getVA() + getGotPltOffset();
|
||||
}
|
||||
|
||||
uint64_t Symbol::getGotPltOffset() const {
|
||||
@ -130,15 +139,20 @@ uint64_t Symbol::getGotPltOffset() const {
|
||||
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->getPltEntryOffset(PltIndex);
|
||||
uint64_t Symbol::getPPC64LongBranchOffset() const {
|
||||
assert(PPC64BranchltIndex != 0xffff);
|
||||
return PPC64BranchltIndex * Target->GotPltEntrySize;
|
||||
}
|
||||
|
||||
uint64_t Symbol::getPltOffset() const {
|
||||
assert(!this->IsInIplt);
|
||||
return Target->getPltEntryOffset(PltIndex);
|
||||
uint64_t Symbol::getPltVA() const {
|
||||
PltSection *Plt = IsInIplt ? In.Iplt : In.Plt;
|
||||
return Plt->getVA() + Plt->HeaderSize + PltIndex * Target->PltEntrySize;
|
||||
}
|
||||
|
||||
uint64_t Symbol::getPPC64LongBranchTableVA() const {
|
||||
assert(PPC64BranchltIndex != 0xffff);
|
||||
return In.PPC64LongBranchTarget->getVA() +
|
||||
PPC64BranchltIndex * Target->GotPltEntrySize;
|
||||
}
|
||||
|
||||
uint64_t Symbol::getSize() const {
|
||||
@ -204,6 +218,15 @@ void Symbol::parseSymbolVersion() {
|
||||
|
||||
InputFile *LazyArchive::fetch() { return cast<ArchiveFile>(File)->fetch(Sym); }
|
||||
|
||||
MemoryBufferRef LazyArchive::getMemberBuffer() {
|
||||
Archive::Child C = CHECK(
|
||||
Sym.getMember(), "could not get the member for symbol " + Sym.getName());
|
||||
|
||||
return CHECK(C.getMemoryBufferRef(),
|
||||
"could not get the buffer for the member defining symbol " +
|
||||
Sym.getName());
|
||||
}
|
||||
|
||||
uint8_t Symbol::computeBinding() const {
|
||||
if (Config->Relocatable)
|
||||
return Binding;
|
||||
@ -243,10 +266,19 @@ void elf::printTraceSymbol(Symbol *Sym) {
|
||||
message(toString(Sym->File) + S + Sym->getName());
|
||||
}
|
||||
|
||||
void elf::warnUnorderableSymbol(const Symbol *Sym) {
|
||||
void elf::maybeWarnUnorderableSymbol(const Symbol *Sym) {
|
||||
if (!Config->WarnSymbolOrdering)
|
||||
return;
|
||||
|
||||
// If UnresolvedPolicy::Ignore is used, no "undefined symbol" error/warning
|
||||
// is emitted. It makes sense to not warn on undefined symbols.
|
||||
//
|
||||
// Note, ld.bfd --symbol-ordering-file= does not warn on undefined symbols,
|
||||
// but we don't have to be compatible here.
|
||||
if (Sym->isUndefined() &&
|
||||
Config->UnresolvedSymbols == UnresolvedPolicy::Ignore)
|
||||
return;
|
||||
|
||||
const InputFile *File = Sym->File;
|
||||
auto *D = dyn_cast<Defined>(Sym);
|
||||
|
||||
|
@ -21,6 +21,14 @@
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class Symbol;
|
||||
class InputFile;
|
||||
} // namespace elf
|
||||
|
||||
std::string toString(const elf::Symbol &);
|
||||
std::string toString(const elf::InputFile *);
|
||||
|
||||
namespace elf {
|
||||
|
||||
class ArchiveFile;
|
||||
@ -50,6 +58,7 @@ struct StringRefZ {
|
||||
class Symbol {
|
||||
public:
|
||||
enum Kind {
|
||||
PlaceholderKind,
|
||||
DefinedKind,
|
||||
SharedKind,
|
||||
UndefinedKind,
|
||||
@ -70,6 +79,7 @@ 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.
|
||||
@ -78,6 +88,9 @@ public:
|
||||
// Version definition index.
|
||||
uint16_t VersionId;
|
||||
|
||||
// An index into the .branch_lt section on PPC64.
|
||||
uint16_t PPC64BranchltIndex = -1;
|
||||
|
||||
// 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.
|
||||
@ -89,7 +102,7 @@ public:
|
||||
uint8_t Type; // symbol type
|
||||
uint8_t StOther; // st_other field value
|
||||
|
||||
const uint8_t SymbolKind;
|
||||
uint8_t SymbolKind;
|
||||
|
||||
// Symbol visibility. This is the computed minimum visibility of all
|
||||
// observed non-DSO symbols.
|
||||
@ -128,8 +141,12 @@ public:
|
||||
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
|
||||
}
|
||||
|
||||
// True if this is an undefined weak symbol.
|
||||
bool isUndefWeak() const { return isWeak() && isUndefined(); }
|
||||
// True if this is an undefined weak symbol. This only works once
|
||||
// all input files have been added.
|
||||
bool isUndefWeak() const {
|
||||
// See comment on lazy symbols for details.
|
||||
return isWeak() && (isUndefined() || isLazy());
|
||||
}
|
||||
|
||||
StringRef getName() const {
|
||||
if (NameSize == (uint32_t)-1)
|
||||
@ -137,10 +154,16 @@ public:
|
||||
return {NameData, NameSize};
|
||||
}
|
||||
|
||||
void setName(StringRef S) {
|
||||
NameData = S.data();
|
||||
NameSize = S.size();
|
||||
}
|
||||
|
||||
void parseSymbolVersion();
|
||||
|
||||
bool isInGot() const { return GotIndex != -1U; }
|
||||
bool isInPlt() const { return PltIndex != -1U; }
|
||||
bool isInPPC64Branchlt() const { return PPC64BranchltIndex != 0xffff; }
|
||||
|
||||
uint64_t getVA(int64_t Addend = 0) const;
|
||||
|
||||
@ -149,7 +172,8 @@ public:
|
||||
uint64_t getGotPltOffset() const;
|
||||
uint64_t getGotPltVA() const;
|
||||
uint64_t getPltVA() const;
|
||||
uint64_t getPltOffset() const;
|
||||
uint64_t getPPC64LongBranchTableVA() const;
|
||||
uint64_t getPPC64LongBranchOffset() const;
|
||||
uint64_t getSize() const;
|
||||
OutputSection *getOutputSection() const;
|
||||
|
||||
@ -159,7 +183,8 @@ protected:
|
||||
: 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) {}
|
||||
Used(!Config->GcSections), NeedsTocRestore(false),
|
||||
ScriptDefined(false) {}
|
||||
|
||||
public:
|
||||
// True the symbol should point to its PLT entry.
|
||||
@ -182,12 +207,8 @@ public:
|
||||
// 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
|
||||
// Type field is normally set to this value for Lazy symbols unless we saw a
|
||||
// weak undefined symbol first, in which case we need to remember the original
|
||||
// symbol's type in order to check for TLS mismatches.
|
||||
enum { UnknownType = 255 };
|
||||
// True if this symbol is defined by a linker script.
|
||||
unsigned ScriptDefined : 1;
|
||||
|
||||
bool isSection() const { return Type == llvm::ELF::STT_SECTION; }
|
||||
bool isTls() const { return Type == llvm::ELF::STT_TLS; }
|
||||
@ -286,6 +307,7 @@ public:
|
||||
static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; }
|
||||
|
||||
InputFile *fetch();
|
||||
MemoryBufferRef getMemberBuffer();
|
||||
|
||||
private:
|
||||
const llvm::object::Archive::Symbol Sym;
|
||||
@ -330,7 +352,8 @@ struct ElfSym {
|
||||
static Defined *MipsGpDisp;
|
||||
static Defined *MipsLocalGp;
|
||||
|
||||
// __rela_iplt_end or __rel_iplt_end
|
||||
// __rel{,a}_iplt_{start,end} symbols.
|
||||
static Defined *RelaIpltStart;
|
||||
static Defined *RelaIpltEnd;
|
||||
};
|
||||
|
||||
@ -349,6 +372,8 @@ void printTraceSymbol(Symbol *Sym);
|
||||
|
||||
template <typename T, typename... ArgT>
|
||||
void replaceSymbol(Symbol *S, ArgT &&... Arg) {
|
||||
using llvm::ELF::STT_TLS;
|
||||
|
||||
static_assert(std::is_trivially_destructible<T>(),
|
||||
"Symbol types must be trivially destructible");
|
||||
static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
|
||||
@ -367,6 +392,19 @@ void replaceSymbol(Symbol *S, ArgT &&... Arg) {
|
||||
S->ExportDynamic = Sym.ExportDynamic;
|
||||
S->CanInline = Sym.CanInline;
|
||||
S->Traced = Sym.Traced;
|
||||
S->ScriptDefined = Sym.ScriptDefined;
|
||||
|
||||
// Symbols representing thread-local variables must be referenced by
|
||||
// TLS-aware relocations, and non-TLS symbols must be reference by
|
||||
// non-TLS relocations, so there's a clear distinction between TLS
|
||||
// and non-TLS symbols. It is an error if the same symbol is defined
|
||||
// as a TLS symbol in one file and as a non-TLS symbol in other file.
|
||||
bool TlsMismatch = (Sym.Type == STT_TLS && S->Type != STT_TLS) ||
|
||||
(Sym.Type != STT_TLS && S->Type == STT_TLS);
|
||||
|
||||
if (Sym.SymbolKind != Symbol::PlaceholderKind && TlsMismatch && !Sym.isLazy())
|
||||
error("TLS attribute mismatch: " + toString(Sym) + "\n>>> defined in " +
|
||||
toString(Sym.File) + "\n>>> defined in " + toString(S->File));
|
||||
|
||||
// Print out a log message if --trace-symbol was specified.
|
||||
// This is for debugging.
|
||||
@ -374,10 +412,8 @@ void replaceSymbol(Symbol *S, ArgT &&... Arg) {
|
||||
printTraceSymbol(S);
|
||||
}
|
||||
|
||||
void warnUnorderableSymbol(const Symbol *Sym);
|
||||
void maybeWarnUnorderableSymbol(const Symbol *Sym);
|
||||
} // namespace elf
|
||||
|
||||
std::string toString(const elf::Symbol &B);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,8 +21,8 @@
|
||||
#ifndef LLD_ELF_SYNTHETIC_SECTION_H
|
||||
#define LLD_ELF_SYNTHETIC_SECTION_H
|
||||
|
||||
#include "DWARF.h"
|
||||
#include "EhFrame.h"
|
||||
#include "GdbIndex.h"
|
||||
#include "InputSection.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
@ -50,8 +50,6 @@ public:
|
||||
// If the section has the SHF_ALLOC flag and the size may be changed if
|
||||
// thunks are added, update the section size.
|
||||
virtual bool updateAllocSize() { return false; }
|
||||
// If any additional finalization of contents are needed post thunk creation.
|
||||
virtual void postThunkContents() {}
|
||||
virtual bool empty() const { return false; }
|
||||
|
||||
static bool classof(const SectionBase *D) {
|
||||
@ -137,6 +135,15 @@ protected:
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
// .note.GNU-stack section.
|
||||
class GnuStackSection : public SyntheticSection {
|
||||
public:
|
||||
GnuStackSection()
|
||||
: SyntheticSection(0, llvm::ELF::SHT_PROGBITS, 1, ".note.GNU-stack") {}
|
||||
void writeTo(uint8_t *Buf) override {}
|
||||
size_t getSize() const override { return 0; }
|
||||
};
|
||||
|
||||
// .note.gnu.build-id section.
|
||||
class BuildIdSection : public SyntheticSection {
|
||||
// First 16 bytes are a header.
|
||||
@ -163,7 +170,9 @@ private:
|
||||
class BssSection final : public SyntheticSection {
|
||||
public:
|
||||
BssSection(StringRef Name, uint64_t Size, uint32_t Alignment);
|
||||
void writeTo(uint8_t *) override {}
|
||||
void writeTo(uint8_t *) override {
|
||||
llvm_unreachable("unexpected writeTo() call for SHT_NOBITS section");
|
||||
}
|
||||
bool empty() const override { return getSize() == 0; }
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
@ -300,8 +309,6 @@ private:
|
||||
|
||||
uint64_t Size = 0;
|
||||
|
||||
size_t LocalEntriesNum = 0;
|
||||
|
||||
// Symbol and addend.
|
||||
typedef std::pair<Symbol *, int64_t> GotEntry;
|
||||
|
||||
@ -333,8 +340,6 @@ private:
|
||||
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.
|
||||
@ -529,6 +534,7 @@ struct RelativeReloc {
|
||||
class RelrBaseSection : public SyntheticSection {
|
||||
public:
|
||||
RelrBaseSection();
|
||||
bool empty() const override { return Relocs.empty(); }
|
||||
std::vector<RelativeReloc> Relocs;
|
||||
};
|
||||
|
||||
@ -561,7 +567,6 @@ class SymbolTableBaseSection : public SyntheticSection {
|
||||
public:
|
||||
SymbolTableBaseSection(StringTableSection &StrTabSec);
|
||||
void finalizeContents() override;
|
||||
void postThunkContents() override;
|
||||
size_t getSize() const override { return getNumSymbols() * Entsize; }
|
||||
void addSymbol(Symbol *Sym);
|
||||
unsigned getNumSymbols() const { return Symbols.size() + 1; }
|
||||
@ -569,6 +574,8 @@ public:
|
||||
ArrayRef<SymbolTableEntry> getSymbols() const { return Symbols; }
|
||||
|
||||
protected:
|
||||
void sortSymTabSymbols();
|
||||
|
||||
// A vector of symbols and their string table offsets.
|
||||
std::vector<SymbolTableEntry> Symbols;
|
||||
|
||||
@ -612,7 +619,8 @@ public:
|
||||
void addSymbols(std::vector<SymbolTableEntry> &Symbols);
|
||||
|
||||
private:
|
||||
enum { Shift2 = 6 };
|
||||
// See the comment in writeBloomFilter.
|
||||
enum { Shift2 = 26 };
|
||||
|
||||
void writeBloomFilter(uint8_t *Buf);
|
||||
void writeHashTable(uint8_t *Buf);
|
||||
@ -652,13 +660,13 @@ public:
|
||||
size_t getSize() const override;
|
||||
bool empty() const override { return Entries.empty(); }
|
||||
void addSymbols();
|
||||
|
||||
template <class ELFT> void addEntry(Symbol &Sym);
|
||||
|
||||
size_t HeaderSize;
|
||||
|
||||
private:
|
||||
unsigned getPltRelocOff() const;
|
||||
std::vector<std::pair<const Symbol *, unsigned>> Entries;
|
||||
size_t HeaderSize;
|
||||
bool IsIplt;
|
||||
};
|
||||
|
||||
@ -676,9 +684,9 @@ public:
|
||||
uint64_t CuLength;
|
||||
};
|
||||
|
||||
struct NameTypeEntry {
|
||||
struct NameAttrEntry {
|
||||
llvm::CachedHashStringRef Name;
|
||||
uint32_t Type;
|
||||
uint32_t CuIndexAndAttrs;
|
||||
};
|
||||
|
||||
struct GdbChunk {
|
||||
@ -748,11 +756,7 @@ public:
|
||||
// shall be contained in the DT_VERDEFNUM entry of the .dynamic section.
|
||||
// The section shall contain an array of Elf_Verdef structures, optionally
|
||||
// followed by an array of Elf_Verdaux structures.
|
||||
template <class ELFT>
|
||||
class VersionDefinitionSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Verdef Elf_Verdef;
|
||||
typedef typename ELFT::Verdaux Elf_Verdaux;
|
||||
|
||||
public:
|
||||
VersionDefinitionSection();
|
||||
void finalizeContents() override;
|
||||
@ -760,6 +764,7 @@ public:
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
||||
private:
|
||||
enum { EntrySize = 28 };
|
||||
void writeOne(uint8_t *Buf, uint32_t Index, StringRef Name, size_t NameOff);
|
||||
|
||||
unsigned FileDefNameOff;
|
||||
@ -773,8 +778,6 @@ private:
|
||||
// the own object or in any of the dependencies.
|
||||
template <class ELFT>
|
||||
class VersionTableSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Versym Elf_Versym;
|
||||
|
||||
public:
|
||||
VersionTableSection();
|
||||
void finalizeContents() override;
|
||||
@ -964,9 +967,27 @@ private:
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
// This section is used to store the addresses of functions that are called
|
||||
// in range-extending thunks on PowerPC64. When producing position dependant
|
||||
// code the addresses are link-time constants and the table is written out to
|
||||
// the binary. When producing position-dependant code the table is allocated and
|
||||
// filled in by the dynamic linker.
|
||||
class PPC64LongBranchTargetSection final : public SyntheticSection {
|
||||
public:
|
||||
PPC64LongBranchTargetSection();
|
||||
void addEntry(Symbol &Sym);
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override;
|
||||
void finalizeContents() override { Finalized = true; }
|
||||
|
||||
private:
|
||||
std::vector<const Symbol *> Entries;
|
||||
bool Finalized = false;
|
||||
};
|
||||
|
||||
InputSection *createInterpSection();
|
||||
MergeInputSection *createCommentSection();
|
||||
void decompressSections();
|
||||
template <class ELFT> void splitSections();
|
||||
void mergeSections();
|
||||
|
||||
@ -974,46 +995,48 @@ 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 {
|
||||
static InputSection *ARMAttributes;
|
||||
static BssSection *Bss;
|
||||
static BssSection *BssRelRo;
|
||||
static BuildIdSection *BuildId;
|
||||
static EhFrameHeader *EhFrameHdr;
|
||||
static EhFrameSection *EhFrame;
|
||||
static SyntheticSection *Dynamic;
|
||||
static StringTableSection *DynStrTab;
|
||||
static SymbolTableBaseSection *DynSymTab;
|
||||
static GnuHashTableSection *GnuHashTab;
|
||||
static HashTableSection *HashTab;
|
||||
static InputSection *Interp;
|
||||
static GdbIndexSection *GdbIndex;
|
||||
static GotSection *Got;
|
||||
static GotPltSection *GotPlt;
|
||||
static IgotPltSection *IgotPlt;
|
||||
static MipsGotSection *MipsGot;
|
||||
static MipsRldMapSection *MipsRldMap;
|
||||
static PltSection *Plt;
|
||||
static PltSection *Iplt;
|
||||
static RelocationBaseSection *RelaDyn;
|
||||
static RelrBaseSection *RelrDyn;
|
||||
static RelocationBaseSection *RelaPlt;
|
||||
static RelocationBaseSection *RelaIplt;
|
||||
static StringTableSection *ShStrTab;
|
||||
static StringTableSection *StrTab;
|
||||
static SymbolTableBaseSection *SymTab;
|
||||
static SymtabShndxSection* SymTabShndx;
|
||||
struct InStruct {
|
||||
InputSection *ARMAttributes;
|
||||
BssSection *Bss;
|
||||
BssSection *BssRelRo;
|
||||
BuildIdSection *BuildId;
|
||||
EhFrameHeader *EhFrameHdr;
|
||||
EhFrameSection *EhFrame;
|
||||
SyntheticSection *Dynamic;
|
||||
StringTableSection *DynStrTab;
|
||||
SymbolTableBaseSection *DynSymTab;
|
||||
GnuHashTableSection *GnuHashTab;
|
||||
HashTableSection *HashTab;
|
||||
InputSection *Interp;
|
||||
GdbIndexSection *GdbIndex;
|
||||
GotSection *Got;
|
||||
GotPltSection *GotPlt;
|
||||
IgotPltSection *IgotPlt;
|
||||
PPC64LongBranchTargetSection *PPC64LongBranchTarget;
|
||||
MipsGotSection *MipsGot;
|
||||
MipsRldMapSection *MipsRldMap;
|
||||
PltSection *Plt;
|
||||
PltSection *Iplt;
|
||||
RelocationBaseSection *RelaDyn;
|
||||
RelrBaseSection *RelrDyn;
|
||||
RelocationBaseSection *RelaPlt;
|
||||
RelocationBaseSection *RelaIplt;
|
||||
StringTableSection *ShStrTab;
|
||||
StringTableSection *StrTab;
|
||||
SymbolTableBaseSection *SymTab;
|
||||
SymtabShndxSection *SymTabShndx;
|
||||
VersionDefinitionSection *VerDef;
|
||||
};
|
||||
|
||||
template <class ELFT> struct In {
|
||||
static VersionDefinitionSection<ELFT> *VerDef;
|
||||
extern InStruct In;
|
||||
|
||||
template <class ELFT> struct InX {
|
||||
static VersionTableSection<ELFT> *VerSym;
|
||||
static VersionNeedSection<ELFT> *VerNeed;
|
||||
};
|
||||
|
||||
template <class ELFT> VersionDefinitionSection<ELFT> *In<ELFT>::VerDef;
|
||||
template <class ELFT> VersionTableSection<ELFT> *In<ELFT>::VerSym;
|
||||
template <class ELFT> VersionNeedSection<ELFT> *In<ELFT>::VerNeed;
|
||||
template <class ELFT> VersionTableSection<ELFT> *InX<ELFT>::VerSym;
|
||||
template <class ELFT> VersionNeedSection<ELFT> *InX<ELFT>::VerNeed;
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
@ -73,12 +73,16 @@ TargetInfo *elf::getTarget() {
|
||||
case ELF64BEKind:
|
||||
return getMipsTargetInfo<ELF64BE>();
|
||||
default:
|
||||
fatal("unsupported MIPS target");
|
||||
llvm_unreachable("unsupported MIPS target");
|
||||
}
|
||||
case EM_MSP430:
|
||||
return getMSP430TargetInfo();
|
||||
case EM_PPC:
|
||||
return getPPCTargetInfo();
|
||||
case EM_PPC64:
|
||||
return getPPC64TargetInfo();
|
||||
case EM_RISCV:
|
||||
return getRISCVTargetInfo();
|
||||
case EM_SPARCV9:
|
||||
return getSPARCV9TargetInfo();
|
||||
case EM_X86_64:
|
||||
@ -86,7 +90,7 @@ TargetInfo *elf::getTarget() {
|
||||
return getX32TargetInfo();
|
||||
return getX86_64TargetInfo();
|
||||
}
|
||||
fatal("unknown target machine");
|
||||
llvm_unreachable("unknown target machine");
|
||||
}
|
||||
|
||||
template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *Loc) {
|
||||
@ -130,12 +134,11 @@ bool TargetInfo::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TargetInfo::adjustPrologueForCrossSplitStack(uint8_t *Loc,
|
||||
uint8_t *End) const {
|
||||
bool TargetInfo::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
|
||||
uint8_t StOther) const {
|
||||
llvm_unreachable("Target doesn't support split stacks.");
|
||||
}
|
||||
|
||||
|
||||
bool TargetInfo::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
|
||||
return true;
|
||||
}
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include "InputSection.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include <array>
|
||||
|
||||
namespace lld {
|
||||
std::string toString(elf::RelType Type);
|
||||
@ -43,10 +45,6 @@ public:
|
||||
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 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
|
||||
@ -60,11 +58,18 @@ public:
|
||||
const InputFile *File, uint64_t BranchAddr,
|
||||
const Symbol &S) const;
|
||||
|
||||
// On systems with range extensions we place collections of Thunks at
|
||||
// regular spacings that enable the majority of branches reach the Thunks.
|
||||
// a value of 0 means range extension thunks are not supported.
|
||||
virtual uint32_t getThunkSectionSpacing() const { return 0; }
|
||||
|
||||
// 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;
|
||||
// The symbols st_other flags are needed on PowerPC64 for determining the
|
||||
// offset to the split-stack prologue.
|
||||
virtual bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
|
||||
uint8_t StOther) const;
|
||||
|
||||
// Return true if we can reach Dst from Src with Relocation RelocType
|
||||
virtual bool inBranchRange(RelType Type, uint64_t Src,
|
||||
@ -87,12 +92,9 @@ public:
|
||||
// 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.
|
||||
uint32_t ThunkSectionSpacing = 0;
|
||||
|
||||
RelType CopyRel;
|
||||
RelType GotRel;
|
||||
RelType NoneRel;
|
||||
RelType PltRel;
|
||||
RelType RelativeRel;
|
||||
RelType IRelativeRel;
|
||||
@ -112,20 +114,16 @@ public:
|
||||
// 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
|
||||
// executable OutputSections.
|
||||
uint32_t TrapInstr = 0;
|
||||
std::array<uint8_t, 4> TrapInstr;
|
||||
|
||||
// If a target needs to rewrite calls to __morestack to instead call
|
||||
// __morestack_non_split when a split-stack enabled caller calls a
|
||||
// non-split-stack callee this will return true. Otherwise returns false.
|
||||
bool NeedsMoreStackNonSplit = true;
|
||||
|
||||
virtual RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
RelExpr Expr) const;
|
||||
@ -148,8 +146,10 @@ TargetInfo *getAMDGPUTargetInfo();
|
||||
TargetInfo *getARMTargetInfo();
|
||||
TargetInfo *getAVRTargetInfo();
|
||||
TargetInfo *getHexagonTargetInfo();
|
||||
TargetInfo *getMSP430TargetInfo();
|
||||
TargetInfo *getPPC64TargetInfo();
|
||||
TargetInfo *getPPCTargetInfo();
|
||||
TargetInfo *getRISCVTargetInfo();
|
||||
TargetInfo *getSPARCV9TargetInfo();
|
||||
TargetInfo *getX32TargetInfo();
|
||||
TargetInfo *getX86TargetInfo();
|
||||
@ -168,6 +168,15 @@ static inline std::string getErrorLocation(const uint8_t *Loc) {
|
||||
return getErrorPlace(Loc).Loc;
|
||||
}
|
||||
|
||||
// In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first is
|
||||
// a global entry point (GEP) which typically is used to intiailzie the TOC
|
||||
// pointer in general purpose register 2. The second is a local entry
|
||||
// point (LEP) which bypasses the TOC pointer initialization code. The
|
||||
// offset between GEP and LEP is encoded in a function's st_other flags.
|
||||
// This function will return the offset (in bytes) from the global entry-point
|
||||
// to the local entry-point.
|
||||
unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther);
|
||||
|
||||
uint64_t getPPC64TocBase();
|
||||
uint64_t getAArch64Page(uint64_t Expr);
|
||||
|
||||
@ -184,19 +193,18 @@ static inline void reportRangeError(uint8_t *Loc, RelType Type, const Twine &V,
|
||||
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);
|
||||
errorOrWarn(ErrPlace.Loc + "relocation " + lld::toString(Type) +
|
||||
" out of range: " + V.str() + " is not in [" + Twine(Min).str() +
|
||||
", " + Twine(Max).str() + "]" + Hint);
|
||||
}
|
||||
|
||||
// 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);
|
||||
inline unsigned getPltEntryOffset(unsigned Idx) {
|
||||
return Target->PltHeaderSize + Target->PltEntrySize * Idx;
|
||||
}
|
||||
|
||||
// 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))
|
||||
if (V != llvm::SignExtend64(V, N))
|
||||
reportRangeError(Loc, Type, Twine(V), llvm::minIntN(N), llvm::maxIntN(N));
|
||||
}
|
||||
|
||||
@ -210,7 +218,7 @@ inline void checkUInt(uint8_t *Loc, uint64_t V, int N, RelType Type) {
|
||||
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)
|
||||
if (V != (uint64_t)llvm::SignExtend64(V, N) && (V >> N) != 0)
|
||||
reportRangeError(Loc, Type, Twine((int64_t)V), llvm::minIntN(N),
|
||||
llvm::maxIntN(N));
|
||||
}
|
||||
|
@ -159,6 +159,50 @@ public:
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
};
|
||||
|
||||
// Implementations of Thunks for older Arm architectures that do not support
|
||||
// the movt/movw instructions. These thunks require at least Architecture v5
|
||||
// as used on processors such as the Arm926ej-s. There are no Thumb entry
|
||||
// points as there is no Thumb branch instruction on these architecture that
|
||||
// can result in a thunk
|
||||
class ARMV5ABSLongThunk final : public ARMThunk {
|
||||
public:
|
||||
ARMV5ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
|
||||
|
||||
uint32_t sizeLong() override { return 8; }
|
||||
void writeLong(uint8_t *Buf) override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
bool isCompatibleWith(uint32_t RelocType) const override;
|
||||
};
|
||||
|
||||
class ARMV5PILongThunk final : public ARMThunk {
|
||||
public:
|
||||
ARMV5PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
|
||||
|
||||
uint32_t sizeLong() override { return 16; }
|
||||
void writeLong(uint8_t *Buf) override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
bool isCompatibleWith(uint32_t RelocType) const override;
|
||||
};
|
||||
|
||||
// Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted
|
||||
class ThumbV6MABSLongThunk final : public ThumbThunk {
|
||||
public:
|
||||
ThumbV6MABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
|
||||
|
||||
uint32_t sizeLong() override { return 12; }
|
||||
void writeLong(uint8_t *Buf) override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
};
|
||||
|
||||
class ThumbV6MPILongThunk final : public ThumbThunk {
|
||||
public:
|
||||
ThumbV6MPILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
|
||||
|
||||
uint32_t sizeLong() override { return 16; }
|
||||
void writeLong(uint8_t *Buf) override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
};
|
||||
|
||||
// MIPS LA25 thunk
|
||||
class MipsThunk final : public Thunk {
|
||||
public:
|
||||
@ -209,6 +253,46 @@ public:
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
};
|
||||
|
||||
// A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
|
||||
// alignment. This gives a possible 26 bits of 'reach'. If the call offset is
|
||||
// larger then that we need to emit a long-branch thunk. The target address
|
||||
// of the callee is stored in a table to be accessed TOC-relative. Since the
|
||||
// call must be local (a non-local call will have a PltCallStub instead) the
|
||||
// table stores the address of the callee's local entry point. For
|
||||
// position-independent code a corresponding relative dynamic relocation is
|
||||
// used.
|
||||
class PPC64LongBranchThunk : public Thunk {
|
||||
public:
|
||||
uint32_t size() override { return 16; }
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
|
||||
protected:
|
||||
PPC64LongBranchThunk(Symbol &Dest) : Thunk(Dest) {}
|
||||
};
|
||||
|
||||
class PPC64PILongBranchThunk final : public PPC64LongBranchThunk {
|
||||
public:
|
||||
PPC64PILongBranchThunk(Symbol &Dest) : PPC64LongBranchThunk(Dest) {
|
||||
assert(!Dest.IsPreemptible);
|
||||
if (Dest.isInPPC64Branchlt())
|
||||
return;
|
||||
|
||||
In.PPC64LongBranchTarget->addEntry(Dest);
|
||||
In.RelaDyn->addReloc({Target->RelativeRel, In.PPC64LongBranchTarget,
|
||||
Dest.getPPC64LongBranchOffset(), true, &Dest,
|
||||
getPPC64GlobalEntryToLocalEntryOffset(Dest.StOther)});
|
||||
}
|
||||
};
|
||||
|
||||
class PPC64PDLongBranchThunk final : public PPC64LongBranchThunk {
|
||||
public:
|
||||
PPC64PDLongBranchThunk(Symbol &Dest) : PPC64LongBranchThunk(Dest) {
|
||||
if (!Dest.isInPPC64Branchlt())
|
||||
In.PPC64LongBranchTarget->addEntry(Dest);
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
|
||||
@ -395,12 +479,12 @@ 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)
|
||||
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
|
||||
0x1c, 0xff, 0x2f, 0xe1, // bx r12
|
||||
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
|
||||
0x1c, 0xff, 0x2f, 0xe1, // bx ip
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA(Destination);
|
||||
uint64_t P = getThunkTargetSym()->getVA();
|
||||
uint64_t Offset = S - P - 16;
|
||||
int64_t Offset = S - P - 16;
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
|
||||
Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, Offset);
|
||||
@ -416,12 +500,12 @@ 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)
|
||||
0xfc, 0x44, // L1: add r12, pc
|
||||
0x60, 0x47, // bx r12
|
||||
0xfc, 0x44, // L1: add ip, pc
|
||||
0x60, 0x47, // bx ip
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA(Destination);
|
||||
uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
|
||||
uint64_t Offset = S - P - 12;
|
||||
int64_t Offset = S - P - 12;
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
|
||||
Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, Offset);
|
||||
@ -433,6 +517,102 @@ void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
|
||||
addSymbol("$t", STT_NOTYPE, 0, IS);
|
||||
}
|
||||
|
||||
void ARMV5ABSLongThunk::writeLong(uint8_t *Buf) {
|
||||
const uint8_t Data[] = {
|
||||
0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc,#-4] ; L1
|
||||
0x00, 0x00, 0x00, 0x00, // L1: .word S
|
||||
};
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf + 4, R_ARM_ABS32, getARMThunkDestVA(Destination));
|
||||
}
|
||||
|
||||
void ARMV5ABSLongThunk::addSymbols(ThunkSection &IS) {
|
||||
addSymbol(Saver.save("__ARMv5ABSLongThunk_" + Destination.getName()),
|
||||
STT_FUNC, 0, IS);
|
||||
addSymbol("$a", STT_NOTYPE, 0, IS);
|
||||
addSymbol("$d", STT_NOTYPE, 4, IS);
|
||||
}
|
||||
|
||||
bool ARMV5ABSLongThunk::isCompatibleWith(uint32_t RelocType) const {
|
||||
// Thumb branch relocations can't use BLX
|
||||
return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
|
||||
}
|
||||
|
||||
void ARMV5PILongThunk::writeLong(uint8_t *Buf) {
|
||||
const uint8_t Data[] = {
|
||||
0x04, 0xc0, 0x9f, 0xe5, // P: ldr ip, [pc,#4] ; L2
|
||||
0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
|
||||
0x1c, 0xff, 0x2f, 0xe1, // bx ip
|
||||
0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA(Destination);
|
||||
uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf + 12, R_ARM_REL32, S - P - 12);
|
||||
}
|
||||
|
||||
void ARMV5PILongThunk::addSymbols(ThunkSection &IS) {
|
||||
addSymbol(Saver.save("__ARMV5PILongThunk_" + Destination.getName()), STT_FUNC,
|
||||
0, IS);
|
||||
addSymbol("$a", STT_NOTYPE, 0, IS);
|
||||
addSymbol("$d", STT_NOTYPE, 12, IS);
|
||||
}
|
||||
|
||||
bool ARMV5PILongThunk::isCompatibleWith(uint32_t RelocType) const {
|
||||
// Thumb branch relocations can't use BLX
|
||||
return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
|
||||
}
|
||||
|
||||
void ThumbV6MABSLongThunk::writeLong(uint8_t *Buf) {
|
||||
// Most Thumb instructions cannot access the high registers r8 - r15. As the
|
||||
// only register we can corrupt is r12 we must instead spill a low register
|
||||
// to the stack to use as a scratch register. We push r1 even though we
|
||||
// don't need to get some space to use for the return address.
|
||||
const uint8_t Data[] = {
|
||||
0x03, 0xb4, // push {r0, r1} ; Obtain scratch registers
|
||||
0x01, 0x48, // ldr r0, [pc, #4] ; L1
|
||||
0x01, 0x90, // str r0, [sp, #4] ; SP + 4 = S
|
||||
0x01, 0xbd, // pop {r0, pc} ; restore r0 and branch to dest
|
||||
0x00, 0x00, 0x00, 0x00 // L1: .word S
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA(Destination);
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf + 8, R_ARM_ABS32, S);
|
||||
}
|
||||
|
||||
void ThumbV6MABSLongThunk::addSymbols(ThunkSection &IS) {
|
||||
addSymbol(Saver.save("__Thumbv6MABSLongThunk_" + Destination.getName()),
|
||||
STT_FUNC, 1, IS);
|
||||
addSymbol("$t", STT_NOTYPE, 0, IS);
|
||||
addSymbol("$d", STT_NOTYPE, 8, IS);
|
||||
}
|
||||
|
||||
void ThumbV6MPILongThunk::writeLong(uint8_t *Buf) {
|
||||
// Most Thumb instructions cannot access the high registers r8 - r15. As the
|
||||
// only register we can corrupt is ip (r12) we must instead spill a low
|
||||
// register to the stack to use as a scratch register.
|
||||
const uint8_t Data[] = {
|
||||
0x01, 0xb4, // P: push {r0} ; Obtain scratch register
|
||||
0x02, 0x48, // ldr r0, [pc, #8] ; L2
|
||||
0x84, 0x46, // mov ip, r0 ; high to low register
|
||||
0x01, 0xbc, // pop {r0} ; restore scratch register
|
||||
0xe7, 0x44, // L1: add pc, ip ; transfer control
|
||||
0xc0, 0x46, // nop ; pad to 4-byte boundary
|
||||
0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4)
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA(Destination);
|
||||
uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf + 12, R_ARM_REL32, S - P - 12);
|
||||
}
|
||||
|
||||
void ThumbV6MPILongThunk::addSymbols(ThunkSection &IS) {
|
||||
addSymbol(Saver.save("__Thumbv6MPILongThunk_" + Destination.getName()),
|
||||
STT_FUNC, 1, IS);
|
||||
addSymbol("$t", STT_NOTYPE, 0, IS);
|
||||
addSymbol("$d", STT_NOTYPE, 12, IS);
|
||||
}
|
||||
|
||||
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
|
||||
void MipsThunk::writeTo(uint8_t *Buf) {
|
||||
uint64_t S = Destination.getVA();
|
||||
@ -502,17 +682,21 @@ 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;
|
||||
static void writePPCLoadAndBranch(uint8_t *Buf, int64_t Offset) {
|
||||
uint16_t OffHa = (Offset + 0x8000) >> 16;
|
||||
uint16_t OffLo = Offset & 0xffff;
|
||||
|
||||
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
|
||||
write32(Buf + 0, 0x3d820000 | OffHa); // addis r12, r2, OffHa
|
||||
write32(Buf + 4, 0xe98c0000 | OffLo); // ld r12, OffLo(r12)
|
||||
write32(Buf + 8, 0x7d8903a6); // mtctr r12
|
||||
write32(Buf + 12, 0x4e800420); // bctr
|
||||
}
|
||||
|
||||
void PPC64PltCallStub::writeTo(uint8_t *Buf) {
|
||||
int64_t Offset = Destination.getGotPltVA() - getPPC64TocBase();
|
||||
// Save the TOC pointer to the save-slot reserved in the call frame.
|
||||
write32(Buf + 0, 0xf8410018); // std r2,24(r1)
|
||||
writePPCLoadAndBranch(Buf + 4, Offset);
|
||||
}
|
||||
|
||||
void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
|
||||
@ -521,6 +705,16 @@ void PPC64PltCallStub::addSymbols(ThunkSection &IS) {
|
||||
S->NeedsTocRestore = true;
|
||||
}
|
||||
|
||||
void PPC64LongBranchThunk::writeTo(uint8_t *Buf) {
|
||||
int64_t Offset = Destination.getPPC64LongBranchTableVA() - getPPC64TocBase();
|
||||
writePPCLoadAndBranch(Buf, Offset);
|
||||
}
|
||||
|
||||
void PPC64LongBranchThunk::addSymbols(ThunkSection &IS) {
|
||||
addSymbol(Saver.save("__long_branch_" + Destination.getName()), STT_FUNC, 0,
|
||||
IS);
|
||||
}
|
||||
|
||||
Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
|
||||
|
||||
Thunk::~Thunk() = default;
|
||||
@ -534,10 +728,67 @@ static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
|
||||
}
|
||||
|
||||
// Creates a thunk for Thumb-ARM interworking.
|
||||
// Arm Architectures v5 and v6 do not support Thumb2 technology. This means
|
||||
// - MOVT and MOVW instructions cannot be used
|
||||
// - Only Thumb relocation that can generate a Thunk is a BL, this can always
|
||||
// be transformed into a BLX
|
||||
static Thunk *addThunkPreArmv7(RelType Reloc, Symbol &S) {
|
||||
switch (Reloc) {
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
case R_ARM_JUMP24:
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_THM_CALL:
|
||||
if (Config->Pic)
|
||||
return make<ARMV5PILongThunk>(S);
|
||||
return make<ARMV5ABSLongThunk>(S);
|
||||
}
|
||||
fatal("relocation " + toString(Reloc) + " to " + toString(S) +
|
||||
" not supported for Armv5 or Armv6 targets");
|
||||
}
|
||||
|
||||
// Create a thunk for Thumb long branch on V6-M.
|
||||
// Arm Architecture v6-M only supports Thumb instructions. This means
|
||||
// - MOVT and MOVW instructions cannot be used.
|
||||
// - Only a limited number of instructions can access registers r8 and above
|
||||
// - No interworking support is needed (all Thumb).
|
||||
static Thunk *addThunkV6M(RelType Reloc, Symbol &S) {
|
||||
switch (Reloc) {
|
||||
case R_ARM_THM_JUMP19:
|
||||
case R_ARM_THM_JUMP24:
|
||||
case R_ARM_THM_CALL:
|
||||
if (Config->Pic)
|
||||
return make<ThumbV6MPILongThunk>(S);
|
||||
return make<ThumbV6MABSLongThunk>(S);
|
||||
}
|
||||
fatal("relocation " + toString(Reloc) + " to " + toString(S) +
|
||||
" not supported for Armv6-M targets");
|
||||
}
|
||||
|
||||
// Creates a thunk for Thumb-ARM interworking or branch range extension.
|
||||
static Thunk *addThunkArm(RelType Reloc, Symbol &S) {
|
||||
// ARM relocations need ARM to Thumb interworking Thunks.
|
||||
// Thumb relocations need Thumb to ARM relocations.
|
||||
// Use position independent Thunks if we require position independent code.
|
||||
// Decide which Thunk is needed based on:
|
||||
// Available instruction set
|
||||
// - An Arm Thunk can only be used if Arm state is available.
|
||||
// - A Thumb Thunk can only be used if Thumb state is available.
|
||||
// - Can only use a Thunk if it uses instructions that the Target supports.
|
||||
// Relocation is branch or branch and link
|
||||
// - Branch instructions cannot change state, can only select Thunk that
|
||||
// starts in the same state as the caller.
|
||||
// - Branch and link relocations can change state, can select Thunks from
|
||||
// either Arm or Thumb.
|
||||
// Position independent Thunks if we require position independent code.
|
||||
|
||||
// Handle architectures that have restrictions on the instructions that they
|
||||
// can use in Thunks. The flags below are set by reading the BuildAttributes
|
||||
// of the input objects. InputFiles.cpp contains the mapping from ARM
|
||||
// architecture to flag.
|
||||
if (!Config->ARMHasMovtMovw) {
|
||||
if (!Config->ARMJ1J2BranchEncoding)
|
||||
return addThunkPreArmv7(Reloc, S);
|
||||
return addThunkV6M(Reloc, S);
|
||||
}
|
||||
|
||||
switch (Reloc) {
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
@ -565,9 +816,14 @@ static Thunk *addThunkMips(RelType Type, Symbol &S) {
|
||||
}
|
||||
|
||||
static Thunk *addThunkPPC64(RelType Type, Symbol &S) {
|
||||
if (Type == R_PPC64_REL24)
|
||||
assert(Type == R_PPC64_REL24 && "unexpected relocation type for thunk");
|
||||
if (S.isInPlt())
|
||||
return make<PPC64PltCallStub>(S);
|
||||
fatal("unexpected relocation type");
|
||||
|
||||
if (Config->Pic)
|
||||
return make<PPC64PILongBranchThunk>(S);
|
||||
|
||||
return make<PPC64PDLongBranchThunk>(S);
|
||||
}
|
||||
|
||||
Thunk *addThunk(RelType Type, Symbol &S) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,6 @@
|
||||
# $FreeBSD$
|
||||
MinGW/
|
||||
cmake/
|
||||
docs/
|
||||
test/
|
||||
unittests/
|
||||
utils/
|
||||
|
@ -4,7 +4,7 @@ lld License
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2011-2018 by the contributors listed in CREDITS.TXT
|
||||
Copyright (c) 2011-2019 by the contributors listed in CREDITS.TXT
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
@ -53,7 +53,7 @@ between speed, simplicity and extensibility.
|
||||
until we need them to continue linking.
|
||||
When we need to do some costly operation (such as looking up
|
||||
a hash table for each symbol), we do it only once.
|
||||
We obtain a handler (which is typically just a pointer to actual data)
|
||||
We obtain a handle (which is typically just a pointer to actual data)
|
||||
on the first operation and use it throughout the process.
|
||||
|
||||
* Efficient archive file handling
|
||||
@ -90,18 +90,18 @@ between speed, simplicity and extensibility.
|
||||
`--end-group`, to let the linker loop over the files between the options until
|
||||
no new symbols are added to the set.
|
||||
|
||||
Visiting the same archive files multiple makes the linker slower.
|
||||
Visiting the same archive files multiple times makes the linker slower.
|
||||
|
||||
Here is how LLD approaches the problem. Instead of memorizing only undefined
|
||||
symbols, we program LLD so that it memorizes all symbols. When it sees an
|
||||
undefined symbol that can be resolved by extracting an object file from an
|
||||
archive file it previously visited, it immediately extracts the file and link
|
||||
it. It is doable because LLD does not forget symbols it have seen in archive
|
||||
archive file it previously visited, it immediately extracts the file and links
|
||||
it. It is doable because LLD does not forget symbols it has seen in archive
|
||||
files.
|
||||
|
||||
We believe that the LLD's way is efficient and easy to justify.
|
||||
We believe that LLD's way is efficient and easy to justify.
|
||||
|
||||
The semantics of LLD's archive handling is different from the traditional
|
||||
The semantics of LLD's archive handling are different from the traditional
|
||||
Unix's. You can observe it if you carefully craft archive files to exploit
|
||||
it. However, in reality, we don't know any program that cannot link with our
|
||||
algorithm so far, so it's not going to cause trouble.
|
||||
@ -157,7 +157,7 @@ functions, the code of the linker should look obvious to you.
|
||||
- Undefined symbols represent undefined symbols, which need to be replaced by
|
||||
Defined symbols by the resolver until the link is complete.
|
||||
- Lazy symbols represent symbols we found in archive file headers
|
||||
which can turn into Defined if we read archieve members.
|
||||
which can turn into Defined if we read archive members.
|
||||
|
||||
There's only one Symbol instance for each unique symbol name. This uniqueness
|
||||
is guaranteed by the symbol table. As the resolver reads symbols from input
|
||||
|
@ -6,7 +6,4 @@ currently tested with Sphinx 1.1.3.
|
||||
|
||||
We currently use the 'nature' theme and a Beaker inspired structure.
|
||||
|
||||
To rebuild documents into html:
|
||||
|
||||
[/lld/docs]> make html
|
||||
|
||||
See sphinx_intro.rst for more details.
|
||||
|
@ -74,7 +74,7 @@ files in parallel. Therefore, there should be no parsing state in you Reader
|
||||
object. Any parsing state should be in ivars of your File subclass or in
|
||||
some temporary object.
|
||||
|
||||
The key method to implement in a reader is::
|
||||
The key function to implement in a reader is::
|
||||
|
||||
virtual error_code loadFile(LinkerInput &input,
|
||||
std::vector<std::unique_ptr<File>> &result);
|
||||
|
@ -1,33 +1,22 @@
|
||||
=======================
|
||||
LLD 7.0.0 Release Notes
|
||||
lld 8.0.0 Release Notes
|
||||
=======================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
.. warning::
|
||||
These are in-progress notes for the upcoming LLVM 8.0.0 release.
|
||||
Release notes for previous releases can be found on
|
||||
`the Download Page <https://releases.llvm.org/download.html>`_.
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
lld is a high-performance linker that supports ELF (Unix), COFF (Windows),
|
||||
Mach-O (macOS), MinGW and WebAssembly. lld is command-line-compatible with GNU
|
||||
linkers and Microsoft link.exe, and is significantly faster than the system
|
||||
default linkers.
|
||||
|
||||
lld 7 for ELF, COFF and MinGW are production-ready.
|
||||
|
||||
* lld/ELF can build the entire FreeBSD/{AMD64,ARMv7} and will be the default
|
||||
linker of the next version of the operating system.
|
||||
|
||||
* lld/COFF is being used to create official builds of large popular programs
|
||||
such as Chrome and Firefox.
|
||||
|
||||
* lld/MinGW is being used by Firefox for their MinGW builds. lld/MinGW still
|
||||
needs a sysroot specifically built for lld, with llvm-dlltool, though.
|
||||
|
||||
* lld/WebAssembly is used as the default (only) linker in Emscripten when using
|
||||
the upstream LLVM compiler.
|
||||
|
||||
* lld/Mach-O is still experimental.
|
||||
This document contains the release notes for the lld linker, release 8.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 <https://llvm.org/releases/>`_.
|
||||
|
||||
Non-comprehensive list of changes in this release
|
||||
=================================================
|
||||
@ -35,80 +24,57 @@ Non-comprehensive list of changes in this release
|
||||
ELF Improvements
|
||||
----------------
|
||||
|
||||
* Fixed a lot of long-tail compatibility issues with GNU linkers.
|
||||
* lld now supports RISC-V. (`r339364
|
||||
<https://reviews.llvm.org/rL339364>`_)
|
||||
|
||||
* Added ``-z retpolineplt`` to emit a PLT entry that doesn't contain an indirect
|
||||
jump instruction to mitigate Spectre v2 vulnerability.
|
||||
* Default image base address has changed from 65536 to 2 MiB for i386
|
||||
and 4 MiB for AArch64 to make lld-generated executables work better
|
||||
with automatic superpage promotion. FreeBSD can promote contiguous
|
||||
non-superpages to a superpage if they are aligned to the superpage
|
||||
size. (`r342746 <https://reviews.llvm.org/rL342746>`_)
|
||||
|
||||
* Added experimental support for `SHT_RELR sections
|
||||
<https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg>`_ to create a
|
||||
compact dynamic relocation table.
|
||||
* lld/Hexagon can now link Linux kernel and musl libc for Qualcomm
|
||||
Hexagon ISA.
|
||||
|
||||
* Added support for `split stacks <https://gcc.gnu.org/wiki/SplitStacks>`_.
|
||||
* Initial MSP430 ISA support has landed.
|
||||
|
||||
* Added support for address significance table (section with type
|
||||
SHT_LLVM_ADDRSIG) to improve Identical Code Folding (ICF). Combined with the
|
||||
``-faddrsig`` compiler option added to Clang 7, lld's ``--icf=all`` can now
|
||||
safely merge functions and data to generate smaller outputs than before.
|
||||
|
||||
* Improved ``--gdb-index`` so that it is faster (`r336790
|
||||
<https://reviews.llvm.org/rL336790>`_) and uses less memory (`r336672
|
||||
<https://reviews.llvm.org/rL336672>`_).
|
||||
|
||||
* Reduced memory usage of ``--compress-debug-sections`` (`r338913
|
||||
<https://reviews.llvm.org/rL338913>`_).
|
||||
|
||||
* Added linker script OVERLAY support (`r335714 <https://reviews.llvm.org/rL335714>`_).
|
||||
|
||||
* Added ``--warn-backref`` to make it easy to identify command line option order
|
||||
that doesn't work with GNU linkers (`r329636 <https://reviews.llvm.org/rL329636>`_)
|
||||
|
||||
* Added ld.lld.1 man page (`r324512 <https://reviews.llvm.org/rL324512>`_).
|
||||
|
||||
* Added support for multi-GOT.
|
||||
|
||||
* Added support for MIPS position-independent executable (PIE).
|
||||
|
||||
* Fixed MIPS TLS GOT entries for local symbols in shared libraries.
|
||||
|
||||
* Fixed calculation of MIPS GP relative relocations in case of relocatable
|
||||
output.
|
||||
|
||||
* Added support for PPCv2 ABI.
|
||||
|
||||
* Removed an incomplete support of PPCv1 ABI.
|
||||
|
||||
* Added support for Qualcomm Hexagon ISA.
|
||||
|
||||
* Added the following flags: ``--apply-dynamic-relocs``, ``--check-sections``,
|
||||
``--cref``, ``--just-symbols``, ``--keep-unique``,
|
||||
``--no-allow-multiple-definition``, ``--no-apply-dynamic-relocs``,
|
||||
``--no-check-sections``, ``--no-gnu-unique, ``--no-pic-executable``,
|
||||
``--no-undefined-version``, ``--no-warn-common``, ``--pack-dyn-relocs=relr``,
|
||||
``--pop-state``, ``--print-icf-sections``, ``--push-state``,
|
||||
``--thinlto-index-only``, ``--thinlto-object-suffix-replace``,
|
||||
``--thinlto-prefix-replace``, ``--warn-backref``, ``-z combreloc``, ``-z
|
||||
copyreloc``, ``-z initfirst``, ``-z keep-text-section-prefix``, ``-z lazy``,
|
||||
``-z noexecstack``, ``-z relro``, ``-z retpolineplt``, ``-z text``
|
||||
* The following flags have been added: ``-z interpose``, ``-z global``
|
||||
|
||||
COFF Improvements
|
||||
-----------------
|
||||
|
||||
* Improved correctness of exporting mangled stdcall symbols.
|
||||
* PDB GUID is set to hash of PDB contents instead to a random byte
|
||||
sequence for build reproducibility.
|
||||
|
||||
* Completed support for ARM64 relocations.
|
||||
* The following flags have been added: ``/force:multiple``
|
||||
|
||||
* Added support for outputting PDB debug info for MinGW targets.
|
||||
* lld now can link against import libraries produced by GNU tools.
|
||||
|
||||
* Improved compatibility of output binaries with GNU binutils objcopy/strip.
|
||||
* lld can create thunks for ARM, to allow linking images over 16 MB.
|
||||
|
||||
* Sped up PDB file creation.
|
||||
MinGW Improvements
|
||||
------------------
|
||||
|
||||
* Changed section layout to improve compatibility with link.exe.
|
||||
* lld can now automatically import data variables from DLLs without the
|
||||
use of the dllimport attribute.
|
||||
|
||||
* `/subsystem` inference is improved to cover more corner cases.
|
||||
* lld can now use existing normal MinGW sysroots with import libraries and
|
||||
CRT startup object files for GNU binutils. lld can handle most object
|
||||
files produced by GCC, and thus works as a drop-in replacement for
|
||||
ld.bfd in such environments. (There are known issues with linking crtend.o
|
||||
from GCC in setups with DWARF exceptions though, where object files are
|
||||
linked in a different order than with GNU ld, inserting a DWARF exception
|
||||
table terminator too early.)
|
||||
|
||||
* Added the following flags: ``--color-diagnostics={always,never,auto}``,
|
||||
``--no-color-diagnostics``, ``/brepro``, ``/debug:full``, ``/debug:ghash``,
|
||||
``/guard:cf``, ``/guard:longjmp``, ``/guard:nolongjmp``, ``/integritycheck``,
|
||||
``/order``, ``/pdbsourcepath``, ``/timestamp``
|
||||
MachO Improvements
|
||||
------------------
|
||||
|
||||
* Item 1.
|
||||
|
||||
WebAssembly Improvements
|
||||
------------------------
|
||||
|
||||
* Add initial support for creating shared libraries (-shared).
|
||||
Note: The shared library format is still under active development and may
|
||||
undergo significant changes in future versions.
|
||||
See: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
|
||||
|
@ -1,13 +1,10 @@
|
||||
WebAssembly lld port
|
||||
====================
|
||||
|
||||
Note: The WebAssembly port is still a work in progress and is be lacking
|
||||
certain features.
|
||||
|
||||
The WebAssembly version of lld takes WebAssembly binaries as inputs and produces
|
||||
a WebAssembly binary as its output. For the most part this port tried to mimic
|
||||
the behaviour of traditional ELF linkers and specifically the ELF lld port.
|
||||
Where possible that command line flags and the semantics should be the same.
|
||||
a WebAssembly binary as its output. For the most part it tries to mimic the
|
||||
behaviour of traditional ELF linkers and specifically the ELF lld port. Where
|
||||
possible that command line flags and the semantics should be the same.
|
||||
|
||||
|
||||
Object file format
|
||||
@ -23,14 +20,95 @@ currently requires enabling the experimental backed using
|
||||
``-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly``.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The WebAssembly version of lld is installed as **wasm-ld**. It shared many
|
||||
common linker flags with **ld.lld** but also includes several
|
||||
WebAssembly-specific options:
|
||||
|
||||
.. option:: --no-entry
|
||||
|
||||
Don't search for the entry point symbol (by default ``_start``).
|
||||
|
||||
.. option:: --export-table
|
||||
|
||||
Export the function table to the environment.
|
||||
|
||||
.. option:: --import-table
|
||||
|
||||
Import the function table from the environment.
|
||||
|
||||
.. option:: --export-all
|
||||
|
||||
Export all symbols (normally combined with --no-gc-sections)
|
||||
|
||||
.. option:: --export-dynamic
|
||||
|
||||
When building an executable, export any non-hidden symbols. By default only
|
||||
the entry point and any symbols marked with --export/--export-all are
|
||||
exported.
|
||||
|
||||
.. option:: --global-base=<value>
|
||||
|
||||
Address at which to place global data.
|
||||
|
||||
.. option:: --no-merge-data-segments
|
||||
|
||||
Disable merging of data segments.
|
||||
|
||||
.. option:: --stack-first
|
||||
|
||||
Place stack at start of linear memory rather than after data.
|
||||
|
||||
.. option:: --compress-relocations
|
||||
|
||||
Relocation targets in the code section 5-bytes wide in order to potentially
|
||||
occomate the largest LEB128 value. This option will cause the linker to
|
||||
shirnk the code section to remove any padding from the final output. However
|
||||
because it effects code offset, this option is not comatible with outputing
|
||||
debug information.
|
||||
|
||||
.. option:: --allow-undefined
|
||||
|
||||
Allow undefined symbols in linked binary.
|
||||
|
||||
.. option:: --import-memory
|
||||
|
||||
Import memory from the environment.
|
||||
|
||||
.. option:: --initial-memory=<value>
|
||||
|
||||
Initial size of the linear memory. Default: static data size.
|
||||
|
||||
.. option:: --max-memory=<value>
|
||||
|
||||
Maximum size of the linear memory. Default: unlimited.
|
||||
|
||||
By default the function table is neither imported nor exported, but defined
|
||||
for internal use only.
|
||||
|
||||
When building shared libraries symbols are exported if they are marked
|
||||
as ``visibility=default``. When building executables only the entry point is
|
||||
exported by default. In addition any symbol included on the command line via
|
||||
``--export`` is also exported.
|
||||
|
||||
Since WebAssembly is designed with size in mind the linker defaults to
|
||||
``--gc-sections`` which means that all unused functions and data segments will
|
||||
be stripped from the binary.
|
||||
|
||||
The symbols which are preserved by default are:
|
||||
|
||||
- The entry point (by default ``_start``).
|
||||
- Any symbol which is to be exported.
|
||||
- Any symbol transitively referenced by the above.
|
||||
|
||||
|
||||
Missing features
|
||||
----------------
|
||||
|
||||
There are several key features that are not yet implement in the WebAssembly
|
||||
ports:
|
||||
|
||||
- COMDAT support. This means that support for C++ is still very limited.
|
||||
- Function stripping. Currently there is no support for ``--gc-sections`` so
|
||||
functions and data from a given object will linked as a unit.
|
||||
- Section start/end symbols. The synthetic symbols that mark the start and
|
||||
of data regions are not yet created in the output file.
|
||||
- Merging of data section similar to ``SHF_MERGE`` in the ELF world is not
|
||||
supported.
|
||||
- No support for creating shared libraries. The spec for shared libraries in
|
||||
WebAssembly is still in flux:
|
||||
https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
|
||||
|
@ -48,9 +48,9 @@ copyright = u'2011-%d, LLVM Project' % date.today().year
|
||||
# built documents.
|
||||
#
|
||||
# The short version.
|
||||
version = '7'
|
||||
version = '8'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '7'
|
||||
release = '8'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -1,7 +1,7 @@
|
||||
LLD - The LLVM Linker
|
||||
=====================
|
||||
|
||||
LLD is a linker from the LLVM project. That is a drop-in replacement
|
||||
LLD is a linker from the LLVM project that is a drop-in replacement
|
||||
for system linkers and runs much faster than them. It also provides
|
||||
features that are useful for toolchain developers.
|
||||
|
||||
@ -17,7 +17,7 @@ read :doc:`AtomLLD`.
|
||||
Features
|
||||
--------
|
||||
|
||||
- LLD is a drop-in replacement for the GNU linkers. That accepts the
|
||||
- LLD is a drop-in replacement for the GNU linkers that accepts the
|
||||
same command line arguments and linker scripts as GNU.
|
||||
|
||||
We are currently working closely with the FreeBSD project to make
|
||||
@ -30,29 +30,27 @@ Features
|
||||
<https://www.freebsd.org/news/status/report-2016-10-2016-12.html#Using-LLVM%27s-LLD-Linker-as-FreeBSD%27s-System-Linker>`_.
|
||||
|
||||
- LLD is very fast. When you link a large program on a multicore
|
||||
machine, you can expect that LLD runs more than twice as fast as GNU
|
||||
machine, you can expect that LLD runs more than twice as fast as the GNU
|
||||
gold linker. Your milage may vary, though.
|
||||
|
||||
- It supports various CPUs/ABIs including x86-64, x86, x32, AArch64,
|
||||
ARM, MIPS 32/64 big/little-endian, PowerPC, PowerPC 64 and AMDGPU.
|
||||
Among these, x86-64 is the most well-supported target and have
|
||||
reached production quality. AArch64 and MIPS seem decent too. x86
|
||||
should be OK but not well tested yet. ARM support is being developed
|
||||
actively.
|
||||
Among these, x86-64, AArch64, and ARM (>= v6) are production quality.
|
||||
MIPS seems decent too. x86 should be OK but is not well tested yet.
|
||||
|
||||
- It is always a cross-linker, meaning that it always supports all the
|
||||
above targets however it was built. In fact, we don't provide a
|
||||
build-time option to enable/disable each target. This should make it
|
||||
easy to use our linker as part of a cross-compile toolchain.
|
||||
|
||||
- You can embed LLD to your program to eliminate dependency to
|
||||
- You can embed LLD in your program to eliminate dependencies on
|
||||
external linkers. All you have to do is to construct object files
|
||||
and command line arguments just like you would do to invoke an
|
||||
external linker and then call the linker's main function,
|
||||
``lld::elf::link``, from your code.
|
||||
|
||||
- It is small. We are using LLVM libObject library to read from object
|
||||
files, so it is not completely a fair comparison, but as of February
|
||||
files, so it is not a completely fair comparison, but as of February
|
||||
2017, LLD/ELF consists only of 21k lines of C++ code while GNU gold
|
||||
consists of 198k lines of C++ code.
|
||||
|
||||
@ -102,8 +100,8 @@ under ``tools`` directory just like you probably did for clang. For the
|
||||
details, see `Getting Started with the LLVM System
|
||||
<http://llvm.org/docs/GettingStarted.html>`_.
|
||||
|
||||
If you haven't checkout out LLVM, the easiest way to build LLD is to
|
||||
checkout the entire LLVM projects/sub-projects from a git mirror and
|
||||
If you haven't checked out LLVM, the easiest way to build LLD is to
|
||||
check out the entire LLVM projects/sub-projects from a git mirror and
|
||||
build that tree. You need `cmake` and of course a C++ compiler.
|
||||
|
||||
.. code-block:: console
|
||||
|
@ -3,7 +3,7 @@
|
||||
.\"
|
||||
.\" This man page documents only lld's ELF linking support, obtained originally
|
||||
.\" from FreeBSD.
|
||||
.Dd September 14, 2018
|
||||
.Dd September 26, 2018
|
||||
.Dt LD.LLD 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -13,6 +13,7 @@
|
||||
.Nm ld.lld
|
||||
.Op Ar options
|
||||
.Ar objfile ...
|
||||
|
||||
.Sh DESCRIPTION
|
||||
A linker takes one or more object, archive, and library files, and combines
|
||||
them into an output file (an executable, a shared library, or another object
|
||||
@ -25,6 +26,21 @@ is a drop-in replacement for the GNU BFD and gold linkers.
|
||||
It accepts most of the same command line arguments and linker scripts
|
||||
as GNU linkers.
|
||||
.Pp
|
||||
.Nm
|
||||
currently supports i386, x86-64, ARM, AArch64, PowerPC32, PowerPC64,
|
||||
MIPS32, MIPS64, RISC-V, AMDGPU, Hexagon and SPARC V9 targets.
|
||||
.Nm
|
||||
acts as a Microsoft link.exe-compatible linker if invoked as
|
||||
.Nm lld-link
|
||||
and as macOS's ld if invoked as
|
||||
.Nm ld.ld64.
|
||||
All these targets are always supported however
|
||||
.Nm
|
||||
was built, so you can always use
|
||||
.Nm
|
||||
as a native linker as well as a cross linker.
|
||||
|
||||
.Sh OPTIONS
|
||||
Many options have both a single-letter and long form.
|
||||
When using the long form options other than those beginning with the
|
||||
letter
|
||||
@ -36,7 +52,6 @@ require two dashes to avoid confusion with the
|
||||
.Fl o Ar path
|
||||
option.
|
||||
.Pp
|
||||
These options are available:
|
||||
.Bl -tag -width indent
|
||||
.It Fl -allow-multiple-definition
|
||||
Do not error if a symbol is defined multiple times.
|
||||
@ -155,6 +170,9 @@ Maximum number of errors to emit before stopping.
|
||||
A value of zero indicates that there is no limit.
|
||||
.It Fl -error-unresolved-symbols
|
||||
Report unresolved symbols as errors.
|
||||
.It Fl -execute-only
|
||||
Mark executable sections unreadable. This option is currently only
|
||||
supported on AArch64.
|
||||
.It Fl -exclude-libs Ns = Ns Ar value
|
||||
Exclude static libraries from automatic export.
|
||||
.It Fl -export-dynamic , Fl E
|
||||
@ -308,6 +326,8 @@ Include hotness information in the optimization remarks file.
|
||||
Create a position independent executable.
|
||||
.It Fl -print-gc-sections
|
||||
List removed unused sections.
|
||||
.It Fl -print-icf-sections
|
||||
List identical folded sections.
|
||||
.It Fl -print-map
|
||||
Print a link map to the standard output.
|
||||
.It Fl -push-state
|
||||
@ -315,7 +335,7 @@ Save the current state of
|
||||
.Fl -as-needed ,
|
||||
.Fl -static ,
|
||||
and
|
||||
.Fl -while-archive.
|
||||
.Fl -whole-archive.
|
||||
.It Fl -pop-state
|
||||
Undo the effect of
|
||||
.Fl -push-state.
|
||||
@ -437,6 +457,17 @@ This can be used to ensure linker invocation remains compatible with
|
||||
traditional Unix-like linkers.
|
||||
.It Fl -warn-common
|
||||
Warn about duplicate common symbols.
|
||||
.It Fl -warn-ifunc-textrel
|
||||
Warn about using ifunc symbols in conjunction with text relocations.
|
||||
Older versions of glibc library (2.28 and earlier) has a bug that causes
|
||||
the segment that includes ifunc symbols to be marked as not executable when
|
||||
they are relocated. As a result, although the program compiles and links
|
||||
successfully, it gives segmentation fault when the instruction pointer reaches
|
||||
an ifunc symbol. Use -warn-ifunc-textrel to let lld give a warning, if the
|
||||
code may include ifunc symbols, may do text relocations and be linked with
|
||||
an older glibc version. Otherwise, there is no need to use it, as the default
|
||||
value does not give a warning. This flag has been introduced in late 2018,
|
||||
has no counter part in ld and gold linkers, and may be removed in the future.
|
||||
.It Fl -warn-unresolved-symbols
|
||||
Report unresolved symbols as warnings.
|
||||
.It Fl -whole-archive
|
||||
@ -451,6 +482,12 @@ Make the main stack executable.
|
||||
Stack permissions are recorded in the
|
||||
.Dv PT_GNU_STACK
|
||||
segment.
|
||||
.It Cm global
|
||||
Sets the
|
||||
.Dv DF_1_GLOBAL flag in the
|
||||
.Dv DYNAMIC
|
||||
section.
|
||||
Different loaders can decide how to handle this flag on their own.
|
||||
.It Cm ifunc-noplt
|
||||
Do not emit PLT entries for GNU ifuncs.
|
||||
Instead, preserve relocations for ifunc call sites so that they may
|
||||
@ -465,9 +502,9 @@ flag to indicate the module should be initialized first.
|
||||
.It Cm interpose
|
||||
Set the
|
||||
.Dv DF_1_INTERPOSE
|
||||
flag to indicate that the object is an interposer.
|
||||
Runtime linkers perform symbol resolution by first searching the application,
|
||||
followed by interposers, and then any other dependencies.
|
||||
flag to indicate to the runtime linker that the object is an interposer.
|
||||
During symbol resolution interposers are searched after the application
|
||||
but before other dependencies.
|
||||
.It Cm muldefs
|
||||
Do not error if a symbol is defined multiple times.
|
||||
The first definition will be used.
|
||||
@ -477,6 +514,10 @@ This is a synonym for
|
||||
Disable combining and sorting multiple relocation sections.
|
||||
.It Cm nocopyreloc
|
||||
Disable the creation of copy relocations.
|
||||
.It Cm nodefaultlib
|
||||
Set the
|
||||
.Dv DF_1_NODEFLIB
|
||||
flag to indicate that default library search paths should be ignored.
|
||||
.It Cm nodelete
|
||||
Set the
|
||||
.Dv DF_1_NODELETE
|
||||
@ -484,7 +525,7 @@ flag to indicate that the object cannot be unloaded from a process.
|
||||
.It Cm nodlopen
|
||||
Set the
|
||||
.Dv DF_1_NOOPEN
|
||||
flag to indcate that the object may not be opened by
|
||||
flag to indicate that the object may not be opened by
|
||||
.Xr dlopen 3 .
|
||||
.It Cm norelro
|
||||
Do not indicate that portions of the object shold be mapped read-only
|
||||
|
84
contrib/llvm/tools/lld/docs/missingkeyfunction.rst
Normal file
84
contrib/llvm/tools/lld/docs/missingkeyfunction.rst
Normal file
@ -0,0 +1,84 @@
|
||||
Missing Key Method
|
||||
==================
|
||||
|
||||
If your build failed with a linker error something like this::
|
||||
|
||||
foo.cc:28: error: undefined reference to 'vtable for C'
|
||||
the vtable symbol may be undefined because the class is missing its key function (see https://lld.llvm.org/missingkeyfunction)
|
||||
|
||||
it's likely that your class C has a key function (defined by the ABI as the first
|
||||
non-pure, non-inline, virtual method), but you haven't actually defined it.
|
||||
|
||||
When a class has a key function, the compiler emits the vtable (and some other
|
||||
things as well) only in the translation unit that defines that key function. Thus,
|
||||
if you're missing the key function, you'll also be missing the vtable. If no other
|
||||
function calls your missing method, you won't see any undefined reference errors
|
||||
for it, but you will see undefined references to the vtable symbol.
|
||||
|
||||
When a class has no non-pure, non-inline, virtual methods, there is no key
|
||||
method, and the compiler is forced to emit the vtable in every translation unit
|
||||
that references the class. In this case, it is emitted in a COMDAT section,
|
||||
which allows the linker to eliminate all duplicate copies. This is still
|
||||
wasteful in terms of object file size and link time, so it's always advisable to
|
||||
ensure there is at least one eligible method that can serve as the key function.
|
||||
|
||||
Here are the most common mistakes that lead to this error:
|
||||
|
||||
Failing to define a virtual destructor
|
||||
--------------------------------------
|
||||
|
||||
Say you have a base class declared in a header file::
|
||||
|
||||
class B {
|
||||
public:
|
||||
B();
|
||||
virtual ~B();
|
||||
...
|
||||
};
|
||||
|
||||
Here, ``~B`` is the first non-pure, non-inline, virtual method, so it is the key
|
||||
method. If you forget to define ``B::~B`` in your source file, the compiler will
|
||||
not emit the vtable for ``B``, and you'll get an undefined reference to "vtable
|
||||
for B".
|
||||
|
||||
This is just an example of the more general mistake of forgetting to define the
|
||||
key function, but it's quite common because virtual destructors are likely to be
|
||||
the first eligible key function and it's easy to forget to implement them. It's
|
||||
also more likely that you won't have any direct references to the destructor, so
|
||||
you won't see any undefined reference errors that point directly to the problem.
|
||||
|
||||
The solution in this case is to implement the missing method.
|
||||
|
||||
Forgetting to declare a virtual method in an abstract class as pure
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Say you have an abstract base class declared in a header file::
|
||||
|
||||
class A {
|
||||
public:
|
||||
A();
|
||||
virtual ~A() {}
|
||||
virtual int foo() = 0;
|
||||
...
|
||||
virtual int bar();
|
||||
...
|
||||
};
|
||||
|
||||
This base class is intended to be abstract, but you forgot to mark one of the
|
||||
methods pure. Here, ``A::bar``, being non-pure, is nominated as the key function,
|
||||
and as a result, the vtable for ``A`` is not emitted, because the compiler is
|
||||
waiting for a translation unit that defines ``A::bar``.
|
||||
|
||||
The solution in this case is to add the missing ``= 0`` to the declaration of
|
||||
``A::bar``.
|
||||
|
||||
Key method is defined, but the linker doesn't see it
|
||||
----------------------------------------------------
|
||||
|
||||
It's also possible that you have defined the key function somewhere, but the
|
||||
object file containing the definition of that method isn't being linked into
|
||||
your application.
|
||||
|
||||
The solution in this case is to check your dependencies to make sure that
|
||||
the object file or the library file containing the key function is given to
|
||||
the linker.
|
@ -3,8 +3,6 @@
|
||||
Open Projects
|
||||
=============
|
||||
|
||||
.. include:: ../include/lld/Core/TODO.txt
|
||||
|
||||
Documentation TODOs
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -20,8 +20,8 @@ command line options, and it drives further linking processes. LLD accepts
|
||||
almost all command line options that the linker shipped with Microsoft Visual
|
||||
C++ (link.exe) supports.
|
||||
|
||||
The current status is that LLD can link itself on Windows x86/x64
|
||||
using Visual C++ 2013 as the compiler.
|
||||
The current status is that LLD is used to link production builds of large
|
||||
real-world binaries such as Firefox and Chromium.
|
||||
|
||||
Development status
|
||||
==================
|
||||
|
@ -29,6 +29,9 @@ uint64_t getZOptionValue(llvm::opt::InputArgList &Args, int Id, StringRef Key,
|
||||
uint64_t Default);
|
||||
|
||||
std::vector<StringRef> getLines(MemoryBufferRef MB);
|
||||
|
||||
StringRef getFilenameWithoutExe(StringRef Path);
|
||||
|
||||
} // namespace args
|
||||
} // namespace lld
|
||||
|
||||
|
@ -153,7 +153,7 @@ T check2(Expected<T> E, llvm::function_ref<std::string()> Prefix) {
|
||||
inline std::string toString(const Twine &S) { return S.str(); }
|
||||
|
||||
// To evaluate the second argument lazily, we use C macro.
|
||||
#define CHECK(E, S) check2(E, [&] { return toString(S); })
|
||||
#define CHECK(E, S) check2((E), [&] { return toString(S); })
|
||||
|
||||
} // namespace lld
|
||||
|
||||
|
@ -22,53 +22,69 @@
|
||||
#include <utility>
|
||||
|
||||
namespace llvm {
|
||||
// ADT's.
|
||||
class Error;
|
||||
class StringRef;
|
||||
class Twine;
|
||||
class MemoryBuffer;
|
||||
class MemoryBufferRef;
|
||||
template<typename T> class ArrayRef;
|
||||
template<unsigned InternalLen> class SmallString;
|
||||
template<typename T, unsigned N> class SmallVector;
|
||||
template<typename T> class SmallVectorImpl;
|
||||
// ADT's.
|
||||
class raw_ostream;
|
||||
class Error;
|
||||
class StringRef;
|
||||
class Twine;
|
||||
class MemoryBuffer;
|
||||
class MemoryBufferRef;
|
||||
template <typename T> class ArrayRef;
|
||||
template <unsigned InternalLen> class SmallString;
|
||||
template <typename T, unsigned N> class SmallVector;
|
||||
template <typename T> class ErrorOr;
|
||||
template <typename T> class Expected;
|
||||
|
||||
template<typename T>
|
||||
struct SaveAndRestore;
|
||||
namespace object {
|
||||
class WasmObjectFile;
|
||||
struct WasmSection;
|
||||
struct WasmSegment;
|
||||
class WasmSymbol;
|
||||
} // namespace object
|
||||
|
||||
template<typename T>
|
||||
class ErrorOr;
|
||||
|
||||
template<typename T>
|
||||
class Expected;
|
||||
|
||||
class raw_ostream;
|
||||
// TODO: DenseMap, ...
|
||||
}
|
||||
namespace wasm {
|
||||
struct WasmEvent;
|
||||
struct WasmEventType;
|
||||
struct WasmFunction;
|
||||
struct WasmGlobal;
|
||||
struct WasmGlobalType;
|
||||
struct WasmRelocation;
|
||||
struct WasmSignature;
|
||||
} // namespace wasm
|
||||
} // namespace llvm
|
||||
|
||||
namespace lld {
|
||||
// Casting operators.
|
||||
using llvm::isa;
|
||||
using llvm::cast;
|
||||
using llvm::dyn_cast;
|
||||
using llvm::dyn_cast_or_null;
|
||||
using llvm::cast_or_null;
|
||||
// Casting operators.
|
||||
using llvm::cast;
|
||||
using llvm::cast_or_null;
|
||||
using llvm::dyn_cast;
|
||||
using llvm::dyn_cast_or_null;
|
||||
using llvm::isa;
|
||||
|
||||
// ADT's.
|
||||
using llvm::Error;
|
||||
using llvm::StringRef;
|
||||
using llvm::Twine;
|
||||
using llvm::MemoryBuffer;
|
||||
using llvm::MemoryBufferRef;
|
||||
using llvm::ArrayRef;
|
||||
using llvm::SmallString;
|
||||
using llvm::SmallVector;
|
||||
using llvm::SmallVectorImpl;
|
||||
using llvm::SaveAndRestore;
|
||||
using llvm::ErrorOr;
|
||||
using llvm::Expected;
|
||||
// ADT's.
|
||||
using llvm::ArrayRef;
|
||||
using llvm::Error;
|
||||
using llvm::ErrorOr;
|
||||
using llvm::Expected;
|
||||
using llvm::MemoryBuffer;
|
||||
using llvm::MemoryBufferRef;
|
||||
using llvm::raw_ostream;
|
||||
using llvm::SmallString;
|
||||
using llvm::SmallVector;
|
||||
using llvm::StringRef;
|
||||
using llvm::Twine;
|
||||
|
||||
using llvm::raw_ostream;
|
||||
using llvm::object::WasmObjectFile;
|
||||
using llvm::object::WasmSection;
|
||||
using llvm::object::WasmSegment;
|
||||
using llvm::object::WasmSymbol;
|
||||
using llvm::wasm::WasmEvent;
|
||||
using llvm::wasm::WasmEventType;
|
||||
using llvm::wasm::WasmFunction;
|
||||
using llvm::wasm::WasmGlobal;
|
||||
using llvm::wasm::WasmGlobalType;
|
||||
using llvm::wasm::WasmRelocation;
|
||||
using llvm::wasm::WasmSignature;
|
||||
} // end namespace lld.
|
||||
|
||||
namespace std {
|
||||
@ -78,6 +94,6 @@ public:
|
||||
return llvm::hash_value(s);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
|
@ -41,9 +41,6 @@ private:
|
||||
std::vector<llvm::GlobPattern> Patterns;
|
||||
};
|
||||
|
||||
inline llvm::ArrayRef<uint8_t> toArrayRef(llvm::StringRef S) {
|
||||
return {reinterpret_cast<const uint8_t *>(S.data()), S.size()};
|
||||
}
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
|
@ -19,4 +19,5 @@ namespace lld {
|
||||
llvm::TargetOptions InitTargetOptionsFromCodeGenFlags();
|
||||
llvm::Optional<llvm::CodeModel::Model> GetCodeModelFromCMModel();
|
||||
std::string GetCPUStr();
|
||||
std::vector<std::string> GetMAttrs();
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ template <typename R, class FuncTy> void parallelForEach(R &&Range, FuncTy Fn) {
|
||||
}
|
||||
|
||||
inline void parallelForEachN(size_t Begin, size_t End,
|
||||
std::function<void(size_t)> Fn) {
|
||||
llvm::function_ref<void(size_t)> Fn) {
|
||||
if (ThreadsEnabled)
|
||||
for_each_n(llvm::parallel::par, Begin, End, Fn);
|
||||
else
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user