Vendor import of lld trunk r321017:
https://llvm.org/svn/llvm-project/lld/trunk@321017
This commit is contained in:
parent
ef458aad8c
commit
197838428c
@ -1,4 +1,4 @@
|
||||
{
|
||||
"project_id" : "lld",
|
||||
"repository.callsign" : "LLD",
|
||||
"conduit_uri" : "https://reviews.llvm.org/"
|
||||
}
|
||||
|
@ -160,8 +160,8 @@ endif ()
|
||||
|
||||
# Configure the Version.inc file.
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Config/Version.inc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/lld/Config/Version.inc)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Common/Version.inc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/lld/Common/Version.inc)
|
||||
|
||||
|
||||
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||||
@ -210,6 +210,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
|
||||
)
|
||||
endif()
|
||||
|
||||
add_subdirectory(Common)
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(tools/lld)
|
||||
|
||||
@ -221,4 +222,5 @@ endif()
|
||||
add_subdirectory(docs)
|
||||
add_subdirectory(COFF)
|
||||
add_subdirectory(ELF)
|
||||
|
||||
add_subdirectory(MinGW)
|
||||
add_subdirectory(wasm)
|
||||
|
@ -17,3 +17,6 @@ N: Lang Hames, Nick Kledzik
|
||||
E: lhames@gmail.com, kledzik@apple.com
|
||||
D: Mach-O backend
|
||||
|
||||
N: Sam Clegg
|
||||
E: sbc@chromium.org
|
||||
D: WebAssembly backend (wasm/*)
|
||||
|
@ -11,12 +11,12 @@ add_lld_library(lldCOFF
|
||||
DLL.cpp
|
||||
Driver.cpp
|
||||
DriverUtils.cpp
|
||||
Error.cpp
|
||||
ICF.cpp
|
||||
InputFiles.cpp
|
||||
LTO.cpp
|
||||
MapFile.cpp
|
||||
MarkLive.cpp
|
||||
MinGW.cpp
|
||||
PDB.cpp
|
||||
Strings.cpp
|
||||
SymbolTable.cpp
|
||||
@ -26,22 +26,20 @@ add_lld_library(lldCOFF
|
||||
LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
BinaryFormat
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoCodeView
|
||||
DebugInfoMSF
|
||||
DebugInfoPDB
|
||||
LTO
|
||||
LibDriver
|
||||
Object
|
||||
LTO
|
||||
MC
|
||||
MCDisassembler
|
||||
Target
|
||||
Object
|
||||
Option
|
||||
Support
|
||||
WindowsManifest
|
||||
|
||||
LINK_LIBS
|
||||
lldCore
|
||||
lldCommon
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
|
@ -8,10 +8,10 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/BinaryFormat/COFF.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
@ -29,17 +29,14 @@ using llvm::support::ulittle32_t;
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
|
||||
SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
|
||||
: Chunk(SectionKind), Repl(this), Header(H), File(F),
|
||||
Relocs(File->getCOFFObj()->getRelocations(Header)),
|
||||
NumRelocs(std::distance(Relocs.begin(), Relocs.end())) {
|
||||
// Initialize SectionName.
|
||||
File->getCOFFObj()->getSectionName(Header, SectionName);
|
||||
|
||||
Align = Header->getAlignment();
|
||||
|
||||
// Chunks may be discarded during comdat merging.
|
||||
Discarded = false;
|
||||
Alignment = Header->getAlignment();
|
||||
|
||||
// If linker GC is disabled, every chunk starts out alive. If linker GC is
|
||||
// enabled, treat non-comdat sections as roots. Generally optimized object
|
||||
@ -62,7 +59,10 @@ static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
|
||||
fatal("SECREL relocation cannot be applied to absolute symbols");
|
||||
}
|
||||
uint64_t SecRel = S - OS->getRVA();
|
||||
assert(SecRel < INT32_MAX && "overflow in SECREL relocation");
|
||||
if (SecRel > UINT32_MAX) {
|
||||
error("overflow in SECREL relocation in section: " + Sec->getSectionName());
|
||||
return;
|
||||
}
|
||||
add32(Off, SecRel);
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ static uint16_t readMOV(uint8_t *Off) {
|
||||
return Imm;
|
||||
}
|
||||
|
||||
static void applyMOV32T(uint8_t *Off, uint32_t V) {
|
||||
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
|
||||
uint32_t Imm = ImmW | (ImmT << 16);
|
||||
@ -129,6 +129,8 @@ static 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");
|
||||
uint32_t S = V < 0 ? 1 : 0;
|
||||
uint32_t J1 = (V >> 19) & 1;
|
||||
uint32_t J2 = (V >> 18) & 1;
|
||||
@ -136,7 +138,7 @@ static void applyBranch20T(uint8_t *Off, int32_t V) {
|
||||
or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
|
||||
}
|
||||
|
||||
static void applyBranch24T(uint8_t *Off, int32_t V) {
|
||||
void applyBranch24T(uint8_t *Off, int32_t V) {
|
||||
if (!isInt<25>(V))
|
||||
fatal("relocation out of range");
|
||||
uint32_t S = V < 0 ? 1 : 0;
|
||||
@ -167,36 +169,61 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
|
||||
}
|
||||
}
|
||||
|
||||
static void applyArm64Addr(uint8_t *Off, uint64_t Imm) {
|
||||
// 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) {
|
||||
uint32_t Orig = read32le(Off);
|
||||
uint64_t Imm = ((Orig >> 29) & 0x3) | ((Orig >> 3) & 0x1FFFFC);
|
||||
S += Imm;
|
||||
Imm = (S >> 12) - (P >> 12);
|
||||
uint32_t ImmLo = (Imm & 0x3) << 29;
|
||||
uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
|
||||
uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
|
||||
write32le(Off, (read32le(Off) & ~Mask) | ImmLo | ImmHi);
|
||||
write32le(Off, (Orig & ~Mask) | ImmLo | ImmHi);
|
||||
}
|
||||
|
||||
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
|
||||
static void applyArm64Imm(uint8_t *Off, uint64_t Imm) {
|
||||
// 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) {
|
||||
uint32_t Orig = read32le(Off);
|
||||
Imm += (Orig >> 10) & 0xFFF;
|
||||
Orig &= ~(0xFFF << 10);
|
||||
write32le(Off, Orig | ((Imm & 0xFFF) << 10));
|
||||
write32le(Off, Orig | ((Imm & (0xFFF >> RangeLimit)) << 10));
|
||||
}
|
||||
|
||||
// Add the 12 bit page offset to the existing immediate.
|
||||
// Ldr/str instructions store the opcode immediate scaled
|
||||
// by the load/store size (giving a larger range for larger
|
||||
// loads/stores). The immediate is always (both before and after
|
||||
// fixing up the relocation) stored scaled similarly.
|
||||
// Even if larger loads/stores have a larger range, limit the
|
||||
// effective offset to 12 bit, since it is intended to be a
|
||||
// page offset.
|
||||
static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) {
|
||||
int Size = read32le(Off) >> 30;
|
||||
Imm >>= Size;
|
||||
applyArm64Imm(Off, Imm);
|
||||
uint32_t Orig = read32le(Off);
|
||||
uint32_t Size = Orig >> 30;
|
||||
// 0x04000000 indicates SIMD/FP registers
|
||||
// 0x00800000 indicates 128 bit
|
||||
if ((Orig & 0x4800000) == 0x4800000)
|
||||
Size += 4;
|
||||
if ((Imm & ((1 << Size) - 1)) != 0)
|
||||
fatal("misaligned ldr/str offset");
|
||||
applyArm64Imm(Off, Imm >> Size, Size);
|
||||
}
|
||||
|
||||
void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS,
|
||||
uint64_t S, uint64_t P) const {
|
||||
switch (Type) {
|
||||
case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, (S >> 12) - (P >> 12)); break;
|
||||
case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff); break;
|
||||
case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, S, P); break;
|
||||
case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff, 0); break;
|
||||
case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(Off, S & 0xfff); break;
|
||||
case IMAGE_REL_ARM64_BRANCH26: or32(Off, ((S - P) & 0x0FFFFFFC) >> 2); break;
|
||||
case IMAGE_REL_ARM64_ADDR32: add32(Off, S + Config->ImageBase); break;
|
||||
case IMAGE_REL_ARM64_ADDR32NB: add32(Off, S); break;
|
||||
case IMAGE_REL_ARM64_ADDR64: add64(Off, S + Config->ImageBase); break;
|
||||
case IMAGE_REL_ARM64_SECREL: applySecRel(this, Off, OS, S); break;
|
||||
default:
|
||||
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
|
||||
}
|
||||
@ -224,8 +251,19 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
|
||||
// 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.
|
||||
SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
|
||||
Defined *Sym = cast<Defined>(Body);
|
||||
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();
|
||||
OutputSection *OS = C ? C->getOutputSection() : nullptr;
|
||||
|
||||
@ -301,8 +339,8 @@ void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
|
||||
uint8_t Ty = getBaserelType(Rel);
|
||||
if (Ty == IMAGE_REL_BASED_ABSOLUTE)
|
||||
continue;
|
||||
SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
|
||||
if (isa<DefinedAbsolute>(Body))
|
||||
Symbol *Target = File->getSymbol(Rel.SymbolTableIndex);
|
||||
if (!Target || isa<DefinedAbsolute>(Target))
|
||||
continue;
|
||||
Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
|
||||
}
|
||||
@ -323,12 +361,8 @@ bool SectionChunk::isCOMDAT() const {
|
||||
void SectionChunk::printDiscardedMessage() const {
|
||||
// Removed by dead-stripping. If it's removed by ICF, ICF already
|
||||
// printed out the name, so don't repeat that here.
|
||||
if (Sym && this == Repl) {
|
||||
if (Discarded)
|
||||
message("Discarded comdat symbol " + Sym->getName());
|
||||
else if (!Live)
|
||||
message("Discarded " + Sym->getName());
|
||||
}
|
||||
if (Sym && this == Repl)
|
||||
message("Discarded " + Sym->getName());
|
||||
}
|
||||
|
||||
StringRef SectionChunk::getDebugName() {
|
||||
@ -351,7 +385,7 @@ void SectionChunk::replace(SectionChunk *Other) {
|
||||
CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
|
||||
// Common symbols are aligned on natural boundaries up to 32 bytes.
|
||||
// This is what MSVC link.exe does.
|
||||
Align = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue()));
|
||||
Alignment = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue()));
|
||||
}
|
||||
|
||||
uint32_t CommonChunk::getPermissions() const {
|
||||
@ -366,7 +400,7 @@ void StringChunk::writeTo(uint8_t *Buf) const {
|
||||
ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) {
|
||||
// Intel Optimization Manual says that all branch targets
|
||||
// should be 16-byte aligned. MSVC linker does this too.
|
||||
Align = 16;
|
||||
Alignment = 16;
|
||||
}
|
||||
|
||||
void ImportThunkChunkX64::writeTo(uint8_t *Buf) const {
|
||||
@ -397,10 +431,9 @@ void ImportThunkChunkARM::writeTo(uint8_t *Buf) const {
|
||||
}
|
||||
|
||||
void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const {
|
||||
int64_t PageOff = (ImpSymbol->getRVA() >> 12) - (RVA >> 12);
|
||||
int64_t Off = ImpSymbol->getRVA() & 0xfff;
|
||||
memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64));
|
||||
applyArm64Addr(Buf + OutputSectionOff, PageOff);
|
||||
applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA);
|
||||
applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
|
||||
}
|
||||
|
||||
@ -488,8 +521,10 @@ void BaserelChunk::writeTo(uint8_t *Buf) const {
|
||||
uint8_t Baserel::getDefaultType() {
|
||||
switch (Config->Machine) {
|
||||
case AMD64:
|
||||
case ARM64:
|
||||
return IMAGE_REL_BASED_DIR64;
|
||||
case I386:
|
||||
case ARMNT:
|
||||
return IMAGE_REL_BASED_HIGHLOW;
|
||||
default:
|
||||
llvm_unreachable("unknown machine type");
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include "Config.h"
|
||||
#include "InputFiles.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/iterator.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
@ -33,9 +33,9 @@ class Baserel;
|
||||
class Defined;
|
||||
class DefinedImportData;
|
||||
class DefinedRegular;
|
||||
class ObjectFile;
|
||||
class ObjFile;
|
||||
class OutputSection;
|
||||
class SymbolBody;
|
||||
class Symbol;
|
||||
|
||||
// Mask for section types (code, data, bss, disacardable, etc.)
|
||||
// and permissions (writable, readable or executable).
|
||||
@ -62,7 +62,6 @@ public:
|
||||
|
||||
// The writer sets and uses the addresses.
|
||||
uint64_t getRVA() const { return RVA; }
|
||||
uint32_t getAlign() const { return Align; }
|
||||
void setRVA(uint64_t V) { RVA = V; }
|
||||
|
||||
// Returns true if this has non-zero data. BSS chunks return
|
||||
@ -82,7 +81,7 @@ public:
|
||||
// An output section has pointers to chunks in the section, and each
|
||||
// chunk has a back pointer to an output section.
|
||||
void setOutputSection(OutputSection *O) { Out = O; }
|
||||
OutputSection *getOutputSection() { return Out; }
|
||||
OutputSection *getOutputSection() const { return Out; }
|
||||
|
||||
// Windows-specific.
|
||||
// Collect all locations that contain absolute addresses for base relocations.
|
||||
@ -92,23 +91,22 @@ public:
|
||||
// bytes, so this is used only for logging or debugging.
|
||||
virtual StringRef getDebugName() { return ""; }
|
||||
|
||||
// The alignment of this chunk. The writer uses the value.
|
||||
uint32_t Alignment = 1;
|
||||
|
||||
protected:
|
||||
Chunk(Kind K = OtherKind) : ChunkKind(K) {}
|
||||
const Kind ChunkKind;
|
||||
|
||||
// The alignment of this chunk. The writer uses the value.
|
||||
uint32_t Align = 1;
|
||||
|
||||
// The RVA of this chunk in the output. The writer sets a value.
|
||||
uint64_t RVA = 0;
|
||||
|
||||
// The output section for this chunk.
|
||||
OutputSection *Out = nullptr;
|
||||
|
||||
public:
|
||||
// The offset from beginning of the output section. The writer sets a value.
|
||||
uint64_t OutputSectionOff = 0;
|
||||
|
||||
protected:
|
||||
// The output section for this chunk.
|
||||
OutputSection *Out = nullptr;
|
||||
};
|
||||
|
||||
// A chunk corresponding a section of an input file.
|
||||
@ -119,23 +117,21 @@ class SectionChunk final : public Chunk {
|
||||
public:
|
||||
class symbol_iterator : public llvm::iterator_adaptor_base<
|
||||
symbol_iterator, const coff_relocation *,
|
||||
std::random_access_iterator_tag, SymbolBody *> {
|
||||
std::random_access_iterator_tag, Symbol *> {
|
||||
friend SectionChunk;
|
||||
|
||||
ObjectFile *File;
|
||||
ObjFile *File;
|
||||
|
||||
symbol_iterator(ObjectFile *File, const coff_relocation *I)
|
||||
symbol_iterator(ObjFile *File, const coff_relocation *I)
|
||||
: symbol_iterator::iterator_adaptor_base(I), File(File) {}
|
||||
|
||||
public:
|
||||
symbol_iterator() = default;
|
||||
|
||||
SymbolBody *operator*() const {
|
||||
return File->getSymbolBody(I->SymbolTableIndex);
|
||||
}
|
||||
Symbol *operator*() const { return File->getSymbol(I->SymbolTableIndex); }
|
||||
};
|
||||
|
||||
SectionChunk(ObjectFile *File, const coff_section *Header);
|
||||
SectionChunk(ObjFile *File, const coff_section *Header);
|
||||
static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
|
||||
size_t getSize() const override { return Header->SizeOfRawData; }
|
||||
ArrayRef<uint8_t> getContents() const;
|
||||
@ -163,10 +159,9 @@ public:
|
||||
void addAssociative(SectionChunk *Child);
|
||||
|
||||
StringRef getDebugName() override;
|
||||
void setSymbol(DefinedRegular *S) { if (!Sym) Sym = S; }
|
||||
|
||||
// Returns true if the chunk was not dropped by GC or COMDAT deduplication.
|
||||
bool isLive() { return Live && !Discarded; }
|
||||
// Returns true if the chunk was not dropped by GC.
|
||||
bool isLive() { return Live; }
|
||||
|
||||
// Used by the garbage collector.
|
||||
void markLive() {
|
||||
@ -175,21 +170,16 @@ public:
|
||||
Live = true;
|
||||
}
|
||||
|
||||
// Returns true if this chunk was dropped by COMDAT deduplication.
|
||||
bool isDiscarded() const { return Discarded; }
|
||||
|
||||
// Used by the SymbolTable when discarding unused comdat sections. This is
|
||||
// redundant when GC is enabled, as all comdat sections will start out dead.
|
||||
void markDiscarded() { Discarded = 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 {
|
||||
return SectionName == ".debug" || SectionName.startswith(".debug$");
|
||||
}
|
||||
|
||||
// True if this is a DWARF debug info chunk.
|
||||
bool isDWARF() const { return SectionName.startswith(".debug_"); }
|
||||
// True if this is a DWARF debug info or exception handling chunk.
|
||||
bool isDWARF() const {
|
||||
return SectionName.startswith(".debug_") || SectionName == ".eh_frame";
|
||||
}
|
||||
|
||||
// Allow iteration over the bodies of this chunk's relocated symbols.
|
||||
llvm::iterator_range<symbol_iterator> symbols() const {
|
||||
@ -213,7 +203,10 @@ public:
|
||||
const coff_section *Header;
|
||||
|
||||
// The file that this chunk was created from.
|
||||
ObjectFile *File;
|
||||
ObjFile *File;
|
||||
|
||||
// The COMDAT leader symbol if this is a COMDAT chunk.
|
||||
DefinedRegular *Sym = nullptr;
|
||||
|
||||
private:
|
||||
StringRef SectionName;
|
||||
@ -221,18 +214,12 @@ private:
|
||||
llvm::iterator_range<const coff_relocation *> Relocs;
|
||||
size_t NumRelocs;
|
||||
|
||||
// True if this chunk was discarded because it was a duplicate comdat section.
|
||||
bool Discarded;
|
||||
|
||||
// Used by the garbage collector.
|
||||
bool Live;
|
||||
|
||||
// Used for ICF (Identical COMDAT Folding)
|
||||
void replace(SectionChunk *Other);
|
||||
uint32_t Class[2] = {0, 0};
|
||||
|
||||
// Sym points to a section symbol if this is a COMDAT chunk.
|
||||
DefinedRegular *Sym = nullptr;
|
||||
};
|
||||
|
||||
// A chunk for common symbols. Common chunks don't have actual data.
|
||||
@ -369,6 +356,9 @@ public:
|
||||
uint8_t Type;
|
||||
};
|
||||
|
||||
void applyMOV32T(uint8_t *Off, uint32_t V);
|
||||
void applyBranch24T(uint8_t *Off, int32_t V);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/CachePruning.h"
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@ -26,8 +27,7 @@ using llvm::StringRef;
|
||||
class DefinedAbsolute;
|
||||
class DefinedRelative;
|
||||
class StringChunk;
|
||||
struct Symbol;
|
||||
class SymbolBody;
|
||||
class Symbol;
|
||||
|
||||
// Short aliases.
|
||||
static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
|
||||
@ -39,7 +39,7 @@ static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
|
||||
struct Export {
|
||||
StringRef Name; // N in /export:N or /export:E=N
|
||||
StringRef ExtName; // E in /export:E=N
|
||||
SymbolBody *Sym = nullptr;
|
||||
Symbol *Sym = nullptr;
|
||||
uint16_t Ordinal = 0;
|
||||
bool Noname = false;
|
||||
bool Data = false;
|
||||
@ -79,24 +79,23 @@ struct Configuration {
|
||||
llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
bool Verbose = false;
|
||||
WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
|
||||
SymbolBody *Entry = nullptr;
|
||||
Symbol *Entry = nullptr;
|
||||
bool NoEntry = false;
|
||||
std::string OutputFile;
|
||||
std::string ImportName;
|
||||
bool ColorDiagnostics;
|
||||
bool DoGC = true;
|
||||
bool DoICF = true;
|
||||
uint64_t ErrorLimit = 20;
|
||||
bool Relocatable = true;
|
||||
bool Force = false;
|
||||
bool Debug = false;
|
||||
bool WriteSymtab = true;
|
||||
bool DebugDwarf = false;
|
||||
bool DebugGHashes = false;
|
||||
unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
|
||||
llvm::SmallString<128> PDBPath;
|
||||
std::vector<llvm::StringRef> Argv;
|
||||
|
||||
// Symbols in this set are considered as live by the garbage collector.
|
||||
std::set<SymbolBody *> GCRoot;
|
||||
std::vector<Symbol *> GCRoot;
|
||||
|
||||
std::set<StringRef> NoDefaultLibs;
|
||||
bool NoDefaultLibAll = false;
|
||||
@ -107,7 +106,7 @@ struct Configuration {
|
||||
std::vector<Export> Exports;
|
||||
std::set<std::string> DelayLoads;
|
||||
std::map<std::string, int> DLLOrder;
|
||||
SymbolBody *DelayLoadHelper = nullptr;
|
||||
Symbol *DelayLoadHelper = nullptr;
|
||||
|
||||
bool SaveTemps = false;
|
||||
|
||||
@ -123,6 +122,11 @@ struct Configuration {
|
||||
// Used for /opt:lldltopartitions=N
|
||||
unsigned LTOPartitions = 1;
|
||||
|
||||
// Used for /opt:lldltocache=path
|
||||
StringRef LTOCache;
|
||||
// Used for /opt:lldltocachepolicy=policy
|
||||
llvm::CachePruningPolicy LTOCachePolicy;
|
||||
|
||||
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
|
||||
std::map<StringRef, StringRef> Merge;
|
||||
|
||||
@ -139,6 +143,9 @@ struct Configuration {
|
||||
StringRef ManifestUIAccess = "'false'";
|
||||
StringRef ManifestFile;
|
||||
|
||||
// Used for /aligncomm.
|
||||
std::map<std::string, int> AlignComm;
|
||||
|
||||
// Used for /failifmismatch.
|
||||
std::map<StringRef, StringRef> MustMatch;
|
||||
|
||||
@ -157,13 +164,16 @@ struct Configuration {
|
||||
uint32_t MinorImageVersion = 0;
|
||||
uint32_t MajorOSVersion = 6;
|
||||
uint32_t MinorOSVersion = 0;
|
||||
bool CanExitEarly = false;
|
||||
bool DynamicBase = true;
|
||||
bool AllowBind = true;
|
||||
bool NxCompat = true;
|
||||
bool AllowIsolation = true;
|
||||
bool TerminalServerAware = true;
|
||||
bool LargeAddressAware = false;
|
||||
bool HighEntropyVA = false;
|
||||
bool AppContainer = false;
|
||||
bool MinGW = false;
|
||||
};
|
||||
|
||||
extern Configuration *Config;
|
||||
|
65
COFF/DLL.cpp
65
COFF/DLL.cpp
@ -61,7 +61,7 @@ private:
|
||||
// A chunk for the import descriptor table.
|
||||
class LookupChunk : public Chunk {
|
||||
public:
|
||||
explicit LookupChunk(Chunk *C) : HintName(C) {}
|
||||
explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = ptrSize(); }
|
||||
size_t getSize() const override { return ptrSize(); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
@ -76,7 +76,7 @@ public:
|
||||
// See Microsoft PE/COFF spec 7.1. Import Header for details.
|
||||
class OrdinalOnlyChunk : public Chunk {
|
||||
public:
|
||||
explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {}
|
||||
explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) { Alignment = ptrSize(); }
|
||||
size_t getSize() const override { return ptrSize(); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
@ -117,7 +117,6 @@ public:
|
||||
explicit NullChunk(size_t N) : Size(N) {}
|
||||
bool hasData() const override { return false; }
|
||||
size_t getSize() const override { return Size; }
|
||||
void setAlign(size_t N) { Align = N; }
|
||||
|
||||
private:
|
||||
size_t Size;
|
||||
@ -215,6 +214,22 @@ static const uint8_t ThunkX86[] = {
|
||||
0xFF, 0xE0, // jmp eax
|
||||
};
|
||||
|
||||
static const uint8_t ThunkARM[] = {
|
||||
0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_<FUNCNAME>
|
||||
0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_<FUNCNAME>
|
||||
0x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr}
|
||||
0x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #16
|
||||
0x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7}
|
||||
0x61, 0x46, // mov r1, ip
|
||||
0x40, 0xf2, 0x00, 0x00, // mov.w r0, #0 DELAY_IMPORT_DESCRIPTOR
|
||||
0xc0, 0xf2, 0x00, 0x00, // mov.t r0, #0 DELAY_IMPORT_DESCRIPTOR
|
||||
0x00, 0xf0, 0x00, 0xd0, // bl #0 __delayLoadHelper2
|
||||
0x84, 0x46, // mov ip, r0
|
||||
0xbd, 0xec, 0x10, 0x0b, // vpop {d0, d1, d2, d3, d4, d5, d6, d7}
|
||||
0xbd, 0xe8, 0x0f, 0x48, // pop.w {r0, r1, r2, r3, r11, lr}
|
||||
0x60, 0x47, // bx ip
|
||||
};
|
||||
|
||||
// A chunk for the delay import thunk.
|
||||
class ThunkChunkX64 : public Chunk {
|
||||
public:
|
||||
@ -259,17 +274,45 @@ public:
|
||||
Defined *Helper = nullptr;
|
||||
};
|
||||
|
||||
class ThunkChunkARM : public Chunk {
|
||||
public:
|
||||
ThunkChunkARM(Defined *I, Chunk *D, Defined *H)
|
||||
: Imp(I), Desc(D), Helper(H) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(ThunkARM); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
memcpy(Buf + OutputSectionOff, ThunkARM, sizeof(ThunkARM));
|
||||
applyMOV32T(Buf + OutputSectionOff + 0, Imp->getRVA() + Config->ImageBase);
|
||||
applyMOV32T(Buf + OutputSectionOff + 22, Desc->getRVA() + Config->ImageBase);
|
||||
applyBranch24T(Buf + OutputSectionOff + 30, Helper->getRVA() - RVA - 34);
|
||||
}
|
||||
|
||||
void getBaserels(std::vector<Baserel> *Res) override {
|
||||
Res->emplace_back(RVA + 0, IMAGE_REL_BASED_ARM_MOV32T);
|
||||
Res->emplace_back(RVA + 22, IMAGE_REL_BASED_ARM_MOV32T);
|
||||
}
|
||||
|
||||
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) {}
|
||||
explicit DelayAddressChunk(Chunk *C) : Thunk(C) { Alignment = ptrSize(); }
|
||||
size_t getSize() const override { return ptrSize(); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
if (Config->is64()) {
|
||||
write64le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
|
||||
} else {
|
||||
write32le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
|
||||
uint32_t Bit = 0;
|
||||
// Pointer to thumb code must have the LSB set, so adjust it.
|
||||
if (Config->Machine == ARMNT)
|
||||
Bit = 1;
|
||||
write32le(Buf + OutputSectionOff, (Thunk->getRVA() + Config->ImageBase) | Bit);
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,12 +362,16 @@ public:
|
||||
size_t getSize() const override { return Size * 4; }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
uint32_t Bit = 0;
|
||||
// Pointer to thumb code must have the LSB set, so adjust it.
|
||||
if (Config->Machine == ARMNT)
|
||||
Bit = 1;
|
||||
for (Export &E : Config->Exports) {
|
||||
uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4;
|
||||
if (E.ForwardChunk) {
|
||||
write32le(P, E.ForwardChunk->getRVA());
|
||||
write32le(P, E.ForwardChunk->getRVA() | Bit);
|
||||
} else {
|
||||
write32le(P, cast<Defined>(E.Sym)->getRVA());
|
||||
write32le(P, cast<Defined>(E.Sym)->getRVA() | Bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -487,7 +534,7 @@ void DelayLoadContents::create(Defined *H) {
|
||||
for (int I = 0, E = Syms.size(); I < E; ++I)
|
||||
Syms[I]->setLocation(Addresses[Base + I]);
|
||||
auto *MH = make<NullChunk>(8);
|
||||
MH->setAlign(8);
|
||||
MH->Alignment = 8;
|
||||
ModuleHandles.push_back(MH);
|
||||
|
||||
// Fill the delay import table header fields.
|
||||
@ -506,6 +553,8 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) {
|
||||
return make<ThunkChunkX64>(S, Dir, Helper);
|
||||
case I386:
|
||||
return make<ThunkChunkX86>(S, Dir, Helper);
|
||||
case ARMNT:
|
||||
return make<ThunkChunkARM>(S, Dir, Helper);
|
||||
default:
|
||||
llvm_unreachable("unsupported machine type");
|
||||
}
|
||||
|
543
COFF/Driver.cpp
543
COFF/Driver.cpp
@ -9,13 +9,15 @@
|
||||
|
||||
#include "Driver.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Memory.h"
|
||||
#include "MinGW.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Driver/Driver.h"
|
||||
#include "lld/Common/Driver.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Version.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/BinaryFormat/Magic.h"
|
||||
@ -48,20 +50,28 @@ namespace coff {
|
||||
Configuration *Config;
|
||||
LinkerDriver *Driver;
|
||||
|
||||
BumpPtrAllocator BAlloc;
|
||||
StringSaver Saver{BAlloc};
|
||||
std::vector<SpecificAllocBase *> SpecificAllocBase::Instances;
|
||||
|
||||
bool link(ArrayRef<const char *> Args, raw_ostream &Diag) {
|
||||
ErrorCount = 0;
|
||||
ErrorOS = &Diag;
|
||||
bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
|
||||
errorHandler().LogName = Args[0];
|
||||
errorHandler().ErrorOS = &Diag;
|
||||
errorHandler().ColorDiagnostics = Diag.has_colors();
|
||||
errorHandler().ErrorLimitExceededMsg =
|
||||
"too many errors emitted, stopping now"
|
||||
" (use /ERRORLIMIT:0 to see all errors)";
|
||||
Config = make<Configuration>();
|
||||
Config->Argv = {Args.begin(), Args.end()};
|
||||
Config->ColorDiagnostics =
|
||||
(ErrorOS == &llvm::errs() && Process::StandardErrHasColors());
|
||||
Config->CanExitEarly = CanExitEarly;
|
||||
|
||||
Symtab = make<SymbolTable>();
|
||||
|
||||
Driver = make<LinkerDriver>();
|
||||
Driver->link(Args);
|
||||
return !ErrorCount;
|
||||
|
||||
// Call exit() if we can to avoid calling destructors.
|
||||
if (CanExitEarly)
|
||||
exitLld(errorCount() ? 1 : 0);
|
||||
|
||||
freeArena();
|
||||
return !errorCount();
|
||||
}
|
||||
|
||||
// Drop directory components and replace extension with ".exe" or ".dll".
|
||||
@ -107,30 +117,46 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) {
|
||||
return MBRef;
|
||||
}
|
||||
|
||||
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB) {
|
||||
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB,
|
||||
bool WholeArchive) {
|
||||
MemoryBufferRef MBRef = takeBuffer(std::move(MB));
|
||||
FilePaths.push_back(MBRef.getBufferIdentifier());
|
||||
|
||||
// File type is detected by contents, not by file extension.
|
||||
file_magic Magic = identify_magic(MBRef.getBuffer());
|
||||
if (Magic == file_magic::windows_resource) {
|
||||
switch (identify_magic(MBRef.getBuffer())) {
|
||||
case file_magic::windows_resource:
|
||||
Resources.push_back(MBRef);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
FilePaths.push_back(MBRef.getBufferIdentifier());
|
||||
if (Magic == file_magic::archive)
|
||||
return Symtab.addFile(make<ArchiveFile>(MBRef));
|
||||
if (Magic == file_magic::bitcode)
|
||||
return Symtab.addFile(make<BitcodeFile>(MBRef));
|
||||
case file_magic::archive:
|
||||
if (WholeArchive) {
|
||||
std::unique_ptr<Archive> File =
|
||||
CHECK(Archive::create(MBRef),
|
||||
MBRef.getBufferIdentifier() + ": failed to parse archive");
|
||||
|
||||
if (Magic == file_magic::coff_cl_gl_object)
|
||||
for (MemoryBufferRef M : getArchiveMembers(File.get()))
|
||||
addArchiveBuffer(M, "<whole-archive>", MBRef.getBufferIdentifier());
|
||||
return;
|
||||
}
|
||||
Symtab->addFile(make<ArchiveFile>(MBRef));
|
||||
break;
|
||||
|
||||
case file_magic::bitcode:
|
||||
Symtab->addFile(make<BitcodeFile>(MBRef));
|
||||
break;
|
||||
|
||||
case file_magic::coff_cl_gl_object:
|
||||
error(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
|
||||
"Recompile without /GL");
|
||||
else
|
||||
Symtab.addFile(make<ObjectFile>(MBRef));
|
||||
break;
|
||||
|
||||
default:
|
||||
Symtab->addFile(make<ObjFile>(MBRef));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LinkerDriver::enqueuePath(StringRef Path) {
|
||||
void LinkerDriver::enqueuePath(StringRef Path, bool WholeArchive) {
|
||||
auto Future =
|
||||
std::make_shared<std::future<MBErrPair>>(createFutureForFile(Path));
|
||||
std::string PathStr = Path;
|
||||
@ -139,7 +165,7 @@ void LinkerDriver::enqueuePath(StringRef Path) {
|
||||
if (MBOrErr.second)
|
||||
error("could not open " + PathStr + ": " + MBOrErr.second.message());
|
||||
else
|
||||
Driver->addBuffer(std::move(MBOrErr.first));
|
||||
Driver->addBuffer(std::move(MBOrErr.first), WholeArchive);
|
||||
});
|
||||
}
|
||||
|
||||
@ -147,13 +173,13 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
|
||||
StringRef ParentName) {
|
||||
file_magic Magic = identify_magic(MB.getBuffer());
|
||||
if (Magic == file_magic::coff_import_library) {
|
||||
Symtab.addFile(make<ImportFile>(MB));
|
||||
Symtab->addFile(make<ImportFile>(MB));
|
||||
return;
|
||||
}
|
||||
|
||||
InputFile *Obj;
|
||||
if (Magic == file_magic::coff_object) {
|
||||
Obj = make<ObjectFile>(MB);
|
||||
Obj = make<ObjFile>(MB);
|
||||
} else if (Magic == file_magic::bitcode) {
|
||||
Obj = make<BitcodeFile>(MB);
|
||||
} else {
|
||||
@ -162,7 +188,7 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
|
||||
}
|
||||
|
||||
Obj->ParentName = ParentName;
|
||||
Symtab.addFile(Obj);
|
||||
Symtab->addFile(Obj);
|
||||
log("Loaded " + toString(Obj) + " for " + SymName);
|
||||
}
|
||||
|
||||
@ -170,7 +196,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
|
||||
StringRef SymName,
|
||||
StringRef ParentName) {
|
||||
if (!C.getParent()->isThin()) {
|
||||
MemoryBufferRef MB = check(
|
||||
MemoryBufferRef MB = CHECK(
|
||||
C.getMemoryBufferRef(),
|
||||
"could not get the buffer for the member defining symbol " + SymName);
|
||||
enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); });
|
||||
@ -178,39 +204,54 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
|
||||
}
|
||||
|
||||
auto Future = std::make_shared<std::future<MBErrPair>>(createFutureForFile(
|
||||
check(C.getFullName(),
|
||||
CHECK(C.getFullName(),
|
||||
"could not get the filename for the member defining symbol " +
|
||||
SymName)));
|
||||
enqueueTask([=]() {
|
||||
auto MBOrErr = Future->get();
|
||||
if (MBOrErr.second)
|
||||
fatal(MBOrErr.second,
|
||||
"could not get the buffer for the member defining " + SymName);
|
||||
fatal("could not get the buffer for the member defining " + SymName +
|
||||
": " + MBOrErr.second.message());
|
||||
Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName,
|
||||
ParentName);
|
||||
});
|
||||
}
|
||||
|
||||
static bool isDecorated(StringRef Sym) {
|
||||
return Sym.startswith("_") || Sym.startswith("@") || Sym.startswith("?");
|
||||
return Sym.startswith("@") || Sym.contains("@@") || Sym.startswith("?") ||
|
||||
(!Config->MinGW && Sym.contains('@'));
|
||||
}
|
||||
|
||||
// Parses .drectve section contents and returns a list of files
|
||||
// specified by /defaultlib.
|
||||
void LinkerDriver::parseDirectives(StringRef S) {
|
||||
ArgParser Parser;
|
||||
// .drectve is always tokenized using Windows shell rules.
|
||||
opt::InputArgList Args = Parser.parse(S);
|
||||
|
||||
for (auto *Arg : Args) {
|
||||
switch (Arg->getOption().getID()) {
|
||||
switch (Arg->getOption().getUnaliasedOption().getID()) {
|
||||
case OPT_aligncomm:
|
||||
parseAligncomm(Arg->getValue());
|
||||
break;
|
||||
case OPT_alternatename:
|
||||
parseAlternateName(Arg->getValue());
|
||||
break;
|
||||
case OPT_defaultlib:
|
||||
if (Optional<StringRef> Path = findLib(Arg->getValue()))
|
||||
enqueuePath(*Path);
|
||||
enqueuePath(*Path, false);
|
||||
break;
|
||||
case OPT_entry:
|
||||
Config->Entry = addUndefined(mangle(Arg->getValue()));
|
||||
break;
|
||||
case OPT_export: {
|
||||
Export E = parseExport(Arg->getValue());
|
||||
if (Config->Machine == I386 && Config->MinGW) {
|
||||
if (!isDecorated(E.Name))
|
||||
E.Name = Saver.save("_" + E.Name);
|
||||
if (!E.ExtName.empty() && !isDecorated(E.ExtName))
|
||||
E.ExtName = Saver.save("_" + E.ExtName);
|
||||
}
|
||||
E.Directives = true;
|
||||
Config->Exports.push_back(E);
|
||||
break;
|
||||
@ -230,9 +271,14 @@ void LinkerDriver::parseDirectives(StringRef S) {
|
||||
case OPT_section:
|
||||
parseSection(Arg->getValue());
|
||||
break;
|
||||
case OPT_subsystem:
|
||||
parseSubsystem(Arg->getValue(), &Config->Subsystem,
|
||||
&Config->MajorOSVersion, &Config->MinorOSVersion);
|
||||
break;
|
||||
case OPT_editandcontinue:
|
||||
case OPT_fastfail:
|
||||
case OPT_guardsym:
|
||||
case OPT_natvis:
|
||||
case OPT_throwingnew:
|
||||
break;
|
||||
default:
|
||||
@ -247,7 +293,7 @@ StringRef LinkerDriver::doFindFile(StringRef Filename) {
|
||||
bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
|
||||
if (HasPathSep)
|
||||
return Filename;
|
||||
bool HasExt = (Filename.find('.') != StringRef::npos);
|
||||
bool HasExt = Filename.contains('.');
|
||||
for (StringRef Dir : SearchPaths) {
|
||||
SmallString<128> Path = Dir;
|
||||
sys::path::append(Path, Filename);
|
||||
@ -269,13 +315,15 @@ Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
|
||||
bool Seen = !VisitedFiles.insert(Path.lower()).second;
|
||||
if (Seen)
|
||||
return None;
|
||||
if (Path.endswith_lower(".lib"))
|
||||
VisitedLibs.insert(sys::path::filename(Path));
|
||||
return Path;
|
||||
}
|
||||
|
||||
// Find library file from search path.
|
||||
StringRef LinkerDriver::doFindLib(StringRef Filename) {
|
||||
// Add ".lib" to Filename if that has no file extension.
|
||||
bool HasExt = (Filename.find('.') != StringRef::npos);
|
||||
bool HasExt = Filename.contains('.');
|
||||
if (!HasExt)
|
||||
Filename = Saver.save(Filename + ".lib");
|
||||
return doFindFile(Filename);
|
||||
@ -310,9 +358,12 @@ void LinkerDriver::addLibSearchPaths() {
|
||||
}
|
||||
}
|
||||
|
||||
SymbolBody *LinkerDriver::addUndefined(StringRef Name) {
|
||||
SymbolBody *B = Symtab.addUndefined(Name);
|
||||
Config->GCRoot.insert(B);
|
||||
Symbol *LinkerDriver::addUndefined(StringRef Name) {
|
||||
Symbol *B = Symtab->addUndefined(Name);
|
||||
if (!B->IsGCRoot) {
|
||||
B->IsGCRoot = true;
|
||||
Config->GCRoot.push_back(B);
|
||||
}
|
||||
return B;
|
||||
}
|
||||
|
||||
@ -334,8 +385,8 @@ StringRef LinkerDriver::findDefaultEntry() {
|
||||
{"wWinMain", "wWinMainCRTStartup"},
|
||||
};
|
||||
for (auto E : Entries) {
|
||||
StringRef Entry = Symtab.findMangle(mangle(E[0]));
|
||||
if (!Entry.empty() && !isa<Undefined>(Symtab.find(Entry)->body()))
|
||||
StringRef Entry = Symtab->findMangle(mangle(E[0]));
|
||||
if (!Entry.empty() && !isa<Undefined>(Symtab->find(Entry)))
|
||||
return mangle(E[1]);
|
||||
}
|
||||
return "";
|
||||
@ -344,9 +395,9 @@ StringRef LinkerDriver::findDefaultEntry() {
|
||||
WindowsSubsystem LinkerDriver::inferSubsystem() {
|
||||
if (Config->DLL)
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
||||
if (Symtab.findUnderscore("main") || Symtab.findUnderscore("wmain"))
|
||||
if (Symtab->findUnderscore("main") || Symtab->findUnderscore("wmain"))
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
|
||||
if (Symtab.findUnderscore("WinMain") || Symtab.findUnderscore("wWinMain"))
|
||||
if (Symtab->findUnderscore("WinMain") || Symtab->findUnderscore("wWinMain"))
|
||||
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
||||
return IMAGE_SUBSYSTEM_UNKNOWN;
|
||||
}
|
||||
@ -369,9 +420,15 @@ static std::string createResponseFile(const opt::InputArgList &Args,
|
||||
case OPT_INPUT:
|
||||
case OPT_defaultlib:
|
||||
case OPT_libpath:
|
||||
case OPT_manifest:
|
||||
case OPT_manifest_colon:
|
||||
case OPT_manifestdependency:
|
||||
case OPT_manifestfile:
|
||||
case OPT_manifestinput:
|
||||
case OPT_manifestuac:
|
||||
break;
|
||||
default:
|
||||
OS << toString(Arg) << "\n";
|
||||
OS << toString(*Arg) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -469,15 +526,17 @@ static void createImportLibrary(bool AsLib) {
|
||||
Exports.push_back(E2);
|
||||
}
|
||||
|
||||
writeImportLibrary(getImportName(AsLib), getImplibPath(), Exports,
|
||||
Config->Machine, false);
|
||||
auto E = writeImportLibrary(getImportName(AsLib), getImplibPath(), Exports,
|
||||
Config->Machine, false);
|
||||
handleAllErrors(std::move(E),
|
||||
[&](ErrorInfoBase &EIB) { error(EIB.message()); });
|
||||
}
|
||||
|
||||
static void parseModuleDefs(StringRef Path) {
|
||||
std::unique_ptr<MemoryBuffer> MB = check(
|
||||
MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
|
||||
COFFModuleDefinition M =
|
||||
check(parseCOFFModuleDefinition(MB->getMemBufferRef(), Config->Machine));
|
||||
std::unique_ptr<MemoryBuffer> MB = CHECK(
|
||||
MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
|
||||
COFFModuleDefinition M = check(parseCOFFModuleDefinition(
|
||||
MB->getMemBufferRef(), Config->Machine, Config->MinGW));
|
||||
|
||||
if (Config->OutputFile.empty())
|
||||
Config->OutputFile = Saver.save(M.OutputFile);
|
||||
@ -515,31 +574,12 @@ static void parseModuleDefs(StringRef Path) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) {
|
||||
std::vector<MemoryBufferRef> V;
|
||||
Error Err = Error::success();
|
||||
for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
|
||||
Archive::Child C =
|
||||
check(COrErr,
|
||||
File->getFileName() + ": could not get the child of the archive");
|
||||
MemoryBufferRef MBRef =
|
||||
check(C.getMemoryBufferRef(),
|
||||
File->getFileName() +
|
||||
": could not get the buffer for a child of the archive");
|
||||
V.push_back(MBRef);
|
||||
}
|
||||
if (Err)
|
||||
fatal(File->getFileName() +
|
||||
": Archive::children failed: " + toString(std::move(Err)));
|
||||
return V;
|
||||
}
|
||||
|
||||
// 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());
|
||||
CHECK(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier());
|
||||
if (File->isThin())
|
||||
return true;
|
||||
|
||||
@ -560,7 +600,7 @@ static bool needsRebuilding(MemoryBufferRef MB) {
|
||||
// its path is returned.
|
||||
static Optional<std::string>
|
||||
filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
|
||||
std::unique_ptr<MemoryBuffer> MB = check(
|
||||
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());
|
||||
@ -573,7 +613,7 @@ filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
|
||||
return Path.str();
|
||||
|
||||
std::unique_ptr<Archive> File =
|
||||
check(Archive::create(MBRef),
|
||||
CHECK(Archive::create(MBRef),
|
||||
MBRef.getBufferIdentifier() + ": failed to parse archive");
|
||||
|
||||
std::vector<NewArchiveMember> New;
|
||||
@ -589,16 +629,17 @@ filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
|
||||
SmallString<128> S;
|
||||
if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path),
|
||||
".lib", S))
|
||||
fatal(EC, "cannot create a temporary file");
|
||||
fatal("cannot create a temporary file: " + EC.message());
|
||||
std::string Temp = S.str();
|
||||
TemporaryFiles.push_back(Temp);
|
||||
|
||||
std::pair<StringRef, std::error_code> Ret =
|
||||
Error E =
|
||||
llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU,
|
||||
/*Deterministics=*/true,
|
||||
/*Thin=*/false);
|
||||
if (Ret.second)
|
||||
error("failed to create a new archive " + S.str() + ": " + Ret.first);
|
||||
handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
|
||||
error("failed to create a new archive " + S.str() + ": " + EI.message());
|
||||
});
|
||||
return Temp;
|
||||
}
|
||||
|
||||
@ -610,16 +651,16 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
|
||||
// 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 (const auto *O : Symtab.ObjectFiles) {
|
||||
if (O->ParentName.empty())
|
||||
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(O->ParentName), ".obj", Fd, S))
|
||||
fatal(EC, "cannot create a temporary file");
|
||||
"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 << O->MB.getBuffer();
|
||||
OS << Obj->MB.getBuffer();
|
||||
Temps.push_back(S.str());
|
||||
Rsp += quote(S) + "\n";
|
||||
}
|
||||
@ -635,7 +676,7 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
|
||||
break;
|
||||
case OPT_opt:
|
||||
if (!StringRef(Arg->getValue()).startswith("lld"))
|
||||
Rsp += toString(Arg) + " ";
|
||||
Rsp += toString(*Arg) + " ";
|
||||
break;
|
||||
case OPT_INPUT: {
|
||||
if (Optional<StringRef> Path = doFindFile(Arg->getValue())) {
|
||||
@ -647,12 +688,12 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Rsp += toString(Arg) + "\n";
|
||||
Rsp += toString(*Arg) + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<StringRef> ObjectFiles = Symtab.compileBitcodeFiles();
|
||||
runMSVCLinker(Rsp, ObjectFiles);
|
||||
std::vector<StringRef> ObjFiles = Symtab->compileBitcodeFiles();
|
||||
runMSVCLinker(Rsp, ObjFiles);
|
||||
|
||||
for (StringRef Path : Temps)
|
||||
sys::fs::remove(Path);
|
||||
@ -689,6 +730,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
InitializeAllDisassemblers();
|
||||
|
||||
// Parse command line options.
|
||||
ArgParser Parser;
|
||||
opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
|
||||
|
||||
// Parse and evaluate -mllvm options.
|
||||
@ -704,7 +746,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S.getAsInteger(10, N))
|
||||
error(Arg->getSpelling() + " number expected, but got " + S);
|
||||
Config->ErrorLimit = N;
|
||||
errorHandler().ErrorLimit = N;
|
||||
}
|
||||
|
||||
// Handle /help
|
||||
@ -713,6 +755,18 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle --version, which is an lld extension. This option is a bit odd
|
||||
// because it doesn't start with "/", but we deliberately chose "--" to
|
||||
// avoid conflict with /version and for compatibility with clang-cl.
|
||||
if (Args.hasArg(OPT_dash_dash_version)) {
|
||||
outs() << getLLDVersion() << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle /lldmingw early, since it can potentially affect how other
|
||||
// options are handled.
|
||||
Config->MinGW = Args.hasArg(OPT_lldmingw);
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_linkrepro)) {
|
||||
SmallString<64> Path = StringRef(Arg->getValue());
|
||||
sys::path::append(Path, "repro.tar");
|
||||
@ -728,8 +782,8 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!Args.hasArgNoClaim(OPT_INPUT)) {
|
||||
if (Args.hasArgNoClaim(OPT_deffile))
|
||||
if (!Args.hasArg(OPT_INPUT)) {
|
||||
if (Args.hasArg(OPT_deffile))
|
||||
Config->NoEntry = true;
|
||||
else
|
||||
fatal("no input files");
|
||||
@ -748,23 +802,26 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
// Handle /verbose
|
||||
if (Args.hasArg(OPT_verbose))
|
||||
Config->Verbose = true;
|
||||
errorHandler().Verbose = Config->Verbose;
|
||||
|
||||
// Handle /force or /force:unresolved
|
||||
if (Args.hasArg(OPT_force) || Args.hasArg(OPT_force_unresolved))
|
||||
if (Args.hasArg(OPT_force, OPT_force_unresolved))
|
||||
Config->Force = true;
|
||||
|
||||
// Handle /debug
|
||||
if (Args.hasArg(OPT_debug)) {
|
||||
if (Args.hasArg(OPT_debug, OPT_debug_dwarf, OPT_debug_ghash)) {
|
||||
Config->Debug = true;
|
||||
Config->DebugTypes =
|
||||
Args.hasArg(OPT_debugtype)
|
||||
? parseDebugType(Args.getLastArg(OPT_debugtype)->getValue())
|
||||
: getDefaultDebugType(Args);
|
||||
if (auto *Arg = Args.getLastArg(OPT_debugtype))
|
||||
Config->DebugTypes = parseDebugType(Arg->getValue());
|
||||
else
|
||||
Config->DebugTypes = getDefaultDebugType(Args);
|
||||
}
|
||||
|
||||
// Create a dummy PDB file to satisfy build sytem rules.
|
||||
if (auto *Arg = Args.getLastArg(OPT_pdb))
|
||||
Config->PDBPath = Arg->getValue();
|
||||
// Handle /pdb
|
||||
bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash);
|
||||
if (ShouldCreatePDB)
|
||||
if (auto *Arg = Args.getLastArg(OPT_pdb))
|
||||
Config->PDBPath = Arg->getValue();
|
||||
|
||||
// Handle /noentry
|
||||
if (Args.hasArg(OPT_noentry)) {
|
||||
@ -780,9 +837,18 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
Config->ManifestID = 2;
|
||||
}
|
||||
|
||||
// Handle /fixed
|
||||
if (Args.hasArg(OPT_fixed)) {
|
||||
if (Args.hasArg(OPT_dynamicbase)) {
|
||||
// Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase
|
||||
// because we need to explicitly check whether that option or its inverse was
|
||||
// present in the argument list in order to handle /fixed.
|
||||
auto *DynamicBaseArg = Args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no);
|
||||
if (DynamicBaseArg &&
|
||||
DynamicBaseArg->getOption().getID() == OPT_dynamicbase_no)
|
||||
Config->DynamicBase = false;
|
||||
|
||||
bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false);
|
||||
if (Fixed) {
|
||||
if (DynamicBaseArg &&
|
||||
DynamicBaseArg->getOption().getID() == OPT_dynamicbase) {
|
||||
error("/fixed must not be specified with /dynamicbase");
|
||||
} else {
|
||||
Config->Relocatable = false;
|
||||
@ -790,8 +856,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
}
|
||||
}
|
||||
|
||||
if (Args.hasArg(OPT_appcontainer))
|
||||
Config->AppContainer = true;
|
||||
// Handle /appcontainer
|
||||
Config->AppContainer =
|
||||
Args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false);
|
||||
|
||||
// Handle /machine
|
||||
if (auto *Arg = Args.getLastArg(OPT_machine))
|
||||
@ -839,54 +906,65 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_implib))
|
||||
Config->Implib = Arg->getValue();
|
||||
|
||||
// Handle /opt
|
||||
// Handle /opt.
|
||||
bool DoGC = !Args.hasArg(OPT_debug);
|
||||
unsigned ICFLevel = 1; // 0: off, 1: limited, 2: on
|
||||
for (auto *Arg : Args.filtered(OPT_opt)) {
|
||||
std::string Str = StringRef(Arg->getValue()).lower();
|
||||
SmallVector<StringRef, 1> Vec;
|
||||
StringRef(Str).split(Vec, ',');
|
||||
for (StringRef S : Vec) {
|
||||
if (S == "noref") {
|
||||
Config->DoGC = false;
|
||||
Config->DoICF = false;
|
||||
continue;
|
||||
}
|
||||
if (S == "icf" || StringRef(S).startswith("icf=")) {
|
||||
Config->DoICF = true;
|
||||
continue;
|
||||
}
|
||||
if (S == "noicf") {
|
||||
Config->DoICF = false;
|
||||
continue;
|
||||
}
|
||||
if (StringRef(S).startswith("lldlto=")) {
|
||||
StringRef OptLevel = StringRef(S).substr(7);
|
||||
if (S == "ref") {
|
||||
DoGC = true;
|
||||
} else if (S == "noref") {
|
||||
DoGC = false;
|
||||
} else if (S == "icf" || S.startswith("icf=")) {
|
||||
ICFLevel = 2;
|
||||
} else if (S == "noicf") {
|
||||
ICFLevel = 0;
|
||||
} else if (S.startswith("lldlto=")) {
|
||||
StringRef OptLevel = S.substr(7);
|
||||
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
|
||||
Config->LTOOptLevel > 3)
|
||||
error("/opt:lldlto: invalid optimization level: " + OptLevel);
|
||||
continue;
|
||||
}
|
||||
if (StringRef(S).startswith("lldltojobs=")) {
|
||||
StringRef Jobs = StringRef(S).substr(11);
|
||||
} else if (S.startswith("lldltojobs=")) {
|
||||
StringRef Jobs = S.substr(11);
|
||||
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
|
||||
error("/opt:lldltojobs: invalid job count: " + Jobs);
|
||||
continue;
|
||||
}
|
||||
if (StringRef(S).startswith("lldltopartitions=")) {
|
||||
StringRef N = StringRef(S).substr(17);
|
||||
} else if (S.startswith("lldltopartitions=")) {
|
||||
StringRef N = S.substr(17);
|
||||
if (N.getAsInteger(10, Config->LTOPartitions) ||
|
||||
Config->LTOPartitions == 0)
|
||||
error("/opt:lldltopartitions: invalid partition count: " + N);
|
||||
continue;
|
||||
}
|
||||
if (S != "ref" && S != "lbr" && S != "nolbr")
|
||||
} else if (S != "lbr" && S != "nolbr")
|
||||
error("/opt: unknown option: " + S);
|
||||
}
|
||||
}
|
||||
|
||||
// Limited ICF is enabled if GC is enabled and ICF was never mentioned
|
||||
// explicitly.
|
||||
// FIXME: LLD only implements "limited" ICF, i.e. it only merges identical
|
||||
// code. If the user passes /OPT:ICF explicitly, LLD should merge identical
|
||||
// comdat readonly data.
|
||||
if (ICFLevel == 1 && !DoGC)
|
||||
ICFLevel = 0;
|
||||
Config->DoGC = DoGC;
|
||||
Config->DoICF = ICFLevel > 0;
|
||||
|
||||
// Handle /lldsavetemps
|
||||
if (Args.hasArg(OPT_lldsavetemps))
|
||||
Config->SaveTemps = true;
|
||||
|
||||
// Handle /lldltocache
|
||||
if (auto *Arg = Args.getLastArg(OPT_lldltocache))
|
||||
Config->LTOCache = Arg->getValue();
|
||||
|
||||
// Handle /lldsavecachepolicy
|
||||
if (auto *Arg = Args.getLastArg(OPT_lldltocachepolicy))
|
||||
Config->LTOCachePolicy = CHECK(
|
||||
parseCachePruningPolicy(Arg->getValue()),
|
||||
Twine("/lldltocachepolicy: invalid cache policy: ") + Arg->getValue());
|
||||
|
||||
// Handle /failifmismatch
|
||||
for (auto *Arg : Args.filtered(OPT_failifmismatch))
|
||||
checkFailIfMismatch(Arg->getValue());
|
||||
@ -899,6 +977,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
for (auto *Arg : Args.filtered(OPT_section))
|
||||
parseSection(Arg->getValue());
|
||||
|
||||
// Handle /aligncomm
|
||||
for (auto *Arg : Args.filtered(OPT_aligncomm))
|
||||
parseAligncomm(Arg->getValue());
|
||||
|
||||
// Handle /manifestdependency. This enables /manifest unless /manifest:no is
|
||||
// also passed.
|
||||
if (auto *Arg = Args.getLastArg(OPT_manifestdependency)) {
|
||||
@ -932,35 +1014,42 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
}
|
||||
|
||||
// Handle miscellaneous boolean flags.
|
||||
if (Args.hasArg(OPT_allowisolation_no))
|
||||
Config->AllowIsolation = false;
|
||||
if (Args.hasArg(OPT_dynamicbase_no))
|
||||
Config->DynamicBase = false;
|
||||
if (Args.hasArg(OPT_nxcompat_no))
|
||||
Config->NxCompat = false;
|
||||
if (Args.hasArg(OPT_tsaware_no))
|
||||
Config->TerminalServerAware = false;
|
||||
if (Args.hasArg(OPT_nosymtab))
|
||||
Config->WriteSymtab = false;
|
||||
Config->AllowBind = Args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
|
||||
Config->AllowIsolation =
|
||||
Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
|
||||
Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
|
||||
Config->TerminalServerAware = Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
|
||||
Config->DebugDwarf = Args.hasArg(OPT_debug_dwarf);
|
||||
Config->DebugGHashes = Args.hasArg(OPT_debug_ghash);
|
||||
|
||||
Config->MapFile = getMapFile(Args);
|
||||
|
||||
if (ErrorCount)
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
bool WholeArchiveFlag = Args.hasArg(OPT_wholearchive_flag);
|
||||
// Create a list of input files. Files can be given as arguments
|
||||
// for /defaultlib option.
|
||||
std::vector<MemoryBufferRef> MBs;
|
||||
for (auto *Arg : Args.filtered(OPT_INPUT))
|
||||
if (Optional<StringRef> Path = findFile(Arg->getValue()))
|
||||
enqueuePath(*Path);
|
||||
for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) {
|
||||
switch (Arg->getOption().getID()) {
|
||||
case OPT_INPUT:
|
||||
if (Optional<StringRef> Path = findFile(Arg->getValue()))
|
||||
enqueuePath(*Path, WholeArchiveFlag);
|
||||
break;
|
||||
case OPT_wholearchive_file:
|
||||
if (Optional<StringRef> Path = findFile(Arg->getValue()))
|
||||
enqueuePath(*Path, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto *Arg : Args.filtered(OPT_defaultlib))
|
||||
if (Optional<StringRef> Path = findLib(Arg->getValue()))
|
||||
enqueuePath(*Path);
|
||||
enqueuePath(*Path, false);
|
||||
|
||||
// Windows specific -- Create a resource file containing a manifest file.
|
||||
if (Config->Manifest == Configuration::Embed)
|
||||
addBuffer(createManifestRes());
|
||||
addBuffer(createManifestRes(), false);
|
||||
|
||||
// Read all input files given via the command line.
|
||||
run();
|
||||
@ -976,7 +1065,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
// WindowsResource to convert resource files to a regular COFF file,
|
||||
// then link the resulting file normally.
|
||||
if (!Resources.empty())
|
||||
addBuffer(convertResToCOFF(Resources));
|
||||
Symtab->addFile(make<ObjFile>(convertResToCOFF(Resources)));
|
||||
|
||||
if (Tar)
|
||||
Tar->append("response.txt",
|
||||
@ -984,28 +1073,36 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
ArrayRef<StringRef>(SearchPaths).slice(1)));
|
||||
|
||||
// Handle /largeaddressaware
|
||||
if (Config->is64() || Args.hasArg(OPT_largeaddressaware))
|
||||
Config->LargeAddressAware = true;
|
||||
Config->LargeAddressAware = Args.hasFlag(
|
||||
OPT_largeaddressaware, OPT_largeaddressaware_no, Config->is64());
|
||||
|
||||
// Handle /highentropyva
|
||||
if (Config->is64() && !Args.hasArg(OPT_highentropyva_no))
|
||||
Config->HighEntropyVA = true;
|
||||
Config->HighEntropyVA =
|
||||
Config->is64() &&
|
||||
Args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true);
|
||||
|
||||
if (!Config->DynamicBase &&
|
||||
(Config->Machine == ARMNT || Config->Machine == ARM64))
|
||||
error("/dynamicbase:no is not compatible with " +
|
||||
machineToStr(Config->Machine));
|
||||
|
||||
// Handle /entry and /dll
|
||||
if (auto *Arg = Args.getLastArg(OPT_entry)) {
|
||||
Config->Entry = addUndefined(mangle(Arg->getValue()));
|
||||
} else if (Args.hasArg(OPT_dll) && !Config->NoEntry) {
|
||||
StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
|
||||
: "_DllMainCRTStartup";
|
||||
Config->Entry = addUndefined(S);
|
||||
} else if (!Config->NoEntry) {
|
||||
// Windows specific -- If entry point name is not given, we need to
|
||||
// infer that from user-defined entry name.
|
||||
StringRef S = findDefaultEntry();
|
||||
if (S.empty())
|
||||
fatal("entry point must be defined");
|
||||
Config->Entry = addUndefined(S);
|
||||
log("Entry name inferred: " + S);
|
||||
} else if (!Config->Entry && !Config->NoEntry) {
|
||||
if (Args.hasArg(OPT_dll)) {
|
||||
StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
|
||||
: "_DllMainCRTStartup";
|
||||
Config->Entry = addUndefined(S);
|
||||
} else {
|
||||
// Windows specific -- If entry point name is not given, we need to
|
||||
// infer that from user-defined entry name.
|
||||
StringRef S = findDefaultEntry();
|
||||
if (S.empty())
|
||||
fatal("entry point must be defined");
|
||||
Config->Entry = addUndefined(S);
|
||||
log("Entry name inferred: " + S);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle /export
|
||||
@ -1027,10 +1124,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
}
|
||||
|
||||
// Handle generation of import library from a def file.
|
||||
if (!Args.hasArgNoClaim(OPT_INPUT)) {
|
||||
if (!Args.hasArg(OPT_INPUT)) {
|
||||
fixupExports();
|
||||
createImportLibrary(/*AsLib=*/true);
|
||||
exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle /delayload
|
||||
@ -1050,34 +1147,32 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
}
|
||||
|
||||
// Put the PDB next to the image if no /pdb flag was passed.
|
||||
if (Config->Debug && Config->PDBPath.empty()) {
|
||||
if (ShouldCreatePDB && Config->PDBPath.empty()) {
|
||||
Config->PDBPath = Config->OutputFile;
|
||||
sys::path::replace_extension(Config->PDBPath, ".pdb");
|
||||
}
|
||||
|
||||
// Disable PDB generation if the user requested it.
|
||||
if (Args.hasArg(OPT_nopdb))
|
||||
Config->PDBPath = "";
|
||||
|
||||
// Set default image base if /base is not given.
|
||||
if (Config->ImageBase == uint64_t(-1))
|
||||
Config->ImageBase = getDefaultImageBase();
|
||||
|
||||
Symtab.addSynthetic(mangle("__ImageBase"), nullptr);
|
||||
Symtab->addSynthetic(mangle("__ImageBase"), nullptr);
|
||||
if (Config->Machine == I386) {
|
||||
Symtab.addAbsolute("___safe_se_handler_table", 0);
|
||||
Symtab.addAbsolute("___safe_se_handler_count", 0);
|
||||
Symtab->addAbsolute("___safe_se_handler_table", 0);
|
||||
Symtab->addAbsolute("___safe_se_handler_count", 0);
|
||||
}
|
||||
|
||||
// We do not support /guard:cf (control flow protection) yet.
|
||||
// Define CFG symbols anyway so that we can link MSVC 2015 CRT.
|
||||
Symtab.addAbsolute(mangle("__guard_fids_count"), 0);
|
||||
Symtab.addAbsolute(mangle("__guard_fids_table"), 0);
|
||||
Symtab.addAbsolute(mangle("__guard_flags"), 0x100);
|
||||
Symtab.addAbsolute(mangle("__guard_iat_count"), 0);
|
||||
Symtab.addAbsolute(mangle("__guard_iat_table"), 0);
|
||||
Symtab.addAbsolute(mangle("__guard_longjmp_count"), 0);
|
||||
Symtab.addAbsolute(mangle("__guard_longjmp_table"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_fids_count"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_fids_table"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_flags"), 0x100);
|
||||
Symtab->addAbsolute(mangle("__guard_iat_count"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_iat_table"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
|
||||
Symtab->addAbsolute(mangle("__guard_longjmp_table"), 0);
|
||||
// Needed for MSVC 2017 15.5 CRT.
|
||||
Symtab->addAbsolute(mangle("__enclave_config"), 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
|
||||
@ -1086,7 +1181,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
// Windows specific -- if entry point is not found,
|
||||
// search for its mangled names.
|
||||
if (Config->Entry)
|
||||
Symtab.mangleMaybe(Config->Entry);
|
||||
Symtab->mangleMaybe(Config->Entry);
|
||||
|
||||
// Windows specific -- Make sure we resolve all dllexported symbols.
|
||||
for (Export &E : Config->Exports) {
|
||||
@ -1094,7 +1189,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
continue;
|
||||
E.Sym = addUndefined(E.Name);
|
||||
if (!E.Directives)
|
||||
Symtab.mangleMaybe(E.Sym);
|
||||
Symtab->mangleMaybe(E.Sym);
|
||||
}
|
||||
|
||||
// Add weak aliases. Weak aliases is a mechanism to give remaining
|
||||
@ -1102,36 +1197,38 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
for (auto Pair : Config->AlternateNames) {
|
||||
StringRef From = Pair.first;
|
||||
StringRef To = Pair.second;
|
||||
Symbol *Sym = Symtab.find(From);
|
||||
Symbol *Sym = Symtab->find(From);
|
||||
if (!Sym)
|
||||
continue;
|
||||
if (auto *U = dyn_cast<Undefined>(Sym->body()))
|
||||
if (auto *U = dyn_cast<Undefined>(Sym))
|
||||
if (!U->WeakAlias)
|
||||
U->WeakAlias = Symtab.addUndefined(To);
|
||||
U->WeakAlias = Symtab->addUndefined(To);
|
||||
}
|
||||
|
||||
// Windows specific -- if __load_config_used can be resolved, resolve it.
|
||||
if (Symtab.findUnderscore("_load_config_used"))
|
||||
if (Symtab->findUnderscore("_load_config_used"))
|
||||
addUndefined(mangle("_load_config_used"));
|
||||
} while (run());
|
||||
|
||||
if (ErrorCount)
|
||||
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);
|
||||
exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
||||
// link those files.
|
||||
Symtab.addCombinedLTOObjects();
|
||||
Symtab->addCombinedLTOObjects();
|
||||
run();
|
||||
|
||||
// Make sure we have resolved all symbols.
|
||||
Symtab.reportRemainingUndefines();
|
||||
Symtab->reportRemainingUndefines();
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// Windows specific -- if no /subsystem is given, we need to infer
|
||||
// that from entry point name.
|
||||
@ -1142,14 +1239,34 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
}
|
||||
|
||||
// Handle /safeseh.
|
||||
if (Args.hasArg(OPT_safeseh)) {
|
||||
for (ObjectFile *File : Symtab.ObjectFiles)
|
||||
if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) {
|
||||
for (ObjFile *File : ObjFile::Instances)
|
||||
if (!File->SEHCompat)
|
||||
error("/safeseh: " + File->getName() + " is not compatible with SEH");
|
||||
if (ErrorCount)
|
||||
if (errorCount())
|
||||
return;
|
||||
}
|
||||
|
||||
// In MinGW, all symbols are automatically exported if no symbols
|
||||
// are chosen to be exported.
|
||||
if (Config->DLL && ((Config->MinGW && Config->Exports.empty()) ||
|
||||
Args.hasArg(OPT_export_all_symbols))) {
|
||||
AutoExporter Exporter;
|
||||
|
||||
Symtab->forEachSymbol([=](Symbol *S) {
|
||||
auto *Def = dyn_cast<Defined>(S);
|
||||
if (!Exporter.shouldExport(Def))
|
||||
return;
|
||||
Export E;
|
||||
E.Name = Def->getName();
|
||||
E.Sym = Def;
|
||||
if (Def->getChunk() &&
|
||||
!(Def->getChunk()->getPermissions() & IMAGE_SCN_MEM_EXECUTE))
|
||||
E.Data = true;
|
||||
Config->Exports.push_back(E);
|
||||
});
|
||||
}
|
||||
|
||||
// Windows specific -- when we are creating a .dll file, we also
|
||||
// need to create a .lib file.
|
||||
if (!Config->Exports.empty() || Config->DLL) {
|
||||
@ -1158,23 +1275,45 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
assignExportOrdinals();
|
||||
}
|
||||
|
||||
// Handle /output-def (MinGW specific).
|
||||
if (auto *Arg = Args.getLastArg(OPT_output_def))
|
||||
writeDefFile(Arg->getValue());
|
||||
|
||||
// Set extra alignment for .comm symbols
|
||||
for (auto Pair : Config->AlignComm) {
|
||||
StringRef Name = Pair.first;
|
||||
uint32_t Alignment = Pair.second;
|
||||
|
||||
Symbol *Sym = Symtab->find(Name);
|
||||
if (!Sym) {
|
||||
warn("/aligncomm symbol " + Name + " not found");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *DC = dyn_cast<DefinedCommon>(Sym);
|
||||
if (!DC) {
|
||||
warn("/aligncomm symbol " + Name + " of wrong kind");
|
||||
continue;
|
||||
}
|
||||
|
||||
CommonChunk *C = DC->getChunk();
|
||||
C->Alignment = std::max(C->Alignment, Alignment);
|
||||
}
|
||||
|
||||
// Windows specific -- Create a side-by-side manifest file.
|
||||
if (Config->Manifest == Configuration::SideBySide)
|
||||
createSideBySideManifest();
|
||||
|
||||
// Identify unreferenced COMDAT sections.
|
||||
if (Config->DoGC)
|
||||
markLive(Symtab.getChunks());
|
||||
markLive(Symtab->getChunks());
|
||||
|
||||
// Identify identical COMDAT sections to merge them.
|
||||
if (Config->DoICF)
|
||||
doICF(Symtab.getChunks());
|
||||
doICF(Symtab->getChunks());
|
||||
|
||||
// Write the result.
|
||||
writeResult(&Symtab);
|
||||
|
||||
// Call exit to avoid calling destructors.
|
||||
exit(0);
|
||||
writeResult();
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
|
@ -12,8 +12,8 @@
|
||||
|
||||
#include "Config.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/Reproduce.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Common/Reproduce.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
@ -36,31 +36,35 @@ using llvm::COFF::WindowsSubsystem;
|
||||
using llvm::Optional;
|
||||
|
||||
// Implemented in MarkLive.cpp.
|
||||
void markLive(const std::vector<Chunk *> &Chunks);
|
||||
void markLive(ArrayRef<Chunk *> Chunks);
|
||||
|
||||
// Implemented in ICF.cpp.
|
||||
void doICF(const std::vector<Chunk *> &Chunks);
|
||||
void doICF(ArrayRef<Chunk *> Chunks);
|
||||
|
||||
class COFFOptTable : public llvm::opt::OptTable {
|
||||
public:
|
||||
COFFOptTable();
|
||||
};
|
||||
|
||||
class ArgParser {
|
||||
public:
|
||||
// Parses command line options.
|
||||
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
|
||||
|
||||
// Concatenate LINK environment varirable and given arguments and parse them.
|
||||
// Concatenate LINK environment variable and given arguments and parse them.
|
||||
llvm::opt::InputArgList parseLINK(std::vector<const char *> Args);
|
||||
|
||||
// Tokenizes a given string and then parses as command line options.
|
||||
llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); }
|
||||
|
||||
private:
|
||||
// Parses command line options.
|
||||
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
|
||||
|
||||
std::vector<const char *> tokenize(StringRef S);
|
||||
|
||||
std::vector<const char *> replaceResponseFiles(std::vector<const char *>);
|
||||
COFFOptTable Table;
|
||||
};
|
||||
|
||||
class LinkerDriver {
|
||||
public:
|
||||
LinkerDriver() { coff::Symtab = &Symtab; }
|
||||
void link(llvm::ArrayRef<const char *> Args);
|
||||
|
||||
// Used by the resolver to parse .drectve section contents.
|
||||
@ -70,10 +74,9 @@ public:
|
||||
void enqueueArchiveMember(const Archive::Child &C, StringRef SymName,
|
||||
StringRef ParentName);
|
||||
|
||||
private:
|
||||
ArgParser Parser;
|
||||
SymbolTable Symtab;
|
||||
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
|
||||
|
||||
private:
|
||||
std::unique_ptr<llvm::TarWriter> Tar; // for /linkrepro
|
||||
|
||||
// Opens a file. Path has to be resolved already.
|
||||
@ -93,7 +96,7 @@ private:
|
||||
std::set<std::string> VisitedFiles;
|
||||
std::set<std::string> VisitedLibs;
|
||||
|
||||
SymbolBody *addUndefined(StringRef Sym);
|
||||
Symbol *addUndefined(StringRef Sym);
|
||||
StringRef mangle(StringRef Sym);
|
||||
|
||||
// Windows specific -- "main" is not the only main function in Windows.
|
||||
@ -108,12 +111,11 @@ private:
|
||||
|
||||
void invokeMSVC(llvm::opt::InputArgList &Args);
|
||||
|
||||
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
|
||||
void addBuffer(std::unique_ptr<MemoryBuffer> MB);
|
||||
void addBuffer(std::unique_ptr<MemoryBuffer> MB, bool WholeArchive);
|
||||
void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
|
||||
StringRef ParentName);
|
||||
|
||||
void enqueuePath(StringRef Path);
|
||||
void enqueuePath(StringRef Path, bool WholeArchive);
|
||||
|
||||
void enqueueTask(std::function<void()> Task);
|
||||
bool run();
|
||||
@ -145,6 +147,7 @@ void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
|
||||
void parseAlternateName(StringRef);
|
||||
void parseMerge(StringRef);
|
||||
void parseSection(StringRef);
|
||||
void parseAligncomm(StringRef);
|
||||
|
||||
// Parses a string in the form of "EMBED[,=<integer>]|NO".
|
||||
void parseManifest(StringRef Arg);
|
||||
@ -167,10 +170,8 @@ void assignExportOrdinals();
|
||||
// incompatible objects.
|
||||
void checkFailIfMismatch(StringRef Arg);
|
||||
|
||||
// Convert Windows resource files (.res files) to a .obj file
|
||||
// using cvtres.exe.
|
||||
std::unique_ptr<MemoryBuffer>
|
||||
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs);
|
||||
// Convert Windows resource files (.res files) to a .obj file.
|
||||
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs);
|
||||
|
||||
void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects);
|
||||
|
||||
|
@ -15,9 +15,9 @@
|
||||
|
||||
#include "Config.h"
|
||||
#include "Driver.h"
|
||||
#include "Error.h"
|
||||
#include "Memory.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/BinaryFormat/COFF.h"
|
||||
@ -32,12 +32,11 @@
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/WindowsManifest/WindowsManifestMerger.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace llvm::COFF;
|
||||
using namespace llvm;
|
||||
using llvm::cl::ExpandResponseFiles;
|
||||
using llvm::cl::TokenizeWindowsCommandLine;
|
||||
using llvm::sys::Process;
|
||||
|
||||
namespace lld {
|
||||
@ -58,7 +57,7 @@ public:
|
||||
void run() {
|
||||
ErrorOr<std::string> ExeOrErr = sys::findProgramByName(Prog);
|
||||
if (auto EC = ExeOrErr.getError())
|
||||
fatal(EC, "unable to find " + Prog + " in PATH: ");
|
||||
fatal("unable to find " + Prog + " in PATH: " + EC.message());
|
||||
StringRef Exe = Saver.save(*ExeOrErr);
|
||||
Args.insert(Args.begin(), Exe);
|
||||
|
||||
@ -221,6 +220,22 @@ void parseSection(StringRef S) {
|
||||
Config->Section[Name] = parseSectionAttributes(Attrs);
|
||||
}
|
||||
|
||||
// Parses /aligncomm option argument.
|
||||
void parseAligncomm(StringRef S) {
|
||||
StringRef Name, Align;
|
||||
std::tie(Name, Align) = S.split(',');
|
||||
if (Name.empty() || Align.empty()) {
|
||||
error("/aligncomm: invalid argument: " + S);
|
||||
return;
|
||||
}
|
||||
int V;
|
||||
if (Align.getAsInteger(0, V)) {
|
||||
error("/aligncomm: invalid argument: " + S);
|
||||
return;
|
||||
}
|
||||
Config->AlignComm[Name] = std::max(Config->AlignComm[Name], 1 << V);
|
||||
}
|
||||
|
||||
// Parses a string in the form of "EMBED[,=<integer>]|NO".
|
||||
// Results are directly written to Config.
|
||||
void parseManifest(StringRef Arg) {
|
||||
@ -273,14 +288,14 @@ public:
|
||||
TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") {
|
||||
SmallString<128> S;
|
||||
if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S))
|
||||
fatal(EC, "cannot create a temporary file");
|
||||
fatal("cannot create a temporary file: " + EC.message());
|
||||
Path = S.str();
|
||||
|
||||
if (!Contents.empty()) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Path, EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
fatal(EC, "failed to open " + Path);
|
||||
fatal("failed to open " + Path + ": " + EC.message());
|
||||
OS << Contents;
|
||||
}
|
||||
}
|
||||
@ -302,7 +317,7 @@ public:
|
||||
// is called (you cannot remove an opened file on Windows.)
|
||||
std::unique_ptr<MemoryBuffer> getMemoryBuffer() {
|
||||
// IsVolatileSize=true forces MemoryBuffer to not use mmap().
|
||||
return check(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
|
||||
return CHECK(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
|
||||
/*RequiresNullTerminator=*/false,
|
||||
/*IsVolatileSize=*/true),
|
||||
"could not open " + Path);
|
||||
@ -312,16 +327,9 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
// Create the default manifest file as a temporary file.
|
||||
TemporaryFile createDefaultXml() {
|
||||
// Create a temporary file.
|
||||
TemporaryFile File("defaultxml", "manifest");
|
||||
|
||||
// Open the temporary file for writing.
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text);
|
||||
if (EC)
|
||||
fatal(EC, "failed to open " + File.Path);
|
||||
static std::string createDefaultXml() {
|
||||
std::string Ret;
|
||||
raw_string_ostream OS(Ret);
|
||||
|
||||
// Emit the XML. Note that we do *not* verify that the XML attributes are
|
||||
// syntactically correct. This is intentional for link.exe compatibility.
|
||||
@ -337,46 +345,77 @@ TemporaryFile createDefaultXml() {
|
||||
<< " </requestedPrivileges>\n"
|
||||
<< " </security>\n"
|
||||
<< " </trustInfo>\n";
|
||||
if (!Config->ManifestDependency.empty()) {
|
||||
OS << " <dependency>\n"
|
||||
<< " <dependentAssembly>\n"
|
||||
<< " <assemblyIdentity " << Config->ManifestDependency << " />\n"
|
||||
<< " </dependentAssembly>\n"
|
||||
<< " </dependency>\n";
|
||||
}
|
||||
}
|
||||
if (!Config->ManifestDependency.empty()) {
|
||||
OS << " <dependency>\n"
|
||||
<< " <dependentAssembly>\n"
|
||||
<< " <assemblyIdentity " << Config->ManifestDependency << " />\n"
|
||||
<< " </dependentAssembly>\n"
|
||||
<< " </dependency>\n";
|
||||
}
|
||||
OS << "</assembly>\n";
|
||||
return OS.str();
|
||||
}
|
||||
|
||||
static std::string createManifestXmlWithInternalMt(StringRef DefaultXml) {
|
||||
std::unique_ptr<MemoryBuffer> DefaultXmlCopy =
|
||||
MemoryBuffer::getMemBufferCopy(DefaultXml);
|
||||
|
||||
windows_manifest::WindowsManifestMerger Merger;
|
||||
if (auto E = Merger.merge(*DefaultXmlCopy.get()))
|
||||
fatal("internal manifest tool failed on default xml: " +
|
||||
toString(std::move(E)));
|
||||
|
||||
for (StringRef Filename : Config->ManifestInput) {
|
||||
std::unique_ptr<MemoryBuffer> Manifest =
|
||||
check(MemoryBuffer::getFile(Filename));
|
||||
if (auto E = Merger.merge(*Manifest.get()))
|
||||
fatal("internal manifest tool failed on file " + Filename + ": " +
|
||||
toString(std::move(E)));
|
||||
}
|
||||
|
||||
return Merger.getMergedManifest().get()->getBuffer();
|
||||
}
|
||||
|
||||
static std::string createManifestXmlWithExternalMt(StringRef DefaultXml) {
|
||||
// Create the default manifest file as a temporary file.
|
||||
TemporaryFile Default("defaultxml", "manifest");
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Default.Path, EC, sys::fs::F_Text);
|
||||
if (EC)
|
||||
fatal("failed to open " + Default.Path + ": " + EC.message());
|
||||
OS << DefaultXml;
|
||||
OS.close();
|
||||
return File;
|
||||
}
|
||||
|
||||
static std::string readFile(StringRef Path) {
|
||||
std::unique_ptr<MemoryBuffer> MB =
|
||||
check(MemoryBuffer::getFile(Path), "could not open " + Path);
|
||||
return MB->getBuffer();
|
||||
}
|
||||
|
||||
static std::string createManifestXml() {
|
||||
// Create the default manifest file.
|
||||
TemporaryFile File1 = createDefaultXml();
|
||||
if (Config->ManifestInput.empty())
|
||||
return readFile(File1.Path);
|
||||
|
||||
// If manifest files are supplied by the user using /MANIFESTINPUT
|
||||
// option, we need to merge them with the default manifest.
|
||||
TemporaryFile File2("user", "manifest");
|
||||
// Merge user-supplied manifests if they are given. Since libxml2 is not
|
||||
// enabled, we must shell out to Microsoft's mt.exe tool.
|
||||
TemporaryFile User("user", "manifest");
|
||||
|
||||
Executor E("mt.exe");
|
||||
E.add("/manifest");
|
||||
E.add(File1.Path);
|
||||
E.add(Default.Path);
|
||||
for (StringRef Filename : Config->ManifestInput) {
|
||||
E.add("/manifest");
|
||||
E.add(Filename);
|
||||
}
|
||||
E.add("/nologo");
|
||||
E.add("/out:" + StringRef(File2.Path));
|
||||
E.add("/out:" + StringRef(User.Path));
|
||||
E.run();
|
||||
return readFile(File2.Path);
|
||||
|
||||
return CHECK(MemoryBuffer::getFile(User.Path), "could not open " + User.Path)
|
||||
.get()
|
||||
->getBuffer();
|
||||
}
|
||||
|
||||
static std::string createManifestXml() {
|
||||
std::string DefaultXml = createDefaultXml();
|
||||
if (Config->ManifestInput.empty())
|
||||
return DefaultXml;
|
||||
|
||||
if (windows_manifest::isAvailable())
|
||||
return createManifestXmlWithInternalMt(DefaultXml);
|
||||
|
||||
return createManifestXmlWithExternalMt(DefaultXml);
|
||||
}
|
||||
|
||||
static std::unique_ptr<MemoryBuffer>
|
||||
@ -386,7 +425,8 @@ createMemoryBufferForManifestRes(size_t ManifestSize) {
|
||||
sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +
|
||||
sizeof(object::WinResHeaderSuffix) + ManifestSize,
|
||||
object::WIN_RES_DATA_ALIGNMENT);
|
||||
return MemoryBuffer::getNewMemBuffer(ResSize);
|
||||
return MemoryBuffer::getNewMemBuffer(ResSize,
|
||||
Config->OutputFile + ".manifest.res");
|
||||
}
|
||||
|
||||
static void writeResFileHeader(char *&Buf) {
|
||||
@ -444,7 +484,7 @@ void createSideBySideManifest() {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream Out(Path, EC, sys::fs::F_Text);
|
||||
if (EC)
|
||||
fatal(EC, "failed to create manifest");
|
||||
fatal("failed to create manifest: " + EC.message());
|
||||
Out << createManifestXml();
|
||||
}
|
||||
|
||||
@ -459,12 +499,12 @@ Export parseExport(StringRef Arg) {
|
||||
if (E.Name.empty())
|
||||
goto err;
|
||||
|
||||
if (E.Name.find('=') != StringRef::npos) {
|
||||
if (E.Name.contains('=')) {
|
||||
StringRef X, Y;
|
||||
std::tie(X, Y) = E.Name.split("=");
|
||||
|
||||
// If "<name>=<dllname>.<name>".
|
||||
if (Y.find(".") != StringRef::npos) {
|
||||
if (Y.contains(".")) {
|
||||
E.Name = X;
|
||||
E.ForwardTo = Y;
|
||||
return E;
|
||||
@ -534,7 +574,7 @@ void fixupExports() {
|
||||
}
|
||||
|
||||
for (Export &E : Config->Exports) {
|
||||
SymbolBody *Sym = E.Sym;
|
||||
Symbol *Sym = E.Sym;
|
||||
if (!E.ForwardTo.empty() || !Sym) {
|
||||
E.SymbolName = E.Name;
|
||||
} else {
|
||||
@ -554,7 +594,7 @@ void fixupExports() {
|
||||
}
|
||||
|
||||
// Uniquefy by name.
|
||||
std::map<StringRef, Export *> Map;
|
||||
DenseMap<StringRef, Export *> Map(Config->Exports.size());
|
||||
std::vector<Export> V;
|
||||
for (Export &E : Config->Exports) {
|
||||
auto Pair = Map.insert(std::make_pair(E.ExportName, &E));
|
||||
@ -601,10 +641,8 @@ void checkFailIfMismatch(StringRef Arg) {
|
||||
Config->MustMatch[K] = V;
|
||||
}
|
||||
|
||||
// Convert Windows resource files (.res files) to a .obj file
|
||||
// using cvtres.exe.
|
||||
std::unique_ptr<MemoryBuffer>
|
||||
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
|
||||
// Convert Windows resource files (.res files) to a .obj file.
|
||||
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs) {
|
||||
object::WindowsResourceParser Parser;
|
||||
|
||||
for (MemoryBufferRef MB : MBs) {
|
||||
@ -613,14 +651,17 @@ convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
|
||||
if (!RF)
|
||||
fatal("cannot compile non-resource file as resource");
|
||||
if (auto EC = Parser.parse(RF))
|
||||
fatal(EC, "failed to parse .res file");
|
||||
fatal("failed to parse .res file: " + toString(std::move(EC)));
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<MemoryBuffer>> E =
|
||||
llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser);
|
||||
if (!E)
|
||||
fatal(errorToErrorCode(E.takeError()), "failed to write .res to COFF");
|
||||
return std::move(E.get());
|
||||
fatal("failed to write .res to COFF: " + toString(E.takeError()));
|
||||
|
||||
MemoryBufferRef MBRef = **E;
|
||||
make<std::unique_ptr<MemoryBuffer>>(std::move(*E)); // take ownership
|
||||
return MBRef;
|
||||
}
|
||||
|
||||
// Run MSVC link.exe for given in-memory object files.
|
||||
@ -651,7 +692,7 @@ void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) {
|
||||
#undef PREFIX
|
||||
|
||||
// Create table mapping all options defined in Options.td
|
||||
static const llvm::opt::OptTable::Info infoTable[] = {
|
||||
static const llvm::opt::OptTable::Info InfoTable[] = {
|
||||
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
|
||||
{X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \
|
||||
X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
|
||||
@ -659,30 +700,49 @@ static const llvm::opt::OptTable::Info infoTable[] = {
|
||||
#undef OPTION
|
||||
};
|
||||
|
||||
class COFFOptTable : public llvm::opt::OptTable {
|
||||
public:
|
||||
COFFOptTable() : OptTable(infoTable, true) {}
|
||||
};
|
||||
COFFOptTable::COFFOptTable() : OptTable(InfoTable, true) {}
|
||||
|
||||
static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S != "windows" && S != "posix")
|
||||
error("invalid response file quoting: " + S);
|
||||
if (S == "windows")
|
||||
return cl::TokenizeWindowsCommandLine;
|
||||
return cl::TokenizeGNUCommandLine;
|
||||
}
|
||||
// The COFF linker always defaults to Windows quoting.
|
||||
return cl::TokenizeWindowsCommandLine;
|
||||
}
|
||||
|
||||
// Parses a given list of options.
|
||||
opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
|
||||
// First, replace respnose files (@<file>-style options).
|
||||
std::vector<const char *> Argv = replaceResponseFiles(ArgsArr);
|
||||
|
||||
opt::InputArgList ArgParser::parse(ArrayRef<const char *> Argv) {
|
||||
// Make InputArgList from string vectors.
|
||||
COFFOptTable Table;
|
||||
unsigned MissingIndex;
|
||||
unsigned MissingCount;
|
||||
opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount);
|
||||
SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
|
||||
|
||||
// We need to get the quoting style for response files before parsing all
|
||||
// options so we parse here before and ignore all the options but
|
||||
// --rsp-quoting.
|
||||
opt::InputArgList Args = Table.ParseArgs(Vec, MissingIndex, MissingCount);
|
||||
|
||||
// Expand response files (arguments in the form of @<filename>)
|
||||
// and then parse the argument again.
|
||||
cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec);
|
||||
Args = Table.ParseArgs(Vec, MissingIndex, MissingCount);
|
||||
|
||||
// Print the real command line if response files are expanded.
|
||||
if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) {
|
||||
if (Args.hasArg(OPT_verbose) && Argv.size() != Vec.size()) {
|
||||
std::string Msg = "Command line:";
|
||||
for (const char *S : Argv)
|
||||
for (const char *S : Vec)
|
||||
Msg += " " + std::string(S);
|
||||
message(Msg);
|
||||
}
|
||||
|
||||
// Handle /WX early since it converts missing argument warnings to errors.
|
||||
errorHandler().FatalWarnings = Args.hasFlag(OPT_WX, OPT_WX_no, false);
|
||||
|
||||
if (MissingCount)
|
||||
fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
|
||||
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
|
||||
@ -693,17 +753,17 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
|
||||
// link.exe has an interesting feature. If LINK or _LINK_ environment
|
||||
// variables exist, their contents are handled as command line strings.
|
||||
// So you can pass extra arguments using them.
|
||||
opt::InputArgList ArgParser::parseLINK(std::vector<const char *> Args) {
|
||||
opt::InputArgList ArgParser::parseLINK(std::vector<const char *> Argv) {
|
||||
// Concatenate LINK env and command line arguments, and then parse them.
|
||||
if (Optional<std::string> S = Process::GetEnv("LINK")) {
|
||||
std::vector<const char *> V = tokenize(*S);
|
||||
Args.insert(Args.begin(), V.begin(), V.end());
|
||||
Argv.insert(Argv.begin(), V.begin(), V.end());
|
||||
}
|
||||
if (Optional<std::string> S = Process::GetEnv("_LINK_")) {
|
||||
std::vector<const char *> V = tokenize(*S);
|
||||
Args.insert(Args.begin(), V.begin(), V.end());
|
||||
Argv.insert(Argv.begin(), V.begin(), V.end());
|
||||
}
|
||||
return parse(Args);
|
||||
return parse(Argv);
|
||||
}
|
||||
|
||||
std::vector<const char *> ArgParser::tokenize(StringRef S) {
|
||||
@ -712,18 +772,8 @@ std::vector<const char *> ArgParser::tokenize(StringRef S) {
|
||||
return std::vector<const char *>(Tokens.begin(), Tokens.end());
|
||||
}
|
||||
|
||||
// Creates a new command line by replacing options starting with '@'
|
||||
// character. '@<filename>' is replaced by the file's contents.
|
||||
std::vector<const char *>
|
||||
ArgParser::replaceResponseFiles(std::vector<const char *> Argv) {
|
||||
SmallVector<const char *, 256> Tokens(Argv.data(), Argv.data() + Argv.size());
|
||||
ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens);
|
||||
return std::vector<const char *>(Tokens.begin(), Tokens.end());
|
||||
}
|
||||
|
||||
void printHelp(const char *Argv0) {
|
||||
COFFOptTable Table;
|
||||
Table.PrintHelp(outs(), Argv0, "LLVM Linker", false);
|
||||
COFFOptTable().PrintHelp(outs(), Argv0, "LLVM Linker", false);
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
|
114
COFF/Error.cpp
114
COFF/Error.cpp
@ -1,114 +0,0 @@
|
||||
//===- Error.cpp ----------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <mutex>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace lld {
|
||||
// The functions defined in this file can be called from multiple threads,
|
||||
// but outs() or errs() are not thread-safe. We protect them using a mutex.
|
||||
static std::mutex Mu;
|
||||
|
||||
namespace coff {
|
||||
uint64_t ErrorCount;
|
||||
raw_ostream *ErrorOS;
|
||||
|
||||
static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) {
|
||||
// Dealloc/destroy ManagedStatic variables before calling
|
||||
// _exit(). In a non-LTO build, this is a nop. In an LTO
|
||||
// build allows us to get the output of -time-passes.
|
||||
llvm_shutdown();
|
||||
|
||||
outs().flush();
|
||||
errs().flush();
|
||||
_exit(Val);
|
||||
}
|
||||
|
||||
static void print(StringRef S, raw_ostream::Colors C) {
|
||||
*ErrorOS << Config->Argv[0] << ": ";
|
||||
if (Config->ColorDiagnostics) {
|
||||
ErrorOS->changeColor(C, true);
|
||||
*ErrorOS << S;
|
||||
ErrorOS->resetColor();
|
||||
} else {
|
||||
*ErrorOS << S;
|
||||
}
|
||||
}
|
||||
|
||||
void log(const Twine &Msg) {
|
||||
if (Config->Verbose) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Config->Argv[0] << ": " << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
}
|
||||
|
||||
void message(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
|
||||
void error(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
|
||||
if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << Msg << "\n";
|
||||
} else if (ErrorCount == Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << "too many errors emitted, stopping now"
|
||||
<< " (use /ERRORLIMIT:0 to see all errors)\n";
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
++ErrorCount;
|
||||
}
|
||||
|
||||
void fatal(const Twine &Msg) {
|
||||
if (Config->ColorDiagnostics) {
|
||||
errs().changeColor(raw_ostream::RED, /*bold=*/true);
|
||||
errs() << "error: ";
|
||||
errs().resetColor();
|
||||
} else {
|
||||
errs() << "error: ";
|
||||
}
|
||||
errs() << Msg << "\n";
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
void fatal(std::error_code EC, const Twine &Msg) {
|
||||
fatal(Msg + ": " + EC.message());
|
||||
}
|
||||
|
||||
void fatal(llvm::Error &Err, const Twine &Msg) {
|
||||
fatal(errorToErrorCode(std::move(Err)), Msg);
|
||||
}
|
||||
|
||||
void warn(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
print("warning: ", raw_ostream::MAGENTA);
|
||||
*ErrorOS << Msg << "\n";
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
62
COFF/Error.h
62
COFF/Error.h
@ -1,62 +0,0 @@
|
||||
//===- Error.h --------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_ERROR_H
|
||||
#define LLD_COFF_ERROR_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
extern uint64_t ErrorCount;
|
||||
extern llvm::raw_ostream *ErrorOS;
|
||||
|
||||
void log(const Twine &Msg);
|
||||
void message(const Twine &Msg);
|
||||
void warn(const Twine &Msg);
|
||||
void error(const Twine &Msg);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix);
|
||||
|
||||
template <class T> T check(ErrorOr<T> V, const Twine &Prefix) {
|
||||
if (auto EC = V.getError())
|
||||
fatal(EC, Prefix);
|
||||
return std::move(*V);
|
||||
}
|
||||
|
||||
template <class T> T check(Expected<T> E, const Twine &Prefix) {
|
||||
if (llvm::Error Err = E.takeError())
|
||||
fatal(Err, Prefix);
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
template <class T> T check(ErrorOr<T> EO) {
|
||||
if (!EO)
|
||||
fatal(EO.getError().message());
|
||||
return std::move(*EO);
|
||||
}
|
||||
|
||||
template <class T> T check(Expected<T> E) {
|
||||
if (!E) {
|
||||
std::string Buf;
|
||||
llvm::raw_string_ostream OS(Buf);
|
||||
logAllUnhandledErrors(E.takeError(), OS, "");
|
||||
OS.flush();
|
||||
fatal(Buf);
|
||||
}
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
51
COFF/ICF.cpp
51
COFF/ICF.cpp
@ -19,8 +19,8 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Parallel.h"
|
||||
@ -36,7 +36,7 @@ namespace coff {
|
||||
|
||||
class ICF {
|
||||
public:
|
||||
void run(const std::vector<Chunk *> &V);
|
||||
void run(ArrayRef<Chunk *> V);
|
||||
|
||||
private:
|
||||
void segregate(size_t Begin, size_t End, bool Constant);
|
||||
@ -61,12 +61,9 @@ private:
|
||||
|
||||
// Returns a hash value for S.
|
||||
uint32_t ICF::getHash(SectionChunk *C) {
|
||||
return hash_combine(C->getPermissions(),
|
||||
hash_value(C->SectionName),
|
||||
C->NumRelocs,
|
||||
C->getAlign(),
|
||||
uint32_t(C->Header->SizeOfRawData),
|
||||
C->Checksum);
|
||||
return hash_combine(C->getPermissions(), C->SectionName, C->NumRelocs,
|
||||
C->Alignment, uint32_t(C->Header->SizeOfRawData),
|
||||
C->Checksum, C->getContents());
|
||||
}
|
||||
|
||||
// Returns true if section S is subject of ICF.
|
||||
@ -76,12 +73,21 @@ uint32_t ICF::getHash(SectionChunk *C) {
|
||||
// 2017) says that /opt:icf folds both functions and read-only data.
|
||||
// Despite that, the MSVC linker folds only functions. We found
|
||||
// a few instances of programs that are not safe for data merging.
|
||||
// Therefore, we merge only functions just like the MSVC tool.
|
||||
// Therefore, we merge only functions just like the MSVC tool. However, we merge
|
||||
// identical .xdata sections, because the address of unwind information is
|
||||
// insignificant to the user program and the Visual C++ linker does this.
|
||||
bool ICF::isEligible(SectionChunk *C) {
|
||||
bool Global = C->Sym && C->Sym->isExternal();
|
||||
bool Executable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE;
|
||||
// Non-comdat chunks, dead chunks, and writable chunks are not elegible.
|
||||
bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
|
||||
return C->isCOMDAT() && C->isLive() && Global && Executable && !Writable;
|
||||
if (!C->isCOMDAT() || !C->isLive() || Writable)
|
||||
return false;
|
||||
|
||||
// Code sections are eligible.
|
||||
if (C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
|
||||
return true;
|
||||
|
||||
// .xdata unwind info sections are eligble.
|
||||
return C->getSectionName().split('$').first == ".xdata";
|
||||
}
|
||||
|
||||
// Split an equivalence class into smaller classes.
|
||||
@ -122,8 +128,8 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
|
||||
R1.VirtualAddress != R2.VirtualAddress) {
|
||||
return false;
|
||||
}
|
||||
SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
|
||||
SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex);
|
||||
Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex);
|
||||
Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex);
|
||||
if (B1 == B2)
|
||||
return true;
|
||||
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
|
||||
@ -137,19 +143,17 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
|
||||
|
||||
// Compare section attributes and contents.
|
||||
return A->getPermissions() == B->getPermissions() &&
|
||||
A->SectionName == B->SectionName &&
|
||||
A->getAlign() == B->getAlign() &&
|
||||
A->SectionName == B->SectionName && A->Alignment == B->Alignment &&
|
||||
A->Header->SizeOfRawData == B->Header->SizeOfRawData &&
|
||||
A->Checksum == B->Checksum &&
|
||||
A->getContents() == B->getContents();
|
||||
A->Checksum == B->Checksum && A->getContents() == B->getContents();
|
||||
}
|
||||
|
||||
// Compare "moving" part of two sections, namely relocation targets.
|
||||
bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
|
||||
// Compare relocations.
|
||||
auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
|
||||
SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
|
||||
SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex);
|
||||
Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex);
|
||||
Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex);
|
||||
if (B1 == B2)
|
||||
return true;
|
||||
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
|
||||
@ -202,7 +206,7 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
|
||||
// Merge identical COMDAT sections.
|
||||
// Two sections are considered the same if their section headers,
|
||||
// contents and relocations are all the same.
|
||||
void ICF::run(const std::vector<Chunk *> &Vec) {
|
||||
void ICF::run(ArrayRef<Chunk *> Vec) {
|
||||
// Collect only mergeable sections and group by hash value.
|
||||
uint32_t NextId = 1;
|
||||
for (Chunk *C : Vec) {
|
||||
@ -215,9 +219,10 @@ void ICF::run(const std::vector<Chunk *> &Vec) {
|
||||
}
|
||||
|
||||
// Initially, we use hash values to partition sections.
|
||||
for (SectionChunk *SC : Chunks)
|
||||
for_each(parallel::par, Chunks.begin(), Chunks.end(), [&](SectionChunk *SC) {
|
||||
// Set MSB to 1 to avoid collisions with non-hash classs.
|
||||
SC->Class[0] = getHash(SC) | (1 << 31);
|
||||
});
|
||||
|
||||
// From now on, sections in Chunks are ordered so that sections in
|
||||
// the same group are consecutive in the vector.
|
||||
@ -252,7 +257,7 @@ void ICF::run(const std::vector<Chunk *> &Vec) {
|
||||
}
|
||||
|
||||
// Entry point to ICF.
|
||||
void doICF(const std::vector<Chunk *> &Chunks) { ICF().run(Chunks); }
|
||||
void doICF(ArrayRef<Chunk *> Chunks) { ICF().run(Chunks); }
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
@ -11,10 +11,10 @@
|
||||
#include "Chunks.h"
|
||||
#include "Config.h"
|
||||
#include "Driver.h"
|
||||
#include "Error.h"
|
||||
#include "Memory.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm-c/lto.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
@ -43,14 +43,18 @@ using llvm::support::ulittle32_t;
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
std::vector<ObjFile *> ObjFile::Instances;
|
||||
std::vector<ImportFile *> ImportFile::Instances;
|
||||
std::vector<BitcodeFile *> BitcodeFile::Instances;
|
||||
|
||||
/// Checks that Source is compatible with being a weak alias to Target.
|
||||
/// If Source is Undefined and has no weak alias set, makes it a weak
|
||||
/// alias to Target.
|
||||
static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
|
||||
SymbolBody *Source, SymbolBody *Target) {
|
||||
Symbol *Source, Symbol *Target) {
|
||||
if (auto *U = dyn_cast<Undefined>(Source)) {
|
||||
if (U->WeakAlias && U->WeakAlias != Target)
|
||||
Symtab->reportDuplicate(Source->symbol(), F);
|
||||
Symtab->reportDuplicate(Source, F);
|
||||
U->WeakAlias = Target;
|
||||
}
|
||||
}
|
||||
@ -59,7 +63,7 @@ ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
|
||||
|
||||
void ArchiveFile::parse() {
|
||||
// Parse a MemoryBufferRef as an archive file.
|
||||
File = check(Archive::create(MB), toString(this));
|
||||
File = CHECK(Archive::create(MB), this);
|
||||
|
||||
// Read the symbol table to construct Lazy objects.
|
||||
for (const Archive::Symbol &Sym : File->symbols())
|
||||
@ -69,7 +73,7 @@ void ArchiveFile::parse() {
|
||||
// Returns a buffer pointing to a member file containing a given symbol.
|
||||
void ArchiveFile::addMember(const Archive::Symbol *Sym) {
|
||||
const Archive::Child &C =
|
||||
check(Sym->getMember(),
|
||||
CHECK(Sym->getMember(),
|
||||
"could not get the member for symbol " + Sym->getName());
|
||||
|
||||
// Return an empty buffer if we have already returned the same buffer.
|
||||
@ -79,9 +83,28 @@ void ArchiveFile::addMember(const Archive::Symbol *Sym) {
|
||||
Driver->enqueueArchiveMember(C, Sym->getName(), getName());
|
||||
}
|
||||
|
||||
void ObjectFile::parse() {
|
||||
std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) {
|
||||
std::vector<MemoryBufferRef> V;
|
||||
Error Err = Error::success();
|
||||
for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
|
||||
Archive::Child C =
|
||||
CHECK(COrErr,
|
||||
File->getFileName() + ": could not get the child of the archive");
|
||||
MemoryBufferRef MBRef =
|
||||
CHECK(C.getMemoryBufferRef(),
|
||||
File->getFileName() +
|
||||
": could not get the buffer for a child of the archive");
|
||||
V.push_back(MBRef);
|
||||
}
|
||||
if (Err)
|
||||
fatal(File->getFileName() +
|
||||
": Archive::children failed: " + toString(std::move(Err)));
|
||||
return V;
|
||||
}
|
||||
|
||||
void ObjFile::parse() {
|
||||
// Parse a memory buffer as a COFF file.
|
||||
std::unique_ptr<Binary> Bin = check(createBinary(MB), toString(this));
|
||||
std::unique_ptr<Binary> Bin = CHECK(createBinary(MB), this);
|
||||
|
||||
if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) {
|
||||
Bin.release();
|
||||
@ -93,114 +116,184 @@ void ObjectFile::parse() {
|
||||
// Read section and symbol tables.
|
||||
initializeChunks();
|
||||
initializeSymbols();
|
||||
initializeSEH();
|
||||
}
|
||||
|
||||
void ObjectFile::initializeChunks() {
|
||||
// We set SectionChunk pointers in the SparseChunks vector to this value
|
||||
// temporarily to mark comdat sections as having an unknown resolution. As we
|
||||
// walk the object file's symbol table, once we visit either a leader symbol or
|
||||
// an associative section definition together with the parent comdat's leader,
|
||||
// we set the pointer to either nullptr (to mark the section as discarded) or a
|
||||
// valid SectionChunk for that section.
|
||||
static SectionChunk *const PendingComdat = reinterpret_cast<SectionChunk *>(1);
|
||||
|
||||
void ObjFile::initializeChunks() {
|
||||
uint32_t NumSections = COFFObj->getNumberOfSections();
|
||||
Chunks.reserve(NumSections);
|
||||
SparseChunks.resize(NumSections + 1);
|
||||
for (uint32_t I = 1; I < NumSections + 1; ++I) {
|
||||
const coff_section *Sec;
|
||||
StringRef Name;
|
||||
if (auto EC = COFFObj->getSection(I, Sec))
|
||||
fatal(EC, "getSection failed: #" + Twine(I));
|
||||
if (auto EC = COFFObj->getSectionName(Sec, Name))
|
||||
fatal(EC, "getSectionName failed: #" + Twine(I));
|
||||
if (Name == ".sxdata") {
|
||||
SXData = Sec;
|
||||
continue;
|
||||
}
|
||||
if (Name == ".drectve") {
|
||||
ArrayRef<uint8_t> Data;
|
||||
COFFObj->getSectionContents(Sec, Data);
|
||||
Directives = std::string((const char *)Data.data(), Data.size());
|
||||
continue;
|
||||
}
|
||||
fatal("getSection failed: #" + Twine(I) + ": " + EC.message());
|
||||
|
||||
// Object files may have DWARF debug info or MS CodeView debug info
|
||||
// (or both).
|
||||
//
|
||||
// DWARF sections don't need any special handling from the perspective
|
||||
// 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.
|
||||
|
||||
// Ignore debug info unless /debug is given.
|
||||
if (!Config->Debug && Name.startswith(".debug"))
|
||||
continue;
|
||||
|
||||
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
|
||||
continue;
|
||||
auto *C = make<SectionChunk>(this, Sec);
|
||||
|
||||
// CodeView sections are stored to a different vector because they are not
|
||||
// linked in the regular manner.
|
||||
if (C->isCodeView())
|
||||
DebugChunks.push_back(C);
|
||||
if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
|
||||
SparseChunks[I] = PendingComdat;
|
||||
else
|
||||
Chunks.push_back(C);
|
||||
|
||||
SparseChunks[I] = C;
|
||||
SparseChunks[I] = readSection(I, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectFile::initializeSymbols() {
|
||||
uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
|
||||
SymbolBodies.reserve(NumSymbols);
|
||||
SparseSymbolBodies.resize(NumSymbols);
|
||||
SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
|
||||
const coff_aux_section_definition *Def) {
|
||||
const coff_section *Sec;
|
||||
StringRef Name;
|
||||
if (auto EC = COFFObj->getSection(SectionNumber, Sec))
|
||||
fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message());
|
||||
if (auto EC = COFFObj->getSectionName(Sec, Name))
|
||||
fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " +
|
||||
EC.message());
|
||||
if (Name == ".sxdata") {
|
||||
ArrayRef<uint8_t> Data;
|
||||
COFFObj->getSectionContents(Sec, Data);
|
||||
if (Data.size() % 4 != 0)
|
||||
fatal(".sxdata must be an array of symbol table indices");
|
||||
SXData = {reinterpret_cast<const ulittle32_t *>(Data.data()),
|
||||
Data.size() / 4};
|
||||
return nullptr;
|
||||
}
|
||||
if (Name == ".drectve") {
|
||||
ArrayRef<uint8_t> Data;
|
||||
COFFObj->getSectionContents(Sec, Data);
|
||||
Directives = std::string((const char *)Data.data(), Data.size());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SmallVector<std::pair<SymbolBody *, uint32_t>, 8> WeakAliases;
|
||||
int32_t LastSectionNumber = 0;
|
||||
// Object files may have DWARF debug info or MS CodeView debug info
|
||||
// (or both).
|
||||
//
|
||||
// DWARF sections don't need any special handling from the perspective
|
||||
// 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.
|
||||
|
||||
// Ignore debug info unless /debug is given.
|
||||
if (!Config->Debug && Name.startswith(".debug"))
|
||||
return nullptr;
|
||||
|
||||
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
|
||||
return nullptr;
|
||||
auto *C = make<SectionChunk>(this, Sec);
|
||||
if (Def)
|
||||
C->Checksum = Def->CheckSum;
|
||||
|
||||
// CodeView sections are stored to a different vector because they are not
|
||||
// linked in the regular manner.
|
||||
if (C->isCodeView())
|
||||
DebugChunks.push_back(C);
|
||||
else
|
||||
Chunks.push_back(C);
|
||||
|
||||
return C;
|
||||
}
|
||||
|
||||
void ObjFile::readAssociativeDefinition(
|
||||
COFFSymbolRef Sym, const coff_aux_section_definition *Def) {
|
||||
SectionChunk *Parent = SparseChunks[Def->getNumber(Sym.isBigObj())];
|
||||
|
||||
// If the parent is pending, it probably means that its section definition
|
||||
// appears after us in the symbol table. Leave the associated section as
|
||||
// pending; we will handle it during the second pass in initializeSymbols().
|
||||
if (Parent == PendingComdat)
|
||||
return;
|
||||
|
||||
// Check whether the parent is prevailing. If it is, so are we, and we read
|
||||
// the section; otherwise mark it as discarded.
|
||||
int32_t SectionNumber = Sym.getSectionNumber();
|
||||
if (Parent) {
|
||||
SparseChunks[SectionNumber] = readSection(SectionNumber, Def);
|
||||
if (SparseChunks[SectionNumber])
|
||||
Parent->addAssociative(SparseChunks[SectionNumber]);
|
||||
} else {
|
||||
SparseChunks[SectionNumber] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Symbol *ObjFile::createRegular(COFFSymbolRef Sym) {
|
||||
SectionChunk *SC = SparseChunks[Sym.getSectionNumber()];
|
||||
if (Sym.isExternal()) {
|
||||
StringRef Name;
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
if (SC)
|
||||
return Symtab->addRegular(this, Name, Sym.getGeneric(), SC);
|
||||
return Symtab->addUndefined(Name, this, false);
|
||||
}
|
||||
if (SC)
|
||||
return make<DefinedRegular>(this, /*Name*/ "", false,
|
||||
/*IsExternal*/ false, Sym.getGeneric(), SC);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ObjFile::initializeSymbols() {
|
||||
uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
|
||||
Symbols.resize(NumSymbols);
|
||||
|
||||
SmallVector<std::pair<Symbol *, uint32_t>, 8> WeakAliases;
|
||||
std::vector<uint32_t> PendingIndexes;
|
||||
PendingIndexes.reserve(NumSymbols);
|
||||
|
||||
std::vector<const coff_aux_section_definition *> ComdatDefs(
|
||||
COFFObj->getNumberOfSections() + 1);
|
||||
|
||||
for (uint32_t I = 0; I < NumSymbols; ++I) {
|
||||
// Get a COFFSymbolRef object.
|
||||
ErrorOr<COFFSymbolRef> SymOrErr = COFFObj->getSymbol(I);
|
||||
if (!SymOrErr)
|
||||
fatal(SymOrErr.getError(), "broken object file: " + toString(this));
|
||||
COFFSymbolRef Sym = *SymOrErr;
|
||||
|
||||
const void *AuxP = nullptr;
|
||||
if (Sym.getNumberOfAuxSymbols())
|
||||
AuxP = COFFObj->getSymbol(I + 1)->getRawPtr();
|
||||
bool IsFirst = (LastSectionNumber != Sym.getSectionNumber());
|
||||
|
||||
SymbolBody *Body = nullptr;
|
||||
if (Sym.isUndefined()) {
|
||||
Body = createUndefined(Sym);
|
||||
} else if (Sym.isWeakExternal()) {
|
||||
Body = createUndefined(Sym);
|
||||
uint32_t TagIndex =
|
||||
static_cast<const coff_aux_weak_external *>(AuxP)->TagIndex;
|
||||
WeakAliases.emplace_back(Body, TagIndex);
|
||||
COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I));
|
||||
if (COFFSym.isUndefined()) {
|
||||
Symbols[I] = createUndefined(COFFSym);
|
||||
} else if (COFFSym.isWeakExternal()) {
|
||||
Symbols[I] = createUndefined(COFFSym);
|
||||
uint32_t TagIndex = COFFSym.getAux<coff_aux_weak_external>()->TagIndex;
|
||||
WeakAliases.emplace_back(Symbols[I], TagIndex);
|
||||
} else if (Optional<Symbol *> OptSym = createDefined(COFFSym, ComdatDefs)) {
|
||||
Symbols[I] = *OptSym;
|
||||
} else {
|
||||
Body = createDefined(Sym, AuxP, IsFirst);
|
||||
// createDefined() returns None if a symbol belongs to a section that
|
||||
// was pending at the point when the symbol was read. This can happen in
|
||||
// two cases:
|
||||
// 1) section definition symbol for a comdat leader;
|
||||
// 2) symbol belongs to a comdat section associated with a section whose
|
||||
// section definition symbol appears later in the symbol table.
|
||||
// In both of these cases, we can expect the section to be resolved by
|
||||
// the time we finish visiting the remaining symbols in the symbol
|
||||
// table. So we postpone the handling of this symbol until that time.
|
||||
PendingIndexes.push_back(I);
|
||||
}
|
||||
if (Body) {
|
||||
SymbolBodies.push_back(Body);
|
||||
SparseSymbolBodies[I] = Body;
|
||||
}
|
||||
I += Sym.getNumberOfAuxSymbols();
|
||||
LastSectionNumber = Sym.getSectionNumber();
|
||||
I += COFFSym.getNumberOfAuxSymbols();
|
||||
}
|
||||
|
||||
for (uint32_t I : PendingIndexes) {
|
||||
COFFSymbolRef Sym = check(COFFObj->getSymbol(I));
|
||||
if (auto *Def = Sym.getSectionDefinition())
|
||||
if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
|
||||
readAssociativeDefinition(Sym, Def);
|
||||
Symbols[I] = createRegular(Sym);
|
||||
}
|
||||
|
||||
for (auto &KV : WeakAliases) {
|
||||
SymbolBody *Sym = KV.first;
|
||||
Symbol *Sym = KV.first;
|
||||
uint32_t Idx = KV.second;
|
||||
checkAndSetWeakAlias(Symtab, this, Sym, SparseSymbolBodies[Idx]);
|
||||
checkAndSetWeakAlias(Symtab, this, Sym, Symbols[Idx]);
|
||||
}
|
||||
}
|
||||
|
||||
SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) {
|
||||
Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) {
|
||||
StringRef Name;
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
return Symtab->addUndefined(Name, this, Sym.isWeakExternal())->body();
|
||||
return Symtab->addUndefined(Name, this, Sym.isWeakExternal());
|
||||
}
|
||||
|
||||
SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
|
||||
bool IsFirst) {
|
||||
Optional<Symbol *> ObjFile::createDefined(
|
||||
COFFSymbolRef Sym,
|
||||
std::vector<const coff_aux_section_definition *> &ComdatDefs) {
|
||||
StringRef Name;
|
||||
if (Sym.isCommon()) {
|
||||
auto *C = make<CommonChunk>(Sym);
|
||||
@ -208,7 +301,7 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
Symbol *S =
|
||||
Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C);
|
||||
return S->body();
|
||||
return S;
|
||||
}
|
||||
if (Sym.isAbsolute()) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
@ -222,7 +315,7 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
|
||||
return nullptr;
|
||||
}
|
||||
if (Sym.isExternal())
|
||||
return Symtab->addAbsolute(Name, Sym)->body();
|
||||
return Symtab->addAbsolute(Name, Sym);
|
||||
else
|
||||
return make<DefinedAbsolute>(Name, Sym);
|
||||
}
|
||||
@ -239,54 +332,49 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
|
||||
if ((uint32_t)SectionNumber >= SparseChunks.size())
|
||||
fatal("broken object file: " + toString(this));
|
||||
|
||||
// Nothing else to do without a section chunk.
|
||||
auto *SC = cast_or_null<SectionChunk>(SparseChunks[SectionNumber]);
|
||||
if (!SC)
|
||||
return nullptr;
|
||||
|
||||
// Handle section definitions
|
||||
if (IsFirst && AuxP) {
|
||||
auto *Aux = reinterpret_cast<const coff_aux_section_definition *>(AuxP);
|
||||
if (Aux->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
|
||||
if (auto *ParentSC = cast_or_null<SectionChunk>(
|
||||
SparseChunks[Aux->getNumber(Sym.isBigObj())])) {
|
||||
ParentSC->addAssociative(SC);
|
||||
// If we already discarded the parent, discard the child.
|
||||
if (ParentSC->isDiscarded())
|
||||
SC->markDiscarded();
|
||||
}
|
||||
SC->Checksum = Aux->CheckSum;
|
||||
// Handle comdat leader symbols.
|
||||
if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) {
|
||||
ComdatDefs[SectionNumber] = nullptr;
|
||||
Symbol *Leader;
|
||||
bool Prevailing;
|
||||
if (Sym.isExternal()) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
std::tie(Leader, Prevailing) =
|
||||
Symtab->addComdat(this, Name, Sym.getGeneric());
|
||||
} else {
|
||||
Leader = make<DefinedRegular>(this, /*Name*/ "", false,
|
||||
/*IsExternal*/ false, Sym.getGeneric());
|
||||
Prevailing = true;
|
||||
}
|
||||
if (Prevailing) {
|
||||
SectionChunk *C = readSection(SectionNumber, Def);
|
||||
SparseChunks[SectionNumber] = C;
|
||||
C->Sym = cast<DefinedRegular>(Leader);
|
||||
cast<DefinedRegular>(Leader)->Data = &C->Repl;
|
||||
} else {
|
||||
SparseChunks[SectionNumber] = nullptr;
|
||||
}
|
||||
return Leader;
|
||||
}
|
||||
|
||||
DefinedRegular *B;
|
||||
if (Sym.isExternal()) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
Symbol *S =
|
||||
Symtab->addRegular(this, Name, SC->isCOMDAT(), Sym.getGeneric(), SC);
|
||||
B = cast<DefinedRegular>(S->body());
|
||||
} else
|
||||
B = make<DefinedRegular>(this, /*Name*/ "", SC->isCOMDAT(),
|
||||
/*IsExternal*/ false, Sym.getGeneric(), SC);
|
||||
if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP)
|
||||
SC->setSymbol(B);
|
||||
// Read associative section definitions and prepare to handle the comdat
|
||||
// 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 (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
|
||||
readAssociativeDefinition(Sym, Def);
|
||||
else
|
||||
ComdatDefs[SectionNumber] = Def;
|
||||
}
|
||||
}
|
||||
|
||||
return B;
|
||||
if (SparseChunks[SectionNumber] == PendingComdat)
|
||||
return None;
|
||||
return createRegular(Sym);
|
||||
}
|
||||
|
||||
void ObjectFile::initializeSEH() {
|
||||
if (!SEHCompat || !SXData)
|
||||
return;
|
||||
ArrayRef<uint8_t> A;
|
||||
COFFObj->getSectionContents(SXData, A);
|
||||
if (A.size() % 4 != 0)
|
||||
fatal(".sxdata must be an array of symbol table indices");
|
||||
auto *I = reinterpret_cast<const ulittle32_t *>(A.data());
|
||||
auto *E = reinterpret_cast<const ulittle32_t *>(A.data() + A.size());
|
||||
for (; I != E; ++I)
|
||||
SEHandlers.insert(SparseSymbolBodies[*I]);
|
||||
}
|
||||
|
||||
MachineTypes ObjectFile::getMachineType() {
|
||||
MachineTypes ObjFile::getMachineType() {
|
||||
if (COFFObj)
|
||||
return static_cast<MachineTypes>(COFFObj->getMachine());
|
||||
return IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
@ -332,26 +420,27 @@ void ImportFile::parse() {
|
||||
this->Hdr = Hdr;
|
||||
ExternalName = ExtName;
|
||||
|
||||
ImpSym = cast<DefinedImportData>(
|
||||
Symtab->addImportData(ImpName, this)->body());
|
||||
ImpSym = Symtab->addImportData(ImpName, this);
|
||||
|
||||
if (Hdr->getType() == llvm::COFF::IMPORT_CONST)
|
||||
ConstSym =
|
||||
cast<DefinedImportData>(Symtab->addImportData(Name, this)->body());
|
||||
static_cast<void>(Symtab->addImportData(Name, this));
|
||||
|
||||
// If type is function, we need to create a thunk which jump to an
|
||||
// address pointed by the __imp_ symbol. (This allows you to call
|
||||
// DLL functions just like regular non-DLL functions.)
|
||||
if (Hdr->getType() != llvm::COFF::IMPORT_CODE)
|
||||
return;
|
||||
ThunkSym = cast<DefinedImportThunk>(
|
||||
Symtab->addImportThunk(Name, ImpSym, Hdr->Machine)->body());
|
||||
if (Hdr->getType() == llvm::COFF::IMPORT_CODE)
|
||||
ThunkSym = Symtab->addImportThunk(Name, ImpSym, Hdr->Machine);
|
||||
}
|
||||
|
||||
void BitcodeFile::parse() {
|
||||
Obj = check(lto::InputFile::create(MemoryBufferRef(
|
||||
MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
|
||||
std::vector<std::pair<Symbol *, bool>> Comdat(Obj->getComdatTable().size());
|
||||
for (size_t I = 0; I != Obj->getComdatTable().size(); ++I)
|
||||
Comdat[I] = Symtab->addComdat(this, Saver.save(Obj->getComdatTable()[I]));
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
|
||||
StringRef SymName = Saver.save(ObjSym.getName());
|
||||
int ComdatIndex = ObjSym.getComdatIndex();
|
||||
Symbol *Sym;
|
||||
if (ObjSym.isUndefined()) {
|
||||
Sym = Symtab->addUndefined(SymName, this, false);
|
||||
@ -361,13 +450,19 @@ void BitcodeFile::parse() {
|
||||
// Weak external.
|
||||
Sym = Symtab->addUndefined(SymName, this, true);
|
||||
std::string Fallback = ObjSym.getCOFFWeakExternalFallback();
|
||||
SymbolBody *Alias = Symtab->addUndefined(Saver.save(Fallback));
|
||||
checkAndSetWeakAlias(Symtab, this, Sym->body(), Alias);
|
||||
Symbol *Alias = Symtab->addUndefined(Saver.save(Fallback));
|
||||
checkAndSetWeakAlias(Symtab, this, Sym, Alias);
|
||||
} else if (ComdatIndex != -1) {
|
||||
if (SymName == Obj->getComdatTable()[ComdatIndex])
|
||||
Sym = Comdat[ComdatIndex].first;
|
||||
else if (Comdat[ComdatIndex].second)
|
||||
Sym = Symtab->addRegular(this, SymName);
|
||||
else
|
||||
Sym = Symtab->addUndefined(SymName, this, false);
|
||||
} else {
|
||||
bool IsCOMDAT = ObjSym.getComdatIndex() != -1;
|
||||
Sym = Symtab->addRegular(this, SymName, IsCOMDAT);
|
||||
Sym = Symtab->addRegular(this, SymName);
|
||||
}
|
||||
SymbolBodies.push_back(Sym->body());
|
||||
SymbolBodies.push_back(Sym);
|
||||
}
|
||||
Directives = Obj->getCOFFLinkerOpts();
|
||||
}
|
||||
@ -398,14 +493,13 @@ static StringRef getBasename(StringRef Path) {
|
||||
}
|
||||
|
||||
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
|
||||
std::string lld::toString(coff::InputFile *File) {
|
||||
std::string lld::toString(const coff::InputFile *File) {
|
||||
if (!File)
|
||||
return "(internal)";
|
||||
return "<internal>";
|
||||
if (File->ParentName.empty())
|
||||
return File->getName().lower();
|
||||
return File->getName();
|
||||
|
||||
std::string Res =
|
||||
(getBasename(File->ParentName) + "(" + getBasename(File->getName()) + ")")
|
||||
.str();
|
||||
return StringRef(Res).lower();
|
||||
return (getBasename(File->ParentName) + "(" + getBasename(File->getName()) +
|
||||
")")
|
||||
.str();
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
#define LLD_COFF_INPUT_FILES_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
@ -31,6 +31,8 @@ class DbiModuleDescriptorBuilder;
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *File);
|
||||
|
||||
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
using llvm::COFF::MachineTypes;
|
||||
using llvm::object::Archive;
|
||||
@ -45,8 +47,7 @@ class DefinedImportData;
|
||||
class DefinedImportThunk;
|
||||
class Lazy;
|
||||
class SectionChunk;
|
||||
struct Symbol;
|
||||
class SymbolBody;
|
||||
class Symbol;
|
||||
class Undefined;
|
||||
|
||||
// The root class of input files.
|
||||
@ -57,7 +58,7 @@ public:
|
||||
virtual ~InputFile() {}
|
||||
|
||||
// Returns the filename.
|
||||
StringRef getName() { return MB.getBufferIdentifier(); }
|
||||
StringRef getName() const { return MB.getBufferIdentifier(); }
|
||||
|
||||
// Reads a file (the constructor doesn't do that).
|
||||
virtual void parse() = 0;
|
||||
@ -101,32 +102,34 @@ private:
|
||||
};
|
||||
|
||||
// .obj or .o file. This may be a member of an archive file.
|
||||
class ObjectFile : public InputFile {
|
||||
class ObjFile : public InputFile {
|
||||
public:
|
||||
explicit ObjectFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
|
||||
explicit ObjFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
|
||||
static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
|
||||
void parse() override;
|
||||
MachineTypes getMachineType() override;
|
||||
std::vector<Chunk *> &getChunks() { return Chunks; }
|
||||
std::vector<SectionChunk *> &getDebugChunks() { return DebugChunks; }
|
||||
std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
|
||||
ArrayRef<Chunk *> getChunks() { return Chunks; }
|
||||
ArrayRef<SectionChunk *> getDebugChunks() { return DebugChunks; }
|
||||
ArrayRef<Symbol *> getSymbols() { return Symbols; }
|
||||
|
||||
// Returns a SymbolBody object for the SymbolIndex'th symbol in the
|
||||
// Returns a Symbol object for the SymbolIndex'th symbol in the
|
||||
// underlying object file.
|
||||
SymbolBody *getSymbolBody(uint32_t SymbolIndex) {
|
||||
return SparseSymbolBodies[SymbolIndex];
|
||||
Symbol *getSymbol(uint32_t SymbolIndex) {
|
||||
return Symbols[SymbolIndex];
|
||||
}
|
||||
|
||||
// Returns the underying COFF file.
|
||||
COFFObjectFile *getCOFFObj() { return COFFObj.get(); }
|
||||
|
||||
static std::vector<ObjFile *> Instances;
|
||||
|
||||
// True if this object file is compatible with SEH.
|
||||
// COFF-specific and x86-only.
|
||||
bool SEHCompat = false;
|
||||
|
||||
// The list of safe exception handlers listed in .sxdata section.
|
||||
// The symbol table indexes of the safe exception handlers.
|
||||
// COFF-specific and x86-only.
|
||||
std::set<SymbolBody *> SEHandlers;
|
||||
ArrayRef<llvm::support::ulittle32_t> SXData;
|
||||
|
||||
// Pointer to the PDB module descriptor builder. Various debug info records
|
||||
// will reference object files by "module index", which is here. Things like
|
||||
@ -137,13 +140,23 @@ public:
|
||||
private:
|
||||
void initializeChunks();
|
||||
void initializeSymbols();
|
||||
void initializeSEH();
|
||||
|
||||
SymbolBody *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst);
|
||||
SymbolBody *createUndefined(COFFSymbolRef Sym);
|
||||
SectionChunk *
|
||||
readSection(uint32_t SectionNumber,
|
||||
const llvm::object::coff_aux_section_definition *Def);
|
||||
|
||||
void readAssociativeDefinition(
|
||||
COFFSymbolRef COFFSym,
|
||||
const llvm::object::coff_aux_section_definition *Def);
|
||||
|
||||
llvm::Optional<Symbol *>
|
||||
createDefined(COFFSymbolRef Sym,
|
||||
std::vector<const llvm::object::coff_aux_section_definition *>
|
||||
&ComdatDefs);
|
||||
Symbol *createRegular(COFFSymbolRef Sym);
|
||||
Symbol *createUndefined(COFFSymbolRef Sym);
|
||||
|
||||
std::unique_ptr<COFFObjectFile> COFFObj;
|
||||
const coff_section *SXData = nullptr;
|
||||
|
||||
// List of all chunks defined by this file. This includes both section
|
||||
// chunks and non-section chunks for common symbols.
|
||||
@ -157,16 +170,13 @@ private:
|
||||
// Nonexistent section indices are filled with null pointers.
|
||||
// (Because section number is 1-based, the first slot is always a
|
||||
// null pointer.)
|
||||
std::vector<Chunk *> SparseChunks;
|
||||
std::vector<SectionChunk *> SparseChunks;
|
||||
|
||||
// List of all symbols referenced or defined by this file.
|
||||
std::vector<SymbolBody *> SymbolBodies;
|
||||
|
||||
// This vector contains the same symbols as SymbolBodies, but they
|
||||
// are indexed such that you can get a SymbolBody by symbol
|
||||
// This vector contains a list of all symbols defined or referenced by this
|
||||
// file. They are indexed such that you can get a Symbol by symbol
|
||||
// index. Nonexistent indices (which are occupied by auxiliary
|
||||
// symbols in the real symbol table) are filled with null pointers.
|
||||
std::vector<SymbolBody *> SparseSymbolBodies;
|
||||
std::vector<Symbol *> Symbols;
|
||||
};
|
||||
|
||||
// This type represents import library members that contain DLL names
|
||||
@ -179,8 +189,9 @@ public:
|
||||
|
||||
static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
|
||||
|
||||
static std::vector<ImportFile *> Instances;
|
||||
|
||||
DefinedImportData *ImpSym = nullptr;
|
||||
DefinedImportData *ConstSym = nullptr;
|
||||
DefinedImportThunk *ThunkSym = nullptr;
|
||||
std::string DLLName;
|
||||
|
||||
@ -206,18 +217,19 @@ class BitcodeFile : public InputFile {
|
||||
public:
|
||||
explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
|
||||
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
|
||||
std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
|
||||
ArrayRef<Symbol *> getSymbols() { return SymbolBodies; }
|
||||
MachineTypes getMachineType() override;
|
||||
static std::vector<BitcodeFile *> Instances;
|
||||
std::unique_ptr<llvm::lto::InputFile> Obj;
|
||||
|
||||
private:
|
||||
void parse() override;
|
||||
|
||||
std::vector<SymbolBody *> SymbolBodies;
|
||||
std::vector<Symbol *> SymbolBodies;
|
||||
};
|
||||
} // namespace coff
|
||||
|
||||
std::string toString(coff::InputFile *File);
|
||||
std::string toString(const coff::InputFile *File);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
|
59
COFF/LTO.cpp
59
COFF/LTO.cpp
@ -9,15 +9,16 @@
|
||||
|
||||
#include "LTO.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Core/TargetOptionsCommandFlags.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/TargetOptionsCommandFlags.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/IR/DiagnosticPrinter.h"
|
||||
#include "llvm/LTO/Caching.h"
|
||||
#include "llvm/LTO/Config.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/SymbolicFile.h"
|
||||
@ -48,10 +49,8 @@ static void diagnosticHandler(const DiagnosticInfo &DI) {
|
||||
}
|
||||
|
||||
static void checkError(Error E) {
|
||||
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
|
||||
error(EIB.message());
|
||||
return Error::success();
|
||||
});
|
||||
handleAllErrors(std::move(E),
|
||||
[&](ErrorInfoBase &EIB) { error(EIB.message()); });
|
||||
}
|
||||
|
||||
static void saveBuffer(StringRef Buffer, const Twine &Path) {
|
||||
@ -65,7 +64,13 @@ static void saveBuffer(StringRef Buffer, const Twine &Path) {
|
||||
static std::unique_ptr<lto::LTO> createLTO() {
|
||||
lto::Config Conf;
|
||||
Conf.Options = InitTargetOptionsFromCodeGenFlags();
|
||||
Conf.RelocModel = Reloc::PIC_;
|
||||
// Use static reloc model on 32-bit x86 because it usually results in more
|
||||
// compact code, and because there are also known code generation bugs when
|
||||
// using the PIC model (see PR34306).
|
||||
if (Config->Machine == COFF::IMAGE_FILE_MACHINE_I386)
|
||||
Conf.RelocModel = Reloc::Static;
|
||||
else
|
||||
Conf.RelocModel = Reloc::PIC_;
|
||||
Conf.DisableVerify = true;
|
||||
Conf.DiagHandler = diagnosticHandler;
|
||||
Conf.OptLevel = Config->LTOOptLevel;
|
||||
@ -83,20 +88,17 @@ BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
|
||||
|
||||
BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
|
||||
static void undefine(Symbol *S) {
|
||||
replaceBody<Undefined>(S, S->body()->getName());
|
||||
}
|
||||
static void undefine(Symbol *S) { replaceSymbol<Undefined>(S, S->getName()); }
|
||||
|
||||
void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
lto::InputFile &Obj = *F.Obj;
|
||||
unsigned SymNum = 0;
|
||||
std::vector<SymbolBody *> SymBodies = F.getSymbols();
|
||||
std::vector<Symbol *> SymBodies = F.getSymbols();
|
||||
std::vector<lto::SymbolResolution> Resols(SymBodies.size());
|
||||
|
||||
// Provide a resolution to the LTO API for each symbol.
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
|
||||
SymbolBody *B = SymBodies[SymNum];
|
||||
Symbol *Sym = B->symbol();
|
||||
Symbol *Sym = SymBodies[SymNum];
|
||||
lto::SymbolResolution &R = Resols[SymNum];
|
||||
++SymNum;
|
||||
|
||||
@ -105,7 +107,7 @@ void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
// flags an undefined in IR with a definition in ASM as prevailing.
|
||||
// Once IRObjectFile is fixed to report only one symbol this hack can
|
||||
// be removed.
|
||||
R.Prevailing = !ObjSym.isUndefined() && B->getFile() == &F;
|
||||
R.Prevailing = !ObjSym.isUndefined() && Sym->getFile() == &F;
|
||||
R.VisibleToRegularObj = Sym->IsUsedInRegularObj;
|
||||
if (R.Prevailing)
|
||||
undefine(Sym);
|
||||
@ -118,11 +120,27 @@ void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
std::vector<StringRef> BitcodeCompiler::compile() {
|
||||
unsigned MaxTasks = LTOObj->getMaxTasks();
|
||||
Buff.resize(MaxTasks);
|
||||
Files.resize(MaxTasks);
|
||||
|
||||
checkError(LTOObj->run([&](size_t Task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
|
||||
}));
|
||||
// The /lldltocache option specifies the path to a directory in which to cache
|
||||
// native object files for ThinLTO incremental builds. If a path was
|
||||
// specified, configure LTO to use it as the cache directory.
|
||||
lto::NativeObjectCache Cache;
|
||||
if (!Config->LTOCache.empty())
|
||||
Cache = check(
|
||||
lto::localCache(Config->LTOCache,
|
||||
[&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
|
||||
StringRef Path) { Files[Task] = std::move(MB); }));
|
||||
|
||||
checkError(LTOObj->run(
|
||||
[&](size_t Task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
|
||||
},
|
||||
Cache));
|
||||
|
||||
if (!Config->LTOCache.empty())
|
||||
pruneCache(Config->LTOCache, Config->LTOCachePolicy);
|
||||
|
||||
std::vector<StringRef> Ret;
|
||||
for (unsigned I = 0; I != MaxTasks; ++I) {
|
||||
@ -136,5 +154,10 @@ std::vector<StringRef> BitcodeCompiler::compile() {
|
||||
}
|
||||
Ret.emplace_back(Buff[I].data(), Buff[I].size());
|
||||
}
|
||||
|
||||
for (std::unique_ptr<MemoryBuffer> &File : Files)
|
||||
if (File)
|
||||
Ret.push_back(File->getBuffer());
|
||||
|
||||
return Ret;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
#ifndef LLD_COFF_LTO_H
|
||||
#define LLD_COFF_LTO_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -49,6 +49,7 @@ public:
|
||||
private:
|
||||
std::unique_ptr<llvm::lto::LTO> LTOObj;
|
||||
std::vector<SmallString<0>> Buff;
|
||||
std::vector<std::unique_ptr<MemoryBuffer>> Files;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,11 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MapFile.h"
|
||||
#include "Error.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Parallel.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
@ -48,9 +48,9 @@ static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
|
||||
// Returns a list of all symbols that we want to print out.
|
||||
static std::vector<DefinedRegular *> getSymbols() {
|
||||
std::vector<DefinedRegular *> V;
|
||||
for (coff::ObjectFile *File : Symtab->ObjectFiles)
|
||||
for (SymbolBody *B : File->getSymbols())
|
||||
if (auto *Sym = dyn_cast<DefinedRegular>(B))
|
||||
for (ObjFile *File : ObjFile::Instances)
|
||||
for (Symbol *B : File->getSymbols())
|
||||
if (auto *Sym = dyn_cast_or_null<DefinedRegular>(B))
|
||||
if (Sym && !Sym->getCOFFSymbol().isSectionDefinition())
|
||||
V.push_back(Sym);
|
||||
return V;
|
||||
@ -115,7 +115,7 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
|
||||
if (!SC)
|
||||
continue;
|
||||
|
||||
writeHeader(OS, SC->getRVA(), SC->getSize(), SC->getAlign());
|
||||
writeHeader(OS, SC->getRVA(), SC->getSize(), SC->Alignment);
|
||||
OS << indent(1) << SC->File->getName() << ":(" << SC->getSectionName()
|
||||
<< ")\n";
|
||||
for (DefinedRegular *Sym : SectionSyms[SC])
|
||||
|
@ -18,7 +18,7 @@ namespace coff {
|
||||
// Set live bit on for each reachable chunk. Unmarked (unreachable)
|
||||
// COMDAT chunks will be ignored by Writer, so they will be excluded
|
||||
// from the final output.
|
||||
void markLive(const std::vector<Chunk *> &Chunks) {
|
||||
void markLive(ArrayRef<Chunk *> Chunks) {
|
||||
// We build up a worklist of sections which have been marked as live. We only
|
||||
// push into the worklist when we discover an unmarked section, and we mark
|
||||
// as we push, so sections never appear twice in the list.
|
||||
@ -37,7 +37,7 @@ void markLive(const std::vector<Chunk *> &Chunks) {
|
||||
Worklist.push_back(C);
|
||||
};
|
||||
|
||||
auto AddSym = [&](SymbolBody *B) {
|
||||
auto AddSym = [&](Symbol *B) {
|
||||
if (auto *Sym = dyn_cast<DefinedRegular>(B))
|
||||
Enqueue(Sym->getChunk());
|
||||
else if (auto *Sym = dyn_cast<DefinedImportData>(B))
|
||||
@ -47,23 +47,17 @@ void markLive(const std::vector<Chunk *> &Chunks) {
|
||||
};
|
||||
|
||||
// Add GC root chunks.
|
||||
for (SymbolBody *B : Config->GCRoot)
|
||||
for (Symbol *B : Config->GCRoot)
|
||||
AddSym(B);
|
||||
|
||||
while (!Worklist.empty()) {
|
||||
SectionChunk *SC = Worklist.pop_back_val();
|
||||
|
||||
// If this section was discarded, there are relocations referring to
|
||||
// discarded sections. Ignore these sections to avoid crashing. They will be
|
||||
// diagnosed during relocation processing.
|
||||
if (SC->isDiscarded())
|
||||
continue;
|
||||
|
||||
assert(SC->isLive() && "We mark as live when pushing onto the worklist!");
|
||||
|
||||
// Mark all symbols listed in the relocation table for this section.
|
||||
for (SymbolBody *B : SC->symbols())
|
||||
AddSym(B);
|
||||
for (Symbol *B : SC->symbols())
|
||||
if (B)
|
||||
AddSym(B);
|
||||
|
||||
// Mark associative sections if any.
|
||||
for (SectionChunk *C : SC->children())
|
||||
|
@ -1,52 +0,0 @@
|
||||
//===- Memory.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// See ELF/Memory.h
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_MEMORY_H
|
||||
#define LLD_COFF_MEMORY_H
|
||||
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/StringSaver.h"
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
extern llvm::BumpPtrAllocator BAlloc;
|
||||
extern llvm::StringSaver Saver;
|
||||
|
||||
struct SpecificAllocBase {
|
||||
SpecificAllocBase() { Instances.push_back(this); }
|
||||
virtual ~SpecificAllocBase() = default;
|
||||
virtual void reset() = 0;
|
||||
static std::vector<SpecificAllocBase *> Instances;
|
||||
};
|
||||
|
||||
template <class T> struct SpecificAlloc : public SpecificAllocBase {
|
||||
void reset() override { Alloc.DestroyAll(); }
|
||||
llvm::SpecificBumpPtrAllocator<T> Alloc;
|
||||
};
|
||||
|
||||
template <typename T, typename... U> T *make(U &&... Args) {
|
||||
static SpecificAlloc<T> Alloc;
|
||||
return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
|
||||
}
|
||||
|
||||
inline void freeArena() {
|
||||
for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
|
||||
Alloc->reset();
|
||||
BAlloc.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
146
COFF/MinGW.cpp
Normal file
146
COFF/MinGW.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
//===- MinGW.cpp ----------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MinGW.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
using namespace llvm::COFF;
|
||||
|
||||
AutoExporter::AutoExporter() {
|
||||
if (Config->Machine == I386) {
|
||||
ExcludeSymbols = {
|
||||
"__NULL_IMPORT_DESCRIPTOR",
|
||||
"__pei386_runtime_relocator",
|
||||
"_do_pseudo_reloc",
|
||||
"_impure_ptr",
|
||||
"__impure_ptr",
|
||||
"__fmode",
|
||||
"_environ",
|
||||
"___dso_handle",
|
||||
// These are the MinGW names that differ from the standard
|
||||
// ones (lacking an extra underscore).
|
||||
"_DllMain@12",
|
||||
"_DllEntryPoint@12",
|
||||
"_DllMainCRTStartup@12",
|
||||
};
|
||||
} else {
|
||||
ExcludeSymbols = {
|
||||
"_NULL_IMPORT_DESCRIPTOR",
|
||||
"_pei386_runtime_relocator",
|
||||
"do_pseudo_reloc",
|
||||
"impure_ptr",
|
||||
"_impure_ptr",
|
||||
"_fmode",
|
||||
"environ",
|
||||
"__dso_handle",
|
||||
// These are the MinGW names that differ from the standard
|
||||
// ones (lacking an extra underscore).
|
||||
"DllMain",
|
||||
"DllEntryPoint",
|
||||
"DllMainCRTStartup",
|
||||
};
|
||||
}
|
||||
|
||||
ExcludeLibs = {
|
||||
"libgcc",
|
||||
"libgcc_s",
|
||||
"libstdc++",
|
||||
"libmingw32",
|
||||
"libmingwex",
|
||||
"libg2c",
|
||||
"libsupc++",
|
||||
"libobjc",
|
||||
"libgcj",
|
||||
"libclang_rt.builtins-aarch64",
|
||||
"libclang_rt.builtins-arm",
|
||||
"libclang_rt.builtins-i386",
|
||||
"libclang_rt.builtins-x86_64",
|
||||
"libc++",
|
||||
"libc++abi",
|
||||
"libunwind",
|
||||
"libmsvcrt",
|
||||
"libucrtbase",
|
||||
};
|
||||
ExcludeObjects = {
|
||||
"crt0.o",
|
||||
"crt1.o",
|
||||
"crt1u.o",
|
||||
"crt2.o",
|
||||
"crt2u.o",
|
||||
"dllcrt1.o",
|
||||
"dllcrt2.o",
|
||||
"gcrt0.o",
|
||||
"gcrt1.o",
|
||||
"gcrt2.o",
|
||||
"crtbegin.o",
|
||||
"crtend.o",
|
||||
};
|
||||
}
|
||||
|
||||
bool AutoExporter::shouldExport(Defined *Sym) const {
|
||||
if (!Sym || !Sym->isLive() || !Sym->getChunk())
|
||||
return false;
|
||||
|
||||
// Only allow the symbol kinds that make sense to export; in particular,
|
||||
// disallow import symbols.
|
||||
if (!isa<DefinedRegular>(Sym) && !isa<DefinedCommon>(Sym))
|
||||
return false;
|
||||
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;
|
||||
|
||||
// If a corresponding __imp_ symbol exists and is defined, don't export it.
|
||||
if (Symtab->find(("__imp_" + Sym->getName()).str()))
|
||||
return false;
|
||||
|
||||
// Check that file is non-null before dereferencing it, symbols not
|
||||
// originating in regular object files probably shouldn't be exported.
|
||||
if (!Sym->getFile())
|
||||
return false;
|
||||
|
||||
StringRef LibName = sys::path::filename(Sym->getFile()->ParentName);
|
||||
|
||||
// Drop the file extension.
|
||||
LibName = LibName.substr(0, LibName.rfind('.'));
|
||||
if (!LibName.empty())
|
||||
return !ExcludeLibs.count(LibName);
|
||||
|
||||
StringRef FileName = sys::path::filename(Sym->getFile()->getName());
|
||||
return !ExcludeObjects.count(FileName);
|
||||
}
|
||||
|
||||
void coff::writeDefFile(StringRef Name) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Name, EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
fatal("cannot open " + Name + ": " + EC.message());
|
||||
|
||||
OS << "EXPORTS\n";
|
||||
for (Export &E : Config->Exports) {
|
||||
OS << " " << E.ExportName << " "
|
||||
<< "@" << E.Ordinal;
|
||||
if (auto *Def = dyn_cast_or_null<Defined>(E.Sym)) {
|
||||
if (Def && Def->getChunk() &&
|
||||
!(Def->getChunk()->getPermissions() & IMAGE_SCN_MEM_EXECUTE))
|
||||
OS << " DATA";
|
||||
}
|
||||
OS << "\n";
|
||||
}
|
||||
}
|
38
COFF/MinGW.h
Normal file
38
COFF/MinGW.h
Normal file
@ -0,0 +1,38 @@
|
||||
//===- MinGW.h --------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_MINGW_H
|
||||
#define LLD_COFF_MINGW_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
// Logic for deciding what symbols to export, when exporting all
|
||||
// symbols for MinGW.
|
||||
class AutoExporter {
|
||||
public:
|
||||
AutoExporter();
|
||||
|
||||
llvm::StringSet<> ExcludeSymbols;
|
||||
llvm::StringSet<> ExcludeLibs;
|
||||
llvm::StringSet<> ExcludeObjects;
|
||||
|
||||
bool shouldExport(Defined *Sym) const;
|
||||
};
|
||||
|
||||
void writeDefFile(StringRef Name);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
@ -9,13 +9,15 @@ class F<string name> : Flag<["/", "-", "-?"], name>;
|
||||
class P<string name, string help> :
|
||||
Joined<["/", "-", "-?"], name#":">, HelpText<help>;
|
||||
|
||||
// Boolean flag suffixed by ":no".
|
||||
multiclass B<string name, string help> {
|
||||
def "" : F<name>;
|
||||
def _no : F<name#":no">, HelpText<help>;
|
||||
// Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the
|
||||
// flag on and using it suffixed by ":no" turns it off.
|
||||
multiclass B<string name, string help_on, string help_off> {
|
||||
def "" : F<name>, HelpText<help_on>;
|
||||
def _no : F<name#":no">, HelpText<help_off>;
|
||||
}
|
||||
|
||||
def align : P<"align", "Section alignment">;
|
||||
def aligncomm : P<"aligncomm", "Set common symbol alignment">;
|
||||
def alternatename : P<"alternatename", "Define weak alias">;
|
||||
def base : P<"base", "Base address of the program">;
|
||||
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
|
||||
@ -30,6 +32,8 @@ def heap : P<"heap", "Size of the heap">;
|
||||
def implib : P<"implib", "Import library name">;
|
||||
def libpath : P<"libpath", "Additional library search path">;
|
||||
def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
|
||||
def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">;
|
||||
def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">;
|
||||
def lldsavetemps : F<"lldsavetemps">,
|
||||
HelpText<"Save temporary files instead of deleting them">;
|
||||
def machine : P<"machine", "Specify target platform">;
|
||||
@ -44,6 +48,7 @@ def stack : P<"stack", "Size of the stack">;
|
||||
def stub : P<"stub", "Specify DOS stub file">;
|
||||
def subsystem : P<"subsystem", "Specify subsystem">;
|
||||
def version : P<"version", "Specify a version number in the PE header">;
|
||||
def wholearchive_file : P<"wholearchive", "Include all object files from this archive">;
|
||||
|
||||
def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
|
||||
|
||||
@ -75,31 +80,53 @@ def profile : F<"profile">;
|
||||
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 symbols when creating executables">;
|
||||
def force_unresolved : F<"force:unresolved">;
|
||||
defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">;
|
||||
|
||||
defm allowbind: B<"allowbind", "Disable DLL binding">;
|
||||
defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">;
|
||||
defm allowbind : B<"allowbind", "Enable DLL binding (default)",
|
||||
"Disable DLL binding">;
|
||||
defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)",
|
||||
"Disable DLL isolation">;
|
||||
defm appcontainer : B<"appcontainer",
|
||||
"Image can only be run in an app container">;
|
||||
defm dynamicbase : B<"dynamicbase",
|
||||
"Disable address space layout randomization">;
|
||||
defm fixed : B<"fixed", "Enable base relocations">;
|
||||
defm highentropyva : B<"highentropyva", "Set HIGH_ENTROPY_VA bit">;
|
||||
defm largeaddressaware : B<"largeaddressaware", "Disable large addresses">;
|
||||
defm nxcompat : B<"nxcompat", "Disable data execution provention">;
|
||||
defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler">;
|
||||
defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">;
|
||||
"Image can only be run in an app container",
|
||||
"Image can run outside an app container (default)">;
|
||||
defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
|
||||
"Disable ASLR (default when /fixed)">;
|
||||
defm fixed : B<"fixed", "Disable base relocations",
|
||||
"Enable base relocations (default)">;
|
||||
defm highentropyva : B<"highentropyva",
|
||||
"Enable 64-bit ASLR (default on 64-bit)",
|
||||
"Disable 64-bit ASLR">;
|
||||
defm largeaddressaware : B<"largeaddressaware",
|
||||
"Enable large addresses (default on 64-bit)",
|
||||
"Disable large addresses (default on 32-bit)">;
|
||||
defm nxcompat : B<"nxcompat", "Enable data execution prevention (default)",
|
||||
"Disable data execution provention">;
|
||||
defm safeseh : B<"safeseh",
|
||||
"Produce an image with Safe Exception Handler (only for x86)",
|
||||
"Don't produce an image with Safe Exception Handler">;
|
||||
defm tsaware : B<"tsaware",
|
||||
"Create Terminal Server aware executable (default)",
|
||||
"Create non-Terminal Server aware executable">;
|
||||
|
||||
def help : F<"help">;
|
||||
def help_q : Flag<["/?", "-?"], "">, Alias<help>;
|
||||
|
||||
// LLD extensions
|
||||
def nopdb : F<"nopdb">, HelpText<"Disable PDB generation for DWARF users">;
|
||||
def nosymtab : F<"nosymtab">;
|
||||
def debug_ghash : F<"debug:ghash">;
|
||||
def debug_dwarf : F<"debug:dwarf">;
|
||||
def export_all_symbols : F<"export-all-symbols">;
|
||||
def lldmingw : F<"lldmingw">;
|
||||
def msvclto : F<"msvclto">;
|
||||
def output_def : Joined<["/", "-"], "output-def:">;
|
||||
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
|
||||
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
|
||||
def dash_dash_version : Flag<["--"], "version">,
|
||||
HelpText<"Print version information">;
|
||||
|
||||
// Flags for debugging
|
||||
def lldmap : F<"lldmap">;
|
||||
@ -130,10 +157,9 @@ def errorreport : QF<"errorreport">;
|
||||
def idlout : QF<"idlout">;
|
||||
def ignore : QF<"ignore">;
|
||||
def maxilksize : QF<"maxilksize">;
|
||||
def natvis : QF<"natvis">;
|
||||
def pdbaltpath : QF<"pdbaltpath">;
|
||||
def tlbid : QF<"tlbid">;
|
||||
def tlbout : QF<"tlbout">;
|
||||
def verbose_all : QF<"verbose">;
|
||||
def guardsym : QF<"guardsym">;
|
||||
|
||||
defm wx : QB<"wx">;
|
||||
|
553
COFF/PDB.cpp
553
COFF/PDB.cpp
@ -10,37 +10,44 @@
|
||||
#include "PDB.h"
|
||||
#include "Chunks.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "Driver.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVDebugRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/RecordName.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
|
||||
#include "llvm/DebugInfo/MSF/MSFCommon.h"
|
||||
#include "llvm/DebugInfo/PDB/GenericError.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/PDB.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/JamCRC.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
#include <memory>
|
||||
@ -67,16 +74,16 @@ class PDBLinker {
|
||||
public:
|
||||
PDBLinker(SymbolTable *Symtab)
|
||||
: Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
|
||||
IDTable(Alloc) {}
|
||||
IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {}
|
||||
|
||||
/// Emit the basic PDB structure: initial streams, headers, etc.
|
||||
void initialize(const llvm::codeview::DebugInfo *DI);
|
||||
void initialize(const llvm::codeview::DebugInfo &BuildId);
|
||||
|
||||
/// Link CodeView from each object file in the symbol table into the PDB.
|
||||
void addObjectsToPDB();
|
||||
|
||||
/// Link CodeView from a single object file into the PDB.
|
||||
void addObjectFile(ObjectFile *File);
|
||||
void addObjFile(ObjFile *File);
|
||||
|
||||
/// Produce a mapping from the type and item indices used in the object
|
||||
/// file to those in the destination PDB.
|
||||
@ -89,13 +96,17 @@ public:
|
||||
/// If the object does not use a type server PDB (compiled with /Z7), we merge
|
||||
/// all the type and item records from the .debug$S stream and fill in the
|
||||
/// caller-provided ObjectIndexMap.
|
||||
const CVIndexMap &mergeDebugT(ObjectFile *File, CVIndexMap &ObjectIndexMap);
|
||||
const CVIndexMap &mergeDebugT(ObjFile *File, CVIndexMap &ObjectIndexMap);
|
||||
|
||||
const CVIndexMap &maybeMergeTypeServerPDB(ObjectFile *File,
|
||||
const CVIndexMap &maybeMergeTypeServerPDB(ObjFile *File,
|
||||
TypeServer2Record &TS);
|
||||
|
||||
/// Add the section map and section contributions to the PDB.
|
||||
void addSections(ArrayRef<uint8_t> SectionTable);
|
||||
void addSections(ArrayRef<OutputSection *> OutputSections,
|
||||
ArrayRef<uint8_t> SectionTable);
|
||||
|
||||
void addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule,
|
||||
OutputSection *OS, Chunk *C);
|
||||
|
||||
/// Write the PDB to disk.
|
||||
void commit();
|
||||
@ -108,10 +119,16 @@ private:
|
||||
pdb::PDBFileBuilder Builder;
|
||||
|
||||
/// Type records that will go into the PDB TPI stream.
|
||||
TypeTableBuilder TypeTable;
|
||||
MergingTypeTableBuilder TypeTable;
|
||||
|
||||
/// Item records that will go into the PDB IPI stream.
|
||||
TypeTableBuilder IDTable;
|
||||
MergingTypeTableBuilder IDTable;
|
||||
|
||||
/// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH)
|
||||
GlobalTypeTableBuilder GlobalTypeTable;
|
||||
|
||||
/// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH)
|
||||
GlobalTypeTableBuilder GlobalIDTable;
|
||||
|
||||
/// PDBs use a single global string table for filenames in the file checksum
|
||||
/// table.
|
||||
@ -126,15 +143,7 @@ private:
|
||||
};
|
||||
}
|
||||
|
||||
// Returns a list of all SectionChunks.
|
||||
static void addSectionContribs(SymbolTable *Symtab,
|
||||
pdb::DbiStreamBuilder &DbiBuilder) {
|
||||
for (Chunk *C : Symtab->getChunks())
|
||||
if (auto *SC = dyn_cast<SectionChunk>(C))
|
||||
DbiBuilder.addSectionContrib(SC->File->ModuleDBI, SC->Header);
|
||||
}
|
||||
|
||||
static SectionChunk *findByName(std::vector<SectionChunk *> &Sections,
|
||||
static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections,
|
||||
StringRef Name) {
|
||||
for (SectionChunk *C : Sections)
|
||||
if (C->getSectionName() == Name)
|
||||
@ -152,21 +161,58 @@ static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> Data,
|
||||
return Data.slice(4);
|
||||
}
|
||||
|
||||
static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) {
|
||||
static ArrayRef<uint8_t> getDebugSection(ObjFile *File, StringRef SecName) {
|
||||
if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName))
|
||||
return consumeDebugMagic(Sec->getContents(), SecName);
|
||||
return {};
|
||||
}
|
||||
|
||||
// A COFF .debug$H section is currently a clang extension. This function checks
|
||||
// if a .debug$H section is in a format that we expect / understand, so that we
|
||||
// can ignore any sections which are coincidentally also named .debug$H but do
|
||||
// not contain a format we recognize.
|
||||
static bool canUseDebugH(ArrayRef<uint8_t> DebugH) {
|
||||
if (DebugH.size() < sizeof(object::debug_h_header))
|
||||
return false;
|
||||
auto *Header =
|
||||
reinterpret_cast<const object::debug_h_header *>(DebugH.data());
|
||||
DebugH = DebugH.drop_front(sizeof(object::debug_h_header));
|
||||
return Header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
|
||||
Header->Version == 0 &&
|
||||
Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1) &&
|
||||
(DebugH.size() % 20 == 0);
|
||||
}
|
||||
|
||||
static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *File) {
|
||||
SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$H");
|
||||
if (!Sec)
|
||||
return llvm::None;
|
||||
ArrayRef<uint8_t> Contents = Sec->getContents();
|
||||
if (!canUseDebugH(Contents))
|
||||
return None;
|
||||
return Contents;
|
||||
}
|
||||
|
||||
static ArrayRef<GloballyHashedType>
|
||||
getHashesFromDebugH(ArrayRef<uint8_t> DebugH) {
|
||||
assert(canUseDebugH(DebugH));
|
||||
|
||||
DebugH = DebugH.drop_front(sizeof(object::debug_h_header));
|
||||
uint32_t Count = DebugH.size() / sizeof(GloballyHashedType);
|
||||
return {reinterpret_cast<const GloballyHashedType *>(DebugH.data()), Count};
|
||||
}
|
||||
|
||||
static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
|
||||
TypeTableBuilder &TypeTable) {
|
||||
TypeCollection &TypeTable) {
|
||||
// Start the TPI or IPI stream header.
|
||||
TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
|
||||
|
||||
// Flatten the in memory type table.
|
||||
TypeTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) {
|
||||
// FIXME: Hash types.
|
||||
TpiBuilder.addTypeRecord(Rec, None);
|
||||
// Flatten the in memory type table and hash each type.
|
||||
TypeTable.ForEachRecord([&](TypeIndex TI, const CVType &Type) {
|
||||
auto Hash = pdb::hashTypeRecord(Type);
|
||||
if (auto E = Hash.takeError())
|
||||
fatal("type hashing error");
|
||||
TpiBuilder.addTypeRecord(Type.RecordData, *Hash);
|
||||
});
|
||||
}
|
||||
|
||||
@ -180,11 +226,11 @@ maybeReadTypeServerRecord(CVTypeArray &Types) {
|
||||
return None;
|
||||
TypeServer2Record TS;
|
||||
if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(Type), TS))
|
||||
fatal(EC, "error reading type server record");
|
||||
fatal("error reading type server record: " + toString(std::move(EC)));
|
||||
return std::move(TS);
|
||||
}
|
||||
|
||||
const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
|
||||
const CVIndexMap &PDBLinker::mergeDebugT(ObjFile *File,
|
||||
CVIndexMap &ObjectIndexMap) {
|
||||
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
|
||||
if (Data.empty())
|
||||
@ -194,7 +240,7 @@ const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
|
||||
CVTypeArray Types;
|
||||
BinaryStreamReader Reader(Stream);
|
||||
if (auto EC = Reader.readArray(Types, Reader.getLength()))
|
||||
fatal(EC, "Reader::readArray failed");
|
||||
fatal("Reader::readArray failed: " + toString(std::move(EC)));
|
||||
|
||||
// Look through type servers. If we've already seen this type server, don't
|
||||
// merge any type information.
|
||||
@ -203,17 +249,41 @@ const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
|
||||
|
||||
// This is a /Z7 object. Fill in the temporary, caller-provided
|
||||
// ObjectIndexMap.
|
||||
if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable,
|
||||
ObjectIndexMap.TPIMap, Types))
|
||||
fatal(Err, "codeview::mergeTypeAndIdRecords failed");
|
||||
if (Config->DebugGHashes) {
|
||||
ArrayRef<GloballyHashedType> Hashes;
|
||||
std::vector<GloballyHashedType> OwnedHashes;
|
||||
if (Optional<ArrayRef<uint8_t>> DebugH = getDebugH(File))
|
||||
Hashes = getHashesFromDebugH(*DebugH);
|
||||
else {
|
||||
OwnedHashes = GloballyHashedType::hashTypes(Types);
|
||||
Hashes = OwnedHashes;
|
||||
}
|
||||
|
||||
if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable,
|
||||
ObjectIndexMap.TPIMap, Types, Hashes))
|
||||
fatal("codeview::mergeTypeAndIdRecords failed: " +
|
||||
toString(std::move(Err)));
|
||||
} else {
|
||||
if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable,
|
||||
ObjectIndexMap.TPIMap, Types))
|
||||
fatal("codeview::mergeTypeAndIdRecords failed: " +
|
||||
toString(std::move(Err)));
|
||||
}
|
||||
return ObjectIndexMap;
|
||||
}
|
||||
|
||||
static Expected<std::unique_ptr<pdb::NativeSession>>
|
||||
tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
|
||||
ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile(
|
||||
TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false);
|
||||
if (!MBOrErr)
|
||||
return errorCodeToError(MBOrErr.getError());
|
||||
|
||||
std::unique_ptr<pdb::IPDBSession> ThisSession;
|
||||
if (auto EC =
|
||||
pdb::loadDataForPDB(pdb::PDB_ReaderType::Native, TSPath, ThisSession))
|
||||
if (auto EC = pdb::NativeSession::createFromPdb(
|
||||
MemoryBuffer::getMemBuffer(Driver->takeBuffer(std::move(*MBOrErr)),
|
||||
/*RequiresNullTerminator=*/false),
|
||||
ThisSession))
|
||||
return std::move(EC);
|
||||
|
||||
std::unique_ptr<pdb::NativeSession> NS(
|
||||
@ -234,7 +304,7 @@ tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
|
||||
return std::move(NS);
|
||||
}
|
||||
|
||||
const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjectFile *File,
|
||||
const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
|
||||
TypeServer2Record &TS) {
|
||||
// First, check if we already loaded a PDB with this GUID. Return the type
|
||||
// index mapping if we have it.
|
||||
@ -260,23 +330,46 @@ const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjectFile *File,
|
||||
ExpectedSession = tryToLoadPDB(TS.getGuid(), Path);
|
||||
}
|
||||
if (auto E = ExpectedSession.takeError())
|
||||
fatal(E, "Type server PDB was not found");
|
||||
fatal("Type server PDB was not found: " + toString(std::move(E)));
|
||||
|
||||
// Merge TPI first, because the IPI stream will reference type indices.
|
||||
auto ExpectedTpi = (*ExpectedSession)->getPDBFile().getPDBTpiStream();
|
||||
if (auto E = ExpectedTpi.takeError())
|
||||
fatal(E, "Type server does not have TPI stream");
|
||||
if (auto Err = mergeTypeRecords(TypeTable, IndexMap.TPIMap,
|
||||
ExpectedTpi->typeArray()))
|
||||
fatal(Err, "codeview::mergeTypeRecords failed");
|
||||
|
||||
// Merge IPI.
|
||||
fatal("Type server does not have TPI stream: " + toString(std::move(E)));
|
||||
auto ExpectedIpi = (*ExpectedSession)->getPDBFile().getPDBIpiStream();
|
||||
if (auto E = ExpectedIpi.takeError())
|
||||
fatal(E, "Type server does not have TPI stream");
|
||||
if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap,
|
||||
ExpectedIpi->typeArray()))
|
||||
fatal(Err, "codeview::mergeIdRecords failed");
|
||||
fatal("Type server does not have TPI stream: " + toString(std::move(E)));
|
||||
|
||||
if (Config->DebugGHashes) {
|
||||
// PDBs do not actually store global hashes, so when merging a type server
|
||||
// PDB we have to synthesize global hashes. To do this, we first synthesize
|
||||
// global hashes for the TPI stream, since it is independent, then we
|
||||
// synthesize hashes for the IPI stream, using the hashes for the TPI stream
|
||||
// as inputs.
|
||||
auto TpiHashes = GloballyHashedType::hashTypes(ExpectedTpi->typeArray());
|
||||
auto IpiHashes =
|
||||
GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes);
|
||||
|
||||
// Merge TPI first, because the IPI stream will reference type indices.
|
||||
if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap,
|
||||
ExpectedTpi->typeArray(), TpiHashes))
|
||||
fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
|
||||
|
||||
// Merge IPI.
|
||||
if (auto Err =
|
||||
mergeIdRecords(GlobalIDTable, IndexMap.TPIMap, IndexMap.IPIMap,
|
||||
ExpectedIpi->typeArray(), IpiHashes))
|
||||
fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
|
||||
} else {
|
||||
// Merge TPI first, because the IPI stream will reference type indices.
|
||||
if (auto Err = mergeTypeRecords(TypeTable, IndexMap.TPIMap,
|
||||
ExpectedTpi->typeArray()))
|
||||
fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
|
||||
|
||||
// Merge IPI.
|
||||
if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap,
|
||||
ExpectedIpi->typeArray()))
|
||||
fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
|
||||
}
|
||||
|
||||
return IndexMap;
|
||||
}
|
||||
@ -290,7 +383,7 @@ static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void remapTypesInSymbolRecord(ObjectFile *File,
|
||||
static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
|
||||
MutableArrayRef<uint8_t> Contents,
|
||||
const CVIndexMap &IndexMap,
|
||||
ArrayRef<TiReference> TypeRefs) {
|
||||
@ -301,27 +394,73 @@ static void remapTypesInSymbolRecord(ObjectFile *File,
|
||||
|
||||
// This can be an item index or a type index. Choose the appropriate map.
|
||||
ArrayRef<TypeIndex> TypeOrItemMap = IndexMap.TPIMap;
|
||||
if (Ref.Kind == TiRefKind::IndexRef && IndexMap.IsTypeServerMap)
|
||||
bool IsItemIndex = Ref.Kind == TiRefKind::IndexRef;
|
||||
if (IsItemIndex && IndexMap.IsTypeServerMap)
|
||||
TypeOrItemMap = IndexMap.IPIMap;
|
||||
|
||||
MutableArrayRef<TypeIndex> TIs(
|
||||
reinterpret_cast<TypeIndex *>(Contents.data() + Ref.Offset), Ref.Count);
|
||||
for (TypeIndex &TI : TIs) {
|
||||
if (!remapTypeIndex(TI, TypeOrItemMap)) {
|
||||
log("ignoring symbol record of kind 0x" + utohexstr(SymKind) + " in " +
|
||||
File->getName() + " with bad " + (IsItemIndex ? "item" : "type") +
|
||||
" index 0x" + utohexstr(TI.getIndex()));
|
||||
TI = TypeIndex(SimpleTypeKind::NotTranslated);
|
||||
log("ignoring symbol record in " + File->getName() +
|
||||
" with bad type index 0x" + utohexstr(TI.getIndex()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// MSVC translates S_PROC_ID_END to S_END.
|
||||
uint16_t canonicalizeSymbolKind(SymbolKind Kind) {
|
||||
if (Kind == SymbolKind::S_PROC_ID_END)
|
||||
return SymbolKind::S_END;
|
||||
return Kind;
|
||||
static SymbolKind symbolKind(ArrayRef<uint8_t> RecordData) {
|
||||
const RecordPrefix *Prefix =
|
||||
reinterpret_cast<const RecordPrefix *>(RecordData.data());
|
||||
return static_cast<SymbolKind>(uint16_t(Prefix->RecordKind));
|
||||
}
|
||||
|
||||
/// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32
|
||||
static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData,
|
||||
TypeCollection &IDTable) {
|
||||
RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(RecordData.data());
|
||||
|
||||
SymbolKind Kind = symbolKind(RecordData);
|
||||
|
||||
if (Kind == SymbolKind::S_PROC_ID_END) {
|
||||
Prefix->RecordKind = SymbolKind::S_END;
|
||||
return;
|
||||
}
|
||||
|
||||
// In an object file, GPROC32_ID has an embedded reference which refers to the
|
||||
// single object file type index namespace. This has already been translated
|
||||
// to the PDB file's ID stream index space, but we need to convert this to a
|
||||
// symbol that refers to the type stream index space. So we remap again from
|
||||
// ID index space to type index space.
|
||||
if (Kind == SymbolKind::S_GPROC32_ID || Kind == SymbolKind::S_LPROC32_ID) {
|
||||
SmallVector<TiReference, 1> Refs;
|
||||
auto Content = RecordData.drop_front(sizeof(RecordPrefix));
|
||||
CVSymbol Sym(Kind, RecordData);
|
||||
discoverTypeIndicesInSymbol(Sym, Refs);
|
||||
assert(Refs.size() == 1);
|
||||
assert(Refs.front().Count == 1);
|
||||
|
||||
TypeIndex *TI =
|
||||
reinterpret_cast<TypeIndex *>(Content.data() + Refs[0].Offset);
|
||||
// `TI` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in
|
||||
// the IPI stream, whose `FunctionType` member refers to the TPI stream.
|
||||
// Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and
|
||||
// in both cases we just need the second type index.
|
||||
if (!TI->isSimple() && !TI->isNoneType()) {
|
||||
CVType FuncIdData = IDTable.getType(*TI);
|
||||
SmallVector<TypeIndex, 2> Indices;
|
||||
discoverTypeIndices(FuncIdData, Indices);
|
||||
assert(Indices.size() == 2);
|
||||
*TI = Indices[1];
|
||||
}
|
||||
|
||||
Kind = (Kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
|
||||
: SymbolKind::S_LPROC32;
|
||||
Prefix->RecordKind = uint16_t(Kind);
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned.
|
||||
@ -339,10 +478,8 @@ static MutableArrayRef<uint8_t> copySymbolForPdb(const CVSymbol &Sym,
|
||||
memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
|
||||
|
||||
// Update the record prefix length. It should point to the beginning of the
|
||||
// next record. MSVC does some canonicalization of the record kind, so we do
|
||||
// that as well.
|
||||
// next record.
|
||||
auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem);
|
||||
Prefix->RecordKind = canonicalizeSymbolKind(Sym.kind());
|
||||
Prefix->RecordLen = Size - 2;
|
||||
return NewData;
|
||||
}
|
||||
@ -402,7 +539,7 @@ static void scopeStackOpen(SmallVectorImpl<SymbolScope> &Stack,
|
||||
}
|
||||
|
||||
static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
|
||||
uint32_t CurOffset, ObjectFile *File) {
|
||||
uint32_t CurOffset, ObjFile *File) {
|
||||
if (Stack.empty()) {
|
||||
warn("symbol scopes are not balanced in " + File->getName());
|
||||
return;
|
||||
@ -411,8 +548,86 @@ static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
|
||||
S.OpeningRecord->PtrEnd = CurOffset;
|
||||
}
|
||||
|
||||
static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
|
||||
static bool symbolGoesInModuleStream(const CVSymbol &Sym) {
|
||||
switch (Sym.kind()) {
|
||||
case SymbolKind::S_GDATA32:
|
||||
case SymbolKind::S_CONSTANT:
|
||||
case SymbolKind::S_UDT:
|
||||
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
|
||||
// since they are synthesized by the linker in response to S_GPROC32 and
|
||||
// S_LPROC32, but if we do see them, don't put them in the module stream I
|
||||
// guess.
|
||||
case SymbolKind::S_PROCREF:
|
||||
case SymbolKind::S_LPROCREF:
|
||||
return false;
|
||||
// S_GDATA32 does not go in the module stream, but S_LDATA32 does.
|
||||
case SymbolKind::S_LDATA32:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool symbolGoesInGlobalsStream(const CVSymbol &Sym) {
|
||||
switch (Sym.kind()) {
|
||||
case SymbolKind::S_CONSTANT:
|
||||
case SymbolKind::S_GDATA32:
|
||||
// S_LDATA32 goes in both the module stream and the globals stream.
|
||||
case SymbolKind::S_LDATA32:
|
||||
case SymbolKind::S_GPROC32:
|
||||
case SymbolKind::S_LPROC32:
|
||||
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
|
||||
// since they are synthesized by the linker in response to S_GPROC32 and
|
||||
// S_LPROC32, but if we do see them, copy them straight through.
|
||||
case SymbolKind::S_PROCREF:
|
||||
case SymbolKind::S_LPROCREF:
|
||||
return true;
|
||||
// FIXME: For now, we drop all S_UDT symbols (i.e. they don't go in the
|
||||
// globals stream or the modules stream). These have special handling which
|
||||
// needs more investigation before we can get right, but by putting them all
|
||||
// into the globals stream WinDbg fails to display local variables of class
|
||||
// types saying that it cannot find the type Foo *. So as a stopgap just to
|
||||
// keep things working, we drop them.
|
||||
case SymbolKind::S_UDT:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File,
|
||||
const CVSymbol &Sym) {
|
||||
switch (Sym.kind()) {
|
||||
case SymbolKind::S_CONSTANT:
|
||||
case SymbolKind::S_UDT:
|
||||
case SymbolKind::S_GDATA32:
|
||||
case SymbolKind::S_LDATA32:
|
||||
case SymbolKind::S_PROCREF:
|
||||
case SymbolKind::S_LPROCREF:
|
||||
Builder.addGlobalSymbol(Sym);
|
||||
break;
|
||||
case SymbolKind::S_GPROC32:
|
||||
case SymbolKind::S_LPROC32: {
|
||||
SymbolRecordKind K = SymbolRecordKind::ProcRefSym;
|
||||
if (Sym.kind() == SymbolKind::S_LPROC32)
|
||||
K = SymbolRecordKind::LocalProcRef;
|
||||
ProcRefSym PS(K);
|
||||
PS.Module = static_cast<uint16_t>(File.ModuleDBI->getModuleIndex());
|
||||
// For some reason, MSVC seems to add one to this value.
|
||||
++PS.Module;
|
||||
PS.Name = getSymbolName(Sym);
|
||||
PS.SumName = 0;
|
||||
PS.SymOffset = File.ModuleDBI->getNextSymbolOffset();
|
||||
Builder.addGlobalSymbol(PS);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("Invalid symbol kind!");
|
||||
}
|
||||
}
|
||||
|
||||
static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File,
|
||||
pdb::GSIStreamBuilder &GsiBuilder,
|
||||
const CVIndexMap &IndexMap,
|
||||
TypeCollection &IDTable,
|
||||
BinaryStreamRef SymData) {
|
||||
// FIXME: Improve error recovery by warning and skipping records when
|
||||
// possible.
|
||||
@ -420,11 +635,11 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
|
||||
BinaryStreamReader Reader(SymData);
|
||||
ExitOnErr(Reader.readArray(Syms, Reader.getLength()));
|
||||
SmallVector<SymbolScope, 4> Scopes;
|
||||
for (const CVSymbol &Sym : Syms) {
|
||||
for (CVSymbol Sym : Syms) {
|
||||
// Discover type index references in the record. Skip it if we don't know
|
||||
// where they are.
|
||||
SmallVector<TiReference, 32> TypeRefs;
|
||||
if (!discoverTypeIndices(Sym, TypeRefs)) {
|
||||
if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) {
|
||||
log("ignoring unknown symbol record with kind 0x" + utohexstr(Sym.kind()));
|
||||
continue;
|
||||
}
|
||||
@ -435,17 +650,30 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
|
||||
// Re-map all the type index references.
|
||||
MutableArrayRef<uint8_t> Contents =
|
||||
NewData.drop_front(sizeof(RecordPrefix));
|
||||
remapTypesInSymbolRecord(File, Contents, IndexMap, TypeRefs);
|
||||
remapTypesInSymbolRecord(File, Sym.kind(), Contents, IndexMap, TypeRefs);
|
||||
|
||||
// An object file may have S_xxx_ID symbols, but these get converted to
|
||||
// "real" symbols in a PDB.
|
||||
translateIdSymbols(NewData, IDTable);
|
||||
|
||||
SymbolKind NewKind = symbolKind(NewData);
|
||||
|
||||
// Fill in "Parent" and "End" fields by maintaining a stack of scopes.
|
||||
CVSymbol NewSym(Sym.kind(), NewData);
|
||||
if (symbolOpensScope(Sym.kind()))
|
||||
CVSymbol NewSym(NewKind, NewData);
|
||||
if (symbolOpensScope(NewKind))
|
||||
scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(), NewSym);
|
||||
else if (symbolEndsScope(Sym.kind()))
|
||||
else if (symbolEndsScope(NewKind))
|
||||
scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File);
|
||||
|
||||
// Add the symbol to the globals stream if necessary. Do this before adding
|
||||
// the symbol to the module since we may need to get the next symbol offset,
|
||||
// and writing to the module's symbol stream will update that offset.
|
||||
if (symbolGoesInGlobalsStream(NewSym))
|
||||
addGlobalSymbol(GsiBuilder, *File, NewSym);
|
||||
|
||||
// Add the symbol to the module.
|
||||
File->ModuleDBI->addSymbol(NewSym);
|
||||
if (symbolGoesInModuleStream(NewSym))
|
||||
File->ModuleDBI->addSymbol(NewSym);
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,7 +688,7 @@ static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
|
||||
".debug$S");
|
||||
}
|
||||
|
||||
void PDBLinker::addObjectFile(ObjectFile *File) {
|
||||
void PDBLinker::addObjFile(ObjFile *File) {
|
||||
// Add a module descriptor for every object file. We need to put an absolute
|
||||
// path to the object into the PDB. If this is a plain object, we make its
|
||||
// path absolute. If it's an object in an archive, we make the archive path
|
||||
@ -511,7 +739,13 @@ void PDBLinker::addObjectFile(ObjectFile *File) {
|
||||
File->ModuleDBI->addDebugSubsection(SS);
|
||||
break;
|
||||
case DebugSubsectionKind::Symbols:
|
||||
mergeSymbolRecords(Alloc, File, IndexMap, SS.getRecordData());
|
||||
if (Config->DebugGHashes) {
|
||||
mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
|
||||
GlobalIDTable, SS.getRecordData());
|
||||
} else {
|
||||
mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
|
||||
IDTable, SS.getRecordData());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// FIXME: Process the rest of the subsections.
|
||||
@ -539,45 +773,88 @@ void PDBLinker::addObjectFile(ObjectFile *File) {
|
||||
}
|
||||
}
|
||||
|
||||
static PublicSym32 createPublic(Defined *Def) {
|
||||
PublicSym32 Pub(SymbolKind::S_PUB32);
|
||||
Pub.Name = Def->getName();
|
||||
if (auto *D = dyn_cast<DefinedCOFF>(Def)) {
|
||||
if (D->getCOFFSymbol().isFunctionDefinition())
|
||||
Pub.Flags = PublicSymFlags::Function;
|
||||
} else if (isa<DefinedImportThunk>(Def)) {
|
||||
Pub.Flags = PublicSymFlags::Function;
|
||||
}
|
||||
|
||||
OutputSection *OS = Def->getChunk()->getOutputSection();
|
||||
assert(OS && "all publics should be in final image");
|
||||
Pub.Offset = Def->getRVA() - OS->getRVA();
|
||||
Pub.Segment = OS->SectionIndex;
|
||||
return Pub;
|
||||
}
|
||||
|
||||
// Add all object files to the PDB. Merge .debug$T sections into IpiData and
|
||||
// TpiData.
|
||||
void PDBLinker::addObjectsToPDB() {
|
||||
for (ObjectFile *File : Symtab->ObjectFiles)
|
||||
addObjectFile(File);
|
||||
for (ObjFile *File : ObjFile::Instances)
|
||||
addObjFile(File);
|
||||
|
||||
Builder.getStringTableBuilder().setStrings(PDBStrTab);
|
||||
|
||||
// Construct TPI stream contents.
|
||||
addTypeInfo(Builder.getTpiBuilder(), TypeTable);
|
||||
// Construct TPI and IPI stream contents.
|
||||
if (Config->DebugGHashes) {
|
||||
addTypeInfo(Builder.getTpiBuilder(), GlobalTypeTable);
|
||||
addTypeInfo(Builder.getIpiBuilder(), GlobalIDTable);
|
||||
} else {
|
||||
addTypeInfo(Builder.getTpiBuilder(), TypeTable);
|
||||
addTypeInfo(Builder.getIpiBuilder(), IDTable);
|
||||
}
|
||||
|
||||
// Construct IPI stream contents.
|
||||
addTypeInfo(Builder.getIpiBuilder(), IDTable);
|
||||
// Compute the public and global symbols.
|
||||
auto &GsiBuilder = Builder.getGsiBuilder();
|
||||
std::vector<PublicSym32> Publics;
|
||||
Symtab->forEachSymbol([&Publics](Symbol *S) {
|
||||
// Only emit defined, live symbols that have a chunk.
|
||||
auto *Def = dyn_cast<Defined>(S);
|
||||
if (Def && Def->isLive() && Def->getChunk())
|
||||
Publics.push_back(createPublic(Def));
|
||||
});
|
||||
|
||||
// Add public and symbol records stream.
|
||||
|
||||
// For now we don't actually write any thing useful to the publics stream, but
|
||||
// the act of "getting" it also creates it lazily so that we write an empty
|
||||
// stream.
|
||||
(void)Builder.getPublicsBuilder();
|
||||
if (!Publics.empty()) {
|
||||
// Sort the public symbols and add them to the stream.
|
||||
std::sort(Publics.begin(), Publics.end(),
|
||||
[](const PublicSym32 &L, const PublicSym32 &R) {
|
||||
return L.Name < R.Name;
|
||||
});
|
||||
for (const PublicSym32 &Pub : Publics)
|
||||
GsiBuilder.addPublicSymbol(Pub);
|
||||
}
|
||||
}
|
||||
|
||||
static void addLinkerModuleSymbols(StringRef Path,
|
||||
pdb::DbiModuleDescriptorBuilder &Mod,
|
||||
BumpPtrAllocator &Allocator) {
|
||||
codeview::SymbolSerializer Serializer(Allocator, CodeViewContainer::Pdb);
|
||||
codeview::ObjNameSym ONS(SymbolRecordKind::ObjNameSym);
|
||||
codeview::Compile3Sym CS(SymbolRecordKind::Compile3Sym);
|
||||
codeview::EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym);
|
||||
static void addCommonLinkerModuleSymbols(StringRef Path,
|
||||
pdb::DbiModuleDescriptorBuilder &Mod,
|
||||
BumpPtrAllocator &Allocator) {
|
||||
ObjNameSym ONS(SymbolRecordKind::ObjNameSym);
|
||||
Compile3Sym CS(SymbolRecordKind::Compile3Sym);
|
||||
EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym);
|
||||
|
||||
ONS.Name = "* Linker *";
|
||||
ONS.Signature = 0;
|
||||
|
||||
CS.Machine = Config->is64() ? CPUType::X64 : CPUType::Intel80386;
|
||||
// Interestingly, if we set the string to 0.0.0.0, then when trying to view
|
||||
// local variables WinDbg emits an error that private symbols are not present.
|
||||
// By setting this to a valid MSVC linker version string, local variables are
|
||||
// displayed properly. As such, even though it is not representative of
|
||||
// LLVM's version information, we need this for compatibility.
|
||||
CS.Flags = CompileSym3Flags::None;
|
||||
CS.VersionBackendBuild = 0;
|
||||
CS.VersionBackendMajor = 0;
|
||||
CS.VersionBackendMinor = 0;
|
||||
CS.VersionBackendBuild = 25019;
|
||||
CS.VersionBackendMajor = 14;
|
||||
CS.VersionBackendMinor = 10;
|
||||
CS.VersionBackendQFE = 0;
|
||||
|
||||
// MSVC also sets the frontend to 0.0.0.0 since this is specifically for the
|
||||
// linker module (which is by definition a backend), so we don't need to do
|
||||
// anything here. Also, it seems we can use "LLVM Linker" for the linker name
|
||||
// without any problems. Only the backend version has to be hardcoded to a
|
||||
// magic number.
|
||||
CS.VersionFrontendBuild = 0;
|
||||
CS.VersionFrontendMajor = 0;
|
||||
CS.VersionFrontendMinor = 0;
|
||||
@ -592,7 +869,9 @@ static void addLinkerModuleSymbols(StringRef Path,
|
||||
sys::fs::current_path(cwd);
|
||||
EBS.Fields.push_back(cwd);
|
||||
EBS.Fields.push_back("exe");
|
||||
EBS.Fields.push_back(Config->Argv[0]);
|
||||
SmallString<64> exe = Config->Argv[0];
|
||||
llvm::sys::fs::make_absolute(exe);
|
||||
EBS.Fields.push_back(exe);
|
||||
EBS.Fields.push_back("pdb");
|
||||
EBS.Fields.push_back(Path);
|
||||
EBS.Fields.push_back("cmd");
|
||||
@ -605,17 +884,33 @@ static void addLinkerModuleSymbols(StringRef Path,
|
||||
EBS, Allocator, CodeViewContainer::Pdb));
|
||||
}
|
||||
|
||||
static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &Mod,
|
||||
OutputSection &OS,
|
||||
BumpPtrAllocator &Allocator) {
|
||||
SectionSym Sym(SymbolRecordKind::SectionSym);
|
||||
Sym.Alignment = 12; // 2^12 = 4KB
|
||||
Sym.Characteristics = OS.getCharacteristics();
|
||||
Sym.Length = OS.getVirtualSize();
|
||||
Sym.Name = OS.getName();
|
||||
Sym.Rva = OS.getRVA();
|
||||
Sym.SectionNumber = OS.SectionIndex;
|
||||
Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
|
||||
Sym, Allocator, CodeViewContainer::Pdb));
|
||||
}
|
||||
|
||||
// Creates a PDB file.
|
||||
void coff::createPDB(SymbolTable *Symtab, ArrayRef<uint8_t> SectionTable,
|
||||
const llvm::codeview::DebugInfo *DI) {
|
||||
void coff::createPDB(SymbolTable *Symtab,
|
||||
ArrayRef<OutputSection *> OutputSections,
|
||||
ArrayRef<uint8_t> SectionTable,
|
||||
const llvm::codeview::DebugInfo &BuildId) {
|
||||
PDBLinker PDB(Symtab);
|
||||
PDB.initialize(DI);
|
||||
PDB.initialize(BuildId);
|
||||
PDB.addObjectsToPDB();
|
||||
PDB.addSections(SectionTable);
|
||||
PDB.addSections(OutputSections, SectionTable);
|
||||
PDB.commit();
|
||||
}
|
||||
|
||||
void PDBLinker::initialize(const llvm::codeview::DebugInfo *DI) {
|
||||
void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) {
|
||||
ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
|
||||
|
||||
// Create streams in MSF for predefined streams, namely
|
||||
@ -625,25 +920,64 @@ void PDBLinker::initialize(const llvm::codeview::DebugInfo *DI) {
|
||||
|
||||
// Add an Info stream.
|
||||
auto &InfoBuilder = Builder.getInfoBuilder();
|
||||
InfoBuilder.setAge(DI ? DI->PDB70.Age : 0);
|
||||
InfoBuilder.setAge(BuildId.PDB70.Age);
|
||||
|
||||
GUID uuid{};
|
||||
if (DI)
|
||||
memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid));
|
||||
GUID uuid;
|
||||
memcpy(&uuid, &BuildId.PDB70.Signature, sizeof(uuid));
|
||||
InfoBuilder.setGuid(uuid);
|
||||
InfoBuilder.setSignature(time(nullptr));
|
||||
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
|
||||
|
||||
// Add an empty DBI stream.
|
||||
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
|
||||
DbiBuilder.setAge(BuildId.PDB70.Age);
|
||||
DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
|
||||
ExitOnErr(DbiBuilder.addDbgStream(pdb::DbgHeaderType::NewFPO, {}));
|
||||
}
|
||||
|
||||
void PDBLinker::addSections(ArrayRef<uint8_t> SectionTable) {
|
||||
// Add Section Contributions.
|
||||
void PDBLinker::addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule,
|
||||
OutputSection *OS, Chunk *C) {
|
||||
pdb::SectionContrib SC;
|
||||
memset(&SC, 0, sizeof(SC));
|
||||
SC.ISect = OS->SectionIndex;
|
||||
SC.Off = C->getRVA() - OS->getRVA();
|
||||
SC.Size = C->getSize();
|
||||
if (auto *SecChunk = dyn_cast<SectionChunk>(C)) {
|
||||
SC.Characteristics = SecChunk->Header->Characteristics;
|
||||
SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex();
|
||||
ArrayRef<uint8_t> Contents = SecChunk->getContents();
|
||||
JamCRC CRC(0);
|
||||
ArrayRef<char> CharContents = makeArrayRef(
|
||||
reinterpret_cast<const char *>(Contents.data()), Contents.size());
|
||||
CRC.update(CharContents);
|
||||
SC.DataCrc = CRC.getCRC();
|
||||
} else {
|
||||
SC.Characteristics = OS->getCharacteristics();
|
||||
// FIXME: When we start creating DBI for import libraries, use those here.
|
||||
SC.Imod = LinkerModule.getModuleIndex();
|
||||
}
|
||||
SC.RelocCrc = 0; // FIXME
|
||||
Builder.getDbiBuilder().addSectionContrib(SC);
|
||||
}
|
||||
|
||||
void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
|
||||
ArrayRef<uint8_t> SectionTable) {
|
||||
// It's not entirely clear what this is, but the * Linker * module uses it.
|
||||
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
|
||||
addSectionContribs(Symtab, DbiBuilder);
|
||||
NativePath = Config->PDBPath;
|
||||
sys::fs::make_absolute(NativePath);
|
||||
sys::path::native(NativePath, sys::path::Style::windows);
|
||||
uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath);
|
||||
auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
|
||||
LinkerModule.setPdbFilePathNI(PdbFilePathNI);
|
||||
addCommonLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
|
||||
|
||||
// Add section contributions. They must be ordered by ascending RVA.
|
||||
for (OutputSection *OS : OutputSections) {
|
||||
addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc);
|
||||
for (Chunk *C : OS->getChunks())
|
||||
addSectionContrib(LinkerModule, OS, C);
|
||||
}
|
||||
|
||||
// Add Section Map stream.
|
||||
ArrayRef<object::coff_section> Sections = {
|
||||
@ -652,15 +986,6 @@ void PDBLinker::addSections(ArrayRef<uint8_t> SectionTable) {
|
||||
SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections);
|
||||
DbiBuilder.setSectionMap(SectionMap);
|
||||
|
||||
// It's not entirely clear what this is, but the * Linker * module uses it.
|
||||
NativePath = Config->PDBPath;
|
||||
sys::fs::make_absolute(NativePath);
|
||||
sys::path::native(NativePath, sys::path::Style::windows);
|
||||
uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath);
|
||||
auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
|
||||
LinkerModule.setPdbFilePathNI(PdbFilePathNI);
|
||||
addLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
|
||||
|
||||
// Add COFF section header stream.
|
||||
ExitOnErr(
|
||||
DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable));
|
||||
|
@ -21,10 +21,13 @@ union DebugInfo;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
class OutputSection;
|
||||
class SymbolTable;
|
||||
|
||||
void createPDB(SymbolTable *Symtab, llvm::ArrayRef<uint8_t> SectionTable,
|
||||
const llvm::codeview::DebugInfo *DI);
|
||||
void createPDB(SymbolTable *Symtab,
|
||||
llvm::ArrayRef<OutputSection *> OutputSections,
|
||||
llvm::ArrayRef<uint8_t> SectionTable,
|
||||
const llvm::codeview::DebugInfo &BuildId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ using namespace lld;
|
||||
using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
|
||||
Optional<std::string> coff::demangle(StringRef S) {
|
||||
Optional<std::string> coff::demangleMSVC(StringRef S) {
|
||||
#if defined(_MSC_VER)
|
||||
// UnDecorateSymbolName is not thread-safe, so we need a mutex.
|
||||
static std::mutex Mu;
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
llvm::Optional<std::string> demangle(llvm::StringRef S);
|
||||
llvm::Optional<std::string> demangleMSVC(llvm::StringRef S);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,10 +10,10 @@
|
||||
#include "SymbolTable.h"
|
||||
#include "Config.h"
|
||||
#include "Driver.h"
|
||||
#include "Error.h"
|
||||
#include "LTO.h"
|
||||
#include "Memory.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@ -24,36 +24,6 @@ using namespace llvm;
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
enum SymbolPreference {
|
||||
SP_EXISTING = -1,
|
||||
SP_CONFLICT = 0,
|
||||
SP_NEW = 1,
|
||||
};
|
||||
|
||||
/// Checks if an existing symbol S should be kept or replaced by a new symbol.
|
||||
/// Returns SP_EXISTING when S should be kept, SP_NEW when the new symbol
|
||||
/// should be kept, and SP_CONFLICT if no valid resolution exists.
|
||||
static SymbolPreference compareDefined(Symbol *S, bool WasInserted,
|
||||
bool NewIsCOMDAT) {
|
||||
// If the symbol wasn't previously known, the new symbol wins by default.
|
||||
if (WasInserted || !isa<Defined>(S->body()))
|
||||
return SP_NEW;
|
||||
|
||||
// If the existing symbol is a DefinedRegular, both it and the new symbol
|
||||
// must be comdats. In that case, we have no reason to prefer one symbol
|
||||
// over the other, and we keep the existing one. If one of the symbols
|
||||
// is not a comdat, we report a conflict.
|
||||
if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
|
||||
if (NewIsCOMDAT && R->isCOMDAT())
|
||||
return SP_EXISTING;
|
||||
else
|
||||
return SP_CONFLICT;
|
||||
}
|
||||
|
||||
// Existing symbol is not a DefinedRegular; new symbol wins.
|
||||
return SP_NEW;
|
||||
}
|
||||
|
||||
SymbolTable *Symtab;
|
||||
|
||||
void SymbolTable::addFile(InputFile *File) {
|
||||
@ -68,12 +38,12 @@ void SymbolTable::addFile(InputFile *File) {
|
||||
" conflicts with " + machineToStr(Config->Machine));
|
||||
}
|
||||
|
||||
if (auto *F = dyn_cast<ObjectFile>(File)) {
|
||||
ObjectFiles.push_back(F);
|
||||
if (auto *F = dyn_cast<ObjFile>(File)) {
|
||||
ObjFile::Instances.push_back(F);
|
||||
} else if (auto *F = dyn_cast<BitcodeFile>(File)) {
|
||||
BitcodeFiles.push_back(F);
|
||||
BitcodeFile::Instances.push_back(F);
|
||||
} else if (auto *F = dyn_cast<ImportFile>(File)) {
|
||||
ImportFiles.push_back(F);
|
||||
ImportFile::Instances.push_back(F);
|
||||
}
|
||||
|
||||
StringRef S = File->getDirectives();
|
||||
@ -84,70 +54,92 @@ void SymbolTable::addFile(InputFile *File) {
|
||||
Driver->parseDirectives(S);
|
||||
}
|
||||
|
||||
static void errorOrWarn(const Twine &S) {
|
||||
if (Config->Force)
|
||||
warn(S);
|
||||
else
|
||||
error(S);
|
||||
}
|
||||
|
||||
void SymbolTable::reportRemainingUndefines() {
|
||||
SmallPtrSet<SymbolBody *, 8> Undefs;
|
||||
for (auto &I : Symtab) {
|
||||
SmallPtrSet<Symbol *, 8> Undefs;
|
||||
DenseMap<Symbol *, Symbol *> LocalImports;
|
||||
|
||||
for (auto &I : SymMap) {
|
||||
Symbol *Sym = I.second;
|
||||
auto *Undef = dyn_cast<Undefined>(Sym->body());
|
||||
auto *Undef = dyn_cast<Undefined>(Sym);
|
||||
if (!Undef)
|
||||
continue;
|
||||
if (!Sym->IsUsedInRegularObj)
|
||||
continue;
|
||||
|
||||
StringRef Name = Undef->getName();
|
||||
|
||||
// A weak alias may have been resolved, so check for that.
|
||||
if (Defined *D = Undef->getWeakAlias()) {
|
||||
// We resolve weak aliases by replacing the alias's SymbolBody with the
|
||||
// target's SymbolBody. This causes all SymbolBody pointers referring to
|
||||
// the old symbol to instead refer to the new symbol. However, we can't
|
||||
// just blindly copy sizeof(Symbol::Body) bytes from D to Sym->Body
|
||||
// because D may be an internal symbol, and internal symbols are stored as
|
||||
// "unparented" SymbolBodies. For that reason we need to check which type
|
||||
// of symbol we are dealing with and copy the correct number of bytes.
|
||||
// We want to replace Sym with D. However, we can't just blindly
|
||||
// copy sizeof(SymbolUnion) bytes from D to Sym because D may be an
|
||||
// internal symbol, and internal symbols are stored as "unparented"
|
||||
// Symbols. For that reason we need to check which type of symbol we
|
||||
// are dealing with and copy the correct number of bytes.
|
||||
if (isa<DefinedRegular>(D))
|
||||
memcpy(Sym->Body.buffer, D, sizeof(DefinedRegular));
|
||||
memcpy(Sym, D, sizeof(DefinedRegular));
|
||||
else if (isa<DefinedAbsolute>(D))
|
||||
memcpy(Sym->Body.buffer, D, sizeof(DefinedAbsolute));
|
||||
memcpy(Sym, D, sizeof(DefinedAbsolute));
|
||||
else
|
||||
// No other internal symbols are possible.
|
||||
Sym->Body = D->symbol()->Body;
|
||||
memcpy(Sym, D, sizeof(SymbolUnion));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we can resolve a symbol by removing __imp_ prefix, do that.
|
||||
// This odd rule is for compatibility with MSVC linker.
|
||||
if (Name.startswith("__imp_")) {
|
||||
Symbol *Imp = find(Name.substr(strlen("__imp_")));
|
||||
if (Imp && isa<Defined>(Imp->body())) {
|
||||
auto *D = cast<Defined>(Imp->body());
|
||||
replaceBody<DefinedLocalImport>(Sym, Name, D);
|
||||
LocalImportChunks.push_back(
|
||||
cast<DefinedLocalImport>(Sym->body())->getChunk());
|
||||
if (Imp && isa<Defined>(Imp)) {
|
||||
auto *D = cast<Defined>(Imp);
|
||||
replaceSymbol<DefinedLocalImport>(Sym, Name, D);
|
||||
LocalImportChunks.push_back(cast<DefinedLocalImport>(Sym)->getChunk());
|
||||
LocalImports[Sym] = D;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining undefined symbols are not fatal if /force is specified.
|
||||
// They are replaced with dummy defined symbols.
|
||||
if (Config->Force)
|
||||
replaceBody<DefinedAbsolute>(Sym, Name, 0);
|
||||
Undefs.insert(Sym->body());
|
||||
replaceSymbol<DefinedAbsolute>(Sym, Name, 0);
|
||||
Undefs.insert(Sym);
|
||||
}
|
||||
if (Undefs.empty())
|
||||
|
||||
if (Undefs.empty() && LocalImports.empty())
|
||||
return;
|
||||
for (SymbolBody *B : Config->GCRoot)
|
||||
|
||||
for (Symbol *B : Config->GCRoot) {
|
||||
if (Undefs.count(B))
|
||||
warn("<root>: undefined symbol: " + B->getName());
|
||||
for (ObjectFile *File : ObjectFiles)
|
||||
for (SymbolBody *Sym : File->getSymbols())
|
||||
errorOrWarn("<root>: undefined symbol: " + B->getName());
|
||||
if (Symbol *Imp = LocalImports.lookup(B))
|
||||
warn("<root>: locally defined symbol imported: " + Imp->getName() +
|
||||
" (defined in " + toString(Imp->getFile()) + ")");
|
||||
}
|
||||
|
||||
for (ObjFile *File : ObjFile::Instances) {
|
||||
for (Symbol *Sym : File->getSymbols()) {
|
||||
if (!Sym)
|
||||
continue;
|
||||
if (Undefs.count(Sym))
|
||||
warn(toString(File) + ": undefined symbol: " + Sym->getName());
|
||||
if (!Config->Force)
|
||||
fatal("link failed");
|
||||
errorOrWarn(toString(File) + ": undefined symbol: " + Sym->getName());
|
||||
if (Symbol *Imp = LocalImports.lookup(Sym))
|
||||
warn(toString(File) + ": locally defined symbol imported: " +
|
||||
Imp->getName() + " (defined in " + toString(Imp->getFile()) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
|
||||
Symbol *&Sym = Symtab[CachedHashStringRef(Name)];
|
||||
Symbol *&Sym = SymMap[CachedHashStringRef(Name)];
|
||||
if (Sym)
|
||||
return {Sym, false};
|
||||
Sym = make<Symbol>();
|
||||
Sym = (Symbol *)make<SymbolUnion>();
|
||||
Sym->IsUsedInRegularObj = false;
|
||||
Sym->PendingArchiveLoad = false;
|
||||
return {Sym, true};
|
||||
@ -160,11 +152,11 @@ Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F,
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
if (!F || !isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || (isa<Lazy>(S->body()) && IsWeakAlias)) {
|
||||
replaceBody<Undefined>(S, Name);
|
||||
if (WasInserted || (isa<Lazy>(S) && IsWeakAlias)) {
|
||||
replaceSymbol<Undefined>(S, Name);
|
||||
return S;
|
||||
}
|
||||
if (auto *L = dyn_cast<Lazy>(S->body())) {
|
||||
if (auto *L = dyn_cast<Lazy>(S)) {
|
||||
if (!S->PendingArchiveLoad) {
|
||||
S->PendingArchiveLoad = true;
|
||||
L->File->addMember(&L->Sym);
|
||||
@ -179,10 +171,10 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
if (WasInserted) {
|
||||
replaceBody<Lazy>(S, F, Sym);
|
||||
replaceSymbol<Lazy>(S, F, Sym);
|
||||
return;
|
||||
}
|
||||
auto *U = dyn_cast<Undefined>(S->body());
|
||||
auto *U = dyn_cast<Undefined>(S);
|
||||
if (!U || U->WeakAlias || S->PendingArchiveLoad)
|
||||
return;
|
||||
S->PendingArchiveLoad = true;
|
||||
@ -190,9 +182,8 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
|
||||
}
|
||||
|
||||
void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
|
||||
error("duplicate symbol: " + toString(*Existing->body()) + " in " +
|
||||
toString(Existing->body()->getFile()) + " and in " +
|
||||
(NewFile ? toString(NewFile) : "(internal)"));
|
||||
error("duplicate symbol: " + toString(*Existing) + " in " +
|
||||
toString(Existing->getFile()) + " and in " + toString(NewFile));
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
|
||||
@ -200,9 +191,9 @@ Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedAbsolute>(S, N, Sym);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
|
||||
replaceSymbol<DefinedAbsolute>(S, N, Sym);
|
||||
else if (!isa<DefinedCOFF>(S))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
}
|
||||
@ -212,9 +203,9 @@ Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedAbsolute>(S, N, VA);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
|
||||
replaceSymbol<DefinedAbsolute>(S, N, VA);
|
||||
else if (!isa<DefinedCOFF>(S))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
}
|
||||
@ -224,14 +215,14 @@ Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) {
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedSynthetic>(S, N, C);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
|
||||
replaceSymbol<DefinedSynthetic>(S, N, C);
|
||||
else if (!isa<DefinedCOFF>(S))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
Symbol *SymbolTable::addRegular(InputFile *F, StringRef N,
|
||||
const coff_symbol_generic *Sym,
|
||||
SectionChunk *C) {
|
||||
Symbol *S;
|
||||
@ -239,21 +230,32 @@ Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT);
|
||||
if (SP == SP_CONFLICT) {
|
||||
if (WasInserted || !isa<DefinedRegular>(S))
|
||||
replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ false,
|
||||
/*IsExternal*/ true, Sym, C);
|
||||
else
|
||||
reportDuplicate(S, F);
|
||||
} else if (SP == SP_NEW) {
|
||||
replaceBody<DefinedRegular>(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C);
|
||||
} else if (SP == SP_EXISTING && IsCOMDAT && C) {
|
||||
C->markDiscarded();
|
||||
// Discard associative chunks that we've parsed so far. No need to recurse
|
||||
// because an associative section cannot have children.
|
||||
for (SectionChunk *Child : C->children())
|
||||
Child->markDiscarded();
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
std::pair<Symbol *, bool>
|
||||
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;
|
||||
if (WasInserted || !isa<DefinedRegular>(S)) {
|
||||
replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ true,
|
||||
/*IsExternal*/ true, Sym, nullptr);
|
||||
return {S, true};
|
||||
}
|
||||
if (!cast<DefinedRegular>(S)->isCOMDAT())
|
||||
reportDuplicate(S, F);
|
||||
return {S, false};
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const coff_symbol_generic *Sym, CommonChunk *C) {
|
||||
Symbol *S;
|
||||
@ -261,51 +263,56 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || !isa<DefinedCOFF>(S->body()))
|
||||
replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
else if (auto *DC = dyn_cast<DefinedCommon>(S->body()))
|
||||
if (WasInserted || !isa<DefinedCOFF>(S))
|
||||
replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
else if (auto *DC = dyn_cast<DefinedCommon>(S))
|
||||
if (Size > DC->getSize())
|
||||
replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) {
|
||||
DefinedImportData *SymbolTable::addImportData(StringRef N, ImportFile *F) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedImportData>(S, N, F);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
|
||||
replaceSymbol<DefinedImportData>(S, N, F);
|
||||
return cast<DefinedImportData>(S);
|
||||
}
|
||||
|
||||
reportDuplicate(S, F);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
|
||||
uint16_t Machine) {
|
||||
DefinedImportThunk *SymbolTable::addImportThunk(StringRef Name,
|
||||
DefinedImportData *ID,
|
||||
uint16_t Machine) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedImportThunk>(S, Name, ID, Machine);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
|
||||
replaceSymbol<DefinedImportThunk>(S, Name, ID, Machine);
|
||||
return cast<DefinedImportThunk>(S);
|
||||
}
|
||||
|
||||
reportDuplicate(S, ID->File);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Chunk *> SymbolTable::getChunks() {
|
||||
std::vector<Chunk *> Res;
|
||||
for (ObjectFile *File : ObjectFiles) {
|
||||
std::vector<Chunk *> &V = File->getChunks();
|
||||
for (ObjFile *File : ObjFile::Instances) {
|
||||
ArrayRef<Chunk *> V = File->getChunks();
|
||||
Res.insert(Res.end(), V.begin(), V.end());
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::find(StringRef Name) {
|
||||
auto It = Symtab.find(CachedHashStringRef(Name));
|
||||
if (It == Symtab.end())
|
||||
auto It = SymMap.find(CachedHashStringRef(Name));
|
||||
if (It == SymMap.end())
|
||||
return nullptr;
|
||||
return It->second;
|
||||
}
|
||||
@ -317,7 +324,7 @@ Symbol *SymbolTable::findUnderscore(StringRef Name) {
|
||||
}
|
||||
|
||||
StringRef SymbolTable::findByPrefix(StringRef Prefix) {
|
||||
for (auto Pair : Symtab) {
|
||||
for (auto Pair : SymMap) {
|
||||
StringRef Name = Pair.first.val();
|
||||
if (Name.startswith(Prefix))
|
||||
return Name;
|
||||
@ -327,47 +334,57 @@ StringRef SymbolTable::findByPrefix(StringRef Prefix) {
|
||||
|
||||
StringRef SymbolTable::findMangle(StringRef Name) {
|
||||
if (Symbol *Sym = find(Name))
|
||||
if (!isa<Undefined>(Sym->body()))
|
||||
if (!isa<Undefined>(Sym))
|
||||
return Name;
|
||||
if (Config->Machine != I386)
|
||||
return findByPrefix(("?" + Name + "@@Y").str());
|
||||
if (!Name.startswith("_"))
|
||||
return "";
|
||||
// Search for x86 C function.
|
||||
// Search for x86 stdcall function.
|
||||
StringRef S = findByPrefix((Name + "@").str());
|
||||
if (!S.empty())
|
||||
return S;
|
||||
// Search for x86 fastcall function.
|
||||
S = findByPrefix(("@" + Name.substr(1) + "@").str());
|
||||
if (!S.empty())
|
||||
return S;
|
||||
// Search for x86 vectorcall function.
|
||||
S = findByPrefix((Name.substr(1) + "@@").str());
|
||||
if (!S.empty())
|
||||
return S;
|
||||
// Search for x86 C++ non-member function.
|
||||
return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
|
||||
}
|
||||
|
||||
void SymbolTable::mangleMaybe(SymbolBody *B) {
|
||||
void SymbolTable::mangleMaybe(Symbol *B) {
|
||||
auto *U = dyn_cast<Undefined>(B);
|
||||
if (!U || U->WeakAlias)
|
||||
return;
|
||||
StringRef Alias = findMangle(U->getName());
|
||||
if (!Alias.empty())
|
||||
if (!Alias.empty()) {
|
||||
log(U->getName() + " aliased to " + Alias);
|
||||
U->WeakAlias = addUndefined(Alias);
|
||||
}
|
||||
}
|
||||
|
||||
SymbolBody *SymbolTable::addUndefined(StringRef Name) {
|
||||
return addUndefined(Name, nullptr, false)->body();
|
||||
Symbol *SymbolTable::addUndefined(StringRef Name) {
|
||||
return addUndefined(Name, nullptr, false);
|
||||
}
|
||||
|
||||
std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
|
||||
LTO.reset(new BitcodeCompiler);
|
||||
for (BitcodeFile *F : BitcodeFiles)
|
||||
for (BitcodeFile *F : BitcodeFile::Instances)
|
||||
LTO->add(*F);
|
||||
return LTO->compile();
|
||||
}
|
||||
|
||||
void SymbolTable::addCombinedLTOObjects() {
|
||||
if (BitcodeFiles.empty())
|
||||
if (BitcodeFile::Instances.empty())
|
||||
return;
|
||||
for (StringRef Object : compileBitcodeFiles()) {
|
||||
auto *Obj = make<ObjectFile>(MemoryBufferRef(Object, "lto.tmp"));
|
||||
auto *Obj = make<ObjFile>(MemoryBufferRef(Object, "lto.tmp"));
|
||||
Obj->parse();
|
||||
ObjectFiles.push_back(Obj);
|
||||
ObjFile::Instances.push_back(Obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,8 +31,7 @@ class DefinedAbsolute;
|
||||
class DefinedRelative;
|
||||
class Lazy;
|
||||
class SectionChunk;
|
||||
class SymbolBody;
|
||||
struct Symbol;
|
||||
class Symbol;
|
||||
|
||||
// SymbolTable is a bucket of all known symbols, including defined,
|
||||
// undefined, or lazy symbols (the last one is symbols in archive
|
||||
@ -66,7 +65,7 @@ public:
|
||||
// mangled symbol. This function tries to find a mangled name
|
||||
// for U from the symbol table, and if found, set the symbol as
|
||||
// a weak alias for U.
|
||||
void mangleMaybe(SymbolBody *B);
|
||||
void mangleMaybe(Symbol *B);
|
||||
StringRef findMangle(StringRef Name);
|
||||
|
||||
// Build a set of COFF objects representing the combined contents of
|
||||
@ -75,15 +74,8 @@ public:
|
||||
void addCombinedLTOObjects();
|
||||
std::vector<StringRef> compileBitcodeFiles();
|
||||
|
||||
// The writer needs to handle DLL import libraries specially in
|
||||
// order to create the import descriptor table.
|
||||
std::vector<ImportFile *> ImportFiles;
|
||||
|
||||
// The writer needs to infer the machine type from the object files.
|
||||
std::vector<ObjectFile *> ObjectFiles;
|
||||
|
||||
// Creates an Undefined symbol for a given name.
|
||||
SymbolBody *addUndefined(StringRef Name);
|
||||
Symbol *addUndefined(StringRef Name);
|
||||
|
||||
Symbol *addSynthetic(StringRef N, Chunk *C);
|
||||
Symbol *addAbsolute(StringRef N, uint64_t VA);
|
||||
@ -91,28 +83,35 @@ public:
|
||||
Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
|
||||
void addLazy(ArchiveFile *F, const Archive::Symbol Sym);
|
||||
Symbol *addAbsolute(StringRef N, COFFSymbolRef S);
|
||||
Symbol *addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
Symbol *addRegular(InputFile *F, StringRef N,
|
||||
const llvm::object::coff_symbol_generic *S = nullptr,
|
||||
SectionChunk *C = nullptr);
|
||||
std::pair<Symbol *, bool>
|
||||
addComdat(InputFile *F, StringRef N,
|
||||
const llvm::object::coff_symbol_generic *S = nullptr);
|
||||
Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const llvm::object::coff_symbol_generic *S = nullptr,
|
||||
CommonChunk *C = nullptr);
|
||||
Symbol *addImportData(StringRef N, ImportFile *F);
|
||||
Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
|
||||
uint16_t Machine);
|
||||
DefinedImportData *addImportData(StringRef N, ImportFile *F);
|
||||
DefinedImportThunk *addImportThunk(StringRef Name, DefinedImportData *S,
|
||||
uint16_t Machine);
|
||||
|
||||
void reportDuplicate(Symbol *Existing, InputFile *NewFile);
|
||||
|
||||
// A list of chunks which to be added to .rdata.
|
||||
std::vector<Chunk *> LocalImportChunks;
|
||||
|
||||
// Iterates symbols in non-determinstic hash table order.
|
||||
template <typename T> void forEachSymbol(T Callback) {
|
||||
for (auto &Pair : SymMap)
|
||||
Callback(Pair.second);
|
||||
}
|
||||
|
||||
private:
|
||||
std::pair<Symbol *, bool> insert(StringRef Name);
|
||||
StringRef findByPrefix(StringRef Prefix);
|
||||
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
|
||||
|
||||
std::vector<BitcodeFile *> BitcodeFiles;
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap;
|
||||
std::unique_ptr<BitcodeCompiler> LTO;
|
||||
};
|
||||
|
||||
|
@ -8,10 +8,10 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Symbols.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Memory.h"
|
||||
#include "Strings.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@ -20,8 +20,8 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
// Returns a symbol name for an error message.
|
||||
std::string lld::toString(coff::SymbolBody &B) {
|
||||
if (Optional<std::string> S = coff::demangle(B.getName()))
|
||||
std::string lld::toString(coff::Symbol &B) {
|
||||
if (Optional<std::string> S = coff::demangleMSVC(B.getName()))
|
||||
return ("\"" + *S + "\" (" + B.getName() + ")").str();
|
||||
return B.getName();
|
||||
}
|
||||
@ -29,7 +29,7 @@ std::string lld::toString(coff::SymbolBody &B) {
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
StringRef SymbolBody::getName() {
|
||||
StringRef Symbol::getName() {
|
||||
// COFF symbol names are read lazily for a performance reason.
|
||||
// Non-external symbol names are never used by the linker except for logging
|
||||
// or debugging. Their internal references are resolved not by name but by
|
||||
@ -39,12 +39,12 @@ StringRef SymbolBody::getName() {
|
||||
// is a waste of time.
|
||||
if (Name.empty()) {
|
||||
auto *D = cast<DefinedCOFF>(this);
|
||||
cast<ObjectFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
|
||||
cast<ObjFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
|
||||
}
|
||||
return Name;
|
||||
}
|
||||
|
||||
InputFile *SymbolBody::getFile() {
|
||||
InputFile *Symbol::getFile() {
|
||||
if (auto *Sym = dyn_cast<DefinedCOFF>(this))
|
||||
return Sym->File;
|
||||
if (auto *Sym = dyn_cast<Lazy>(this))
|
||||
@ -52,9 +52,19 @@ InputFile *SymbolBody::getFile() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Symbol::isLive() const {
|
||||
if (auto *R = dyn_cast<DefinedRegular>(this))
|
||||
return R->getChunk()->isLive();
|
||||
if (auto *Imp = dyn_cast<DefinedImportData>(this))
|
||||
return Imp->File->Live;
|
||||
if (auto *Imp = dyn_cast<DefinedImportThunk>(this))
|
||||
return Imp->WrappedSym->File->Live;
|
||||
// Assume any other kind of symbol is live.
|
||||
return true;
|
||||
}
|
||||
|
||||
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
|
||||
size_t SymSize =
|
||||
cast<ObjectFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
|
||||
size_t SymSize = cast<ObjFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
|
||||
if (SymSize == sizeof(coff_symbol16))
|
||||
return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym));
|
||||
assert(SymSize == sizeof(coff_symbol32));
|
||||
@ -81,7 +91,7 @@ DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S,
|
||||
|
||||
Defined *Undefined::getWeakAlias() {
|
||||
// A weak alias may be a weak alias to another symbol, so check recursively.
|
||||
for (SymbolBody *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
|
||||
for (Symbol *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
|
||||
if (auto *D = dyn_cast<Defined>(A))
|
||||
return D;
|
||||
return nullptr;
|
||||
|
146
COFF/Symbols.h
146
COFF/Symbols.h
@ -12,8 +12,8 @@
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Config.h"
|
||||
#include "Memory.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
@ -31,12 +31,11 @@ using llvm::object::coff_symbol_generic;
|
||||
|
||||
class ArchiveFile;
|
||||
class InputFile;
|
||||
class ObjectFile;
|
||||
struct Symbol;
|
||||
class ObjFile;
|
||||
class SymbolTable;
|
||||
|
||||
// The base class for real symbol classes.
|
||||
class SymbolBody {
|
||||
class Symbol {
|
||||
public:
|
||||
enum Kind {
|
||||
// The order of these is significant. We start with the regular defined
|
||||
@ -70,16 +69,16 @@ public:
|
||||
// Returns the file from which this symbol was created.
|
||||
InputFile *getFile();
|
||||
|
||||
Symbol *symbol();
|
||||
const Symbol *symbol() const {
|
||||
return const_cast<SymbolBody *>(this)->symbol();
|
||||
}
|
||||
// Indicates that this symbol will be included in the final image. Only valid
|
||||
// after calling markLive.
|
||||
bool isLive() const;
|
||||
|
||||
protected:
|
||||
friend SymbolTable;
|
||||
explicit SymbolBody(Kind K, StringRef N = "")
|
||||
explicit Symbol(Kind K, StringRef N = "")
|
||||
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
|
||||
WrittenToSymtab(false), Name(N) {}
|
||||
WrittenToSymtab(false), PendingArchiveLoad(false), IsGCRoot(false),
|
||||
Name(N) {}
|
||||
|
||||
const unsigned SymbolKind : 8;
|
||||
unsigned IsExternal : 1;
|
||||
@ -92,19 +91,28 @@ public:
|
||||
// symbols from being written to the symbol table more than once.
|
||||
unsigned WrittenToSymtab : 1;
|
||||
|
||||
// True if this symbol was referenced by a regular (non-bitcode) object.
|
||||
unsigned IsUsedInRegularObj : 1;
|
||||
|
||||
// True if we've seen both a lazy and an undefined symbol with this symbol
|
||||
// name, which means that we have enqueued an archive member load and should
|
||||
// not load any more archive members to resolve the same symbol.
|
||||
unsigned PendingArchiveLoad : 1;
|
||||
|
||||
/// True if we've already added this symbol to the list of GC roots.
|
||||
unsigned IsGCRoot : 1;
|
||||
|
||||
protected:
|
||||
StringRef Name;
|
||||
};
|
||||
|
||||
// The base class for any defined symbols, including absolute symbols,
|
||||
// etc.
|
||||
class Defined : public SymbolBody {
|
||||
class Defined : public Symbol {
|
||||
public:
|
||||
Defined(Kind K, StringRef N) : SymbolBody(K, N) {}
|
||||
Defined(Kind K, StringRef N) : Symbol(K, N) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() <= LastDefinedKind;
|
||||
}
|
||||
static bool classof(const Symbol *S) { return S->kind() <= LastDefinedKind; }
|
||||
|
||||
// Returns the RVA (relative virtual address) of this symbol. The
|
||||
// writer sets and uses RVAs.
|
||||
@ -120,12 +128,13 @@ public:
|
||||
// loaded through that. For bitcode files, Sym is nullptr and the name is stored
|
||||
// as a StringRef.
|
||||
class DefinedCOFF : public Defined {
|
||||
friend SymbolBody;
|
||||
friend Symbol;
|
||||
|
||||
public:
|
||||
DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S)
|
||||
: Defined(K, N), File(F), Sym(S) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
static bool classof(const Symbol *S) {
|
||||
return S->kind() <= LastDefinedCOFFKind;
|
||||
}
|
||||
|
||||
@ -151,16 +160,15 @@ public:
|
||||
this->IsCOMDAT = IsCOMDAT;
|
||||
}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
static bool classof(const Symbol *S) {
|
||||
return S->kind() == DefinedRegularKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return (*Data)->getRVA() + Sym->Value; }
|
||||
bool isCOMDAT() { return IsCOMDAT; }
|
||||
SectionChunk *getChunk() { return *Data; }
|
||||
uint32_t getValue() { return Sym->Value; }
|
||||
uint64_t getRVA() const { return (*Data)->getRVA() + Sym->Value; }
|
||||
bool isCOMDAT() const { return IsCOMDAT; }
|
||||
SectionChunk *getChunk() const { return *Data; }
|
||||
uint32_t getValue() const { return Sym->Value; }
|
||||
|
||||
private:
|
||||
SectionChunk **Data;
|
||||
};
|
||||
|
||||
@ -173,12 +181,12 @@ public:
|
||||
this->IsExternal = true;
|
||||
}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
static bool classof(const Symbol *S) {
|
||||
return S->kind() == DefinedCommonKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return Data->getRVA(); }
|
||||
Chunk *getChunk() { return Data; }
|
||||
CommonChunk *getChunk() { return Data; }
|
||||
|
||||
private:
|
||||
friend SymbolTable;
|
||||
@ -198,7 +206,7 @@ public:
|
||||
DefinedAbsolute(StringRef N, uint64_t V)
|
||||
: Defined(DefinedAbsoluteKind, N), VA(V) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
static bool classof(const Symbol *S) {
|
||||
return S->kind() == DefinedAbsoluteKind;
|
||||
}
|
||||
|
||||
@ -222,7 +230,7 @@ public:
|
||||
explicit DefinedSynthetic(StringRef Name, Chunk *C)
|
||||
: Defined(DefinedSyntheticKind, Name), C(C) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
static bool classof(const Symbol *S) {
|
||||
return S->kind() == DefinedSyntheticKind;
|
||||
}
|
||||
|
||||
@ -240,12 +248,12 @@ private:
|
||||
// object file from an archive to replace itself with a defined
|
||||
// symbol. If the resolver finds both Undefined and Lazy for
|
||||
// the same name, it will ask the Lazy to load a file.
|
||||
class Lazy : public SymbolBody {
|
||||
class Lazy : public Symbol {
|
||||
public:
|
||||
Lazy(ArchiveFile *F, const Archive::Symbol S)
|
||||
: SymbolBody(LazyKind, S.getName()), File(F), Sym(S) {}
|
||||
: Symbol(LazyKind, S.getName()), File(F), Sym(S) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; }
|
||||
static bool classof(const Symbol *S) { return S->kind() == LazyKind; }
|
||||
|
||||
ArchiveFile *File;
|
||||
|
||||
@ -257,19 +265,17 @@ private:
|
||||
};
|
||||
|
||||
// Undefined symbols.
|
||||
class Undefined : public SymbolBody {
|
||||
class Undefined : public Symbol {
|
||||
public:
|
||||
explicit Undefined(StringRef N) : SymbolBody(UndefinedKind, N) {}
|
||||
explicit Undefined(StringRef N) : Symbol(UndefinedKind, N) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == UndefinedKind;
|
||||
}
|
||||
static bool classof(const Symbol *S) { return S->kind() == UndefinedKind; }
|
||||
|
||||
// An undefined symbol can have a fallback symbol which gives an
|
||||
// undefined symbol a second chance if it would remain undefined.
|
||||
// If it remains undefined, it'll be replaced with whatever the
|
||||
// Alias pointer points to.
|
||||
SymbolBody *WeakAlias = nullptr;
|
||||
Symbol *WeakAlias = nullptr;
|
||||
|
||||
// If this symbol is external weak, try to resolve it to a defined
|
||||
// symbol by searching the chain of fallback symbols. Returns the symbol if
|
||||
@ -289,7 +295,7 @@ public:
|
||||
: Defined(DefinedImportDataKind, N), File(F) {
|
||||
}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
static bool classof(const Symbol *S) {
|
||||
return S->kind() == DefinedImportDataKind;
|
||||
}
|
||||
|
||||
@ -313,7 +319,7 @@ class DefinedImportThunk : public Defined {
|
||||
public:
|
||||
DefinedImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine);
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
static bool classof(const Symbol *S) {
|
||||
return S->kind() == DefinedImportThunkKind;
|
||||
}
|
||||
|
||||
@ -336,7 +342,7 @@ public:
|
||||
DefinedLocalImport(StringRef N, Defined *S)
|
||||
: Defined(DefinedLocalImportKind, N), Data(make<LocalImportChunk>(S)) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
static bool classof(const Symbol *S) {
|
||||
return S->kind() == DefinedLocalImportKind;
|
||||
}
|
||||
|
||||
@ -393,51 +399,33 @@ inline Chunk *Defined::getChunk() {
|
||||
llvm_unreachable("unknown symbol kind");
|
||||
}
|
||||
|
||||
// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
|
||||
// always one Symbol for each symbol name. The resolver updates the SymbolBody
|
||||
// stored in the Body field of this object as it resolves symbols. Symbol also
|
||||
// holds computed properties of symbol names.
|
||||
struct Symbol {
|
||||
// True if this symbol was referenced by a regular (non-bitcode) object.
|
||||
unsigned IsUsedInRegularObj : 1;
|
||||
|
||||
// True if we've seen both a lazy and an undefined symbol with this symbol
|
||||
// name, which means that we have enqueued an archive member load and should
|
||||
// not load any more archive members to resolve the same symbol.
|
||||
unsigned PendingArchiveLoad : 1;
|
||||
|
||||
// This field is used to store the Symbol's SymbolBody. This instantiation of
|
||||
// AlignedCharArrayUnion gives us a struct with a char array field that is
|
||||
// large and aligned enough to store any derived class of SymbolBody.
|
||||
llvm::AlignedCharArrayUnion<
|
||||
DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedSynthetic, Lazy,
|
||||
Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport>
|
||||
Body;
|
||||
|
||||
SymbolBody *body() {
|
||||
return reinterpret_cast<SymbolBody *>(Body.buffer);
|
||||
}
|
||||
const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
|
||||
// A buffer class that is large enough to hold any Symbol-derived
|
||||
// object. We allocate memory using this class and instantiate a symbol
|
||||
// using the placement new.
|
||||
union SymbolUnion {
|
||||
alignas(DefinedRegular) char A[sizeof(DefinedRegular)];
|
||||
alignas(DefinedCommon) char B[sizeof(DefinedCommon)];
|
||||
alignas(DefinedAbsolute) char C[sizeof(DefinedAbsolute)];
|
||||
alignas(DefinedSynthetic) char D[sizeof(DefinedSynthetic)];
|
||||
alignas(Lazy) char E[sizeof(Lazy)];
|
||||
alignas(Undefined) char F[sizeof(Undefined)];
|
||||
alignas(DefinedImportData) char G[sizeof(DefinedImportData)];
|
||||
alignas(DefinedImportThunk) char H[sizeof(DefinedImportThunk)];
|
||||
alignas(DefinedLocalImport) char I[sizeof(DefinedLocalImport)];
|
||||
};
|
||||
|
||||
template <typename T, typename... ArgT>
|
||||
void replaceBody(Symbol *S, ArgT &&... Arg) {
|
||||
static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
|
||||
static_assert(alignof(T) <= alignof(decltype(S->Body)),
|
||||
"Body not aligned enough");
|
||||
assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
|
||||
"Not a SymbolBody");
|
||||
new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
|
||||
}
|
||||
|
||||
inline Symbol *SymbolBody::symbol() {
|
||||
assert(isExternal());
|
||||
return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
|
||||
offsetof(Symbol, Body));
|
||||
void replaceSymbol(Symbol *S, ArgT &&... Arg) {
|
||||
static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small");
|
||||
static_assert(alignof(T) <= alignof(SymbolUnion),
|
||||
"SymbolUnion not aligned enough");
|
||||
assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
|
||||
"Not a Symbol");
|
||||
new (S) T(std::forward<ArgT>(Arg)...);
|
||||
}
|
||||
} // namespace coff
|
||||
|
||||
std::string toString(coff::SymbolBody &B);
|
||||
std::string toString(coff::Symbol &B);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
|
331
COFF/Writer.cpp
331
COFF/Writer.cpp
@ -10,22 +10,22 @@
|
||||
#include "Writer.h"
|
||||
#include "Config.h"
|
||||
#include "DLL.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "MapFile.h"
|
||||
#include "Memory.h"
|
||||
#include "PDB.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/Support/BinaryStreamReader.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/Parallel.h"
|
||||
#include "llvm/Support/RandomNumberGenerator.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
@ -65,8 +65,9 @@ public:
|
||||
D->Type = COFF::IMAGE_DEBUG_TYPE_CODEVIEW;
|
||||
D->SizeOfData = Record->getSize();
|
||||
D->AddressOfRawData = Record->getRVA();
|
||||
// TODO(compnerd) get the file offset
|
||||
D->PointerToRawData = 0;
|
||||
OutputSection *OS = Record->getOutputSection();
|
||||
uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA());
|
||||
D->PointerToRawData = Offs;
|
||||
|
||||
++D;
|
||||
}
|
||||
@ -77,32 +78,37 @@ private:
|
||||
};
|
||||
|
||||
class CVDebugRecordChunk : public Chunk {
|
||||
public:
|
||||
CVDebugRecordChunk() {
|
||||
PDBAbsPath = Config->PDBPath;
|
||||
if (!PDBAbsPath.empty())
|
||||
llvm::sys::fs::make_absolute(PDBAbsPath);
|
||||
}
|
||||
|
||||
size_t getSize() const override {
|
||||
return sizeof(codeview::DebugInfo) + Config->PDBPath.size() + 1;
|
||||
return sizeof(codeview::DebugInfo) + PDBAbsPath.size() + 1;
|
||||
}
|
||||
|
||||
void writeTo(uint8_t *B) const override {
|
||||
// Save off the DebugInfo entry to backfill the file signature (build id)
|
||||
// in Writer::writeBuildId
|
||||
DI = reinterpret_cast<codeview::DebugInfo *>(B + OutputSectionOff);
|
||||
|
||||
DI->Signature.CVSignature = OMF::Signature::PDB70;
|
||||
BuildId = reinterpret_cast<codeview::DebugInfo *>(B + OutputSectionOff);
|
||||
|
||||
// variable sized field (PDB Path)
|
||||
auto *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*DI));
|
||||
if (!Config->PDBPath.empty())
|
||||
memcpy(P, Config->PDBPath.data(), Config->PDBPath.size());
|
||||
P[Config->PDBPath.size()] = '\0';
|
||||
char *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*BuildId));
|
||||
if (!PDBAbsPath.empty())
|
||||
memcpy(P, PDBAbsPath.data(), PDBAbsPath.size());
|
||||
P[PDBAbsPath.size()] = '\0';
|
||||
}
|
||||
|
||||
public:
|
||||
mutable codeview::DebugInfo *DI = nullptr;
|
||||
SmallString<128> PDBAbsPath;
|
||||
mutable codeview::DebugInfo *BuildId = nullptr;
|
||||
};
|
||||
|
||||
// The writer writes a SymbolTable result to a file.
|
||||
class Writer {
|
||||
public:
|
||||
Writer(SymbolTable *T) : Symtab(T) {}
|
||||
Writer() : Buffer(errorHandler().OutputBuffer) {}
|
||||
void run();
|
||||
|
||||
private:
|
||||
@ -115,11 +121,11 @@ private:
|
||||
void createSymbolAndStringTable();
|
||||
void openFile(StringRef OutputPath);
|
||||
template <typename PEHeaderTy> void writeHeader();
|
||||
void fixSafeSEHSymbols();
|
||||
void createSEHTable(OutputSection *RData);
|
||||
void setSectionPermissions();
|
||||
void writeSections();
|
||||
void sortExceptionTable();
|
||||
void writeBuildId();
|
||||
void sortExceptionTable();
|
||||
|
||||
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
|
||||
size_t addEntryToStringTable(StringRef Str);
|
||||
@ -132,8 +138,7 @@ private:
|
||||
uint32_t getSizeOfInitializedData();
|
||||
std::map<StringRef, std::vector<DefinedImportData *>> binImports();
|
||||
|
||||
SymbolTable *Symtab;
|
||||
std::unique_ptr<FileOutputBuffer> Buffer;
|
||||
std::unique_ptr<FileOutputBuffer> &Buffer;
|
||||
std::vector<OutputSection *> OutputSections;
|
||||
std::vector<char> Strtab;
|
||||
std::vector<llvm::object::coff_symbol16> OutputSymtab;
|
||||
@ -145,6 +150,7 @@ private:
|
||||
Chunk *DebugDirectory = nullptr;
|
||||
std::vector<Chunk *> DebugRecords;
|
||||
CVDebugRecordChunk *BuildId = nullptr;
|
||||
Optional<codeview::DebugInfo> PreviousBuildId;
|
||||
ArrayRef<uint8_t> SectionTable;
|
||||
|
||||
uint64_t FileSize;
|
||||
@ -157,7 +163,7 @@ private:
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
void writeResult(SymbolTable *T) { Writer(T).run(); }
|
||||
void writeResult() { Writer().run(); }
|
||||
|
||||
void OutputSection::setRVA(uint64_t RVA) {
|
||||
Header.VirtualAddress = RVA;
|
||||
@ -178,10 +184,12 @@ void OutputSection::addChunk(Chunk *C) {
|
||||
Chunks.push_back(C);
|
||||
C->setOutputSection(this);
|
||||
uint64_t Off = Header.VirtualSize;
|
||||
Off = alignTo(Off, C->getAlign());
|
||||
Off = alignTo(Off, C->Alignment);
|
||||
C->setRVA(Off);
|
||||
C->OutputSectionOff = Off;
|
||||
Off += C->getSize();
|
||||
if (Off > UINT32_MAX)
|
||||
error("section larger than 4 GiB: " + Name);
|
||||
Header.VirtualSize = Off;
|
||||
if (C->hasData())
|
||||
Header.SizeOfRawData = alignTo(Off, SectorSize);
|
||||
@ -203,7 +211,8 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
|
||||
// If name is too long, write offset into the string table as a name.
|
||||
sprintf(Hdr->Name, "/%d", StringTableOff);
|
||||
} else {
|
||||
assert(!Config->Debug || Name.size() <= COFF::NameSize);
|
||||
assert(!Config->Debug || Name.size() <= COFF::NameSize ||
|
||||
(Hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0);
|
||||
strncpy(Hdr->Name, Name.data(),
|
||||
std::min(Name.size(), (size_t)COFF::NameSize));
|
||||
}
|
||||
@ -212,6 +221,67 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
// PDBs are matched against executables using a build id which consists of three
|
||||
// components:
|
||||
// 1. A 16-bit GUID
|
||||
// 2. An age
|
||||
// 3. A time stamp.
|
||||
//
|
||||
// Debuggers and symbol servers match executables against debug info by checking
|
||||
// each of these components of the EXE/DLL against the corresponding value in
|
||||
// the PDB and failing a match if any of the components differ. In the case of
|
||||
// symbol servers, symbols are cached in a folder that is a function of the
|
||||
// GUID. As a result, in order to avoid symbol cache pollution where every
|
||||
// incremental build copies a new PDB to the symbol cache, we must try to re-use
|
||||
// the existing GUID if one exists, but bump the age. This way the match will
|
||||
// fail, so the symbol cache knows to use the new PDB, but the GUID matches, so
|
||||
// it overwrites the existing item in the symbol cache rather than making a new
|
||||
// one.
|
||||
static Optional<codeview::DebugInfo> loadExistingBuildId(StringRef Path) {
|
||||
// We don't need to incrementally update a previous build id if we're not
|
||||
// writing codeview debug info.
|
||||
if (!Config->Debug)
|
||||
return None;
|
||||
|
||||
auto ExpectedBinary = llvm::object::createBinary(Path);
|
||||
if (!ExpectedBinary) {
|
||||
consumeError(ExpectedBinary.takeError());
|
||||
return None;
|
||||
}
|
||||
|
||||
auto Binary = std::move(*ExpectedBinary);
|
||||
if (!Binary.getBinary()->isCOFF())
|
||||
return None;
|
||||
|
||||
std::error_code EC;
|
||||
COFFObjectFile File(Binary.getBinary()->getMemoryBufferRef(), EC);
|
||||
if (EC)
|
||||
return None;
|
||||
|
||||
// If the machine of the binary we're outputting doesn't match the machine
|
||||
// of the existing binary, don't try to re-use the build id.
|
||||
if (File.is64() != Config->is64() || File.getMachine() != Config->Machine)
|
||||
return None;
|
||||
|
||||
for (const auto &DebugDir : File.debug_directories()) {
|
||||
if (DebugDir.Type != IMAGE_DEBUG_TYPE_CODEVIEW)
|
||||
continue;
|
||||
|
||||
const codeview::DebugInfo *ExistingDI = nullptr;
|
||||
StringRef PDBFileName;
|
||||
if (auto EC = File.getDebugPDBInfo(ExistingDI, PDBFileName)) {
|
||||
(void)EC;
|
||||
return None;
|
||||
}
|
||||
// We only support writing PDBs in v70 format. So if this is not a build
|
||||
// id that we recognize / support, ignore it.
|
||||
if (ExistingDI->Signature.CVSignature != OMF::Signature::PDB70)
|
||||
return None;
|
||||
return *ExistingDI;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// The main function of the writer.
|
||||
void Writer::run() {
|
||||
createSections();
|
||||
@ -224,32 +294,39 @@ void Writer::run() {
|
||||
removeEmptySections();
|
||||
setSectionPermissions();
|
||||
createSymbolAndStringTable();
|
||||
|
||||
// We must do this before opening the output file, as it depends on being able
|
||||
// to read the contents of the existing output file.
|
||||
PreviousBuildId = loadExistingBuildId(Config->OutputFile);
|
||||
openFile(Config->OutputFile);
|
||||
if (Config->is64()) {
|
||||
writeHeader<pe32plus_header>();
|
||||
} else {
|
||||
writeHeader<pe32_header>();
|
||||
}
|
||||
fixSafeSEHSymbols();
|
||||
writeSections();
|
||||
sortExceptionTable();
|
||||
writeBuildId();
|
||||
|
||||
if (!Config->PDBPath.empty() && Config->Debug) {
|
||||
const llvm::codeview::DebugInfo *DI = nullptr;
|
||||
if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV))
|
||||
DI = BuildId->DI;
|
||||
createPDB(Symtab, SectionTable, DI);
|
||||
|
||||
assert(BuildId);
|
||||
createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId);
|
||||
}
|
||||
|
||||
writeMapFile(OutputSections);
|
||||
|
||||
if (auto EC = Buffer->commit())
|
||||
fatal(EC, "failed to write the output file");
|
||||
if (auto E = Buffer->commit())
|
||||
fatal("failed to write the output file: " + toString(std::move(E)));
|
||||
}
|
||||
|
||||
static StringRef getOutputSection(StringRef Name) {
|
||||
StringRef S = Name.split('$').first;
|
||||
|
||||
// Treat a later period as a separator for MinGW, for sections like
|
||||
// ".ctors.01234".
|
||||
S = S.substr(0, S.find('.', 1));
|
||||
|
||||
auto It = Config->Merge.find(S);
|
||||
if (It == Config->Merge.end())
|
||||
return S;
|
||||
@ -303,41 +380,20 @@ void Writer::createMiscChunks() {
|
||||
if (Config->Debug) {
|
||||
DebugDirectory = make<DebugDirectoryChunk>(DebugRecords);
|
||||
|
||||
// TODO(compnerd) create a coffgrp entry if DebugType::CV is not enabled
|
||||
if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV)) {
|
||||
auto *Chunk = make<CVDebugRecordChunk>();
|
||||
|
||||
BuildId = Chunk;
|
||||
DebugRecords.push_back(Chunk);
|
||||
}
|
||||
// Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We
|
||||
// output a PDB no matter what, and this chunk provides the only means of
|
||||
// allowing a debugger to match a PDB and an executable. So we need it even
|
||||
// if we're ultimately not going to write CodeView data to the PDB.
|
||||
auto *CVChunk = make<CVDebugRecordChunk>();
|
||||
BuildId = CVChunk;
|
||||
DebugRecords.push_back(CVChunk);
|
||||
|
||||
RData->addChunk(DebugDirectory);
|
||||
for (Chunk *C : DebugRecords)
|
||||
RData->addChunk(C);
|
||||
}
|
||||
|
||||
// Create SEH table. x86-only.
|
||||
if (Config->Machine != I386)
|
||||
return;
|
||||
|
||||
std::set<Defined *> Handlers;
|
||||
|
||||
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
|
||||
if (!File->SEHCompat)
|
||||
return;
|
||||
for (SymbolBody *B : File->SEHandlers) {
|
||||
// Make sure the handler is still live. Assume all handlers are regular
|
||||
// symbols.
|
||||
auto *D = dyn_cast<DefinedRegular>(B);
|
||||
if (D && D->getChunk()->isLive())
|
||||
Handlers.insert(D);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Handlers.empty()) {
|
||||
SEHTable = make<SEHTableChunk>(Handlers);
|
||||
RData->addChunk(SEHTable);
|
||||
}
|
||||
createSEHTable(RData);
|
||||
}
|
||||
|
||||
// Create .idata section for the DLL-imported symbol table.
|
||||
@ -345,13 +401,13 @@ void Writer::createMiscChunks() {
|
||||
// IdataContents class abstracted away the details for us,
|
||||
// so we just let it create chunks and add them to the section.
|
||||
void Writer::createImportTables() {
|
||||
if (Symtab->ImportFiles.empty())
|
||||
if (ImportFile::Instances.empty())
|
||||
return;
|
||||
|
||||
// Initialize DLLOrder so that import entries are ordered in
|
||||
// the same order as in the command line. (That affects DLL
|
||||
// initialization order, and this ordering is MSVC-compatible.)
|
||||
for (ImportFile *File : Symtab->ImportFiles) {
|
||||
for (ImportFile *File : ImportFile::Instances) {
|
||||
if (!File->Live)
|
||||
continue;
|
||||
|
||||
@ -361,7 +417,7 @@ void Writer::createImportTables() {
|
||||
}
|
||||
|
||||
OutputSection *Text = createSection(".text");
|
||||
for (ImportFile *File : Symtab->ImportFiles) {
|
||||
for (ImportFile *File : ImportFile::Instances) {
|
||||
if (!File->Live)
|
||||
continue;
|
||||
|
||||
@ -432,19 +488,12 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
|
||||
if (isa<DefinedSynthetic>(Def))
|
||||
return None;
|
||||
|
||||
if (auto *D = dyn_cast<DefinedRegular>(Def)) {
|
||||
// Don't write dead symbols or symbols in codeview sections to the symbol
|
||||
// table.
|
||||
if (!D->getChunk()->isLive() || D->getChunk()->isCodeView())
|
||||
return None;
|
||||
}
|
||||
|
||||
if (auto *Sym = dyn_cast<DefinedImportData>(Def))
|
||||
if (!Sym->File->Live)
|
||||
return None;
|
||||
|
||||
if (auto *Sym = dyn_cast<DefinedImportThunk>(Def))
|
||||
if (!Sym->WrappedSym->File->Live)
|
||||
// Don't write dead symbols or symbols in codeview sections to the symbol
|
||||
// table.
|
||||
if (!Def->isLive())
|
||||
return None;
|
||||
if (auto *D = dyn_cast<DefinedRegular>(Def))
|
||||
if (D->getChunk()->isCodeView())
|
||||
return None;
|
||||
|
||||
coff_symbol16 Sym;
|
||||
@ -468,7 +517,7 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
|
||||
Sym.NumberOfAuxSymbols = 0;
|
||||
|
||||
switch (Def->kind()) {
|
||||
case SymbolBody::DefinedAbsoluteKind:
|
||||
case Symbol::DefinedAbsoluteKind:
|
||||
Sym.Value = Def->getRVA();
|
||||
Sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
|
||||
break;
|
||||
@ -489,40 +538,46 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
|
||||
}
|
||||
|
||||
void Writer::createSymbolAndStringTable() {
|
||||
if (!Config->Debug || !Config->WriteSymtab)
|
||||
return;
|
||||
|
||||
// Name field in the section table is 8 byte long. Longer names need
|
||||
// to be written to the string table. First, construct string table.
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
StringRef Name = Sec->getName();
|
||||
if (Name.size() <= COFF::NameSize)
|
||||
continue;
|
||||
// If a section isn't discardable (i.e. will be mapped at runtime),
|
||||
// prefer a truncated section name over a long section name in
|
||||
// the string table that is unavailable at runtime. This is different from
|
||||
// what link.exe does, but finding ".eh_fram" instead of "/4" is useful
|
||||
// to libunwind.
|
||||
if ((Sec->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0)
|
||||
continue;
|
||||
Sec->setStringTableOff(addEntryToStringTable(Name));
|
||||
}
|
||||
|
||||
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
|
||||
for (SymbolBody *B : File->getSymbols()) {
|
||||
auto *D = dyn_cast<Defined>(B);
|
||||
if (!D || D->WrittenToSymtab)
|
||||
continue;
|
||||
D->WrittenToSymtab = true;
|
||||
if (Config->DebugDwarf) {
|
||||
for (ObjFile *File : ObjFile::Instances) {
|
||||
for (Symbol *B : File->getSymbols()) {
|
||||
auto *D = dyn_cast_or_null<Defined>(B);
|
||||
if (!D || D->WrittenToSymtab)
|
||||
continue;
|
||||
D->WrittenToSymtab = true;
|
||||
|
||||
if (Optional<coff_symbol16> Sym = createSymbol(D))
|
||||
OutputSymtab.push_back(*Sym);
|
||||
if (Optional<coff_symbol16> Sym = createSymbol(D))
|
||||
OutputSymtab.push_back(*Sym);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OutputSymtab.empty() && Strtab.empty())
|
||||
return;
|
||||
|
||||
OutputSection *LastSection = OutputSections.back();
|
||||
// We position the symbol table to be adjacent to the end of the last section.
|
||||
uint64_t FileOff = LastSection->getFileOff() +
|
||||
alignTo(LastSection->getRawSize(), SectorSize);
|
||||
if (!OutputSymtab.empty()) {
|
||||
PointerToSymbolTable = FileOff;
|
||||
FileOff += OutputSymtab.size() * sizeof(coff_symbol16);
|
||||
}
|
||||
if (!Strtab.empty())
|
||||
FileOff += Strtab.size() + 4;
|
||||
PointerToSymbolTable = FileOff;
|
||||
FileOff += OutputSymtab.size() * sizeof(coff_symbol16);
|
||||
FileOff += 4 + Strtab.size();
|
||||
FileSize = alignTo(FileOff, SectorSize);
|
||||
}
|
||||
|
||||
@ -551,7 +606,7 @@ void Writer::assignAddresses() {
|
||||
RVA += alignTo(Sec->getVirtualSize(), PageSize);
|
||||
FileSize += alignTo(Sec->getRawSize(), SectorSize);
|
||||
}
|
||||
SizeOfImage = SizeOfHeaders + alignTo(RVA - 0x1000, PageSize);
|
||||
SizeOfImage = alignTo(RVA, PageSize);
|
||||
}
|
||||
|
||||
template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
@ -621,23 +676,21 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
PE->SizeOfStackCommit = Config->StackCommit;
|
||||
PE->SizeOfHeapReserve = Config->HeapReserve;
|
||||
PE->SizeOfHeapCommit = Config->HeapCommit;
|
||||
|
||||
// Import Descriptor Tables and Import Address Tables are merged
|
||||
// in our output. That's not compatible with the Binding feature
|
||||
// that is sort of prelinking. Setting this flag to make it clear
|
||||
// that our outputs are not for the Binding.
|
||||
PE->DLLCharacteristics = IMAGE_DLL_CHARACTERISTICS_NO_BIND;
|
||||
|
||||
if (Config->AppContainer)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER;
|
||||
if (Config->DynamicBase)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
|
||||
if (Config->HighEntropyVA)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;
|
||||
if (!Config->AllowBind)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND;
|
||||
if (Config->NxCompat)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
|
||||
if (!Config->AllowIsolation)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
|
||||
if (Config->Machine == I386 && !SEHTable &&
|
||||
!Symtab->findUnderscore("_load_config_used"))
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;
|
||||
if (Config->TerminalServerAware)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
|
||||
PE->NumberOfRvaAndSize = NumberfOfDataDirectory;
|
||||
@ -673,7 +726,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
|
||||
}
|
||||
if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) {
|
||||
if (Defined *B = dyn_cast<Defined>(Sym->body())) {
|
||||
if (Defined *B = dyn_cast<Defined>(Sym)) {
|
||||
Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA();
|
||||
Dir[TLS_TABLE].Size = Config->is64()
|
||||
? sizeof(object::coff_tls_directory64)
|
||||
@ -685,7 +738,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize();
|
||||
}
|
||||
if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) {
|
||||
if (auto *B = dyn_cast<DefinedRegular>(Sym->body())) {
|
||||
if (auto *B = dyn_cast<DefinedRegular>(Sym)) {
|
||||
SectionChunk *SC = B->getChunk();
|
||||
assert(B->getRVA() >= SC->getRVA());
|
||||
uint64_t OffsetInChunk = B->getRVA() - SC->getRVA();
|
||||
@ -715,7 +768,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
SectionTable = ArrayRef<uint8_t>(
|
||||
Buf - OutputSections.size() * sizeof(coff_section), Buf);
|
||||
|
||||
if (OutputSymtab.empty())
|
||||
if (OutputSymtab.empty() && Strtab.empty())
|
||||
return;
|
||||
|
||||
COFF->PointerToSymbolTable = PointerToSymbolTable;
|
||||
@ -734,21 +787,40 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
}
|
||||
|
||||
void Writer::openFile(StringRef Path) {
|
||||
Buffer = check(
|
||||
Buffer = CHECK(
|
||||
FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable),
|
||||
"failed to open " + Path);
|
||||
}
|
||||
|
||||
void Writer::fixSafeSEHSymbols() {
|
||||
if (!SEHTable)
|
||||
void Writer::createSEHTable(OutputSection *RData) {
|
||||
// Create SEH table. x86-only.
|
||||
if (Config->Machine != I386)
|
||||
return;
|
||||
|
||||
std::set<Defined *> Handlers;
|
||||
|
||||
for (ObjFile *File : ObjFile::Instances) {
|
||||
if (!File->SEHCompat)
|
||||
return;
|
||||
for (uint32_t I : File->SXData)
|
||||
if (Symbol *B = File->getSymbol(I))
|
||||
if (B->isLive())
|
||||
Handlers.insert(cast<Defined>(B));
|
||||
}
|
||||
|
||||
if (Handlers.empty())
|
||||
return;
|
||||
|
||||
SEHTable = make<SEHTableChunk>(Handlers);
|
||||
RData->addChunk(SEHTable);
|
||||
|
||||
// Replace the absolute table symbol with a synthetic symbol pointing to the
|
||||
// SEHTable chunk so that we can emit base relocations for it and resolve
|
||||
// section relative relocations.
|
||||
Symbol *T = Symtab->find("___safe_se_handler_table");
|
||||
Symbol *C = Symtab->find("___safe_se_handler_count");
|
||||
replaceBody<DefinedSynthetic>(T, T->body()->getName(), SEHTable);
|
||||
cast<DefinedAbsolute>(C->body())->setVA(SEHTable->getSize() / 4);
|
||||
replaceSymbol<DefinedSynthetic>(T, T->getName(), SEHTable);
|
||||
cast<DefinedAbsolute>(C)->setVA(SEHTable->getSize() / 4);
|
||||
}
|
||||
|
||||
// Handles /section options to allow users to overwrite
|
||||
@ -781,6 +853,25 @@ void Writer::writeSections() {
|
||||
}
|
||||
}
|
||||
|
||||
void Writer::writeBuildId() {
|
||||
// If we're not writing a build id (e.g. because /debug is not specified),
|
||||
// then just return;
|
||||
if (!Config->Debug)
|
||||
return;
|
||||
|
||||
assert(BuildId && "BuildId is not set!");
|
||||
|
||||
if (PreviousBuildId.hasValue()) {
|
||||
*BuildId->BuildId = *PreviousBuildId;
|
||||
BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
|
||||
BuildId->BuildId->PDB70.Age = 1;
|
||||
llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
|
||||
}
|
||||
|
||||
// Sort .pdata section contents according to PE/COFF spec 5.5.
|
||||
void Writer::sortExceptionTable() {
|
||||
OutputSection *Sec = findSection(".pdata");
|
||||
@ -795,7 +886,7 @@ void Writer::sortExceptionTable() {
|
||||
[](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
|
||||
return;
|
||||
}
|
||||
if (Config->Machine == ARMNT) {
|
||||
if (Config->Machine == ARMNT || Config->Machine == ARM64) {
|
||||
struct Entry { ulittle32_t Begin, Unwind; };
|
||||
sort(parallel::par, (Entry *)Begin, (Entry *)End,
|
||||
[](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
|
||||
@ -804,26 +895,6 @@ void Writer::sortExceptionTable() {
|
||||
errs() << "warning: don't know how to handle .pdata.\n";
|
||||
}
|
||||
|
||||
// Backfill the CVSignature in a PDB70 Debug Record. This backfilling allows us
|
||||
// to get reproducible builds.
|
||||
void Writer::writeBuildId() {
|
||||
// There is nothing to backfill if BuildId was not setup.
|
||||
if (BuildId == nullptr)
|
||||
return;
|
||||
|
||||
assert(BuildId->DI->Signature.CVSignature == OMF::Signature::PDB70 &&
|
||||
"only PDB 7.0 is supported");
|
||||
assert(sizeof(BuildId->DI->PDB70.Signature) == 16 &&
|
||||
"signature size mismatch");
|
||||
|
||||
// Compute an MD5 hash.
|
||||
ArrayRef<uint8_t> Buf(Buffer->getBufferStart(), Buffer->getBufferEnd());
|
||||
memcpy(BuildId->DI->PDB70.Signature, MD5::hash(Buf).data(), 16);
|
||||
|
||||
// TODO(compnerd) track the Age
|
||||
BuildId->DI->PDB70.Age = 1;
|
||||
}
|
||||
|
||||
OutputSection *Writer::findSection(StringRef Name) {
|
||||
for (OutputSection *Sec : OutputSections)
|
||||
if (Sec->getName() == Name)
|
||||
|
@ -18,11 +18,9 @@
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
class SymbolTable;
|
||||
|
||||
static const int PageSize = 4096;
|
||||
|
||||
void writeResult(SymbolTable *T);
|
||||
void writeResult();
|
||||
|
||||
// OutputSection represents a section in an output file. It's a
|
||||
// container of chunks. OutputSection and Chunk are 1:N relationship.
|
||||
@ -36,7 +34,7 @@ public:
|
||||
void setFileOffset(uint64_t);
|
||||
void addChunk(Chunk *C);
|
||||
llvm::StringRef getName() { return Name; }
|
||||
std::vector<Chunk *> &getChunks() { return Chunks; }
|
||||
ArrayRef<Chunk *> getChunks() { return Chunks; }
|
||||
void addPermissions(uint32_t C);
|
||||
void setPermissions(uint32_t C);
|
||||
uint32_t getPermissions() { return Header.Characteristics & PermMask; }
|
||||
|
62
Common/Args.cpp
Normal file
62
Common/Args.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
//===- Args.cpp -----------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Common/Args.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
|
||||
int lld::args::getInteger(opt::InputArgList &Args, unsigned Key, int Default) {
|
||||
int V = Default;
|
||||
if (auto *Arg = Args.getLastArg(Key)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (!to_integer(S, V, 10))
|
||||
error(Arg->getSpelling() + ": number expected, but got '" + S + "'");
|
||||
}
|
||||
return V;
|
||||
}
|
||||
|
||||
std::vector<StringRef> lld::args::getStrings(opt::InputArgList &Args, int Id) {
|
||||
std::vector<StringRef> V;
|
||||
for (auto *Arg : Args.filtered(Id))
|
||||
V.push_back(Arg->getValue());
|
||||
return V;
|
||||
}
|
||||
|
||||
uint64_t lld::args::getZOptionValue(opt::InputArgList &Args, int Id,
|
||||
StringRef Key, uint64_t Default) {
|
||||
for (auto *Arg : Args.filtered(Id)) {
|
||||
std::pair<StringRef, StringRef> KV = StringRef(Arg->getValue()).split('=');
|
||||
if (KV.first == Key) {
|
||||
uint64_t Result = Default;
|
||||
if (!to_integer(KV.second, Result))
|
||||
error("invalid " + Key + ": " + KV.second);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
return Default;
|
||||
}
|
||||
|
||||
std::vector<StringRef> lld::args::getLines(MemoryBufferRef MB) {
|
||||
SmallVector<StringRef, 0> Arr;
|
||||
MB.getBuffer().split(Arr, '\n');
|
||||
|
||||
std::vector<StringRef> Ret;
|
||||
for (StringRef S : Arr) {
|
||||
S = S.trim();
|
||||
if (!S.empty() && S[0] != '#')
|
||||
Ret.push_back(S);
|
||||
}
|
||||
return Ret;
|
||||
}
|
32
Common/CMakeLists.txt
Normal file
32
Common/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
||||
if(NOT LLD_BUILT_STANDALONE)
|
||||
set(tablegen_deps intrinsics_gen)
|
||||
endif()
|
||||
|
||||
add_lld_library(lldCommon
|
||||
Args.cpp
|
||||
ErrorHandler.cpp
|
||||
Memory.cpp
|
||||
Reproduce.cpp
|
||||
Strings.cpp
|
||||
TargetOptionsCommandFlags.cpp
|
||||
Threads.cpp
|
||||
Version.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLD_INCLUDE_DIR}/lld/Common
|
||||
|
||||
LINK_COMPONENTS
|
||||
Codegen
|
||||
Core
|
||||
Demangle
|
||||
MC
|
||||
Option
|
||||
Support
|
||||
Target
|
||||
|
||||
LINK_LIBS
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
${tablegen_deps}
|
||||
)
|
@ -1,4 +1,4 @@
|
||||
//===- Error.cpp ----------------------------------------------------------===//
|
||||
//===- ErrorHandler.cpp ---------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
@ -7,8 +7,9 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Config.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
|
||||
#include "lld/Common/Threads.h"
|
||||
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
@ -21,12 +22,7 @@
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
uint64_t elf::ErrorCount;
|
||||
raw_ostream *elf::ErrorOS;
|
||||
|
||||
// The functions defined in this file can be called from multiple threads,
|
||||
// but outs() or errs() are not thread-safe. We protect them using a mutex.
|
||||
@ -34,72 +30,25 @@ static std::mutex Mu;
|
||||
|
||||
// Prints "\n" or does nothing, depending on Msg contents of
|
||||
// the previous call of this function.
|
||||
static void newline(const Twine &Msg) {
|
||||
static void newline(raw_ostream *ErrorOS, const Twine &Msg) {
|
||||
// True if the previous error message contained "\n".
|
||||
// We want to separate multi-line error messages with a newline.
|
||||
static bool Flag;
|
||||
|
||||
if (Flag)
|
||||
*ErrorOS << "\n";
|
||||
Flag = (StringRef(Msg.str()).find('\n') != StringRef::npos);
|
||||
Flag = StringRef(Msg.str()).contains('\n');
|
||||
}
|
||||
|
||||
static void print(StringRef S, raw_ostream::Colors C) {
|
||||
*ErrorOS << Config->Argv[0] << ": ";
|
||||
if (Config->ColorDiagnostics) {
|
||||
ErrorOS->changeColor(C, true);
|
||||
*ErrorOS << S;
|
||||
ErrorOS->resetColor();
|
||||
} else {
|
||||
*ErrorOS << S;
|
||||
}
|
||||
ErrorHandler &lld::errorHandler() {
|
||||
static ErrorHandler Handler;
|
||||
return Handler;
|
||||
}
|
||||
|
||||
void elf::log(const Twine &Msg) {
|
||||
if (Config->Verbose) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Config->Argv[0] << ": " << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
}
|
||||
void lld::exitLld(int Val) {
|
||||
// Delete the output buffer so that any tempory file is deleted.
|
||||
errorHandler().OutputBuffer.reset();
|
||||
|
||||
void elf::message(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
|
||||
void elf::warn(const Twine &Msg) {
|
||||
if (Config->FatalWarnings) {
|
||||
error(Msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
newline(Msg);
|
||||
print("warning: ", raw_ostream::MAGENTA);
|
||||
*ErrorOS << Msg << "\n";
|
||||
}
|
||||
|
||||
void elf::error(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
newline(Msg);
|
||||
|
||||
if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << Msg << "\n";
|
||||
} else if (ErrorCount == Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << "too many errors emitted, stopping now"
|
||||
<< " (use -error-limit=0 to see all errors)\n";
|
||||
if (Config->ExitEarly)
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
++ErrorCount;
|
||||
}
|
||||
|
||||
void elf::exitLld(int Val) {
|
||||
// Dealloc/destroy ManagedStatic variables before calling
|
||||
// _exit(). In a non-LTO build, this is a nop. In an LTO
|
||||
// build allows us to get the output of -time-passes.
|
||||
@ -110,7 +59,60 @@ void elf::exitLld(int Val) {
|
||||
_exit(Val);
|
||||
}
|
||||
|
||||
void elf::fatal(const Twine &Msg) {
|
||||
void ErrorHandler::print(StringRef S, raw_ostream::Colors C) {
|
||||
*ErrorOS << LogName << ": ";
|
||||
if (ColorDiagnostics) {
|
||||
ErrorOS->changeColor(C, true);
|
||||
*ErrorOS << S;
|
||||
ErrorOS->resetColor();
|
||||
} else {
|
||||
*ErrorOS << S;
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorHandler::log(const Twine &Msg) {
|
||||
if (Verbose) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
*ErrorOS << LogName << ": " << Msg << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorHandler::message(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
|
||||
void ErrorHandler::warn(const Twine &Msg) {
|
||||
if (FatalWarnings) {
|
||||
error(Msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
newline(ErrorOS, Msg);
|
||||
print("warning: ", raw_ostream::MAGENTA);
|
||||
*ErrorOS << Msg << "\n";
|
||||
}
|
||||
|
||||
void ErrorHandler::error(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
newline(ErrorOS, Msg);
|
||||
|
||||
if (ErrorLimit == 0 || ErrorCount < ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << Msg << "\n";
|
||||
} else if (ErrorCount == ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << ErrorLimitExceededMsg << "\n";
|
||||
if (ExitEarly)
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
++ErrorCount;
|
||||
}
|
||||
|
||||
void ErrorHandler::fatal(const Twine &Msg) {
|
||||
error(Msg);
|
||||
exitLld(1);
|
||||
}
|
23
Common/Memory.cpp
Normal file
23
Common/Memory.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
//===- Memory.cpp ---------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Common/Memory.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
|
||||
BumpPtrAllocator lld::BAlloc;
|
||||
StringSaver lld::Saver{BAlloc};
|
||||
std::vector<SpecificAllocBase *> lld::SpecificAllocBase::Instances;
|
||||
|
||||
void lld::freeArena() {
|
||||
for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
|
||||
Alloc->reset();
|
||||
BAlloc.Reset();
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/Reproduce.h"
|
||||
#include "lld/Common/Reproduce.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
@ -44,9 +44,9 @@ std::string lld::relativeToRoot(StringRef Path) {
|
||||
|
||||
// Quote a given string if it contains a space character.
|
||||
std::string lld::quote(StringRef S) {
|
||||
if (S.find(' ') == StringRef::npos)
|
||||
return S;
|
||||
return ("\"" + S + "\"").str();
|
||||
if (S.contains(' '))
|
||||
return ("\"" + S + "\"").str();
|
||||
return S;
|
||||
}
|
||||
|
||||
std::string lld::rewritePath(StringRef S) {
|
||||
@ -55,12 +55,12 @@ std::string lld::rewritePath(StringRef S) {
|
||||
return S;
|
||||
}
|
||||
|
||||
std::string lld::toString(opt::Arg *Arg) {
|
||||
std::string K = Arg->getSpelling();
|
||||
if (Arg->getNumValues() == 0)
|
||||
std::string lld::toString(const opt::Arg &Arg) {
|
||||
std::string K = Arg.getSpelling();
|
||||
if (Arg.getNumValues() == 0)
|
||||
return K;
|
||||
std::string V = quote(Arg->getValue());
|
||||
if (Arg->getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
|
||||
std::string V = quote(Arg.getValue());
|
||||
if (Arg.getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
|
||||
return K + V;
|
||||
return K + " " + V;
|
||||
}
|
32
Common/Strings.cpp
Normal file
32
Common/Strings.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
//===- Strings.cpp -------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "llvm/Demangle/Demangle.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
|
||||
// Returns the demangled C++ symbol name for Name.
|
||||
Optional<std::string> lld::demangleItanium(StringRef Name) {
|
||||
// itaniumDemangle can be used to demangle strings other than symbol
|
||||
// names which do not necessarily start with "_Z". Name can be
|
||||
// either a C or C++ symbol. Don't call itaniumDemangle if the name
|
||||
// does not look like a C++ symbol name to avoid getting unexpected
|
||||
// result for a C symbol that happens to match a mangled type name.
|
||||
if (!Name.startswith("_Z"))
|
||||
return None;
|
||||
|
||||
char *Buf = itaniumDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
|
||||
if (!Buf)
|
||||
return None;
|
||||
std::string S(Buf);
|
||||
free(Buf);
|
||||
return S;
|
||||
}
|
@ -8,25 +8,25 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file exists as a place for global variables defined in LLVM's
|
||||
// CodeGen/CommandFlags.h. By putting the resulting object file in
|
||||
// CodeGen/CommandFlags.def. By putting the resulting object file in
|
||||
// an archive and linking with it, the definitions will automatically be
|
||||
// included when needed and skipped when already present.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/TargetOptionsCommandFlags.h"
|
||||
#include "lld/Common/TargetOptionsCommandFlags.h"
|
||||
|
||||
#include "llvm/CodeGen/CommandFlags.h"
|
||||
#include "llvm/CodeGen/CommandFlags.def"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
|
||||
// Define an externally visible version of
|
||||
// InitTargetOptionsFromCodeGenFlags, so that its functionality can be
|
||||
// used without having to include llvm/CodeGen/CommandFlags.h, which
|
||||
// used without having to include llvm/CodeGen/CommandFlags.def, which
|
||||
// would lead to multiple definitions of the command line flags.
|
||||
llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() {
|
||||
return ::InitTargetOptionsFromCodeGenFlags();
|
||||
}
|
||||
|
||||
llvm::CodeModel::Model lld::GetCodeModelFromCMModel() {
|
||||
return CMModel;
|
||||
llvm::Optional<llvm::CodeModel::Model> lld::GetCodeModelFromCMModel() {
|
||||
return getCodeModel();
|
||||
}
|
12
Common/Threads.cpp
Normal file
12
Common/Threads.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
//===- Threads.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Common/Threads.h"
|
||||
|
||||
bool lld::ThreadsEnabled = true;
|
@ -1,4 +1,4 @@
|
||||
//===- lib/Config/Version.cpp - LLD Version Number ---------------*- C++-=====//
|
||||
//===- lib/Common/Version.cpp - LLD Version Number ---------------*- C++-=====//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -11,7 +11,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Config/Version.h"
|
||||
#include "lld/Common/Version.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
648
ELF/AArch64ErrataFix.cpp
Normal file
648
ELF/AArch64ErrataFix.cpp
Normal file
@ -0,0 +1,648 @@
|
||||
//===- AArch64ErrataFix.cpp -----------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This file implements Section Patching for the purpose of working around
|
||||
// errata in CPUs. The general principle is that an erratum sequence of one or
|
||||
// more instructions is detected in the instruction stream, one of the
|
||||
// instructions in the sequence is replaced with a branch to a patch sequence
|
||||
// of replacement instructions. At the end of the replacement sequence the
|
||||
// patch branches back to the instruction stream.
|
||||
|
||||
// This technique is only suitable for fixing an erratum when:
|
||||
// - There is a set of necessary conditions required to trigger the erratum that
|
||||
// can be detected at static link time.
|
||||
// - There is a set of replacement instructions that can be used to remove at
|
||||
// least one of the necessary conditions that trigger the erratum.
|
||||
// - We can overwrite an instruction in the erratum sequence with a branch to
|
||||
// the replacement sequence.
|
||||
// - We can place the replacement sequence within range of the branch.
|
||||
|
||||
// FIXME:
|
||||
// - The implementation here only supports one patch, the AArch64 Cortex-53
|
||||
// errata 843419 that affects r0p0, r0p1, r0p2 and r0p4 versions of the core.
|
||||
// To keep the initial version simple there is no support for multiple
|
||||
// architectures or selection of different patches.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AArch64ErrataFix.h"
|
||||
#include "Config.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Relocations.h"
|
||||
#include "Strings.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// Helper functions to identify instructions and conditions needed to trigger
|
||||
// the Cortex-A53-843419 erratum.
|
||||
|
||||
// ADRP
|
||||
// | 1 | immlo (2) | 1 | 0 0 0 0 | immhi (19) | Rd (5) |
|
||||
static bool isADRP(uint32_t Instr) {
|
||||
return (Instr & 0x9f000000) == 0x90000000;
|
||||
}
|
||||
|
||||
// Load and store bit patterns from ARMv8-A ARM ARM.
|
||||
// Instructions appear in order of appearance starting from table in
|
||||
// C4.1.3 Loads and Stores.
|
||||
|
||||
// All loads and stores have 1 (at bit postion 27), (0 at bit position 25).
|
||||
// | op0 x op1 (2) | 1 op2 0 op3 (2) | x | op4 (5) | xxxx | op5 (2) | x (10) |
|
||||
static bool isLoadStoreClass(uint32_t Instr) {
|
||||
return (Instr & 0x0a000000) == 0x08000000;
|
||||
}
|
||||
|
||||
// LDN/STN multiple no offset
|
||||
// | 0 Q 00 | 1100 | 0 L 00 | 0000 | opcode (4) | size (2) | Rn (5) | Rt (5) |
|
||||
// LDN/STN multiple post-indexed
|
||||
// | 0 Q 00 | 1100 | 1 L 0 | Rm (5)| opcode (4) | size (2) | Rn (5) | Rt (5) |
|
||||
// L == 0 for stores.
|
||||
|
||||
// Utility routine to decode opcode field of LDN/STN multiple structure
|
||||
// instructions to find the ST1 instructions.
|
||||
// opcode == 0010 ST1 4 registers.
|
||||
// opcode == 0110 ST1 3 registers.
|
||||
// opcode == 0111 ST1 1 register.
|
||||
// opcode == 1010 ST1 2 registers.
|
||||
static bool isST1MultipleOpcode(uint32_t Instr) {
|
||||
return (Instr & 0x0000f000) == 0x00002000 ||
|
||||
(Instr & 0x0000f000) == 0x00006000 ||
|
||||
(Instr & 0x0000f000) == 0x00007000 ||
|
||||
(Instr & 0x0000f000) == 0x0000a000;
|
||||
}
|
||||
|
||||
static bool isST1Multiple(uint32_t Instr) {
|
||||
return (Instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(Instr);
|
||||
}
|
||||
|
||||
// Writes to Rn (writeback).
|
||||
static bool isST1MultiplePost(uint32_t Instr) {
|
||||
return (Instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(Instr);
|
||||
}
|
||||
|
||||
// LDN/STN single no offset
|
||||
// | 0 Q 00 | 1101 | 0 L R 0 | 0000 | opc (3) S | size (2) | Rn (5) | Rt (5)|
|
||||
// LDN/STN single post-indexed
|
||||
// | 0 Q 00 | 1101 | 1 L R | Rm (5) | opc (3) S | size (2) | Rn (5) | Rt (5)|
|
||||
// L == 0 for stores
|
||||
|
||||
// Utility routine to decode opcode field of LDN/STN single structure
|
||||
// instructions to find the ST1 instructions.
|
||||
// R == 0 for ST1 and ST3, R == 1 for ST2 and ST4.
|
||||
// opcode == 000 ST1 8-bit.
|
||||
// opcode == 010 ST1 16-bit.
|
||||
// opcode == 100 ST1 32 or 64-bit (Size determines which).
|
||||
static bool isST1SingleOpcode(uint32_t Instr) {
|
||||
return (Instr & 0x0040e000) == 0x00000000 ||
|
||||
(Instr & 0x0040e000) == 0x00004000 ||
|
||||
(Instr & 0x0040e000) == 0x00008000;
|
||||
}
|
||||
|
||||
static bool isST1Single(uint32_t Instr) {
|
||||
return (Instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(Instr);
|
||||
}
|
||||
|
||||
// Writes to Rn (writeback).
|
||||
static bool isST1SinglePost(uint32_t Instr) {
|
||||
return (Instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(Instr);
|
||||
}
|
||||
|
||||
static bool isST1(uint32_t Instr) {
|
||||
return isST1Multiple(Instr) || isST1MultiplePost(Instr) ||
|
||||
isST1Single(Instr) || isST1SinglePost(Instr);
|
||||
}
|
||||
|
||||
// Load/store exclusive
|
||||
// | size (2) 00 | 1000 | o2 L o1 | Rs (5) | o0 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
// L == 0 for Stores.
|
||||
static bool isLoadStoreExclusive(uint32_t Instr) {
|
||||
return (Instr & 0x3f000000) == 0x08000000;
|
||||
}
|
||||
|
||||
static bool isLoadExclusive(uint32_t Instr) {
|
||||
return (Instr & 0x3f400000) == 0x08400000;
|
||||
}
|
||||
|
||||
// Load register literal
|
||||
// | opc (2) 01 | 1 V 00 | imm19 | Rt (5) |
|
||||
static bool isLoadLiteral(uint32_t Instr) {
|
||||
return (Instr & 0x3b000000) == 0x18000000;
|
||||
}
|
||||
|
||||
// Load/store no-allocate pair
|
||||
// (offset)
|
||||
// | opc (2) 10 | 1 V 00 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
// L == 0 for stores.
|
||||
// Never writes to register
|
||||
static bool isSTNP(uint32_t Instr) {
|
||||
return (Instr & 0x3bc00000) == 0x28000000;
|
||||
}
|
||||
|
||||
// Load/store register pair
|
||||
// (post-indexed)
|
||||
// | opc (2) 10 | 1 V 00 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
// L == 0 for stores, V == 0 for Scalar, V == 1 for Simd/FP
|
||||
// Writes to Rn.
|
||||
static bool isSTPPost(uint32_t Instr) {
|
||||
return (Instr & 0x3bc00000) == 0x28800000;
|
||||
}
|
||||
|
||||
// (offset)
|
||||
// | opc (2) 10 | 1 V 01 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
static bool isSTPOffset(uint32_t Instr) {
|
||||
return (Instr & 0x3bc00000) == 0x29000000;
|
||||
}
|
||||
|
||||
// (pre-index)
|
||||
// | opc (2) 10 | 1 V 01 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
// Writes to Rn.
|
||||
static bool isSTPPre(uint32_t Instr) {
|
||||
return (Instr & 0x3bc00000) == 0x29800000;
|
||||
}
|
||||
|
||||
static bool isSTP(uint32_t Instr) {
|
||||
return isSTPPost(Instr) || isSTPOffset(Instr) || isSTPPre(Instr);
|
||||
}
|
||||
|
||||
// Load/store register (unscaled immediate)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 00 | Rn (5) | Rt (5) |
|
||||
// V == 0 for Scalar, V == 1 for Simd/FP.
|
||||
static bool isLoadStoreUnscaled(uint32_t Instr) {
|
||||
return (Instr & 0x3b000c00) == 0x38000000;
|
||||
}
|
||||
|
||||
// Load/store register (immediate post-indexed)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 01 | Rn (5) | Rt (5) |
|
||||
static bool isLoadStoreImmediatePost(uint32_t Instr) {
|
||||
return (Instr & 0x3b200c00) == 0x38000400;
|
||||
}
|
||||
|
||||
// Load/store register (unprivileged)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 10 | Rn (5) | Rt (5) |
|
||||
static bool isLoadStoreUnpriv(uint32_t Instr) {
|
||||
return (Instr & 0x3b200c00) == 0x38000800;
|
||||
}
|
||||
|
||||
// Load/store register (immediate pre-indexed)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 11 | Rn (5) | Rt (5) |
|
||||
static bool isLoadStoreImmediatePre(uint32_t Instr) {
|
||||
return (Instr & 0x3b200c00) == 0x38000c00;
|
||||
}
|
||||
|
||||
// Load/store register (register offset)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 1 | Rm (5) | option (3) S | 10 | Rn | Rt |
|
||||
static bool isLoadStoreRegisterOff(uint32_t Instr) {
|
||||
return (Instr & 0x3b200c00) == 0x38200800;
|
||||
}
|
||||
|
||||
// Load/store register (unsigned immediate)
|
||||
// | size (2) 11 | 1 V 01 | opc (2) | imm12 | Rn (5) | Rt (5) |
|
||||
static bool isLoadStoreRegisterUnsigned(uint32_t Instr) {
|
||||
return (Instr & 0x3b000000) == 0x39000000;
|
||||
}
|
||||
|
||||
// Rt is always in bit position 0 - 4.
|
||||
static uint32_t getRt(uint32_t Instr) { return (Instr & 0x1f); }
|
||||
|
||||
// Rn is always in bit position 5 - 9.
|
||||
static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; }
|
||||
|
||||
// C4.1.2 Branches, Exception Generating and System instructions
|
||||
// | op0 (3) 1 | 01 op1 (4) | x (22) |
|
||||
// op0 == 010 101 op1 == 0xxx Conditional Branch.
|
||||
// op0 == 110 101 op1 == 1xxx Unconditional Branch Register.
|
||||
// op0 == x00 101 op1 == xxxx Unconditional Branch immediate.
|
||||
// op0 == x01 101 op1 == 0xxx Compare and branch immediate.
|
||||
// op0 == x01 101 op1 == 1xxx Test and branch immediate.
|
||||
static bool isBranch(uint32_t Instr) {
|
||||
return ((Instr & 0xfe000000) == 0xd6000000) || // Cond branch.
|
||||
((Instr & 0xfe000000) == 0x54000000) || // Uncond branch reg.
|
||||
((Instr & 0x7c000000) == 0x14000000) || // Uncond branch imm.
|
||||
((Instr & 0x7c000000) == 0x34000000); // Compare and test branch.
|
||||
}
|
||||
|
||||
static bool isV8SingleRegisterNonStructureLoadStore(uint32_t Instr) {
|
||||
return isLoadStoreUnscaled(Instr) || isLoadStoreImmediatePost(Instr) ||
|
||||
isLoadStoreUnpriv(Instr) || isLoadStoreImmediatePre(Instr) ||
|
||||
isLoadStoreRegisterOff(Instr) || isLoadStoreRegisterUnsigned(Instr);
|
||||
}
|
||||
|
||||
// Note that this function refers to v8.0 only and does not include the
|
||||
// additional load and store instructions added for in later revisions of
|
||||
// the architecture such as the Atomic memory operations introduced
|
||||
// in v8.1.
|
||||
static bool isV8NonStructureLoad(uint32_t Instr) {
|
||||
if (isLoadExclusive(Instr))
|
||||
return true;
|
||||
if (isLoadLiteral(Instr))
|
||||
return true;
|
||||
else if (isV8SingleRegisterNonStructureLoadStore(Instr)) {
|
||||
// For Load and Store single register, Loads are derived from a
|
||||
// combination of the Size, V and Opc fields.
|
||||
uint32_t Size = (Instr >> 30) & 0xff;
|
||||
uint32_t V = (Instr >> 26) & 0x1;
|
||||
uint32_t Opc = (Instr >> 22) & 0x3;
|
||||
// For the load and store instructions that we are decoding.
|
||||
// Opc == 0 are all stores.
|
||||
// Opc == 1 with a couple of exceptions are loads. The exceptions are:
|
||||
// Size == 00 (0), V == 1, Opc == 10 (2) which is a store and
|
||||
// Size == 11 (3), V == 0, Opc == 10 (2) which is a prefetch.
|
||||
return Opc != 0 && !(Size == 0 && V == 1 && Opc == 2) &&
|
||||
!(Size == 3 && V == 0 && Opc == 2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The following decode instructions are only complete up to the instructions
|
||||
// needed for errata 843419.
|
||||
|
||||
// Instruction with writeback updates the index register after the load/store.
|
||||
static bool hasWriteback(uint32_t Instr) {
|
||||
return isLoadStoreImmediatePre(Instr) || isLoadStoreImmediatePost(Instr) ||
|
||||
isSTPPre(Instr) || isSTPPost(Instr) || isST1SinglePost(Instr) ||
|
||||
isST1MultiplePost(Instr);
|
||||
}
|
||||
|
||||
// For the load and store class of instructions, a load can write to the
|
||||
// destination register, a load and a store can write to the base register when
|
||||
// the instruction has writeback.
|
||||
static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) {
|
||||
return (isV8NonStructureLoad(Instr) && getRt(Instr) == Reg) ||
|
||||
(hasWriteback(Instr) && getRn(Instr) == Reg);
|
||||
}
|
||||
|
||||
// Scanner for Cortex-A53 errata 843419
|
||||
// Full details are available in the Cortex A53 MPCore revision 0 Software
|
||||
// Developers Errata Notice (ARM-EPM-048406).
|
||||
//
|
||||
// The instruction sequence that triggers the erratum is common in compiled
|
||||
// AArch64 code, however it is sensitive to the offset of the sequence within
|
||||
// a 4k page. This means that by scanning and fixing the patch after we have
|
||||
// assigned addresses we only need to disassemble and fix instances of the
|
||||
// sequence in the range of affected offsets.
|
||||
//
|
||||
// In summary the erratum conditions are a series of 4 instructions:
|
||||
// 1.) An ADRP instruction that writes to register Rn with low 12 bits of
|
||||
// address of instruction either 0xff8 or 0xffc.
|
||||
// 2.) A load or store instruction that can be:
|
||||
// - A single register load or store, of either integer or vector registers.
|
||||
// - An STP or STNP, of either integer or vector registers.
|
||||
// - An Advanced SIMD ST1 store instruction.
|
||||
// - Must not write to Rn, but may optionally read from it.
|
||||
// 3.) An optional instruction that is not a branch and does not write to Rn.
|
||||
// 4.) A load or store from the Load/store register (unsigned immediate) class
|
||||
// that uses Rn as the base address register.
|
||||
//
|
||||
// Note that we do not attempt to scan for Sequence 2 as described in the
|
||||
// Software Developers Errata Notice as this has been assessed to be extremely
|
||||
// unlikely to occur in compiled code. This matches gold and ld.bfd behavior.
|
||||
|
||||
// Return true if the Instruction sequence Adrp, Instr2, and Instr4 match
|
||||
// the erratum sequence. The Adrp, Instr2 and Instr4 correspond to 1.), 2.),
|
||||
// and 4.) in the Scanner for Cortex-A53 errata comment above.
|
||||
static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2,
|
||||
uint32_t Instr4) {
|
||||
if (!isADRP(Instr1))
|
||||
return false;
|
||||
|
||||
uint32_t Rn = getRt(Instr1);
|
||||
return isLoadStoreClass(Instr2) &&
|
||||
(isLoadStoreExclusive(Instr2) || isLoadLiteral(Instr2) ||
|
||||
isV8SingleRegisterNonStructureLoadStore(Instr2) || isSTP(Instr2) ||
|
||||
isSTNP(Instr2) || isST1(Instr2)) &&
|
||||
!doesLoadStoreWriteToReg(Instr2, Rn) &&
|
||||
isLoadStoreRegisterUnsigned(Instr4) && getRn(Instr4) == Rn;
|
||||
}
|
||||
|
||||
// Scan the instruction sequence starting at Offset Off from the base of
|
||||
// InputSection IS. We update Off in this function rather than in the caller as
|
||||
// we can skip ahead much further into the section when we know how many
|
||||
// instructions we've scanned.
|
||||
// Return the offset of the load or store instruction in IS that we want to
|
||||
// patch or 0 if no patch required.
|
||||
static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
|
||||
uint64_t Limit) {
|
||||
uint64_t ISAddr = IS->getParent()->Addr + IS->OutSecOff;
|
||||
|
||||
// Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8.
|
||||
uint64_t InitialPageOff = (ISAddr + Off) & 0xfff;
|
||||
if (InitialPageOff < 0xff8)
|
||||
Off += 0xff8 - InitialPageOff;
|
||||
|
||||
bool OptionalAllowed = Limit - Off > 12;
|
||||
if (Off >= Limit || Limit - Off < 12) {
|
||||
// Need at least 3 4-byte sized instructions to trigger erratum.
|
||||
Off = Limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t PatchOff = 0;
|
||||
const uint8_t *Buf = IS->Data.begin();
|
||||
const uint32_t *InstBuf = reinterpret_cast<const uint32_t *>(Buf + Off);
|
||||
uint32_t Instr1 = *InstBuf++;
|
||||
uint32_t Instr2 = *InstBuf++;
|
||||
uint32_t Instr3 = *InstBuf++;
|
||||
if (is843419ErratumSequence(Instr1, Instr2, Instr3)) {
|
||||
PatchOff = Off + 8;
|
||||
} else if (OptionalAllowed && !isBranch(Instr3)) {
|
||||
uint32_t Instr4 = *InstBuf++;
|
||||
if (is843419ErratumSequence(Instr1, Instr2, Instr4))
|
||||
PatchOff = Off + 12;
|
||||
}
|
||||
if (((ISAddr + Off) & 0xfff) == 0xff8)
|
||||
Off += 4;
|
||||
else
|
||||
Off += 0xffc;
|
||||
return PatchOff;
|
||||
}
|
||||
|
||||
class lld::elf::Patch843419Section : public SyntheticSection {
|
||||
public:
|
||||
Patch843419Section(InputSection *P, uint64_t Off);
|
||||
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
||||
size_t getSize() const override { return 8; }
|
||||
|
||||
uint64_t getLDSTAddr() const;
|
||||
|
||||
// The Section we are patching.
|
||||
const InputSection *Patchee;
|
||||
// The offset of the instruction in the Patchee section we are patching.
|
||||
uint64_t PatcheeOffset;
|
||||
// A label for the start of the Patch that we can use as a relocation target.
|
||||
Symbol *PatchSym;
|
||||
};
|
||||
|
||||
lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off)
|
||||
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
|
||||
".text.patch"),
|
||||
Patchee(P), PatcheeOffset(Off) {
|
||||
this->Parent = P->getParent();
|
||||
PatchSym = addSyntheticLocal(
|
||||
Saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0,
|
||||
getSize(), this);
|
||||
addSyntheticLocal(Saver.save("$x"), STT_NOTYPE, 0, 0, this);
|
||||
}
|
||||
|
||||
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
|
||||
return Patchee->getParent()->Addr + Patchee->OutSecOff + PatcheeOffset;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
// Apply any relocation transferred from the original PatcheeSection.
|
||||
// For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc
|
||||
// also adds OutSecOff so we need to subtract to avoid double counting.
|
||||
this->relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + getSize());
|
||||
|
||||
// Return address is the next instruction after the one we have just copied.
|
||||
uint64_t S = getLDSTAddr() + 4;
|
||||
uint64_t P = PatchSym->getVA() + 4;
|
||||
Target->relocateOne(Buf + 4, R_AARCH64_JUMP26, S - P);
|
||||
}
|
||||
|
||||
void AArch64Err843419Patcher::init() {
|
||||
// The AArch64 ABI permits data in executable sections. We must avoid scanning
|
||||
// this data as if it were instructions to avoid false matches. We use the
|
||||
// mapping symbols in the InputObjects to identify this data, caching the
|
||||
// results in SectionMap so we don't have to recalculate it each pass.
|
||||
|
||||
// The ABI Section 4.5.4 Mapping symbols; defines local symbols that describe
|
||||
// half open intervals [Symbol Value, Next Symbol Value) of code and data
|
||||
// within sections. If there is no next symbol then the half open interval is
|
||||
// [Symbol Value, End of section). The type, code or data, is determined by
|
||||
// the mapping symbol name, $x for code, $d for data.
|
||||
auto IsCodeMapSymbol = [](const Symbol *B) {
|
||||
return B->getName() == "$x" || B->getName().startswith("$x.");
|
||||
};
|
||||
auto IsDataMapSymbol = [](const Symbol *B) {
|
||||
return B->getName() == "$d" || B->getName().startswith("$d.");
|
||||
};
|
||||
|
||||
// Collect mapping symbols for every executable InputSection.
|
||||
for (InputFile *File : ObjectFiles) {
|
||||
auto *F = cast<ObjFile<ELF64LE>>(File);
|
||||
for (Symbol *B : F->getLocalSymbols()) {
|
||||
auto *Def = dyn_cast<Defined>(B);
|
||||
if (!Def)
|
||||
continue;
|
||||
if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def))
|
||||
continue;
|
||||
if (auto *Sec = dyn_cast<InputSection>(Def->Section))
|
||||
if (Sec->Flags & SHF_EXECINSTR)
|
||||
SectionMap[Sec].push_back(Def);
|
||||
}
|
||||
}
|
||||
// For each InputSection make sure the mapping symbols are in sorted in
|
||||
// ascending order and free from consecutive runs of mapping symbols with
|
||||
// the same type. For example we must remove the redundant $d.1 from $x.0
|
||||
// $d.0 $d.1 $x.1.
|
||||
for (auto &KV : SectionMap) {
|
||||
std::vector<const Defined *> &MapSyms = KV.second;
|
||||
if (MapSyms.size() <= 1)
|
||||
continue;
|
||||
std::stable_sort(
|
||||
MapSyms.begin(), MapSyms.end(),
|
||||
[](const Defined *A, const Defined *B) { return A->Value < B->Value; });
|
||||
MapSyms.erase(
|
||||
std::unique(MapSyms.begin(), MapSyms.end(),
|
||||
[=](const Defined *A, const Defined *B) {
|
||||
return (IsCodeMapSymbol(A) && IsCodeMapSymbol(B)) ||
|
||||
(IsDataMapSymbol(A) && IsDataMapSymbol(B));
|
||||
}),
|
||||
MapSyms.end());
|
||||
}
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
// Insert the PatchSections we have created back into the
|
||||
// InputSectionDescription. As inserting patches alters the addresses of
|
||||
// InputSections that follow them, we try and place the patches after all the
|
||||
// executable sections, although we may need to insert them earlier if the
|
||||
// InputSectionDescription is larger than the maximum branch range.
|
||||
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;
|
||||
|
||||
// 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
|
||||
// every multiple of maximum branch range.
|
||||
auto PatchIt = Patches.begin();
|
||||
auto PatchEnd = Patches.end();
|
||||
for (const InputSection *IS : ISD.Sections) {
|
||||
ISLimit = IS->OutSecOff + IS->getSize();
|
||||
if (ISLimit > PatchUpperBound) {
|
||||
while (PatchIt != PatchEnd) {
|
||||
if ((*PatchIt)->getLDSTAddr() >= PrevISLimit)
|
||||
break;
|
||||
(*PatchIt)->OutSecOff = PrevISLimit;
|
||||
++PatchIt;
|
||||
}
|
||||
PatchUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
|
||||
}
|
||||
PrevISLimit = ISLimit;
|
||||
}
|
||||
for (; PatchIt != PatchEnd; ++PatchIt) {
|
||||
(*PatchIt)->OutSecOff = ISLimit;
|
||||
}
|
||||
|
||||
// merge all patch sections. We use the OutSecOff assigned above to
|
||||
// determine the insertion point. This is ok as we only merge into an
|
||||
// InputSectionDescription once per pass, and at the end of the pass
|
||||
// assignAddresses() will recalculate all the OutSecOff values.
|
||||
std::vector<InputSection *> Tmp;
|
||||
Tmp.reserve(ISD.Sections.size() + Patches.size());
|
||||
auto MergeCmp = [](const InputSection *A, const InputSection *B) {
|
||||
if (A->OutSecOff < B->OutSecOff)
|
||||
return true;
|
||||
if (A->OutSecOff == B->OutSecOff && isa<Patch843419Section>(A) &&
|
||||
!isa<Patch843419Section>(B))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
std::merge(ISD.Sections.begin(), ISD.Sections.end(), Patches.begin(),
|
||||
Patches.end(), std::back_inserter(Tmp), MergeCmp);
|
||||
ISD.Sections = std::move(Tmp);
|
||||
}
|
||||
|
||||
// Given an erratum sequence that starts at address AdrpAddr, with an
|
||||
// instruction that we need to patch at PatcheeOffset from the start of
|
||||
// InputSection IS, create a Patch843419 Section and add it to the
|
||||
// Patches that we need to insert.
|
||||
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.
|
||||
// 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
|
||||
// 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
|
||||
// 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)
|
||||
return;
|
||||
|
||||
if (Config->Verbose)
|
||||
message("detected cortex-a53-843419 erratum sequence starting at " +
|
||||
utohexstr(AdrpAddr) + " in unpatched output.");
|
||||
|
||||
auto *PS = make<Patch843419Section>(IS, PatcheeOffset);
|
||||
Patches.push_back(PS);
|
||||
|
||||
auto MakeRelToPatch = [](uint64_t Offset, Symbol *PatchSym) {
|
||||
return Relocation{R_PC, R_AARCH64_JUMP26, Offset, 0, PatchSym};
|
||||
};
|
||||
|
||||
if (RelIt != IS->Relocations.end()) {
|
||||
PS->Relocations.push_back(
|
||||
{RelIt->Expr, RelIt->Type, 0, RelIt->Addend, RelIt->Sym});
|
||||
*RelIt = MakeRelToPatch(PatcheeOffset, PS->PatchSym);
|
||||
} else
|
||||
IS->Relocations.push_back(MakeRelToPatch(PatcheeOffset, PS->PatchSym));
|
||||
}
|
||||
|
||||
// Scan all the instructions in InputSectionDescription, for each instance of
|
||||
// the erratum sequence create a Patch843419Section. We return the list of
|
||||
// Patch843419Sections that need to be applied to ISD.
|
||||
std::vector<Patch843419Section *>
|
||||
AArch64Err843419Patcher::patchInputSectionDescription(
|
||||
InputSectionDescription &ISD) {
|
||||
std::vector<Patch843419Section *> Patches;
|
||||
for (InputSection *IS : ISD.Sections) {
|
||||
// LLD doesn't use the erratum sequence in SyntheticSections.
|
||||
if (isa<SyntheticSection>(IS))
|
||||
continue;
|
||||
// Use SectionMap to make sure we only scan code and not inline data.
|
||||
// We have already sorted MapSyms in ascending order and removed consecutive
|
||||
// mapping symbols of the same type. Our range of executable instructions to
|
||||
// scan is therefore [CodeSym->Value, DataSym->Value) or [CodeSym->Value,
|
||||
// section size).
|
||||
std::vector<const Defined *> &MapSyms = SectionMap[IS];
|
||||
|
||||
auto CodeSym = llvm::find_if(MapSyms, [&](const Defined *MS) {
|
||||
return MS->getName().startswith("$x");
|
||||
});
|
||||
|
||||
while (CodeSym != MapSyms.end()) {
|
||||
auto DataSym = std::next(CodeSym);
|
||||
uint64_t Off = (*CodeSym)->Value;
|
||||
uint64_t Limit =
|
||||
(DataSym == MapSyms.end()) ? IS->Data.size() : (*DataSym)->Value;
|
||||
|
||||
while (Off < Limit) {
|
||||
uint64_t StartAddr = IS->getParent()->Addr + IS->OutSecOff + Off;
|
||||
if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit))
|
||||
implementPatch(StartAddr, PatcheeOffset, IS, Patches);
|
||||
}
|
||||
if (DataSym == MapSyms.end())
|
||||
break;
|
||||
CodeSym = std::next(DataSym);
|
||||
}
|
||||
}
|
||||
return Patches;
|
||||
}
|
||||
|
||||
// For each InputSectionDescription make one pass over the executable sections
|
||||
// looking for the erratum sequence; creating a synthetic Patch843419Section
|
||||
// for each instance found. We insert these synthetic patch sections after the
|
||||
// executable code in each InputSectionDescription.
|
||||
//
|
||||
// PreConditions:
|
||||
// The Output and Input Sections have had their final addresses assigned.
|
||||
//
|
||||
// PostConditions:
|
||||
// Returns true if at least one patch was added. The addresses of the
|
||||
// Ouptut and Input Sections may have been changed.
|
||||
// Returns false if no patches were required and no changes were made.
|
||||
bool AArch64Err843419Patcher::createFixes() {
|
||||
if (Initialized == false)
|
||||
init();
|
||||
|
||||
bool AddressesChanged = false;
|
||||
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)) {
|
||||
std::vector<Patch843419Section *> Patches =
|
||||
patchInputSectionDescription(*ISD);
|
||||
if (!Patches.empty()) {
|
||||
insertPatches(*ISD, Patches);
|
||||
AddressesChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return AddressesChanged;
|
||||
}
|
52
ELF/AArch64ErrataFix.h
Normal file
52
ELF/AArch64ErrataFix.h
Normal file
@ -0,0 +1,52 @@
|
||||
//===- AArch64ErrataFix.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_AARCH64ERRATAFIX_H
|
||||
#define LLD_ELF_AARCH64ERRATAFIX_H
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class Defined;
|
||||
class InputSection;
|
||||
struct InputSectionDescription;
|
||||
class OutputSection;
|
||||
class Patch843419Section;
|
||||
|
||||
class AArch64Err843419Patcher {
|
||||
public:
|
||||
// return true if Patches have been added to the OutputSections.
|
||||
bool createFixes();
|
||||
|
||||
private:
|
||||
std::vector<Patch843419Section *>
|
||||
patchInputSectionDescription(InputSectionDescription &ISD);
|
||||
|
||||
void insertPatches(InputSectionDescription &ISD,
|
||||
std::vector<Patch843419Section *> &Patches);
|
||||
|
||||
void init();
|
||||
|
||||
// A cache of the mapping symbols defined by the InputSecion sorted in order
|
||||
// of ascending value with redundant symbols removed. These describe
|
||||
// the ranges of code and data in an executable InputSection.
|
||||
std::map<InputSection *, std::vector<const Defined *>> SectionMap;
|
||||
|
||||
bool Initialized = false;
|
||||
};
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
@ -7,11 +7,11 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
@ -32,20 +32,23 @@ namespace {
|
||||
class AArch64 final : public TargetInfo {
|
||||
public:
|
||||
AArch64();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
bool isPicRel(RelType Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
bool usesOnlyLowPageBits(uint32_t Type) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
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;
|
||||
bool usesOnlyLowPageBits(RelType Type) const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
RelExpr Expr) const override;
|
||||
void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -66,13 +69,17 @@ AArch64::AArch64() {
|
||||
// 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(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
case R_AARCH64_TLSDESC_ADR_PAGE21:
|
||||
return R_TLSDESC_PAGE;
|
||||
case R_AARCH64_TLSDESC_LD64_LO12:
|
||||
@ -92,6 +99,7 @@ RelExpr AArch64::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
case R_AARCH64_PREL32:
|
||||
case R_AARCH64_PREL64:
|
||||
case R_AARCH64_ADR_PREL_LO21:
|
||||
case R_AARCH64_LD_PREL_LO19:
|
||||
return R_PC;
|
||||
case R_AARCH64_ADR_PREL_PG_HI21:
|
||||
return R_PAGE_PC;
|
||||
@ -103,10 +111,12 @@ RelExpr AArch64::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
return R_GOT_PAGE_PC;
|
||||
case R_AARCH64_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr AArch64::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
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)
|
||||
@ -116,7 +126,7 @@ RelExpr AArch64::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
return Expr;
|
||||
}
|
||||
|
||||
bool AArch64::usesOnlyLowPageBits(uint32_t Type) const {
|
||||
bool AArch64::usesOnlyLowPageBits(RelType Type) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return false;
|
||||
@ -134,11 +144,11 @@ bool AArch64::usesOnlyLowPageBits(uint32_t Type) const {
|
||||
}
|
||||
}
|
||||
|
||||
bool AArch64::isPicRel(uint32_t Type) const {
|
||||
bool AArch64::isPicRel(RelType Type) const {
|
||||
return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64;
|
||||
}
|
||||
|
||||
void AArch64::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
|
||||
void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const {
|
||||
write64le(Buf, InX::Plt->getVA());
|
||||
}
|
||||
|
||||
@ -180,6 +190,31 @@ void AArch64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr);
|
||||
}
|
||||
|
||||
bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
uint64_t BranchAddr, const Symbol &S) const {
|
||||
// ELF for the ARM 64-bit architecture, section Call and Jump relocations
|
||||
// only permits range extension thunks for R_AARCH64_CALL26 and
|
||||
// R_AARCH64_JUMP26 relocation types.
|
||||
if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
|
||||
return false;
|
||||
uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
|
||||
return !inBranchRange(Type, BranchAddr, Dst);
|
||||
}
|
||||
|
||||
bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
|
||||
if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
|
||||
return true;
|
||||
// The AArch64 call and unconditional branch instructions have a range of
|
||||
// +/- 128 MiB.
|
||||
uint64_t Range = 128 * 1024 * 1024;
|
||||
if (Dst > Src) {
|
||||
// Immediate of branch is signed.
|
||||
Range -= 4;
|
||||
return Dst - Src <= Range;
|
||||
}
|
||||
return Src - Dst <= Range;
|
||||
}
|
||||
|
||||
static void write32AArch64Addr(uint8_t *L, uint64_t Imm) {
|
||||
uint32_t ImmLo = (Imm & 0x3) << 29;
|
||||
uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
|
||||
@ -201,7 +236,7 @@ static void or32AArch64Imm(uint8_t *L, uint64_t Imm) {
|
||||
or32le(L, (Imm & 0xFFF) << 10);
|
||||
}
|
||||
|
||||
void AArch64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_AARCH64_ABS16:
|
||||
case R_AARCH64_PREL16:
|
||||
@ -232,12 +267,23 @@ void AArch64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
checkInt<21>(Loc, Val, Type);
|
||||
write32AArch64Addr(Loc, Val);
|
||||
break;
|
||||
case R_AARCH64_CALL26:
|
||||
case R_AARCH64_JUMP26:
|
||||
// Normally we would just write the bits of the immediate field, however
|
||||
// when patching instructions for the cpu errata fix -fix-cortex-a53-843419
|
||||
// we want to replace a non-branch instruction with a branch immediate
|
||||
// instruction. By writing all the bits of the instruction including the
|
||||
// opcode and the immediate (0 001 | 01 imm26) we can do this
|
||||
// transformation by placing a R_AARCH64_JUMP26 relocation at the offset of
|
||||
// the instruction we want to patch.
|
||||
write32le(Loc, 0x14000000);
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_AARCH64_CALL26:
|
||||
checkInt<28>(Loc, Val, Type);
|
||||
or32le(Loc, (Val & 0x0FFFFFFC) >> 2);
|
||||
break;
|
||||
case R_AARCH64_CONDBR19:
|
||||
case R_AARCH64_LD_PREL_LO19:
|
||||
checkAlignment<4>(Loc, Val, Type);
|
||||
checkInt<21>(Loc, Val, Type);
|
||||
or32le(Loc, (Val & 0x1FFFFC) << 3);
|
||||
break;
|
||||
@ -251,15 +297,19 @@ void AArch64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
or32AArch64Imm(Loc, getBits(Val, 0, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST16_ABS_LO12_NC:
|
||||
checkAlignment<2>(Loc, Val, Type);
|
||||
or32AArch64Imm(Loc, getBits(Val, 1, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST32_ABS_LO12_NC:
|
||||
checkAlignment<4>(Loc, Val, Type);
|
||||
or32AArch64Imm(Loc, getBits(Val, 2, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST64_ABS_LO12_NC:
|
||||
checkAlignment<8>(Loc, Val, Type);
|
||||
or32AArch64Imm(Loc, getBits(Val, 3, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST128_ABS_LO12_NC:
|
||||
checkAlignment<16>(Loc, Val, Type);
|
||||
or32AArch64Imm(Loc, getBits(Val, 4, 11));
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G0_NC:
|
||||
@ -291,7 +341,7 @@ void AArch64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
}
|
||||
}
|
||||
|
||||
void AArch64::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
// TLSDESC Global-Dynamic relocation are in the form:
|
||||
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
|
||||
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
|
||||
@ -321,7 +371,7 @@ void AArch64::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
}
|
||||
}
|
||||
|
||||
void AArch64::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
// TLSDESC Global-Dynamic relocation are in the form:
|
||||
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
|
||||
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
|
||||
@ -352,7 +402,7 @@ void AArch64::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
}
|
||||
}
|
||||
|
||||
void AArch64::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
checkUInt<32>(Loc, Val, Type);
|
||||
|
||||
if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
|
||||
|
@ -7,10 +7,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
@ -25,19 +25,38 @@ namespace {
|
||||
class AMDGPU final : public TargetInfo {
|
||||
public:
|
||||
AMDGPU();
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
uint32_t calcEFlags() const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
AMDGPU::AMDGPU() {
|
||||
RelativeRel = R_AMDGPU_REL64;
|
||||
RelativeRel = R_AMDGPU_RELATIVE64;
|
||||
GotRel = R_AMDGPU_ABS64;
|
||||
GotEntrySize = 8;
|
||||
}
|
||||
|
||||
void AMDGPU::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
static uint32_t getEFlags(InputFile *File) {
|
||||
return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags;
|
||||
}
|
||||
|
||||
uint32_t AMDGPU::calcEFlags() const {
|
||||
assert(!ObjectFiles.empty());
|
||||
uint32_t Ret = getEFlags(ObjectFiles[0]);
|
||||
|
||||
// Verify that all input files have the same e_flags.
|
||||
for (InputFile *F : makeArrayRef(ObjectFiles).slice(1)) {
|
||||
if (Ret == getEFlags(F))
|
||||
continue;
|
||||
error("incompatible e_flags: " + toString(F));
|
||||
return 0;
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_AMDGPU_ABS32:
|
||||
case R_AMDGPU_GOTPCREL:
|
||||
@ -58,7 +77,7 @@ void AMDGPU::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr AMDGPU::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr AMDGPU::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_AMDGPU_ABS32:
|
||||
@ -73,8 +92,7 @@ RelExpr AMDGPU::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
case R_AMDGPU_GOTPCREL32_HI:
|
||||
return R_GOT_PC;
|
||||
default:
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
return R_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
|
189
ELF/Arch/ARM.cpp
189
ELF/Arch/ARM.cpp
@ -7,12 +7,12 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
@ -26,23 +26,23 @@ namespace {
|
||||
class ARM final : public TargetInfo {
|
||||
public:
|
||||
ARM();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
uint32_t calcEFlags() const override;
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
uint32_t getDynRel(uint32_t Type) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
bool isPicRel(RelType Type) const override;
|
||||
RelType getDynRel(RelType Type) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
|
||||
void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
void addPltSymbols(InputSectionBase *IS, uint64_t Off) const override;
|
||||
void addPltHeaderSymbols(InputSectionBase *ISD) const override;
|
||||
bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
|
||||
const SymbolBody &S) const override;
|
||||
bool inBranchRange(uint32_t RelocType, uint64_t Src,
|
||||
uint64_t Dst) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) 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;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -58,18 +58,54 @@ ARM::ARM() {
|
||||
GotEntrySize = 4;
|
||||
GotPltEntrySize = 4;
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 20;
|
||||
PltHeaderSize = 32;
|
||||
TrapInstr = 0xd4d4d4d4;
|
||||
// ARM uses Variant 1 TLS
|
||||
TcbSize = 8;
|
||||
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;
|
||||
}
|
||||
|
||||
RelExpr ARM::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
uint32_t ARM::calcEFlags() const {
|
||||
// We don't currently use any features incompatible with EF_ARM_EABI_VER5,
|
||||
// but we don't have any firm guarantees of conformance. Linux AArch64
|
||||
// kernels (as of 2016) require an EABI version to be set.
|
||||
return EF_ARM_EABI_VER5;
|
||||
}
|
||||
|
||||
RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
case R_ARM_THM_JUMP11:
|
||||
return R_PC;
|
||||
case R_ARM_CALL:
|
||||
@ -120,15 +156,17 @@ RelExpr ARM::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
return R_NONE;
|
||||
case R_ARM_TLS_LE32:
|
||||
return R_TLS;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
bool ARM::isPicRel(uint32_t Type) const {
|
||||
bool ARM::isPicRel(RelType Type) const {
|
||||
return (Type == R_ARM_TARGET1 && !Config->Target1Rel) ||
|
||||
(Type == R_ARM_ABS32);
|
||||
}
|
||||
|
||||
uint32_t ARM::getDynRel(uint32_t Type) const {
|
||||
RelType ARM::getDynRel(RelType Type) const {
|
||||
if (Type == R_ARM_TARGET1 && !Config->Target1Rel)
|
||||
return R_ARM_ABS32;
|
||||
if (Type == R_ARM_ABS32)
|
||||
@ -137,41 +175,74 @@ uint32_t ARM::getDynRel(uint32_t Type) const {
|
||||
return R_ARM_ABS32;
|
||||
}
|
||||
|
||||
void ARM::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
|
||||
void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const {
|
||||
write32le(Buf, InX::Plt->getVA());
|
||||
}
|
||||
|
||||
void ARM::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
|
||||
// An ARM entry is the address of the ifunc resolver function.
|
||||
write32le(Buf, S.getVA());
|
||||
}
|
||||
|
||||
void ARM::writePltHeader(uint8_t *Buf) const {
|
||||
// Long form PLT Heade that does not have any restrictions on the displacement
|
||||
// of the .plt from the .plt.got.
|
||||
static void writePltHeaderLong(uint8_t *Buf) {
|
||||
const uint8_t PltData[] = {
|
||||
0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]!
|
||||
0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2
|
||||
0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr
|
||||
0x08, 0xf0, 0xbe, 0xe5, // ldr pc, [lr, #8]
|
||||
0x00, 0x00, 0x00, 0x00, // L2: .word &(.got.plt) - L1 - 8
|
||||
};
|
||||
0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
|
||||
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;
|
||||
write32le(Buf + 16, GotPlt - L1 - 8);
|
||||
}
|
||||
|
||||
// The default PLT header requires the .plt.got to be within 128 Mb of the
|
||||
// .plt in the positive direction.
|
||||
void ARM::writePltHeader(uint8_t *Buf) const {
|
||||
// Use a similar sequence to that in writePlt(), the difference is the calling
|
||||
// conventions mean we use lr instead of ip. The PLT entry is responsible for
|
||||
// saving lr on the stack, the dynamic loader is responsible for reloading
|
||||
// it.
|
||||
const uint32_t PltData[] = {
|
||||
0xe52de004, // L1: str lr, [sp,#-4]!
|
||||
0xe28fe600, // add lr, pc, #0x0NN00000 &(.got.plt - L1 - 4)
|
||||
0xe28eea00, // add lr, lr, #0x000NN000 &(.got.plt - L1 - 4)
|
||||
0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4)
|
||||
};
|
||||
|
||||
uint64_t Offset = InX::GotPlt->getVA() - InX::Plt->getVA() - 4;
|
||||
if (!llvm::isUInt<27>(Offset)) {
|
||||
// We cannot encode the Offset, use the long form.
|
||||
writePltHeaderLong(Buf);
|
||||
return;
|
||||
}
|
||||
write32le(Buf + 0, PltData[0]);
|
||||
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);
|
||||
}
|
||||
|
||||
void ARM::addPltHeaderSymbols(InputSectionBase *ISD) const {
|
||||
auto *IS = cast<InputSection>(ISD);
|
||||
addSyntheticLocal("$a", STT_NOTYPE, 0, 0, IS);
|
||||
addSyntheticLocal("$d", STT_NOTYPE, 16, 0, IS);
|
||||
}
|
||||
|
||||
void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
// FIXME: Using simple code sequence with simple relocations.
|
||||
// There is a more optimal sequence but it requires support for the group
|
||||
// relocations. See ELF for the ARM Architecture Appendix A.3
|
||||
// Long form PLT entries that do not have any restrictions on the displacement
|
||||
// of the .plt from the .plt.got.
|
||||
static void writePltLong(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) {
|
||||
const uint8_t PltData[] = {
|
||||
0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2
|
||||
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
|
||||
@ -183,24 +254,50 @@ void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
write32le(Buf + 12, GotPltEntryAddr - L1 - 8);
|
||||
}
|
||||
|
||||
// The default PLT entries require the .plt.got to be within 128 Mb of the
|
||||
// .plt in the positive direction.
|
||||
void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
// The PLT entry is similar to the example given in Appendix A of ELF for
|
||||
// the Arm Architecture. Instead of using the Group Relocations to find the
|
||||
// optimal rotation for the 8-bit immediate used in the add instructions we
|
||||
// hard code the most compact rotations for simplicity. This saves a load
|
||||
// instruction over the long plt sequences.
|
||||
const uint32_t PltData[] = {
|
||||
0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.plt.got) - L1 - 8
|
||||
0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.plt.got) - L1 - 8
|
||||
0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8
|
||||
};
|
||||
|
||||
uint64_t Offset = GotPltEntryAddr - PltEntryAddr - 8;
|
||||
if (!llvm::isUInt<27>(Offset)) {
|
||||
// We cannot encode the Offset, use the long form.
|
||||
writePltLong(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff);
|
||||
return;
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
void ARM::addPltSymbols(InputSectionBase *ISD, uint64_t Off) const {
|
||||
auto *IS = cast<InputSection>(ISD);
|
||||
addSyntheticLocal("$a", STT_NOTYPE, Off, 0, IS);
|
||||
addSyntheticLocal("$d", STT_NOTYPE, Off + 12, 0, IS);
|
||||
}
|
||||
|
||||
bool ARM::needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
|
||||
const SymbolBody &S) const {
|
||||
// If S is an undefined weak symbol in an executable we don't need a Thunk.
|
||||
// In a DSO calls to undefined symbols, including weak ones get PLT entries
|
||||
// which may need a thunk.
|
||||
if (S.isUndefined() && !S.isLocal() && S.symbol()->isWeak() &&
|
||||
!Config->Shared)
|
||||
bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
uint64_t BranchAddr, const Symbol &S) const {
|
||||
// If S is an undefined weak symbol and does not have a PLT entry then it
|
||||
// will be resolved as a branch to the next instruction.
|
||||
if (S.isUndefWeak() && !S.isInPlt())
|
||||
return false;
|
||||
// A state change from ARM to Thumb and vice versa must go through an
|
||||
// interworking thunk if the relocation type is not R_ARM_CALL or
|
||||
// R_ARM_THM_CALL.
|
||||
switch (RelocType) {
|
||||
switch (Type) {
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
case R_ARM_JUMP24:
|
||||
@ -208,23 +305,31 @@ bool ARM::needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
|
||||
// Otherwise we need to interwork if Symbol has bit 0 set (Thumb).
|
||||
if (Expr == R_PC && ((S.getVA() & 1) == 1))
|
||||
return true;
|
||||
break;
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_ARM_CALL: {
|
||||
uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
|
||||
return !inBranchRange(Type, BranchAddr, Dst);
|
||||
}
|
||||
case R_ARM_THM_JUMP19:
|
||||
case R_ARM_THM_JUMP24:
|
||||
// Source is Thumb, all PLT entries are ARM so interworking is required.
|
||||
// Otherwise we need to interwork if Symbol has bit 0 clear (ARM).
|
||||
if (Expr == R_PLT_PC || ((S.getVA() & 1) == 0))
|
||||
return true;
|
||||
break;
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_ARM_THM_CALL: {
|
||||
uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
|
||||
return !inBranchRange(Type, BranchAddr, Dst);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ARM::inBranchRange(uint32_t RelocType, uint64_t Src, uint64_t Dst) const {
|
||||
bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
|
||||
uint64_t Range;
|
||||
uint64_t InstrSize;
|
||||
|
||||
switch (RelocType) {
|
||||
switch (Type) {
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
case R_ARM_JUMP24:
|
||||
@ -263,7 +368,7 @@ bool ARM::inBranchRange(uint32_t RelocType, uint64_t Src, uint64_t Dst) const {
|
||||
return Distance <= Range;
|
||||
}
|
||||
|
||||
void ARM::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_BASE_PREL:
|
||||
@ -400,7 +505,7 @@ void ARM::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
}
|
||||
}
|
||||
|
||||
int64_t ARM::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return 0;
|
||||
|
@ -26,10 +26,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
@ -43,24 +43,18 @@ using namespace lld::elf;
|
||||
namespace {
|
||||
class AVR final : public TargetInfo {
|
||||
public:
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
RelExpr AVR::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr AVR::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_AVR_CALL:
|
||||
return R_ABS;
|
||||
default:
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
}
|
||||
return R_ABS;
|
||||
}
|
||||
|
||||
void AVR::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_AVR_CALL: {
|
||||
uint16_t Hi = Val >> 17;
|
||||
|
@ -7,13 +7,13 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
@ -28,19 +28,20 @@ namespace {
|
||||
template <class ELFT> class MIPS final : public TargetInfo {
|
||||
public:
|
||||
MIPS();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
uint32_t calcEFlags() const override;
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
uint32_t getDynRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
|
||||
bool isPicRel(RelType Type) const override;
|
||||
RelType getDynRel(RelType Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
|
||||
const SymbolBody &S) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
bool usesOnlyLowPageBits(uint32_t Type) const override;
|
||||
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
uint64_t BranchAddr, const Symbol &S) const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
bool usesOnlyLowPageBits(RelType Type) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -69,24 +70,39 @@ template <class ELFT> MIPS<ELFT>::MIPS() {
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t MIPS<ELFT>::calcEFlags() const {
|
||||
return calcMipsEFlags<ELFT>();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr MIPS<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
// See comment in the calculateMipsRelChain.
|
||||
if (ELFT::Is64Bits || Config->MipsN32Abi)
|
||||
Type &= 0xff;
|
||||
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
case R_MIPS_JALR:
|
||||
case R_MICROMIPS_JALR:
|
||||
return R_HINT;
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_GPREL32:
|
||||
case R_MICROMIPS_GPREL16:
|
||||
case R_MICROMIPS_GPREL7_S2:
|
||||
return R_MIPS_GOTREL;
|
||||
case R_MIPS_26:
|
||||
case R_MICROMIPS_26_S1:
|
||||
return R_PLT;
|
||||
case R_MICROMIPS_PC26_S1:
|
||||
return R_PLT_PC;
|
||||
case R_MIPS_HI16:
|
||||
case R_MIPS_LO16:
|
||||
case R_MIPS_HIGHER:
|
||||
case R_MIPS_HIGHEST:
|
||||
case R_MICROMIPS_HI16:
|
||||
case R_MICROMIPS_LO16:
|
||||
case R_MICROMIPS_HIGHER:
|
||||
case R_MICROMIPS_HIGHEST:
|
||||
// R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate
|
||||
// offset between start of function and 'gp' value which by default
|
||||
// equal to the start of .got section. In that case we consider these
|
||||
@ -96,7 +112,24 @@ RelExpr MIPS<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
if (&S == ElfSym::MipsLocalGp)
|
||||
return R_MIPS_GOT_GP;
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_MIPS_32:
|
||||
case R_MIPS_64:
|
||||
case R_MIPS_GOT_OFST:
|
||||
case R_MIPS_SUB:
|
||||
case R_MIPS_TLS_DTPREL_HI16:
|
||||
case R_MIPS_TLS_DTPREL_LO16:
|
||||
case R_MIPS_TLS_DTPREL32:
|
||||
case R_MIPS_TLS_DTPREL64:
|
||||
case R_MIPS_TLS_TPREL_HI16:
|
||||
case R_MIPS_TLS_TPREL_LO16:
|
||||
case R_MIPS_TLS_TPREL32:
|
||||
case R_MIPS_TLS_TPREL64:
|
||||
case R_MICROMIPS_GOT_OFST:
|
||||
case R_MICROMIPS_SUB:
|
||||
case R_MICROMIPS_TLS_DTPREL_HI16:
|
||||
case R_MICROMIPS_TLS_DTPREL_LO16:
|
||||
case R_MICROMIPS_TLS_TPREL_HI16:
|
||||
case R_MICROMIPS_TLS_TPREL_LO16:
|
||||
return R_ABS;
|
||||
case R_MIPS_PC32:
|
||||
case R_MIPS_PC16:
|
||||
@ -105,111 +138,171 @@ RelExpr MIPS<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
case R_MIPS_PC26_S2:
|
||||
case R_MIPS_PCHI16:
|
||||
case R_MIPS_PCLO16:
|
||||
case R_MICROMIPS_PC7_S1:
|
||||
case R_MICROMIPS_PC10_S1:
|
||||
case R_MICROMIPS_PC16_S1:
|
||||
case R_MICROMIPS_PC18_S3:
|
||||
case R_MICROMIPS_PC19_S2:
|
||||
case R_MICROMIPS_PC23_S2:
|
||||
case R_MICROMIPS_PC21_S1:
|
||||
return R_PC;
|
||||
case R_MIPS_GOT16:
|
||||
case R_MICROMIPS_GOT16:
|
||||
if (S.isLocal())
|
||||
return R_MIPS_GOT_LOCAL_PAGE;
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_MIPS_CALL16:
|
||||
case R_MIPS_GOT_DISP:
|
||||
case R_MIPS_TLS_GOTTPREL:
|
||||
case R_MICROMIPS_CALL16:
|
||||
case R_MICROMIPS_GOT_DISP:
|
||||
case R_MICROMIPS_TLS_GOTTPREL:
|
||||
return R_MIPS_GOT_OFF;
|
||||
case R_MIPS_CALL_HI16:
|
||||
case R_MIPS_CALL_LO16:
|
||||
case R_MIPS_GOT_HI16:
|
||||
case R_MIPS_GOT_LO16:
|
||||
case R_MICROMIPS_CALL_HI16:
|
||||
case R_MICROMIPS_CALL_LO16:
|
||||
case R_MICROMIPS_GOT_HI16:
|
||||
case R_MICROMIPS_GOT_LO16:
|
||||
return R_MIPS_GOT_OFF32;
|
||||
case R_MIPS_GOT_PAGE:
|
||||
case R_MICROMIPS_GOT_PAGE:
|
||||
return R_MIPS_GOT_LOCAL_PAGE;
|
||||
case R_MIPS_TLS_GD:
|
||||
case R_MICROMIPS_TLS_GD:
|
||||
return R_MIPS_TLSGD;
|
||||
case R_MIPS_TLS_LDM:
|
||||
case R_MICROMIPS_TLS_LDM:
|
||||
return R_MIPS_TLSLD;
|
||||
case R_MIPS_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
return R_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> bool MIPS<ELFT>::isPicRel(uint32_t Type) const {
|
||||
template <class ELFT> bool MIPS<ELFT>::isPicRel(RelType Type) const {
|
||||
return Type == R_MIPS_32 || Type == R_MIPS_64;
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t MIPS<ELFT>::getDynRel(uint32_t Type) const {
|
||||
template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const {
|
||||
return RelativeRel;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
|
||||
write32<ELFT::TargetEndianness>(Buf, InX::Plt->getVA());
|
||||
void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &) const {
|
||||
uint64_t VA = InX::Plt->getVA();
|
||||
if (isMicroMips())
|
||||
VA |= 1;
|
||||
write32<ELFT::TargetEndianness>(Buf, VA);
|
||||
}
|
||||
|
||||
template <endianness E, uint8_t BSIZE, uint8_t SHIFT>
|
||||
static int64_t getPcRelocAddend(const uint8_t *Loc) {
|
||||
template <endianness E> static uint32_t readShuffle(const uint8_t *Loc) {
|
||||
// The major opcode of a microMIPS instruction needs to appear
|
||||
// in the first 16-bit word (lowest address) for efficient hardware
|
||||
// decode so that it knows if the instruction is 16-bit or 32-bit
|
||||
// as early as possible. To do so, little-endian binaries keep 16-bit
|
||||
// words in a big-endian order. That is why we have to swap these
|
||||
// words to get a correct value.
|
||||
uint32_t V = read32<E>(Loc);
|
||||
if (E == support::little)
|
||||
return (V << 16) | (V >> 16);
|
||||
return V;
|
||||
}
|
||||
|
||||
template <endianness E>
|
||||
static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
|
||||
uint8_t Shift) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
uint32_t Mask = 0xffffffff >> (32 - BSIZE);
|
||||
return SignExtend64<BSIZE + SHIFT>((Instr & Mask) << SHIFT);
|
||||
uint32_t Mask = 0xffffffff >> (32 - BitsSize);
|
||||
uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
|
||||
write32<E>(Loc, Data);
|
||||
}
|
||||
|
||||
template <endianness E, uint8_t BSIZE, uint8_t SHIFT>
|
||||
static void applyMipsPcReloc(uint8_t *Loc, uint32_t Type, uint64_t V) {
|
||||
uint32_t Mask = 0xffffffff >> (32 - BSIZE);
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
if (SHIFT > 0)
|
||||
checkAlignment<(1 << SHIFT)>(Loc, V, Type);
|
||||
checkInt<BSIZE + SHIFT>(Loc, V, Type);
|
||||
write32<E>(Loc, (Instr & ~Mask) | ((V >> SHIFT) & Mask));
|
||||
template <endianness E>
|
||||
static void writeMicroRelocation32(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
|
||||
uint8_t Shift) {
|
||||
// See comments in readShuffle for purpose of this code.
|
||||
uint16_t *Words = (uint16_t *)Loc;
|
||||
if (E == support::little)
|
||||
std::swap(Words[0], Words[1]);
|
||||
|
||||
writeRelocation<E>(Loc, V, BitsSize, Shift);
|
||||
|
||||
if (E == support::little)
|
||||
std::swap(Words[0], Words[1]);
|
||||
}
|
||||
|
||||
template <endianness E> static void writeMipsHi16(uint8_t *Loc, uint64_t V) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
uint16_t Res = ((V + 0x8000) >> 16) & 0xffff;
|
||||
write32<E>(Loc, (Instr & 0xffff0000) | Res);
|
||||
}
|
||||
|
||||
template <endianness E> static void writeMipsHigher(uint8_t *Loc, uint64_t V) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
uint16_t Res = ((V + 0x80008000) >> 32) & 0xffff;
|
||||
write32<E>(Loc, (Instr & 0xffff0000) | Res);
|
||||
}
|
||||
|
||||
template <endianness E> static void writeMipsHighest(uint8_t *Loc, uint64_t V) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
uint16_t Res = ((V + 0x800080008000) >> 48) & 0xffff;
|
||||
write32<E>(Loc, (Instr & 0xffff0000) | Res);
|
||||
}
|
||||
|
||||
template <endianness E> static void writeMipsLo16(uint8_t *Loc, uint64_t V) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
write32<E>(Loc, (Instr & 0xffff0000) | (V & 0xffff));
|
||||
}
|
||||
|
||||
template <class ELFT> static bool isMipsR6() {
|
||||
const auto &FirstObj = cast<ELFFileBase<ELFT>>(*Config->FirstElf);
|
||||
uint32_t Arch = FirstObj.getObj().getHeader()->e_flags & EF_MIPS_ARCH;
|
||||
return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6;
|
||||
template <endianness E>
|
||||
static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
|
||||
uint8_t Shift) {
|
||||
uint16_t Instr = read16<E>(Loc);
|
||||
uint16_t Mask = 0xffff >> (16 - BitsSize);
|
||||
uint16_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
|
||||
write16<E>(Loc, Data);
|
||||
}
|
||||
|
||||
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();
|
||||
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
||||
memset(Buf, 0, PltHeaderSize);
|
||||
|
||||
write16<E>(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
|
||||
write16<E>(Buf + 4, 0xff23); // lw $25, 0($3)
|
||||
write16<E>(Buf + 8, 0x0535); // subu16 $2, $2, $3
|
||||
write16<E>(Buf + 10, 0x2525); // srl16 $2, $2, 2
|
||||
write16<E>(Buf + 12, 0x3302); // addiu $24, $2, -2
|
||||
write16<E>(Buf + 14, 0xfffe);
|
||||
write16<E>(Buf + 16, 0x0dff); // move $15, $31
|
||||
if (isMipsR6()) {
|
||||
write16<E>(Buf + 18, 0x0f83); // move $28, $3
|
||||
write16<E>(Buf + 20, 0x472b); // jalrc $25
|
||||
write16<E>(Buf + 22, 0x0c00); // nop
|
||||
relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt);
|
||||
} else {
|
||||
write16<E>(Buf + 18, 0x45f9); // jalrc $25
|
||||
write16<E>(Buf + 20, 0x0f83); // move $28, $3
|
||||
write16<E>(Buf + 22, 0x0c00); // nop
|
||||
relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config->MipsN32Abi) {
|
||||
write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||
write32<E>(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
|
||||
write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||
write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||
write32<E>(Buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
} else if (ELFT::Is64Bits) {
|
||||
write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||
write32<E>(Buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
|
||||
write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||
write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||
write32<E>(Buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<E>(Buf + 20, 0x0018c0c2); // srl $24, $24, 3
|
||||
} else {
|
||||
write32<E>(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
|
||||
write32<E>(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
|
||||
write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
|
||||
write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28
|
||||
write32<E>(Buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
}
|
||||
|
||||
write32<E>(Buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
write32<E>(Buf + 24, 0x0320f809); // jalr $25
|
||||
write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
|
||||
|
||||
uint64_t GotPlt = InX::GotPlt->getVA();
|
||||
writeMipsHi16<E>(Buf, GotPlt);
|
||||
writeMipsLo16<E>(Buf + 4, GotPlt);
|
||||
writeMipsLo16<E>(Buf + 8, GotPlt);
|
||||
writeRelocation<E>(Buf, GotPlt + 0x8000, 16, 16);
|
||||
writeRelocation<E>(Buf + 4, GotPlt, 16, 0);
|
||||
writeRelocation<E>(Buf + 8, GotPlt, 16, 0);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
@ -217,25 +310,45 @@ void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
if (isMicroMips()) {
|
||||
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
||||
memset(Buf, 0, PltEntrySize);
|
||||
|
||||
if (isMipsR6()) {
|
||||
write16<E>(Buf, 0x7840); // addiupc $2, (GOTPLT) - .
|
||||
write16<E>(Buf + 4, 0xff22); // lw $25, 0($2)
|
||||
write16<E>(Buf + 8, 0x0f02); // move $24, $2
|
||||
write16<E>(Buf + 10, 0x4723); // jrc $25 / jr16 $25
|
||||
relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr);
|
||||
} else {
|
||||
write16<E>(Buf, 0x7900); // addiupc $2, (GOTPLT) - .
|
||||
write16<E>(Buf + 4, 0xff22); // lw $25, 0($2)
|
||||
write16<E>(Buf + 8, 0x4599); // jrc $25 / jr16 $25
|
||||
write16<E>(Buf + 10, 0x0f02); // move $24, $2
|
||||
relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
write32<E>(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
|
||||
write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15)
|
||||
// jr $25
|
||||
write32<E>(Buf + 8, isMipsR6<ELFT>() ? 0x03200009 : 0x03200008);
|
||||
write32<E>(Buf + 8, isMipsR6() ? 0x03200009 : 0x03200008); // jr $25
|
||||
write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
|
||||
writeMipsHi16<E>(Buf, GotPltEntryAddr);
|
||||
writeMipsLo16<E>(Buf + 4, GotPltEntryAddr);
|
||||
writeMipsLo16<E>(Buf + 12, GotPltEntryAddr);
|
||||
writeRelocation<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
|
||||
writeRelocation<E>(Buf + 4, GotPltEntryAddr, 16, 0);
|
||||
writeRelocation<E>(Buf + 12, GotPltEntryAddr, 16, 0);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool MIPS<ELFT>::needsThunk(RelExpr Expr, uint32_t Type, const InputFile *File,
|
||||
const SymbolBody &S) const {
|
||||
bool MIPS<ELFT>::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
uint64_t BranchAddr, const Symbol &S) const {
|
||||
// Any MIPS PIC code function is invoked with its address in register $t9.
|
||||
// So if we have a branch instruction from non-PIC code to the PIC one
|
||||
// we cannot make the jump directly and need to create a small stubs
|
||||
// to save the target function address.
|
||||
// See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
|
||||
if (Type != R_MIPS_26)
|
||||
if (Type != R_MIPS_26 && Type != R_MICROMIPS_26_S1 &&
|
||||
Type != R_MICROMIPS_PC26_S1)
|
||||
return false;
|
||||
auto *F = dyn_cast_or_null<ELFFileBase<ELFT>>(File);
|
||||
if (!F)
|
||||
@ -243,18 +356,16 @@ bool MIPS<ELFT>::needsThunk(RelExpr Expr, uint32_t Type, const InputFile *File,
|
||||
// If current file has PIC code, LA25 stub is not required.
|
||||
if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC)
|
||||
return false;
|
||||
auto *D = dyn_cast<DefinedRegular>(&S);
|
||||
auto *D = dyn_cast<Defined>(&S);
|
||||
// LA25 is required if target file has PIC code
|
||||
// or target symbol is a PIC symbol.
|
||||
return D && D->isMipsPIC<ELFT>();
|
||||
return D && isMipsPIC<ELFT>(D);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
switch (Type) {
|
||||
default:
|
||||
return 0;
|
||||
case R_MIPS_32:
|
||||
case R_MIPS_GPREL32:
|
||||
case R_MIPS_TLS_DTPREL32:
|
||||
@ -264,7 +375,11 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
// FIXME (simon): If the relocation target symbol is not a PLT entry
|
||||
// we should use another expression for calculation:
|
||||
// ((A << 2) | (P & 0xf0000000)) >> 2
|
||||
return SignExtend64<28>((read32<E>(Buf) & 0x3ffffff) << 2);
|
||||
return SignExtend64<28>(read32<E>(Buf) << 2);
|
||||
case R_MIPS_GOT16:
|
||||
case R_MIPS_HI16:
|
||||
case R_MIPS_PCHI16:
|
||||
return SignExtend64<16>(read32<E>(Buf)) << 16;
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_LO16:
|
||||
case R_MIPS_PCLO16:
|
||||
@ -273,21 +388,53 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
case R_MIPS_TLS_TPREL_HI16:
|
||||
case R_MIPS_TLS_TPREL_LO16:
|
||||
return SignExtend64<16>(read32<E>(Buf));
|
||||
case R_MICROMIPS_GOT16:
|
||||
case R_MICROMIPS_HI16:
|
||||
return SignExtend64<16>(readShuffle<E>(Buf)) << 16;
|
||||
case R_MICROMIPS_GPREL16:
|
||||
case R_MICROMIPS_LO16:
|
||||
case R_MICROMIPS_TLS_DTPREL_HI16:
|
||||
case R_MICROMIPS_TLS_DTPREL_LO16:
|
||||
case R_MICROMIPS_TLS_TPREL_HI16:
|
||||
case R_MICROMIPS_TLS_TPREL_LO16:
|
||||
return SignExtend64<16>(readShuffle<E>(Buf));
|
||||
case R_MICROMIPS_GPREL7_S2:
|
||||
return SignExtend64<9>(readShuffle<E>(Buf) << 2);
|
||||
case R_MIPS_PC16:
|
||||
return getPcRelocAddend<E, 16, 2>(Buf);
|
||||
return SignExtend64<18>(read32<E>(Buf) << 2);
|
||||
case R_MIPS_PC19_S2:
|
||||
return getPcRelocAddend<E, 19, 2>(Buf);
|
||||
return SignExtend64<21>(read32<E>(Buf) << 2);
|
||||
case R_MIPS_PC21_S2:
|
||||
return getPcRelocAddend<E, 21, 2>(Buf);
|
||||
return SignExtend64<23>(read32<E>(Buf) << 2);
|
||||
case R_MIPS_PC26_S2:
|
||||
return getPcRelocAddend<E, 26, 2>(Buf);
|
||||
return SignExtend64<28>(read32<E>(Buf) << 2);
|
||||
case R_MIPS_PC32:
|
||||
return getPcRelocAddend<E, 32, 0>(Buf);
|
||||
return SignExtend64<32>(read32<E>(Buf));
|
||||
case R_MICROMIPS_26_S1:
|
||||
return SignExtend64<27>(readShuffle<E>(Buf) << 1);
|
||||
case R_MICROMIPS_PC7_S1:
|
||||
return SignExtend64<8>(read16<E>(Buf) << 1);
|
||||
case R_MICROMIPS_PC10_S1:
|
||||
return SignExtend64<11>(read16<E>(Buf) << 1);
|
||||
case R_MICROMIPS_PC16_S1:
|
||||
return SignExtend64<17>(readShuffle<E>(Buf) << 1);
|
||||
case R_MICROMIPS_PC18_S3:
|
||||
return SignExtend64<21>(readShuffle<E>(Buf) << 3);
|
||||
case R_MICROMIPS_PC19_S2:
|
||||
return SignExtend64<21>(readShuffle<E>(Buf) << 2);
|
||||
case R_MICROMIPS_PC21_S1:
|
||||
return SignExtend64<22>(readShuffle<E>(Buf) << 1);
|
||||
case R_MICROMIPS_PC23_S2:
|
||||
return SignExtend64<25>(readShuffle<E>(Buf) << 2);
|
||||
case R_MICROMIPS_PC26_S1:
|
||||
return SignExtend64<27>(readShuffle<E>(Buf) << 1);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<uint32_t, uint64_t>
|
||||
calculateMipsRelChain(uint8_t *Loc, uint32_t Type, uint64_t Val) {
|
||||
calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) {
|
||||
// MIPS N64 ABI packs multiple relocations into the single relocation
|
||||
// record. In general, all up to three relocations can have arbitrary
|
||||
// types. In fact, Clang and GCC uses only a few combinations. For now,
|
||||
@ -300,32 +447,43 @@ calculateMipsRelChain(uint8_t *Loc, uint32_t Type, uint64_t Val) {
|
||||
// relocations used to modify result of the first one: extend it to
|
||||
// 64-bit, extract high or low part etc. For details, see part 2.9 Relocation
|
||||
// at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf
|
||||
uint32_t Type2 = (Type >> 8) & 0xff;
|
||||
uint32_t Type3 = (Type >> 16) & 0xff;
|
||||
RelType Type2 = (Type >> 8) & 0xff;
|
||||
RelType Type3 = (Type >> 16) & 0xff;
|
||||
if (Type2 == R_MIPS_NONE && Type3 == R_MIPS_NONE)
|
||||
return std::make_pair(Type, Val);
|
||||
if (Type2 == R_MIPS_64 && Type3 == R_MIPS_NONE)
|
||||
return std::make_pair(Type2, Val);
|
||||
if (Type2 == R_MIPS_SUB && (Type3 == R_MIPS_HI16 || Type3 == R_MIPS_LO16))
|
||||
return std::make_pair(Type3, -Val);
|
||||
if (Type2 == R_MICROMIPS_SUB &&
|
||||
(Type3 == R_MICROMIPS_HI16 || Type3 == R_MICROMIPS_LO16))
|
||||
return std::make_pair(Type3, -Val);
|
||||
error(getErrorLocation(Loc) + "unsupported relocations combination " +
|
||||
Twine(Type));
|
||||
return std::make_pair(Type & 0xff, Val);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MIPS<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
|
||||
// Thread pointer and DRP offsets from the start of TLS data area.
|
||||
// https://www.linux-mips.org/wiki/NPTL
|
||||
if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 ||
|
||||
Type == R_MIPS_TLS_DTPREL32 || Type == R_MIPS_TLS_DTPREL64)
|
||||
Type == R_MIPS_TLS_DTPREL32 || Type == R_MIPS_TLS_DTPREL64 ||
|
||||
Type == R_MICROMIPS_TLS_DTPREL_HI16 ||
|
||||
Type == R_MICROMIPS_TLS_DTPREL_LO16) {
|
||||
Val -= 0x8000;
|
||||
else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 ||
|
||||
Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64)
|
||||
} else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 ||
|
||||
Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64 ||
|
||||
Type == R_MICROMIPS_TLS_TPREL_HI16 ||
|
||||
Type == R_MICROMIPS_TLS_TPREL_LO16) {
|
||||
Val -= 0x7000;
|
||||
}
|
||||
|
||||
if (ELFT::Is64Bits || Config->MipsN32Abi)
|
||||
std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
|
||||
|
||||
switch (Type) {
|
||||
case R_MIPS_32:
|
||||
case R_MIPS_GPREL32:
|
||||
@ -339,36 +497,65 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
write64<E>(Loc, Val);
|
||||
break;
|
||||
case R_MIPS_26:
|
||||
write32<E>(Loc, (read32<E>(Loc) & ~0x3ffffff) | ((Val >> 2) & 0x3ffffff));
|
||||
writeRelocation<E>(Loc, Val, 26, 2);
|
||||
break;
|
||||
case R_MIPS_GOT16:
|
||||
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
|
||||
// is updated addend (not a GOT index). In that case write high 16 bits
|
||||
// to store a correct addend value.
|
||||
if (Config->Relocatable)
|
||||
writeMipsHi16<E>(Loc, Val);
|
||||
else {
|
||||
if (Config->Relocatable) {
|
||||
writeRelocation<E>(Loc, Val + 0x8000, 16, 16);
|
||||
} else {
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
writeMipsLo16<E>(Loc, Val);
|
||||
writeRelocation<E>(Loc, Val, 16, 0);
|
||||
}
|
||||
break;
|
||||
case R_MICROMIPS_GOT16:
|
||||
if (Config->Relocatable) {
|
||||
writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16);
|
||||
} else {
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
writeMicroRelocation32<E>(Loc, Val, 16, 0);
|
||||
}
|
||||
break;
|
||||
case R_MIPS_CALL16:
|
||||
case R_MIPS_GOT_DISP:
|
||||
case R_MIPS_GOT_PAGE:
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_TLS_GD:
|
||||
case R_MIPS_TLS_GOTTPREL:
|
||||
case R_MIPS_TLS_LDM:
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_MIPS_CALL16:
|
||||
case R_MIPS_CALL_LO16:
|
||||
case R_MIPS_GOT_LO16:
|
||||
case R_MIPS_GOT_OFST:
|
||||
case R_MIPS_LO16:
|
||||
case R_MIPS_PCLO16:
|
||||
case R_MIPS_TLS_DTPREL_LO16:
|
||||
case R_MIPS_TLS_GOTTPREL:
|
||||
case R_MIPS_TLS_TPREL_LO16:
|
||||
writeMipsLo16<E>(Loc, Val);
|
||||
writeRelocation<E>(Loc, Val, 16, 0);
|
||||
break;
|
||||
case R_MICROMIPS_GOT_DISP:
|
||||
case R_MICROMIPS_GOT_PAGE:
|
||||
case R_MICROMIPS_GPREL16:
|
||||
case R_MICROMIPS_TLS_GD:
|
||||
case R_MICROMIPS_TLS_LDM:
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
writeMicroRelocation32<E>(Loc, Val, 16, 0);
|
||||
break;
|
||||
case R_MICROMIPS_CALL16:
|
||||
case R_MICROMIPS_CALL_LO16:
|
||||
case R_MICROMIPS_GOT_OFST:
|
||||
case R_MICROMIPS_LO16:
|
||||
case R_MICROMIPS_TLS_DTPREL_LO16:
|
||||
case R_MICROMIPS_TLS_GOTTPREL:
|
||||
case R_MICROMIPS_TLS_TPREL_LO16:
|
||||
writeMicroRelocation32<E>(Loc, Val, 16, 0);
|
||||
break;
|
||||
case R_MICROMIPS_GPREL7_S2:
|
||||
checkInt<7>(Loc, Val, Type);
|
||||
writeMicroRelocation32<E>(Loc, Val, 7, 2);
|
||||
break;
|
||||
case R_MIPS_CALL_HI16:
|
||||
case R_MIPS_GOT_HI16:
|
||||
@ -376,40 +563,107 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
case R_MIPS_PCHI16:
|
||||
case R_MIPS_TLS_DTPREL_HI16:
|
||||
case R_MIPS_TLS_TPREL_HI16:
|
||||
writeMipsHi16<E>(Loc, Val);
|
||||
writeRelocation<E>(Loc, Val + 0x8000, 16, 16);
|
||||
break;
|
||||
case R_MICROMIPS_CALL_HI16:
|
||||
case R_MICROMIPS_GOT_HI16:
|
||||
case R_MICROMIPS_HI16:
|
||||
case R_MICROMIPS_TLS_DTPREL_HI16:
|
||||
case R_MICROMIPS_TLS_TPREL_HI16:
|
||||
writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16);
|
||||
break;
|
||||
case R_MIPS_HIGHER:
|
||||
writeMipsHigher<E>(Loc, Val);
|
||||
writeRelocation<E>(Loc, Val + 0x80008000, 16, 32);
|
||||
break;
|
||||
case R_MIPS_HIGHEST:
|
||||
writeMipsHighest<E>(Loc, Val);
|
||||
writeRelocation<E>(Loc, Val + 0x800080008000, 16, 48);
|
||||
break;
|
||||
case R_MICROMIPS_HIGHER:
|
||||
writeMicroRelocation32<E>(Loc, Val + 0x80008000, 16, 32);
|
||||
break;
|
||||
case R_MICROMIPS_HIGHEST:
|
||||
writeMicroRelocation32<E>(Loc, Val + 0x800080008000, 16, 48);
|
||||
break;
|
||||
case R_MIPS_JALR:
|
||||
case R_MICROMIPS_JALR:
|
||||
// Ignore this optimization relocation for now
|
||||
break;
|
||||
case R_MIPS_PC16:
|
||||
applyMipsPcReloc<E, 16, 2>(Loc, Type, Val);
|
||||
checkAlignment<4>(Loc, Val, Type);
|
||||
checkInt<18>(Loc, Val, Type);
|
||||
writeRelocation<E>(Loc, Val, 16, 2);
|
||||
break;
|
||||
case R_MIPS_PC19_S2:
|
||||
applyMipsPcReloc<E, 19, 2>(Loc, Type, Val);
|
||||
checkAlignment<4>(Loc, Val, Type);
|
||||
checkInt<21>(Loc, Val, Type);
|
||||
writeRelocation<E>(Loc, Val, 19, 2);
|
||||
break;
|
||||
case R_MIPS_PC21_S2:
|
||||
applyMipsPcReloc<E, 21, 2>(Loc, Type, Val);
|
||||
checkAlignment<4>(Loc, Val, Type);
|
||||
checkInt<23>(Loc, Val, Type);
|
||||
writeRelocation<E>(Loc, Val, 21, 2);
|
||||
break;
|
||||
case R_MIPS_PC26_S2:
|
||||
applyMipsPcReloc<E, 26, 2>(Loc, Type, Val);
|
||||
checkAlignment<4>(Loc, Val, Type);
|
||||
checkInt<28>(Loc, Val, Type);
|
||||
writeRelocation<E>(Loc, Val, 26, 2);
|
||||
break;
|
||||
case R_MIPS_PC32:
|
||||
applyMipsPcReloc<E, 32, 0>(Loc, Type, Val);
|
||||
writeRelocation<E>(Loc, Val, 32, 0);
|
||||
break;
|
||||
case R_MICROMIPS_26_S1:
|
||||
case R_MICROMIPS_PC26_S1:
|
||||
checkInt<27>(Loc, Val, Type);
|
||||
writeMicroRelocation32<E>(Loc, Val, 26, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC7_S1:
|
||||
checkInt<8>(Loc, Val, Type);
|
||||
writeMicroRelocation16<E>(Loc, Val, 7, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC10_S1:
|
||||
checkInt<11>(Loc, Val, Type);
|
||||
writeMicroRelocation16<E>(Loc, Val, 10, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC16_S1:
|
||||
checkInt<17>(Loc, Val, Type);
|
||||
writeMicroRelocation32<E>(Loc, Val, 16, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC18_S3:
|
||||
checkInt<21>(Loc, Val, Type);
|
||||
writeMicroRelocation32<E>(Loc, Val, 18, 3);
|
||||
break;
|
||||
case R_MICROMIPS_PC19_S2:
|
||||
checkInt<21>(Loc, Val, Type);
|
||||
writeMicroRelocation32<E>(Loc, Val, 19, 2);
|
||||
break;
|
||||
case R_MICROMIPS_PC21_S1:
|
||||
checkInt<22>(Loc, Val, Type);
|
||||
writeMicroRelocation32<E>(Loc, Val, 21, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC23_S2:
|
||||
checkInt<25>(Loc, Val, Type);
|
||||
writeMicroRelocation32<E>(Loc, Val, 23, 2);
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool MIPS<ELFT>::usesOnlyLowPageBits(uint32_t Type) const {
|
||||
return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST;
|
||||
template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType Type) const {
|
||||
return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST ||
|
||||
Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_GOT_OFST;
|
||||
}
|
||||
|
||||
// Return true if the symbol is a PIC function.
|
||||
template <class ELFT> bool elf::isMipsPIC(const Defined *Sym) {
|
||||
typedef typename ELFT::Ehdr Elf_Ehdr;
|
||||
if (!Sym->Section || !Sym->isFunc())
|
||||
return false;
|
||||
|
||||
auto *Sec = cast<InputSectionBase>(Sym->Section);
|
||||
const Elf_Ehdr *Hdr = Sec->template getFile<ELFT>()->getObj().getHeader();
|
||||
return (Sym->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC ||
|
||||
(Hdr->e_flags & EF_MIPS_PIC);
|
||||
}
|
||||
|
||||
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
|
||||
@ -421,3 +675,8 @@ template TargetInfo *elf::getMipsTargetInfo<ELF32LE>();
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF32BE>();
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF64LE>();
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF64BE>();
|
||||
|
||||
template bool elf::isMipsPIC<ELF32LE>(const Defined *);
|
||||
template bool elf::isMipsPIC<ELF32BE>(const Defined *);
|
||||
template bool elf::isMipsPIC<ELF64LE>(const Defined *);
|
||||
template bool elf::isMipsPIC<ELF64BE>(const Defined *);
|
||||
|
@ -11,11 +11,11 @@
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/MipsABIFlags.h"
|
||||
@ -34,7 +34,7 @@ struct ArchTreeEdge {
|
||||
};
|
||||
|
||||
struct FileFlags {
|
||||
StringRef Filename;
|
||||
InputFile *File;
|
||||
uint32_t Flags;
|
||||
};
|
||||
} // namespace
|
||||
@ -73,17 +73,17 @@ static void checkFlags(ArrayRef<FileFlags> Files) {
|
||||
uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
|
||||
if (ABI != ABI2)
|
||||
error("target ABI '" + getAbiName(ABI) + "' is incompatible with '" +
|
||||
getAbiName(ABI2) + "': " + F.Filename);
|
||||
getAbiName(ABI2) + "': " + toString(F.File));
|
||||
|
||||
bool Nan2 = F.Flags & EF_MIPS_NAN2008;
|
||||
if (Nan != Nan2)
|
||||
error("target -mnan=" + getNanName(Nan) + " is incompatible with -mnan=" +
|
||||
getNanName(Nan2) + ": " + F.Filename);
|
||||
getNanName(Nan2) + ": " + toString(F.File));
|
||||
|
||||
bool Fp2 = F.Flags & EF_MIPS_FP64;
|
||||
if (Fp != Fp2)
|
||||
error("target -mfp" + getFpName(Fp) + " is incompatible with -mfp" +
|
||||
getFpName(Fp2) + ": " + F.Filename);
|
||||
getFpName(Fp2) + ": " + toString(F.File));
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,9 +102,11 @@ static uint32_t getPicFlags(ArrayRef<FileFlags> Files) {
|
||||
for (const FileFlags &F : Files.slice(1)) {
|
||||
bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
|
||||
if (IsPic && !IsPic2)
|
||||
warn("linking abicalls code with non-abicalls file: " + F.Filename);
|
||||
warn("linking abicalls code " + toString(Files[0].File) +
|
||||
" with non-abicalls file: " + toString(F.File));
|
||||
if (!IsPic && IsPic2)
|
||||
warn("linking non-abicalls code with abicalls file: " + F.Filename);
|
||||
warn("linking non-abicalls code " + toString(Files[0].File) +
|
||||
" with abicalls file: " + toString(F.File));
|
||||
}
|
||||
|
||||
// Compute the result PIC/non-PIC flag.
|
||||
@ -221,10 +223,6 @@ static StringRef getMachName(uint32_t Flags) {
|
||||
}
|
||||
|
||||
static StringRef getArchName(uint32_t Flags) {
|
||||
StringRef S = getMachName(Flags);
|
||||
if (!S.empty())
|
||||
return S;
|
||||
|
||||
switch (Flags & EF_MIPS_ARCH) {
|
||||
case EF_MIPS_ARCH_1:
|
||||
return "mips1";
|
||||
@ -253,6 +251,14 @@ static StringRef getArchName(uint32_t Flags) {
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getFullArchName(uint32_t Flags) {
|
||||
StringRef Arch = getArchName(Flags);
|
||||
StringRef Mach = getMachName(Flags);
|
||||
if (Mach.empty())
|
||||
return Arch.str();
|
||||
return (Arch + " (" + Mach + ")").str();
|
||||
}
|
||||
|
||||
// There are (arguably too) many MIPS ISAs out there. Their relationships
|
||||
// can be represented as a forest. If all input files have ISAs which
|
||||
// reachable by repeated proceeding from the single child to the parent,
|
||||
@ -272,8 +278,9 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> Files) {
|
||||
if (isArchMatched(New, Ret))
|
||||
continue;
|
||||
if (!isArchMatched(Ret, New)) {
|
||||
error("target ISA '" + getArchName(Ret) + "' is incompatible with '" +
|
||||
getArchName(New) + "': " + F.Filename);
|
||||
error("incompatible target ISA:\n>>> " + toString(Files[0].File) + ": " +
|
||||
getFullArchName(Ret) + "\n>>> " + toString(F.File) + ": " +
|
||||
getFullArchName(New));
|
||||
return 0;
|
||||
}
|
||||
Ret = New;
|
||||
@ -281,10 +288,10 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> Files) {
|
||||
return Ret;
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t elf::getMipsEFlags() {
|
||||
template <class ELFT> uint32_t elf::calcMipsEFlags() {
|
||||
std::vector<FileFlags> V;
|
||||
for (elf::ObjectFile<ELFT> *F : Symtab<ELFT>::X->getObjectFiles())
|
||||
V.push_back({F->getName(), F->getObj().getHeader()->e_flags});
|
||||
for (InputFile *F : ObjectFiles)
|
||||
V.push_back({F, cast<ObjFile<ELFT>>(F)->getObj().getHeader()->e_flags});
|
||||
if (V.empty())
|
||||
return 0;
|
||||
checkFlags(V);
|
||||
@ -363,7 +370,14 @@ bool elf::isMipsN32Abi(const InputFile *F) {
|
||||
}
|
||||
}
|
||||
|
||||
template uint32_t elf::getMipsEFlags<ELF32LE>();
|
||||
template uint32_t elf::getMipsEFlags<ELF32BE>();
|
||||
template uint32_t elf::getMipsEFlags<ELF64LE>();
|
||||
template uint32_t elf::getMipsEFlags<ELF64BE>();
|
||||
bool elf::isMicroMips() { return Config->EFlags & EF_MIPS_MICROMIPS; }
|
||||
|
||||
bool elf::isMipsR6() {
|
||||
uint32_t Arch = Config->EFlags & EF_MIPS_ARCH;
|
||||
return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6;
|
||||
}
|
||||
|
||||
template uint32_t elf::calcMipsEFlags<ELF32LE>();
|
||||
template uint32_t elf::calcMipsEFlags<ELF32BE>();
|
||||
template uint32_t elf::calcMipsEFlags<ELF64LE>();
|
||||
template uint32_t elf::calcMipsEFlags<ELF64BE>();
|
||||
|
@ -7,9 +7,9 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -22,17 +22,33 @@ namespace {
|
||||
class PPC final : public TargetInfo {
|
||||
public:
|
||||
PPC() { GotBaseSymOff = 0x8000; }
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void PPC::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_PPC_REL24:
|
||||
case R_PPC_REL32:
|
||||
return R_PC;
|
||||
case R_PPC_PLTREL24:
|
||||
return R_PLT_PC;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_PPC_ADDR16_HA:
|
||||
write16be(Loc, (Val + 0x8000) >> 16);
|
||||
break;
|
||||
case R_PPC_ADDR16_HI:
|
||||
write16be(Loc, Val >> 16);
|
||||
break;
|
||||
case R_PPC_ADDR16_LO:
|
||||
write16be(Loc, Val);
|
||||
break;
|
||||
@ -40,6 +56,7 @@ void PPC::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
case R_PPC_REL32:
|
||||
write32be(Loc, Val);
|
||||
break;
|
||||
case R_PPC_PLTREL24:
|
||||
case R_PPC_REL24:
|
||||
write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC));
|
||||
break;
|
||||
@ -48,17 +65,6 @@ void PPC::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr PPC::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_PPC_REL24:
|
||||
case R_PPC_REL32:
|
||||
return R_PC;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getPPCTargetInfo() {
|
||||
static PPC Target;
|
||||
return &Target;
|
||||
|
@ -7,10 +7,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -39,11 +39,11 @@ namespace {
|
||||
class PPC64 final : public TargetInfo {
|
||||
public:
|
||||
PPC64();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -82,11 +82,9 @@ PPC64::PPC64() {
|
||||
DefaultImageBase = 0x10000000;
|
||||
}
|
||||
|
||||
RelExpr PPC64::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
case R_PPC64_TOC16:
|
||||
case R_PPC64_TOC16_DS:
|
||||
case R_PPC64_TOC16_HA:
|
||||
@ -98,6 +96,8 @@ RelExpr PPC64::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
return R_PPC_TOC;
|
||||
case R_PPC64_REL24:
|
||||
return R_PPC_PLT_OPD;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +122,7 @@ void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
write32be(Buf + 28, 0x4e800420); // bctr
|
||||
}
|
||||
|
||||
static std::pair<uint32_t, uint64_t> toAddr16Rel(uint32_t Type, uint64_t Val) {
|
||||
static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
|
||||
uint64_t V = Val - PPC64TocOffset;
|
||||
switch (Type) {
|
||||
case R_PPC64_TOC16:
|
||||
@ -142,7 +142,7 @@ static std::pair<uint32_t, uint64_t> toAddr16Rel(uint32_t Type, uint64_t Val) {
|
||||
}
|
||||
}
|
||||
|
||||
void PPC64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
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.
|
||||
std::tie(Type, Val) = toAddr16Rel(Type, Val);
|
||||
|
@ -7,11 +7,11 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -24,11 +24,11 @@ namespace {
|
||||
class SPARCV9 final : public TargetInfo {
|
||||
public:
|
||||
SPARCV9();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -46,7 +46,7 @@ SPARCV9::SPARCV9() {
|
||||
DefaultImageBase = 0x100000;
|
||||
}
|
||||
|
||||
RelExpr SPARCV9::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_SPARC_32:
|
||||
@ -68,12 +68,11 @@ RelExpr SPARCV9::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
case R_SPARC_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
return R_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
void SPARCV9::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_SPARC_32:
|
||||
case R_SPARC_UA32:
|
||||
|
137
ELF/Arch/X86.cpp
137
ELF/Arch/X86.cpp
@ -7,11 +7,11 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -24,24 +24,24 @@ namespace {
|
||||
class X86 final : public TargetInfo {
|
||||
public:
|
||||
X86();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
|
||||
void writeGotPltHeader(uint8_t *Buf) const override;
|
||||
uint32_t getDynRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
RelType getDynRel(RelType Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
|
||||
void writeIgotPlt(uint8_t *Buf, const Symbol &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
|
||||
RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
RelExpr Expr) const override;
|
||||
void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) 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 relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -63,7 +63,9 @@ X86::X86() {
|
||||
TrapInstr = 0xcccccccc; // 0xcc = INT3
|
||||
}
|
||||
|
||||
RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; }
|
||||
|
||||
RelExpr X86::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_386_8:
|
||||
@ -87,24 +89,42 @@ RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
return R_GOT;
|
||||
case R_386_GOT32:
|
||||
case R_386_GOT32X:
|
||||
// These relocations can be calculated in two different ways.
|
||||
// Usual calculation is G + A - GOT what means an offset in GOT table
|
||||
// (R_GOT_FROM_END). When instruction pointed by relocation has no base
|
||||
// register, then relocations can be used when PIC code is disabled. In that
|
||||
// case calculation is G + A, it resolves to an address of entry in GOT
|
||||
// (R_GOT) and not an offset.
|
||||
// These relocations are arguably mis-designed because their calculations
|
||||
// depend on the instructions they are applied to. This is bad because we
|
||||
// usually don't care about whether the target section contains valid
|
||||
// machine instructions or not. But this is part of the documented ABI, so
|
||||
// we had to implement as the standard requires.
|
||||
//
|
||||
// To check that instruction has no base register we scan ModR/M byte.
|
||||
// See "Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte"
|
||||
// (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/
|
||||
// 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf)
|
||||
if ((Loc[-1] & 0xc7) != 0x5)
|
||||
return R_GOT_FROM_END;
|
||||
if (Config->Pic)
|
||||
error(toString(S.File) + ": relocation " + toString(Type) + " against '" +
|
||||
S.getName() +
|
||||
"' without base register can not be used when PIC enabled");
|
||||
return R_GOT;
|
||||
// x86 does not support PC-relative data access. Therefore, in order to
|
||||
// access GOT contents, a GOT address needs to be known at link-time
|
||||
// (which means non-PIC) or compilers have to emit code to get a GOT
|
||||
// address at runtime (which means code is position-independent but
|
||||
// compilers need to emit extra code for each GOT access.) This decision
|
||||
// is made at compile-time. In the latter case, compilers emit code to
|
||||
// load an GOT address to a register, which is usually %ebx.
|
||||
//
|
||||
// So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or
|
||||
// foo@GOT(%reg).
|
||||
//
|
||||
// foo@GOT is not usable in PIC. If we are creating a PIC output and if we
|
||||
// find such relocation, we should report an error. foo@GOT is resolved to
|
||||
// an *absolute* address of foo's GOT entry, because both GOT address and
|
||||
// foo's offset are known. In other words, it's G + A.
|
||||
//
|
||||
// foo@GOT(%reg) needs to be resolved to a *relative* offset from a GOT to
|
||||
// foo's GOT entry in the table, because GOT address is not known but foo's
|
||||
// offset in the table is known. It's G + A - GOT.
|
||||
//
|
||||
// It's unfortunate that compilers emit the same relocation for these
|
||||
// different use cases. In order to distinguish them, we have to read a
|
||||
// machine instruction.
|
||||
//
|
||||
// The following code implements it. We assume that Loc[0] is the first
|
||||
// byte of a displacement or an immediate field of a valid machine
|
||||
// instruction. That means a ModRM byte is at Loc[-1]. By taking a look at
|
||||
// the byte, we can determine whether the instruction is register-relative
|
||||
// (i.e. it was generated for foo@GOT(%reg)) or absolute (i.e. foo@GOT).
|
||||
return hasBaseReg(Loc[-1]) ? R_GOT_FROM_END : R_GOT;
|
||||
case R_386_TLS_GOTIE:
|
||||
return R_GOT_FROM_END;
|
||||
case R_386_GOTOFF:
|
||||
@ -116,12 +136,11 @@ RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
case R_386_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
return R_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr X86::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
RelExpr Expr) const {
|
||||
switch (Expr) {
|
||||
default:
|
||||
@ -137,18 +156,18 @@ void X86::writeGotPltHeader(uint8_t *Buf) const {
|
||||
write32le(Buf, InX::Dynamic->getVA());
|
||||
}
|
||||
|
||||
void X86::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
|
||||
// Entries in .got.plt initially points back to the corresponding
|
||||
// PLT entries with a fixed offset to skip the first instruction.
|
||||
write32le(Buf, S.getPltVA() + 6);
|
||||
}
|
||||
|
||||
void X86::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
|
||||
// An x86 entry is the address of the ifunc resolver function.
|
||||
write32le(Buf, S.getVA());
|
||||
}
|
||||
|
||||
uint32_t X86::getDynRel(uint32_t Type) const {
|
||||
RelType X86::getDynRel(RelType Type) const {
|
||||
if (Type == R_386_TLS_LE)
|
||||
return R_386_TLS_TPOFF;
|
||||
if (Type == R_386_TLS_LE_32)
|
||||
@ -208,10 +227,8 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
|
||||
}
|
||||
|
||||
int64_t X86::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return 0;
|
||||
case R_386_8:
|
||||
case R_386_PC8:
|
||||
return SignExtend64<8>(*Buf);
|
||||
@ -228,15 +245,17 @@ int64_t X86::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
case R_386_TLS_LDO_32:
|
||||
case R_386_TLS_LE:
|
||||
return SignExtend64<32>(read32le(Buf));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void X86::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
// R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
|
||||
// being used for some 16-bit programs such as boot loaders, so
|
||||
// we want to support them.
|
||||
void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_386_8:
|
||||
// R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
|
||||
// being used for some 16-bit programs such as boot loaders, so
|
||||
// we want to support them.
|
||||
checkUInt<8>(Loc, Val, Type);
|
||||
*Loc = Val;
|
||||
break;
|
||||
@ -262,13 +281,35 @@ void X86::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
checkInt<17>(Loc, Val, Type);
|
||||
write16le(Loc, Val);
|
||||
break;
|
||||
default:
|
||||
case R_386_32:
|
||||
case R_386_GLOB_DAT:
|
||||
case R_386_GOT32:
|
||||
case R_386_GOT32X:
|
||||
case R_386_GOTOFF:
|
||||
case R_386_GOTPC:
|
||||
case R_386_PC32:
|
||||
case R_386_PLT32:
|
||||
case R_386_RELATIVE:
|
||||
case R_386_TLS_DTPMOD32:
|
||||
case R_386_TLS_DTPOFF32:
|
||||
case R_386_TLS_GD:
|
||||
case R_386_TLS_GOTIE:
|
||||
case R_386_TLS_IE:
|
||||
case R_386_TLS_LDM:
|
||||
case R_386_TLS_LDO_32:
|
||||
case R_386_TLS_LE:
|
||||
case R_386_TLS_LE_32:
|
||||
case R_386_TLS_TPOFF:
|
||||
case R_386_TLS_TPOFF32:
|
||||
checkInt<32>(Loc, Val, Type);
|
||||
write32le(Loc, Val);
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
void X86::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void X86::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
// Convert
|
||||
// leal x@tlsgd(, %ebx, 1),
|
||||
// call __tls_get_addr@plt
|
||||
@ -283,7 +324,7 @@ void X86::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
write32le(Loc + 5, Val);
|
||||
}
|
||||
|
||||
void X86::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void X86::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
// Convert
|
||||
// leal x@tlsgd(, %ebx, 1),
|
||||
// call __tls_get_addr@plt
|
||||
@ -300,7 +341,7 @@ void X86::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
|
||||
// In some conditions, relocations can be optimized to avoid using GOT.
|
||||
// This function does that for Initial Exec to Local Exec case.
|
||||
void X86::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void X86::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
// Ulrich's document section 6.2 says that @gotntpoff can
|
||||
// be used with MOVL or ADDL instructions.
|
||||
// @indntpoff is similar to @gotntpoff, but for use in
|
||||
@ -337,7 +378,7 @@ void X86::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
write32le(Loc, Val);
|
||||
}
|
||||
|
||||
void X86::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
if (Type == R_386_TLS_LDO_32) {
|
||||
write32le(Loc, Val);
|
||||
return;
|
||||
|
@ -7,11 +7,11 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
@ -26,23 +26,23 @@ namespace {
|
||||
template <class ELFT> class X86_64 final : public TargetInfo {
|
||||
public:
|
||||
X86_64();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
bool isPicRel(RelType Type) const override;
|
||||
void writeGotPltHeader(uint8_t *Buf) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
|
||||
RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
RelExpr Expr) const override;
|
||||
void relaxGot(uint8_t *Loc, uint64_t Val) const override;
|
||||
void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) 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 relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
|
||||
|
||||
private:
|
||||
void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
|
||||
@ -73,7 +73,7 @@ template <class ELFT> X86_64<ELFT>::X86_64() {
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr X86_64<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_X86_64_8:
|
||||
@ -109,8 +109,7 @@ RelExpr X86_64<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
case R_X86_64_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
return R_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,8 +122,8 @@ template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const {
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
// See comments in X86TargetInfo::writeGotPlt.
|
||||
void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
|
||||
// See comments in X86::writeGotPlt.
|
||||
write32le(Buf, S.getPltVA() + 6);
|
||||
}
|
||||
|
||||
@ -157,13 +156,13 @@ void X86_64<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
|
||||
}
|
||||
|
||||
template <class ELFT> bool X86_64<ELFT>::isPicRel(uint32_t Type) const {
|
||||
template <class ELFT> bool X86_64<ELFT>::isPicRel(RelType Type) const {
|
||||
return Type != R_X86_64_PC32 && Type != R_X86_64_32 &&
|
||||
Type != R_X86_64_TPOFF32;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
|
||||
void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, RelType Type,
|
||||
uint64_t Val) const {
|
||||
// Convert
|
||||
// .byte 0x66
|
||||
@ -186,7 +185,7 @@ void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
|
||||
void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, RelType Type,
|
||||
uint64_t Val) const {
|
||||
// Convert
|
||||
// .byte 0x66
|
||||
@ -211,7 +210,7 @@ void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
|
||||
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
|
||||
// R_X86_64_TPOFF32 so that it does not use GOT.
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
|
||||
void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, RelType Type,
|
||||
uint64_t Val) const {
|
||||
uint8_t *Inst = Loc - 3;
|
||||
uint8_t Reg = Loc[-1] >> 3;
|
||||
@ -254,7 +253,7 @@ void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
|
||||
void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, RelType Type,
|
||||
uint64_t Val) const {
|
||||
// Convert
|
||||
// leaq bar@tlsld(%rip), %rdi
|
||||
@ -283,8 +282,7 @@ void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_X86_64_8:
|
||||
checkUInt<8>(Loc, Val, Type);
|
||||
@ -323,12 +321,12 @@ void X86_64<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
|
||||
write64le(Loc, Val);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unexpected relocation");
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr X86_64<ELFT>::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr X86_64<ELFT>::adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
RelExpr RelExpr) const {
|
||||
if (Type != R_X86_64_GOTPCRELX && Type != R_X86_64_REX_GOTPCRELX)
|
||||
return RelExpr;
|
||||
|
35
ELF/Bits.h
Normal file
35
ELF/Bits.h
Normal file
@ -0,0 +1,35 @@
|
||||
//===- Bits.h ---------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_BITS_H
|
||||
#define LLD_ELF_BITS_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
inline uint64_t readUint(uint8_t *Buf) {
|
||||
if (Config->Is64)
|
||||
return llvm::support::endian::read64(Buf, Config->Endianness);
|
||||
return llvm::support::endian::read32(Buf, Config->Endianness);
|
||||
}
|
||||
|
||||
inline void writeUint(uint8_t *Buf, uint64_t Val) {
|
||||
if (Config->Is64)
|
||||
llvm::support::endian::write64(Buf, Val, Config->Endianness);
|
||||
else
|
||||
llvm::support::endian::write32(Buf, Val, Config->Endianness);
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
@ -7,6 +7,7 @@ if(NOT LLD_BUILT_STANDALONE)
|
||||
endif()
|
||||
|
||||
add_lld_library(lldELF
|
||||
AArch64ErrataFix.cpp
|
||||
Arch/AArch64.cpp
|
||||
Arch/AMDGPU.cpp
|
||||
Arch/ARM.cpp
|
||||
@ -21,7 +22,6 @@ add_lld_library(lldELF
|
||||
Driver.cpp
|
||||
DriverUtils.cpp
|
||||
EhFrame.cpp
|
||||
Error.cpp
|
||||
Filesystem.cpp
|
||||
GdbIndex.cpp
|
||||
ICF.cpp
|
||||
@ -45,28 +45,17 @@ add_lld_library(lldELF
|
||||
|
||||
LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
Analysis
|
||||
BinaryFormat
|
||||
BitReader
|
||||
BitWriter
|
||||
Codegen
|
||||
Core
|
||||
DebugInfoDWARF
|
||||
Demangle
|
||||
IPO
|
||||
Linker
|
||||
LTO
|
||||
MC
|
||||
Object
|
||||
Option
|
||||
Passes
|
||||
MC
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
|
||||
LINK_LIBS
|
||||
lldConfig
|
||||
lldCore
|
||||
lldCommon
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
|
40
ELF/Config.h
40
ELF/Config.h
@ -24,7 +24,6 @@ namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class InputFile;
|
||||
struct Symbol;
|
||||
|
||||
enum ELFKind {
|
||||
ELFNoneKind,
|
||||
@ -44,7 +43,10 @@ enum class DiscardPolicy { Default, All, Locals, None };
|
||||
enum class StripPolicy { None, All, Debug };
|
||||
|
||||
// For --unresolved-symbols.
|
||||
enum class UnresolvedPolicy { ReportError, Warn, WarnAll, Ignore, IgnoreAll };
|
||||
enum class UnresolvedPolicy { ReportError, Warn, Ignore, IgnoreAll };
|
||||
|
||||
// For --orphan-handling.
|
||||
enum class OrphanHandlingPolicy { Place, Warn, Error };
|
||||
|
||||
// For --sort-section and linkerscript sorting rules.
|
||||
enum class SortSectionPolicy { Default, None, Alignment, Name, Priority };
|
||||
@ -67,21 +69,15 @@ struct VersionDefinition {
|
||||
size_t NameOff = 0; // Offset in the string table
|
||||
};
|
||||
|
||||
// Structure for mapping renamed symbols
|
||||
struct RenamedSymbol {
|
||||
Symbol *Target;
|
||||
uint8_t OriginalBinding;
|
||||
};
|
||||
|
||||
// This struct contains the global configuration for the linker.
|
||||
// Most fields are direct mapping from the command line options
|
||||
// and such fields have the same name as the corresponding options.
|
||||
// Most fields are initialized by the driver.
|
||||
struct Configuration {
|
||||
InputFile *FirstElf = nullptr;
|
||||
uint8_t OSABI = 0;
|
||||
llvm::CachePruningPolicy ThinLTOCachePolicy;
|
||||
llvm::StringMap<uint64_t> SectionStartMap;
|
||||
llvm::StringRef Chroot;
|
||||
llvm::StringRef DynamicLinker;
|
||||
llvm::StringRef Entry;
|
||||
llvm::StringRef Emulation;
|
||||
@ -103,15 +99,18 @@ struct Configuration {
|
||||
std::vector<llvm::StringRef> SearchPaths;
|
||||
std::vector<llvm::StringRef> SymbolOrderingFile;
|
||||
std::vector<llvm::StringRef> Undefined;
|
||||
std::vector<SymbolVersion> DynamicList;
|
||||
std::vector<SymbolVersion> VersionScriptGlobals;
|
||||
std::vector<SymbolVersion> VersionScriptLocals;
|
||||
std::vector<uint8_t> BuildIdVector;
|
||||
llvm::MapVector<Symbol *, RenamedSymbol> RenamedSymbols;
|
||||
bool AllowMultipleDefinition;
|
||||
bool AndroidPackDynRelocs = false;
|
||||
bool ARMHasBlx = false;
|
||||
bool ARMHasMovtMovw = false;
|
||||
bool ARMJ1J2BranchEncoding = false;
|
||||
bool AsNeeded = false;
|
||||
bool Bsymbolic;
|
||||
bool BsymbolicFunctions;
|
||||
bool ColorDiagnostics = false;
|
||||
bool CompressDebugSections;
|
||||
bool DefineCommon;
|
||||
bool Demangle = true;
|
||||
@ -120,14 +119,19 @@ struct Configuration {
|
||||
bool EmitRelocs;
|
||||
bool EnableNewDtags;
|
||||
bool ExportDynamic;
|
||||
bool FatalWarnings;
|
||||
bool FixCortexA53Errata843419;
|
||||
bool GcSections;
|
||||
bool GdbIndex;
|
||||
bool GnuHash;
|
||||
bool GnuHash = false;
|
||||
bool HasDynamicList = false;
|
||||
bool HasDynSymTab;
|
||||
bool ICF;
|
||||
bool ICFData;
|
||||
bool MergeArmExidx;
|
||||
bool MipsN32Abi = false;
|
||||
bool NoGnuUnique;
|
||||
bool NoUndefinedVersion;
|
||||
bool NoinhibitExec;
|
||||
bool Nostdlib;
|
||||
bool OFormatBinary;
|
||||
bool Omagic;
|
||||
@ -139,9 +143,8 @@ struct Configuration {
|
||||
bool SingleRoRx;
|
||||
bool Shared;
|
||||
bool Static = false;
|
||||
bool SysvHash;
|
||||
bool SysvHash = false;
|
||||
bool Target1Rel;
|
||||
bool Threads;
|
||||
bool Trace;
|
||||
bool Verbose;
|
||||
bool WarnCommon;
|
||||
@ -159,6 +162,7 @@ struct Configuration {
|
||||
bool ExitEarly;
|
||||
bool ZWxneeded;
|
||||
DiscardPolicy Discard;
|
||||
OrphanHandlingPolicy OrphanHandling;
|
||||
SortSectionPolicy SortSection;
|
||||
StripPolicy Strip;
|
||||
UnresolvedPolicy UnresolvedSymbols;
|
||||
@ -167,8 +171,7 @@ struct Configuration {
|
||||
ELFKind EKind = ELFNoneKind;
|
||||
uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
|
||||
uint16_t EMachine = llvm::ELF::EM_NONE;
|
||||
uint64_t ErrorLimit = 20;
|
||||
uint64_t ImageBase;
|
||||
llvm::Optional<uint64_t> ImageBase;
|
||||
uint64_t MaxPageSize;
|
||||
uint64_t ZStackSize;
|
||||
unsigned LTOPartitions;
|
||||
@ -206,6 +209,9 @@ struct Configuration {
|
||||
// if that's true.)
|
||||
bool IsMips64EL;
|
||||
|
||||
// Holds set of ELF header flags for the target.
|
||||
uint32_t EFlags = 0;
|
||||
|
||||
// The ELF spec defines two types of relocation table entries, RELA and
|
||||
// REL. RELA is a triplet of (offset, info, addend) while REL is a
|
||||
// tuple of (offset, info). Addends for REL are implicit and read from
|
||||
|
485
ELF/Driver.cpp
485
ELF/Driver.cpp
@ -25,23 +25,25 @@
|
||||
|
||||
#include "Driver.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "Filesystem.h"
|
||||
#include "ICF.h"
|
||||
#include "InputFiles.h"
|
||||
#include "InputSection.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "OutputSections.h"
|
||||
#include "ScriptParser.h"
|
||||
#include "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Threads.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Config/Version.h"
|
||||
#include "lld/Driver/Driver.h"
|
||||
#include "lld/Common/Args.h"
|
||||
#include "lld/Common/Driver.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "lld/Common/Version.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
@ -64,27 +66,40 @@ using namespace lld::elf;
|
||||
Configuration *elf::Config;
|
||||
LinkerDriver *elf::Driver;
|
||||
|
||||
BumpPtrAllocator elf::BAlloc;
|
||||
StringSaver elf::Saver{BAlloc};
|
||||
std::vector<SpecificAllocBase *> elf::SpecificAllocBase::Instances;
|
||||
|
||||
static void setConfigs();
|
||||
|
||||
bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
|
||||
raw_ostream &Error) {
|
||||
ErrorCount = 0;
|
||||
ErrorOS = &Error;
|
||||
errorHandler().LogName = Args[0];
|
||||
errorHandler().ErrorLimitExceededMsg =
|
||||
"too many errors emitted, stopping now (use "
|
||||
"-error-limit=0 to see all errors)";
|
||||
errorHandler().ErrorOS = &Error;
|
||||
errorHandler().ColorDiagnostics = Error.has_colors();
|
||||
InputSections.clear();
|
||||
OutputSections.clear();
|
||||
Tar = nullptr;
|
||||
BinaryFiles.clear();
|
||||
BitcodeFiles.clear();
|
||||
ObjectFiles.clear();
|
||||
SharedFiles.clear();
|
||||
|
||||
Config = make<Configuration>();
|
||||
Driver = make<LinkerDriver>();
|
||||
Script = make<LinkerScript>();
|
||||
Symtab = make<SymbolTable>();
|
||||
Config->Argv = {Args.begin(), Args.end()};
|
||||
|
||||
Driver->main(Args, CanExitEarly);
|
||||
|
||||
// Exit immediately if we don't need to return to the caller.
|
||||
// This saves time because the overhead of calling destructors
|
||||
// for all globally-allocated objects is not negligible.
|
||||
if (Config->ExitEarly)
|
||||
exitLld(errorCount() ? 1 : 0);
|
||||
|
||||
freeArena();
|
||||
return !ErrorCount;
|
||||
return !errorCount();
|
||||
}
|
||||
|
||||
// Parses a linker -m option.
|
||||
@ -112,12 +127,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
|
||||
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
|
||||
.Default({ELFNoneKind, EM_NONE});
|
||||
|
||||
if (Ret.first == ELFNoneKind) {
|
||||
if (S == "i386pe" || S == "i386pep" || S == "thumb2pe")
|
||||
error("Windows targets are not supported on the ELF frontend: " + Emul);
|
||||
else
|
||||
error("unknown emulation: " + Emul);
|
||||
}
|
||||
if (Ret.first == ELFNoneKind)
|
||||
error("unknown emulation: " + Emul);
|
||||
return std::make_tuple(Ret.first, Ret.second, OSABI);
|
||||
}
|
||||
|
||||
@ -126,19 +137,22 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
|
||||
std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers(
|
||||
MemoryBufferRef MB) {
|
||||
std::unique_ptr<Archive> File =
|
||||
check(Archive::create(MB),
|
||||
CHECK(Archive::create(MB),
|
||||
MB.getBufferIdentifier() + ": failed to parse archive");
|
||||
|
||||
std::vector<std::pair<MemoryBufferRef, uint64_t>> V;
|
||||
Error Err = Error::success();
|
||||
bool AddToTar = File->isThin() && Tar;
|
||||
for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
|
||||
Archive::Child C =
|
||||
check(COrErr, MB.getBufferIdentifier() +
|
||||
CHECK(COrErr, MB.getBufferIdentifier() +
|
||||
": could not get the child of the archive");
|
||||
MemoryBufferRef MBRef =
|
||||
check(C.getMemoryBufferRef(),
|
||||
CHECK(C.getMemoryBufferRef(),
|
||||
MB.getBufferIdentifier() +
|
||||
": could not get the buffer for a child of the archive");
|
||||
if (AddToTar)
|
||||
Tar->append(relativeToRoot(check(C.getFullName())), MBRef.getBuffer());
|
||||
V.push_back(std::make_pair(MBRef, C.getChildOffset()));
|
||||
}
|
||||
if (Err)
|
||||
@ -179,7 +193,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
|
||||
}
|
||||
|
||||
std::unique_ptr<Archive> File =
|
||||
check(Archive::create(MBRef), Path + ": failed to parse archive");
|
||||
CHECK(Archive::create(MBRef), Path + ": failed to parse archive");
|
||||
|
||||
// If an archive file has no symbol table, it is likely that a user
|
||||
// is attempting LTO and using a default ar command that doesn't
|
||||
@ -187,7 +201,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
|
||||
// we'll handle it as if it had a symbol table.
|
||||
if (!File->isEmpty() && !File->hasSymbolTable()) {
|
||||
for (const auto &P : getArchiveMembers(MBRef))
|
||||
Files.push_back(make<LazyObjectFile>(P.first, Path, P.second));
|
||||
Files.push_back(make<LazyObjFile>(P.first, Path, P.second));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -216,7 +230,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
|
||||
return;
|
||||
default:
|
||||
if (InLib)
|
||||
Files.push_back(make<LazyObjectFile>(MBRef, "", 0));
|
||||
Files.push_back(make<LazyObjFile>(MBRef, "", 0));
|
||||
else
|
||||
Files.push_back(createObjectFile(MBRef));
|
||||
}
|
||||
@ -256,6 +270,9 @@ static void checkOptions(opt::InputArgList &Args) {
|
||||
if (Config->EMachine == EM_MIPS && Config->GnuHash)
|
||||
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.");
|
||||
|
||||
if (Config->Pie && Config->Shared)
|
||||
error("-shared and -pie may not be used together");
|
||||
|
||||
@ -265,6 +282,9 @@ static void checkOptions(opt::InputArgList &Args) {
|
||||
if (!Config->Shared && !Config->AuxiliaryList.empty())
|
||||
error("-f may not be used without -shared");
|
||||
|
||||
if (!Config->Relocatable && !Config->DefineCommon)
|
||||
error("-no-define-common not supported in non relocatable output");
|
||||
|
||||
if (Config->Relocatable) {
|
||||
if (Config->Shared)
|
||||
error("-r and -shared may not be used together");
|
||||
@ -277,16 +297,6 @@ static void checkOptions(opt::InputArgList &Args) {
|
||||
}
|
||||
}
|
||||
|
||||
static int getInteger(opt::InputArgList &Args, unsigned Key, int Default) {
|
||||
int V = Default;
|
||||
if (auto *Arg = Args.getLastArg(Key)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (!to_integer(S, V, 10))
|
||||
error(Arg->getSpelling() + ": number expected, but got " + S);
|
||||
}
|
||||
return V;
|
||||
}
|
||||
|
||||
static const char *getReproduceOption(opt::InputArgList &Args) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_reproduce))
|
||||
return Arg->getValue();
|
||||
@ -300,26 +310,12 @@ static bool hasZOption(opt::InputArgList &Args, StringRef Key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint64_t getZOptionValue(opt::InputArgList &Args, StringRef Key,
|
||||
uint64_t Default) {
|
||||
for (auto *Arg : Args.filtered(OPT_z)) {
|
||||
std::pair<StringRef, StringRef> KV = StringRef(Arg->getValue()).split('=');
|
||||
if (KV.first == Key) {
|
||||
uint64_t Result = Default;
|
||||
if (!to_integer(KV.second, Result))
|
||||
error("invalid " + Key + ": " + KV.second);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
return Default;
|
||||
}
|
||||
|
||||
void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
|
||||
ELFOptTable Parser;
|
||||
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
|
||||
|
||||
// Interpret this flag early because error() depends on them.
|
||||
Config->ErrorLimit = getInteger(Args, OPT_error_limit, 20);
|
||||
errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20);
|
||||
|
||||
// Handle -help
|
||||
if (Args.hasArg(OPT_help)) {
|
||||
@ -345,13 +341,15 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
|
||||
if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version))
|
||||
message(getLLDVersion() + " (compatible with GNU linkers)");
|
||||
|
||||
// ld.bfd always exits after printing out the version string.
|
||||
// ld.gold proceeds if a given option is -v. Because gold's behavior
|
||||
// is more permissive than ld.bfd, we chose what gold does here.
|
||||
// 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))
|
||||
return;
|
||||
if (Args.hasArg(OPT_version))
|
||||
return;
|
||||
|
||||
Config->ExitEarly = CanExitEarly && !Args.hasArg(OPT_full_shutdown);
|
||||
errorHandler().ExitEarly = Config->ExitEarly;
|
||||
|
||||
if (const char *Path = getReproduceOption(Args)) {
|
||||
// Note that --reproduce is a debug option so you can ignore it
|
||||
@ -375,7 +373,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
|
||||
inferMachineType();
|
||||
setConfigs();
|
||||
checkOptions(Args);
|
||||
if (ErrorCount)
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
switch (Config->EKind) {
|
||||
@ -396,36 +394,19 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool getArg(opt::InputArgList &Args, unsigned K1, unsigned K2,
|
||||
bool Default) {
|
||||
if (auto *Arg = Args.getLastArg(K1, K2))
|
||||
return Arg->getOption().getID() == K1;
|
||||
return Default;
|
||||
}
|
||||
|
||||
static std::vector<StringRef> getArgs(opt::InputArgList &Args, int Id) {
|
||||
std::vector<StringRef> V;
|
||||
for (auto *Arg : Args.filtered(Id))
|
||||
V.push_back(Arg->getValue());
|
||||
return V;
|
||||
}
|
||||
|
||||
static std::string getRpath(opt::InputArgList &Args) {
|
||||
std::vector<StringRef> V = getArgs(Args, OPT_rpath);
|
||||
std::vector<StringRef> V = args::getStrings(Args, OPT_rpath);
|
||||
return llvm::join(V.begin(), V.end(), ":");
|
||||
}
|
||||
|
||||
// Determines what we should do if there are remaining unresolved
|
||||
// symbols after the name resolution.
|
||||
static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) {
|
||||
// -noinhibit-exec or -r imply some default values.
|
||||
if (Args.hasArg(OPT_noinhibit_exec))
|
||||
return UnresolvedPolicy::WarnAll;
|
||||
if (Args.hasArg(OPT_relocatable))
|
||||
return UnresolvedPolicy::IgnoreAll;
|
||||
|
||||
UnresolvedPolicy ErrorOrWarn = getArg(Args, OPT_error_unresolved_symbols,
|
||||
OPT_warn_unresolved_symbols, true)
|
||||
UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols,
|
||||
OPT_warn_unresolved_symbols, true)
|
||||
? UnresolvedPolicy::ReportError
|
||||
: UnresolvedPolicy::Warn;
|
||||
|
||||
@ -513,7 +494,7 @@ static StripPolicy getStrip(opt::InputArgList &Args) {
|
||||
return StripPolicy::Debug;
|
||||
}
|
||||
|
||||
static uint64_t parseSectionAddress(StringRef S, opt::Arg *Arg) {
|
||||
static uint64_t parseSectionAddress(StringRef S, const opt::Arg &Arg) {
|
||||
uint64_t VA = 0;
|
||||
if (S.startswith("0x"))
|
||||
S = S.drop_front(2);
|
||||
@ -528,15 +509,15 @@ static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &Args) {
|
||||
StringRef Name;
|
||||
StringRef Addr;
|
||||
std::tie(Name, Addr) = StringRef(Arg->getValue()).split('=');
|
||||
Ret[Name] = parseSectionAddress(Addr, Arg);
|
||||
Ret[Name] = parseSectionAddress(Addr, *Arg);
|
||||
}
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_Ttext))
|
||||
Ret[".text"] = parseSectionAddress(Arg->getValue(), Arg);
|
||||
Ret[".text"] = parseSectionAddress(Arg->getValue(), *Arg);
|
||||
if (auto *Arg = Args.getLastArg(OPT_Tdata))
|
||||
Ret[".data"] = parseSectionAddress(Arg->getValue(), Arg);
|
||||
Ret[".data"] = parseSectionAddress(Arg->getValue(), *Arg);
|
||||
if (auto *Arg = Args.getLastArg(OPT_Tbss))
|
||||
Ret[".bss"] = parseSectionAddress(Arg->getValue(), Arg);
|
||||
Ret[".bss"] = parseSectionAddress(Arg->getValue(), *Arg);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
@ -551,15 +532,15 @@ static SortSectionPolicy getSortSection(opt::InputArgList &Args) {
|
||||
return SortSectionPolicy::Default;
|
||||
}
|
||||
|
||||
static std::pair<bool, bool> getHashStyle(opt::InputArgList &Args) {
|
||||
StringRef S = Args.getLastArgValue(OPT_hash_style, "sysv");
|
||||
if (S == "sysv")
|
||||
return {true, false};
|
||||
if (S == "gnu")
|
||||
return {false, true};
|
||||
if (S != "both")
|
||||
error("unknown -hash-style: " + S);
|
||||
return {true, true};
|
||||
static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) {
|
||||
StringRef S = Args.getLastArgValue(OPT_orphan_handling, "place");
|
||||
if (S == "warn")
|
||||
return OrphanHandlingPolicy::Warn;
|
||||
if (S == "error")
|
||||
return OrphanHandlingPolicy::Error;
|
||||
if (S != "place")
|
||||
error("unknown --orphan-handling mode: " + S);
|
||||
return OrphanHandlingPolicy::Place;
|
||||
}
|
||||
|
||||
// Parse --build-id or --build-id=<style>. We handle "tree" as a
|
||||
@ -589,19 +570,6 @@ getBuildId(opt::InputArgList &Args) {
|
||||
return {BuildIdKind::None, {}};
|
||||
}
|
||||
|
||||
static std::vector<StringRef> getLines(MemoryBufferRef MB) {
|
||||
SmallVector<StringRef, 0> Arr;
|
||||
MB.getBuffer().split(Arr, '\n');
|
||||
|
||||
std::vector<StringRef> Ret;
|
||||
for (StringRef S : Arr) {
|
||||
S = S.trim();
|
||||
if (!S.empty())
|
||||
Ret.push_back(S);
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
static bool getCompressDebugSections(opt::InputArgList &Args) {
|
||||
StringRef S = Args.getLastArgValue(OPT_compress_debug_sections, "none");
|
||||
if (S == "none")
|
||||
@ -613,53 +581,70 @@ static bool getCompressDebugSections(opt::InputArgList &Args) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static int parseInt(StringRef S, opt::Arg *Arg) {
|
||||
int V = 0;
|
||||
if (!to_integer(S, V, 10))
|
||||
error(Arg->getSpelling() + ": number expected, but got '" + S + "'");
|
||||
return V;
|
||||
}
|
||||
|
||||
// Initializes Config members by the command line options.
|
||||
void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition);
|
||||
Config->AuxiliaryList = getArgs(Args, OPT_auxiliary);
|
||||
Config->AllowMultipleDefinition =
|
||||
Args.hasArg(OPT_allow_multiple_definition) || hasZOption(Args, "muldefs");
|
||||
Config->AuxiliaryList = args::getStrings(Args, OPT_auxiliary);
|
||||
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
|
||||
Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
|
||||
Config->Chroot = Args.getLastArgValue(OPT_chroot);
|
||||
Config->CompressDebugSections = getCompressDebugSections(Args);
|
||||
Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common,
|
||||
!Args.hasArg(OPT_relocatable));
|
||||
Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true);
|
||||
Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common,
|
||||
!Args.hasArg(OPT_relocatable));
|
||||
Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
|
||||
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
|
||||
Config->Discard = getDiscard(Args);
|
||||
Config->DynamicLinker = getDynamicLinker(Args);
|
||||
Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr);
|
||||
Config->EhFrameHdr =
|
||||
Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false);
|
||||
Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
|
||||
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
|
||||
Config->Entry = Args.getLastArgValue(OPT_entry);
|
||||
Config->ExportDynamic =
|
||||
getArg(Args, OPT_export_dynamic, OPT_no_export_dynamic, false);
|
||||
Config->FatalWarnings =
|
||||
getArg(Args, OPT_fatal_warnings, OPT_no_fatal_warnings, false);
|
||||
Config->FilterList = getArgs(Args, OPT_filter);
|
||||
Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
|
||||
errorHandler().FatalWarnings =
|
||||
Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
|
||||
Config->FilterList = args::getStrings(Args, OPT_filter);
|
||||
Config->Fini = Args.getLastArgValue(OPT_fini, "_fini");
|
||||
Config->GcSections = getArg(Args, OPT_gc_sections, OPT_no_gc_sections, false);
|
||||
Config->GdbIndex = Args.hasArg(OPT_gdb_index);
|
||||
Config->ICF = getArg(Args, OPT_icf_all, OPT_icf_none, false);
|
||||
Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419);
|
||||
Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
|
||||
Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false);
|
||||
Config->ICF = Args.hasFlag(OPT_icf_all, OPT_icf_none, false);
|
||||
Config->ICFData = Args.hasArg(OPT_icf_data);
|
||||
Config->Init = Args.getLastArgValue(OPT_init, "_init");
|
||||
Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline);
|
||||
Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes);
|
||||
Config->LTOO = getInteger(Args, OPT_lto_O, 2);
|
||||
Config->LTOPartitions = getInteger(Args, OPT_lto_partitions, 1);
|
||||
Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
|
||||
Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
|
||||
Config->MapFile = Args.getLastArgValue(OPT_Map);
|
||||
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
|
||||
Config->MergeArmExidx =
|
||||
Args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true);
|
||||
Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
|
||||
Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec);
|
||||
Config->Nostdlib = Args.hasArg(OPT_nostdlib);
|
||||
Config->OFormatBinary = isOutputFormatBinary(Args);
|
||||
Config->Omagic = Args.hasArg(OPT_omagic);
|
||||
Config->Omagic = Args.hasFlag(OPT_omagic, OPT_no_omagic, false);
|
||||
Config->OptRemarksFilename = Args.getLastArgValue(OPT_opt_remarks_filename);
|
||||
Config->OptRemarksWithHotness = Args.hasArg(OPT_opt_remarks_with_hotness);
|
||||
Config->Optimize = getInteger(Args, OPT_O, 1);
|
||||
Config->Optimize = args::getInteger(Args, OPT_O, 1);
|
||||
Config->OrphanHandling = getOrphanHandling(Args);
|
||||
Config->OutputFile = Args.getLastArgValue(OPT_o);
|
||||
Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false);
|
||||
Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
|
||||
Config->Pie = Args.hasFlag(OPT_pie, OPT_nopie, false);
|
||||
Config->PrintGcSections =
|
||||
Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
|
||||
Config->Rpath = getRpath(Args);
|
||||
Config->Relocatable = Args.hasArg(OPT_relocatable);
|
||||
Config->SaveTemps = Args.hasArg(OPT_save_temps);
|
||||
Config->SearchPaths = getArgs(Args, OPT_L);
|
||||
Config->SearchPaths = args::getStrings(Args, OPT_library_path);
|
||||
Config->SectionStartMap = getSectionStartMap(Args);
|
||||
Config->Shared = Args.hasArg(OPT_shared);
|
||||
Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
|
||||
@ -667,18 +652,19 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
Config->SortSection = getSortSection(Args);
|
||||
Config->Strip = getStrip(Args);
|
||||
Config->Sysroot = Args.getLastArgValue(OPT_sysroot);
|
||||
Config->Target1Rel = getArg(Args, OPT_target1_rel, OPT_target1_abs, false);
|
||||
Config->Target1Rel = Args.hasFlag(OPT_target1_rel, OPT_target1_abs, false);
|
||||
Config->Target2 = getTarget2(Args);
|
||||
Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir);
|
||||
Config->ThinLTOCachePolicy = check(
|
||||
Config->ThinLTOCachePolicy = CHECK(
|
||||
parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)),
|
||||
"--thinlto-cache-policy: invalid cache policy");
|
||||
Config->ThinLTOJobs = getInteger(Args, OPT_thinlto_jobs, -1u);
|
||||
Config->Threads = getArg(Args, OPT_threads, OPT_no_threads, true);
|
||||
Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u);
|
||||
ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
|
||||
Config->Trace = Args.hasArg(OPT_trace);
|
||||
Config->Undefined = getArgs(Args, OPT_undefined);
|
||||
Config->Undefined = args::getStrings(Args, OPT_undefined);
|
||||
Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args);
|
||||
Config->Verbose = Args.hasArg(OPT_verbose);
|
||||
errorHandler().Verbose = Config->Verbose;
|
||||
Config->WarnCommon = Args.hasArg(OPT_warn_common);
|
||||
Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
|
||||
Config->ZExecstack = hasZOption(Args, "execstack");
|
||||
@ -689,20 +675,39 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
Config->ZOrigin = hasZOption(Args, "origin");
|
||||
Config->ZRelro = !hasZOption(Args, "norelro");
|
||||
Config->ZRodynamic = hasZOption(Args, "rodynamic");
|
||||
Config->ZStackSize = getZOptionValue(Args, "stack-size", 0);
|
||||
Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", 0);
|
||||
Config->ZText = !hasZOption(Args, "notext");
|
||||
Config->ZWxneeded = hasZOption(Args, "wxneeded");
|
||||
|
||||
// Parse LTO plugin-related options for compatibility with gold.
|
||||
for (auto *Arg : Args.filtered(OPT_plugin_opt, OPT_plugin_opt_eq)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "disable-verify")
|
||||
Config->DisableVerify = true;
|
||||
else if (S == "save-temps")
|
||||
Config->SaveTemps = true;
|
||||
else if (S.startswith("O"))
|
||||
Config->LTOO = parseInt(S.substr(1), Arg);
|
||||
else if (S.startswith("lto-partitions="))
|
||||
Config->LTOPartitions = parseInt(S.substr(15), Arg);
|
||||
else if (S.startswith("jobs="))
|
||||
Config->ThinLTOJobs = parseInt(S.substr(5), Arg);
|
||||
else if (!S.startswith("/") && !S.startswith("-fresolution=") &&
|
||||
!S.startswith("-pass-through=") && !S.startswith("mcpu=") &&
|
||||
!S.startswith("thinlto") && S != "-function-sections" &&
|
||||
S != "-data-sections")
|
||||
error(Arg->getSpelling() + ": unknown option: " + S);
|
||||
}
|
||||
|
||||
if (Config->LTOO > 3)
|
||||
error("invalid optimization level for LTO: " +
|
||||
Args.getLastArgValue(OPT_lto_O));
|
||||
error("invalid optimization level for LTO: " + Twine(Config->LTOO));
|
||||
if (Config->LTOPartitions == 0)
|
||||
error("--lto-partitions: number of threads must be > 0");
|
||||
if (Config->ThinLTOJobs == 0)
|
||||
error("--thinlto-jobs: number of threads must be > 0");
|
||||
|
||||
// Parse ELF{32,64}{LE,BE} and CPU type.
|
||||
if (auto *Arg = Args.getLastArg(OPT_m)) {
|
||||
// Parse ELF{32,64}{LE,BE} and CPU type.
|
||||
StringRef S = Arg->getValue();
|
||||
std::tie(Config->EKind, Config->EMachine, Config->OSABI) =
|
||||
parseEmulation(S);
|
||||
@ -710,6 +715,19 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
Config->Emulation = S;
|
||||
}
|
||||
|
||||
// Parse -hash-style={sysv,gnu,both}.
|
||||
if (auto *Arg = Args.getLastArg(OPT_hash_style)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "sysv")
|
||||
Config->SysvHash = true;
|
||||
else if (S == "gnu")
|
||||
Config->GnuHash = true;
|
||||
else if (S == "both")
|
||||
Config->SysvHash = Config->GnuHash = true;
|
||||
else
|
||||
error("unknown -hash-style: " + S);
|
||||
}
|
||||
|
||||
if (Args.hasArg(OPT_print_map))
|
||||
Config->MapFile = "-";
|
||||
|
||||
@ -720,25 +738,32 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
if (Config->Omagic)
|
||||
Config->ZRelro = false;
|
||||
|
||||
std::tie(Config->SysvHash, Config->GnuHash) = getHashStyle(Args);
|
||||
std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args);
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_pack_dyn_relocs_eq)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "android")
|
||||
Config->AndroidPackDynRelocs = true;
|
||||
else if (S != "none")
|
||||
error("unknown -pack-dyn-relocs format: " + S);
|
||||
}
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
|
||||
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
|
||||
Config->SymbolOrderingFile = getLines(*Buffer);
|
||||
Config->SymbolOrderingFile = args::getLines(*Buffer);
|
||||
|
||||
// If --retain-symbol-file is used, we'll keep only the symbols listed in
|
||||
// the file and discard all others.
|
||||
if (auto *Arg = Args.getLastArg(OPT_retain_symbols_file)) {
|
||||
Config->DefaultSymbolVersion = VER_NDX_LOCAL;
|
||||
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
|
||||
for (StringRef S : getLines(*Buffer))
|
||||
for (StringRef S : args::getLines(*Buffer))
|
||||
Config->VersionScriptGlobals.push_back(
|
||||
{S, /*IsExternCpp*/ false, /*HasWildcard*/ false});
|
||||
}
|
||||
|
||||
bool HasExportDynamic =
|
||||
getArg(Args, OPT_export_dynamic, OPT_no_export_dynamic, false);
|
||||
Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
|
||||
|
||||
// Parses -dynamic-list and -export-dynamic-symbol. They make some
|
||||
// symbols private. Note that -export-dynamic takes precedence over them
|
||||
@ -749,21 +774,11 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
readDynamicList(*Buffer);
|
||||
|
||||
for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
|
||||
Config->VersionScriptGlobals.push_back(
|
||||
Config->DynamicList.push_back(
|
||||
{Arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false});
|
||||
|
||||
// Dynamic lists are a simplified linker script that doesn't need the
|
||||
// "global:" and implicitly ends with a "local:*". Set the variables
|
||||
// needed to simulate that.
|
||||
if (Args.hasArg(OPT_dynamic_list) ||
|
||||
Args.hasArg(OPT_export_dynamic_symbol)) {
|
||||
Config->ExportDynamic = true;
|
||||
if (!Config->Shared)
|
||||
Config->DefaultSymbolVersion = VER_NDX_LOCAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_version_script))
|
||||
for (auto *Arg : Args.filtered(OPT_version_script))
|
||||
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
|
||||
readVersionScript(*Buffer);
|
||||
}
|
||||
@ -804,17 +819,20 @@ static bool getBinaryOption(StringRef S) {
|
||||
|
||||
void LinkerDriver::createFiles(opt::InputArgList &Args) {
|
||||
for (auto *Arg : Args) {
|
||||
switch (Arg->getOption().getID()) {
|
||||
case OPT_l:
|
||||
switch (Arg->getOption().getUnaliasedOption().getID()) {
|
||||
case OPT_library:
|
||||
addLibrary(Arg->getValue());
|
||||
break;
|
||||
case OPT_INPUT:
|
||||
addFile(Arg->getValue(), /*WithLOption=*/false);
|
||||
break;
|
||||
case OPT_alias_script_T:
|
||||
case OPT_script:
|
||||
if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue()))
|
||||
readLinkerScript(*MB);
|
||||
if (Optional<std::string> Path = searchLinkerScript(Arg->getValue())) {
|
||||
if (Optional<MemoryBufferRef> MB = readFile(*Path))
|
||||
readLinkerScript(*MB);
|
||||
break;
|
||||
}
|
||||
error(Twine("cannot find linker script ") + Arg->getValue());
|
||||
break;
|
||||
case OPT_as_needed:
|
||||
Config->AsNeeded = true;
|
||||
@ -846,7 +864,7 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
|
||||
}
|
||||
}
|
||||
|
||||
if (Files.empty() && ErrorCount == 0)
|
||||
if (Files.empty() && errorCount() == 0)
|
||||
error("no input files");
|
||||
}
|
||||
|
||||
@ -870,21 +888,20 @@ void LinkerDriver::inferMachineType() {
|
||||
// Parse -z max-page-size=<value>. The default value is defined by
|
||||
// each target.
|
||||
static uint64_t getMaxPageSize(opt::InputArgList &Args) {
|
||||
uint64_t Val =
|
||||
getZOptionValue(Args, "max-page-size", Target->DefaultMaxPageSize);
|
||||
uint64_t Val = args::getZOptionValue(Args, OPT_z, "max-page-size",
|
||||
Target->DefaultMaxPageSize);
|
||||
if (!isPowerOf2_64(Val))
|
||||
error("max-page-size: value isn't a power of 2");
|
||||
return Val;
|
||||
}
|
||||
|
||||
// Parses -image-base option.
|
||||
static uint64_t getImageBase(opt::InputArgList &Args) {
|
||||
// Use default if no -image-base option is given.
|
||||
// Because we are using "Target" here, this function
|
||||
// has to be called after the variable is initialized.
|
||||
static Optional<uint64_t> getImageBase(opt::InputArgList &Args) {
|
||||
// Because we are using "Config->MaxPageSize" here, this function has to be
|
||||
// called after the variable is initialized.
|
||||
auto *Arg = Args.getLastArg(OPT_image_base);
|
||||
if (!Arg)
|
||||
return Config->Pic ? 0 : Target->DefaultImageBase;
|
||||
return None;
|
||||
|
||||
StringRef S = Arg->getValue();
|
||||
uint64_t V;
|
||||
@ -897,21 +914,6 @@ static uint64_t getImageBase(opt::InputArgList &Args) {
|
||||
return V;
|
||||
}
|
||||
|
||||
// Parses --defsym=alias option.
|
||||
static std::vector<std::pair<StringRef, StringRef>>
|
||||
getDefsym(opt::InputArgList &Args) {
|
||||
std::vector<std::pair<StringRef, StringRef>> Ret;
|
||||
for (auto *Arg : Args.filtered(OPT_defsym)) {
|
||||
StringRef From;
|
||||
StringRef To;
|
||||
std::tie(From, To) = StringRef(Arg->getValue()).split('=');
|
||||
if (!isValidCIdentifier(To))
|
||||
error("--defsym: symbol name expected, but got " + To);
|
||||
Ret.push_back({From, To});
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
// Parses `--exclude-libs=lib,lib,...`.
|
||||
// The library names may be delimited by commas or colons.
|
||||
static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
|
||||
@ -930,33 +932,50 @@ static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
|
||||
return Ret;
|
||||
}
|
||||
|
||||
static Optional<StringRef> getArchiveName(InputFile *File) {
|
||||
if (isa<ArchiveFile>(File))
|
||||
return File->getName();
|
||||
if (!File->ArchiveName.empty())
|
||||
return File->ArchiveName;
|
||||
return None;
|
||||
}
|
||||
|
||||
// Handles the -exclude-libs option. If a static library file is specified
|
||||
// by the -exclude-libs option, all public symbols from the archive become
|
||||
// private unless otherwise specified by version scripts or something.
|
||||
// A special library name "ALL" means all archive files.
|
||||
//
|
||||
// This is not a popular option, but some programs such as bionic libc use it.
|
||||
template <class ELFT>
|
||||
static void excludeLibs(opt::InputArgList &Args, ArrayRef<InputFile *> Files) {
|
||||
DenseSet<StringRef> Libs = getExcludeLibs(Args);
|
||||
bool All = Libs.count("ALL");
|
||||
|
||||
for (InputFile *File : Files)
|
||||
if (auto *F = dyn_cast<ArchiveFile>(File))
|
||||
if (All || Libs.count(path::filename(F->getName())))
|
||||
for (Symbol *Sym : F->getSymbols())
|
||||
Sym->VersionId = VER_NDX_LOCAL;
|
||||
if (Optional<StringRef> Archive = getArchiveName(File))
|
||||
if (All || Libs.count(path::filename(*Archive)))
|
||||
for (Symbol *Sym : File->getSymbols())
|
||||
if (!Sym->isLocal())
|
||||
Sym->VersionId = VER_NDX_LOCAL;
|
||||
}
|
||||
|
||||
// Do actual linking. Note that when this function is called,
|
||||
// all linker scripts have already been parsed.
|
||||
template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
SymbolTable<ELFT> Symtab;
|
||||
elf::Symtab<ELFT>::X = &Symtab;
|
||||
Target = getTarget();
|
||||
|
||||
Config->MaxPageSize = getMaxPageSize(Args);
|
||||
Config->ImageBase = getImageBase(Args);
|
||||
|
||||
// If a -hash-style option was not given, set to a default value,
|
||||
// which varies depending on the target.
|
||||
if (!Args.hasArg(OPT_hash_style)) {
|
||||
if (Config->EMachine == EM_MIPS)
|
||||
Config->SysvHash = true;
|
||||
else
|
||||
Config->SysvHash = Config->GnuHash = true;
|
||||
}
|
||||
|
||||
// Default output filename is "a.out" by the Unix tradition.
|
||||
if (Config->OutputFile.empty())
|
||||
Config->OutputFile = "a.out";
|
||||
@ -968,7 +987,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
error("cannot open output file " + Config->OutputFile + ": " + E.message());
|
||||
if (auto E = tryCreateFile(Config->MapFile))
|
||||
error("cannot open map file " + Config->MapFile + ": " + E.message());
|
||||
if (ErrorCount)
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// Use default entry point name if no name was given via the command
|
||||
@ -981,67 +1000,113 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
|
||||
// Handle --trace-symbol.
|
||||
for (auto *Arg : Args.filtered(OPT_trace_symbol))
|
||||
Symtab.trace(Arg->getValue());
|
||||
Symtab->trace(Arg->getValue());
|
||||
|
||||
// Add all files to the symbol table. This will add almost all
|
||||
// symbols that we need to the symbol table.
|
||||
for (InputFile *F : Files)
|
||||
Symtab.addFile(F);
|
||||
Symtab->addFile<ELFT>(F);
|
||||
|
||||
// Process -defsym option.
|
||||
for (auto *Arg : Args.filtered(OPT_defsym)) {
|
||||
StringRef From;
|
||||
StringRef To;
|
||||
std::tie(From, To) = StringRef(Arg->getValue()).split('=');
|
||||
readDefsym(From, MemoryBufferRef(To, "-defsym"));
|
||||
}
|
||||
|
||||
// Now that we have every file, we can decide if we will need a
|
||||
// dynamic symbol table.
|
||||
// We need one if we were asked to export dynamic symbols or if we are
|
||||
// producing a shared library.
|
||||
// We also need one if any shared libraries are used and for pie executables
|
||||
// (probably because the dynamic linker needs it).
|
||||
Config->HasDynSymTab =
|
||||
!SharedFiles.empty() || Config->Pic || Config->ExportDynamic;
|
||||
|
||||
// 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);
|
||||
|
||||
// Handle the `--undefined <sym>` options.
|
||||
for (StringRef S : Config->Undefined)
|
||||
Symtab->fetchIfLazy<ELFT>(S);
|
||||
|
||||
// If an entry symbol is in a static archive, pull out that file now
|
||||
// to complete the symbol table. After this, no new names except a
|
||||
// few linker-synthesized ones will be added to the symbol table.
|
||||
if (Symtab.find(Config->Entry))
|
||||
Symtab.addUndefined(Config->Entry);
|
||||
Symtab->fetchIfLazy<ELFT>(Config->Entry);
|
||||
|
||||
// Return if there were name resolution errors.
|
||||
if (ErrorCount)
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// Handle the `--undefined <sym>` options.
|
||||
Symtab.scanUndefinedFlags();
|
||||
|
||||
// Handle undefined symbols in DSOs.
|
||||
Symtab.scanShlibUndefined();
|
||||
Symtab->scanShlibUndefined<ELFT>();
|
||||
|
||||
// Handle the -exclude-libs option.
|
||||
if (Args.hasArg(OPT_exclude_libs))
|
||||
excludeLibs(Args, Files);
|
||||
excludeLibs<ELFT>(Args, Files);
|
||||
|
||||
// Create ElfHeader early. We need a dummy section in
|
||||
// addReservedSymbols to mark the created symbols as not absolute.
|
||||
Out::ElfHeader = make<OutputSection>("", 0, SHF_ALLOC);
|
||||
Out::ElfHeader->Size = sizeof(typename ELFT::Ehdr);
|
||||
|
||||
// We need to create some reserved symbols such as _end. Create them.
|
||||
if (!Config->Relocatable)
|
||||
addReservedSymbols<ELFT>();
|
||||
|
||||
// Apply version scripts.
|
||||
Symtab.scanVersionScript();
|
||||
Symtab->scanVersionScript();
|
||||
|
||||
// Create wrapped symbols for -wrap option.
|
||||
for (auto *Arg : Args.filtered(OPT_wrap))
|
||||
Symtab.addSymbolWrap(Arg->getValue());
|
||||
Symtab->addSymbolWrap<ELFT>(Arg->getValue());
|
||||
|
||||
// Create alias symbols for -defsym option.
|
||||
for (std::pair<StringRef, StringRef> &Def : getDefsym(Args))
|
||||
Symtab.addSymbolAlias(Def.first, Def.second);
|
||||
|
||||
Symtab.addCombinedLTOObject();
|
||||
if (ErrorCount)
|
||||
Symtab->addCombinedLTOObject<ELFT>();
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// 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->Opt.ReferencedSymbols)
|
||||
Symtab.addUndefined(Sym);
|
||||
|
||||
// Apply symbol renames for -wrap and -defsym
|
||||
Symtab.applySymbolRenames();
|
||||
// Apply symbol renames for -wrap.
|
||||
Symtab->applySymbolWrap();
|
||||
|
||||
// Now that we have a complete list of input files.
|
||||
// Beyond this point, no new files are added.
|
||||
// Aggregate all input sections into one place.
|
||||
for (elf::ObjectFile<ELFT> *F : Symtab.getObjectFiles())
|
||||
for (InputFile *F : ObjectFiles)
|
||||
for (InputSectionBase *S : F->getSections())
|
||||
if (S && S != &InputSection::Discarded)
|
||||
InputSections.push_back(S);
|
||||
for (BinaryFile *F : Symtab.getBinaryFiles())
|
||||
for (BinaryFile *F : BinaryFiles)
|
||||
for (InputSectionBase *S : F->getSections())
|
||||
InputSections.push_back(cast<InputSection>(S));
|
||||
|
||||
// We do not want to emit debug sections if --strip-all
|
||||
// or -strip-debug are given.
|
||||
if (Config->Strip != StripPolicy::None)
|
||||
llvm::erase_if(InputSections, [](InputSectionBase *S) {
|
||||
return S->Name.startswith(".debug") || S->Name.startswith(".zdebug");
|
||||
});
|
||||
|
||||
Config->EFlags = Target->calcEFlags();
|
||||
|
||||
if (Config->EMachine == EM_ARM) {
|
||||
// FIXME: These warnings can be removed when lld only uses these features
|
||||
// when the input objects have been compiled with an architecture that
|
||||
// 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.");
|
||||
}
|
||||
|
||||
// This adds a .comment section containing a version string. We have to add it
|
||||
// before decompressAndMergeSections because the .comment section is a
|
||||
// mergeable section.
|
||||
@ -1050,9 +1115,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
||||
|
||||
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
|
||||
// and identical code folding.
|
||||
if (Config->GcSections)
|
||||
markLive<ELFT>();
|
||||
decompressAndMergeSections();
|
||||
markLive<ELFT>();
|
||||
decompressSections();
|
||||
mergeSections();
|
||||
if (Config->ICF)
|
||||
doIcf<ELFT>();
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
||||
#define LLD_ELF_DRIVER_H
|
||||
|
||||
#include "SymbolTable.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/Reproduce.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Common/Reproduce.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
@ -67,6 +67,7 @@ void printHelp(const char *Argv0);
|
||||
std::string createResponseFile(const llvm::opt::InputArgList &Args);
|
||||
|
||||
llvm::Optional<std::string> findFromSearchPaths(StringRef Path);
|
||||
llvm::Optional<std::string> searchLinkerScript(StringRef Path);
|
||||
llvm::Optional<std::string> searchLibrary(StringRef Path);
|
||||
|
||||
} // namespace elf
|
||||
|
@ -14,10 +14,10 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Driver.h"
|
||||
#include "Error.h"
|
||||
#include "Memory.h"
|
||||
#include "lld/Config/Version.h"
|
||||
#include "lld/Core/Reproduce.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Reproduce.h"
|
||||
#include "lld/Common/Version.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
@ -51,25 +51,26 @@ static const opt::OptTable::Info OptInfo[] = {
|
||||
|
||||
ELFOptTable::ELFOptTable() : OptTable(OptInfo) {}
|
||||
|
||||
// Parse -color-diagnostics={auto,always,never} or -no-color-diagnostics.
|
||||
static bool getColorDiagnostics(opt::InputArgList &Args) {
|
||||
// Set color diagnostics according to -color-diagnostics={auto,always,never}
|
||||
// or -no-color-diagnostics flags.
|
||||
static void handleColorDiagnostics(opt::InputArgList &Args) {
|
||||
auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
|
||||
OPT_no_color_diagnostics);
|
||||
if (!Arg)
|
||||
return ErrorOS->has_colors();
|
||||
if (Arg->getOption().getID() == OPT_color_diagnostics)
|
||||
return true;
|
||||
if (Arg->getOption().getID() == OPT_no_color_diagnostics)
|
||||
return false;
|
||||
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "auto")
|
||||
return ErrorOS->has_colors();
|
||||
if (S == "always")
|
||||
return true;
|
||||
if (S != "never")
|
||||
error("unknown option: -color-diagnostics=" + S);
|
||||
return false;
|
||||
return;
|
||||
else if (Arg->getOption().getID() == OPT_color_diagnostics)
|
||||
errorHandler().ColorDiagnostics = true;
|
||||
else if (Arg->getOption().getID() == OPT_no_color_diagnostics)
|
||||
errorHandler().ColorDiagnostics = false;
|
||||
else {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "always")
|
||||
errorHandler().ColorDiagnostics = true;
|
||||
else if (S == "never")
|
||||
errorHandler().ColorDiagnostics = false;
|
||||
else if (S != "auto")
|
||||
error("unknown option: -color-diagnostics=" + S);
|
||||
}
|
||||
}
|
||||
|
||||
static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
|
||||
@ -103,9 +104,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
|
||||
cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec);
|
||||
Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
|
||||
|
||||
// Interpret -color-diagnostics early so that error messages
|
||||
// for unknown flags are colored.
|
||||
Config->ColorDiagnostics = getColorDiagnostics(Args);
|
||||
handleColorDiagnostics(Args);
|
||||
if (MissingCount)
|
||||
error(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
|
||||
|
||||
@ -115,8 +114,8 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
|
||||
}
|
||||
|
||||
void elf::printHelp(const char *Argv0) {
|
||||
ELFOptTable Table;
|
||||
Table.PrintHelp(outs(), Argv0, "lld", false);
|
||||
ELFOptTable().PrintHelp(outs(), Argv0, "lld", false /*ShowHidden*/,
|
||||
true /*ShowAllAliases*/);
|
||||
outs() << "\n";
|
||||
|
||||
// Scripts generated by Libtool versions up to at least 2.4.6 (the most
|
||||
@ -138,10 +137,11 @@ void elf::printHelp(const char *Argv0) {
|
||||
std::string elf::createResponseFile(const opt::InputArgList &Args) {
|
||||
SmallString<0> Data;
|
||||
raw_svector_ostream OS(Data);
|
||||
OS << "--chroot .\n";
|
||||
|
||||
// Copy the command line to the output while rewriting paths.
|
||||
for (auto *Arg : Args) {
|
||||
switch (Arg->getOption().getID()) {
|
||||
switch (Arg->getOption().getUnaliasedOption().getID()) {
|
||||
case OPT_reproduce:
|
||||
break;
|
||||
case OPT_INPUT:
|
||||
@ -154,17 +154,18 @@ std::string elf::createResponseFile(const opt::InputArgList &Args) {
|
||||
// Strip directories to prevent the issue.
|
||||
OS << "-o " << quote(sys::path::filename(Arg->getValue())) << "\n";
|
||||
break;
|
||||
case OPT_L:
|
||||
case OPT_dynamic_list:
|
||||
case OPT_library_path:
|
||||
case OPT_rpath:
|
||||
case OPT_alias_script_T:
|
||||
case OPT_script:
|
||||
case OPT_symbol_ordering_file:
|
||||
case OPT_sysroot:
|
||||
case OPT_version_script:
|
||||
OS << Arg->getSpelling() << " " << quote(rewritePath(Arg->getValue()))
|
||||
<< "\n";
|
||||
break;
|
||||
default:
|
||||
OS << toString(Arg) << "\n";
|
||||
OS << toString(*Arg) << "\n";
|
||||
}
|
||||
}
|
||||
return Data.str();
|
||||
@ -206,3 +207,12 @@ Optional<std::string> elf::searchLibrary(StringRef Name) {
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// If a linker script doesn't exist in the current directory, we also look for
|
||||
// the script in the '-L' search paths. This matches the behaviour of both '-T'
|
||||
// and linker script INPUT() directives in ld.bfd.
|
||||
Optional<std::string> elf::searchLinkerScript(StringRef Name) {
|
||||
if (fs::exists(Name))
|
||||
return Name.str();
|
||||
return findFromSearchPaths(Name);
|
||||
}
|
||||
|
@ -17,11 +17,12 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "EhFrame.h"
|
||||
#include "Error.h"
|
||||
#include "Config.h"
|
||||
#include "InputSection.h"
|
||||
#include "Relocations.h"
|
||||
#include "Strings.h"
|
||||
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/BinaryFormat/Dwarf.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
@ -36,7 +37,7 @@ using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
template <class ELFT> class EhReader {
|
||||
class EhReader {
|
||||
public:
|
||||
EhReader(InputSectionBase *S, ArrayRef<uint8_t> D) : IS(S), D(D) {}
|
||||
size_t readEhRecordSize();
|
||||
@ -45,7 +46,7 @@ public:
|
||||
private:
|
||||
template <class P> void failOn(const P *Loc, const Twine &Msg) {
|
||||
fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " +
|
||||
IS->getObjMsg<ELFT>((const uint8_t *)Loc - IS->Data.data()));
|
||||
IS->getObjMsg((const uint8_t *)Loc - IS->Data.data()));
|
||||
}
|
||||
|
||||
uint8_t readByte();
|
||||
@ -59,22 +60,20 @@ private:
|
||||
};
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) {
|
||||
return EhReader<ELFT>(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
|
||||
// a 4 byte length field. This function reads the length.
|
||||
template <class ELFT> size_t EhReader<ELFT>::readEhRecordSize() {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
size_t EhReader::readEhRecordSize() {
|
||||
if (D.size() < 4)
|
||||
failOn(D.data(), "CIE/FDE too small");
|
||||
|
||||
// First 4 bytes of CIE/FDE is the size of the record.
|
||||
// If it is 0xFFFFFFFF, the next 8 bytes contain the size instead,
|
||||
// but we do not support that format yet.
|
||||
uint64_t V = read32<E>(D.data());
|
||||
uint64_t V = read32(D.data(), Config->Endianness);
|
||||
if (V == UINT32_MAX)
|
||||
failOn(D.data(), "CIE/FDE too large");
|
||||
uint64_t Size = V + 4;
|
||||
@ -84,7 +83,7 @@ template <class ELFT> size_t EhReader<ELFT>::readEhRecordSize() {
|
||||
}
|
||||
|
||||
// Read a byte and advance D by one byte.
|
||||
template <class ELFT> uint8_t EhReader<ELFT>::readByte() {
|
||||
uint8_t EhReader::readByte() {
|
||||
if (D.empty())
|
||||
failOn(D.data(), "unexpected end of CIE");
|
||||
uint8_t B = D.front();
|
||||
@ -92,14 +91,14 @@ template <class ELFT> uint8_t EhReader<ELFT>::readByte() {
|
||||
return B;
|
||||
}
|
||||
|
||||
template <class ELFT> void EhReader<ELFT>::skipBytes(size_t Count) {
|
||||
void EhReader::skipBytes(size_t Count) {
|
||||
if (D.size() < Count)
|
||||
failOn(D.data(), "CIE is too small");
|
||||
D = D.slice(Count);
|
||||
}
|
||||
|
||||
// Read a null-terminated string.
|
||||
template <class ELFT> StringRef EhReader<ELFT>::readString() {
|
||||
StringRef EhReader::readString() {
|
||||
const uint8_t *End = std::find(D.begin(), D.end(), '\0');
|
||||
if (End == D.end())
|
||||
failOn(D.data(), "corrupted CIE (failed to read string)");
|
||||
@ -112,7 +111,7 @@ template <class ELFT> StringRef EhReader<ELFT>::readString() {
|
||||
// Actual number is not of interest because only the runtime needs it.
|
||||
// But we need to be at least able to skip it so that we can read
|
||||
// the field that follows a LEB128 number.
|
||||
template <class ELFT> void EhReader<ELFT>::skipLeb128() {
|
||||
void EhReader::skipLeb128() {
|
||||
const uint8_t *ErrPos = D.data();
|
||||
while (!D.empty()) {
|
||||
uint8_t Val = D.front();
|
||||
@ -141,7 +140,7 @@ static size_t getAugPSize(unsigned Enc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class ELFT> void EhReader<ELFT>::skipAugP() {
|
||||
void EhReader::skipAugP() {
|
||||
uint8_t Enc = readByte();
|
||||
if ((Enc & 0xf0) == DW_EH_PE_aligned)
|
||||
failOn(D.data() - 1, "DW_EH_PE_aligned encoding is not supported");
|
||||
@ -153,12 +152,11 @@ template <class ELFT> void EhReader<ELFT>::skipAugP() {
|
||||
D = D.slice(Size);
|
||||
}
|
||||
|
||||
template <class ELFT> uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
|
||||
auto *IS = static_cast<InputSectionBase *>(P->ID);
|
||||
return EhReader<ELFT>(IS, P->data()).getFdeEncoding();
|
||||
uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
|
||||
return EhReader(P->Sec, P->data()).getFdeEncoding();
|
||||
}
|
||||
|
||||
template <class ELFT> uint8_t EhReader<ELFT>::getFdeEncoding() {
|
||||
uint8_t EhReader::getFdeEncoding() {
|
||||
skipBytes(8);
|
||||
int Version = readByte();
|
||||
if (Version != 1 && Version != 3)
|
||||
@ -200,13 +198,3 @@ template <class ELFT> uint8_t EhReader<ELFT>::getFdeEncoding() {
|
||||
}
|
||||
return DW_EH_PE_absptr;
|
||||
}
|
||||
|
||||
template size_t elf::readEhRecordSize<ELF32LE>(InputSectionBase *S, size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF32BE>(InputSectionBase *S, size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF64LE>(InputSectionBase *S, size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF64BE>(InputSectionBase *S, size_t Off);
|
||||
|
||||
template uint8_t elf::getFdeEncoding<ELF32LE>(EhSectionPiece *P);
|
||||
template uint8_t elf::getFdeEncoding<ELF32BE>(EhSectionPiece *P);
|
||||
template uint8_t elf::getFdeEncoding<ELF64LE>(EhSectionPiece *P);
|
||||
template uint8_t elf::getFdeEncoding<ELF64BE>(EhSectionPiece *P);
|
||||
|
@ -10,15 +10,15 @@
|
||||
#ifndef LLD_ELF_EHFRAME_H
|
||||
#define LLD_ELF_EHFRAME_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputSectionBase;
|
||||
struct EhSectionPiece;
|
||||
|
||||
template <class ELFT> size_t readEhRecordSize(InputSectionBase *S, size_t Off);
|
||||
template <class ELFT> uint8_t getFdeEncoding(EhSectionPiece *P);
|
||||
size_t readEhRecordSize(InputSectionBase *S, size_t Off);
|
||||
uint8_t getFdeEncoding(EhSectionPiece *P);
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
78
ELF/Error.h
78
ELF/Error.h
@ -1,78 +0,0 @@
|
||||
//===- Error.h --------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// In LLD, we have three levels of errors: fatal, error or warn.
|
||||
//
|
||||
// Fatal makes the program exit immediately with an error message.
|
||||
// You shouldn't use it except for reporting a corrupted input file.
|
||||
//
|
||||
// Error prints out an error message and increment a global variable
|
||||
// ErrorCount to record the fact that we met an error condition. It does
|
||||
// not exit, so it is safe for a lld-as-a-library use case. It is generally
|
||||
// useful because it can report more than one error in a single run.
|
||||
//
|
||||
// Warn doesn't do anything but printing out a given message.
|
||||
//
|
||||
// It is not recommended to use llvm::outs() or llvm::errs() directly
|
||||
// in LLD because they are not thread-safe. The functions declared in
|
||||
// this file are mutually excluded, so you want to use them instead.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_ERROR_H
|
||||
#define LLD_ELF_ERROR_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
extern uint64_t ErrorCount;
|
||||
extern llvm::raw_ostream *ErrorOS;
|
||||
|
||||
void log(const Twine &Msg);
|
||||
void message(const Twine &Msg);
|
||||
void warn(const Twine &Msg);
|
||||
void error(const Twine &Msg);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
|
||||
|
||||
LLVM_ATTRIBUTE_NORETURN void exitLld(int Val);
|
||||
|
||||
// check() functions are convenient functions to strip errors
|
||||
// from error-or-value objects.
|
||||
template <class T> T check(ErrorOr<T> E) {
|
||||
if (auto EC = E.getError())
|
||||
fatal(EC.message());
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
template <class T> T check(Expected<T> E) {
|
||||
if (!E)
|
||||
fatal(llvm::toString(E.takeError()));
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
template <class T> T check(ErrorOr<T> E, const Twine &Prefix) {
|
||||
if (auto EC = E.getError())
|
||||
fatal(Prefix + ": " + EC.message());
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
template <class T> T check(Expected<T> E, const Twine &Prefix) {
|
||||
if (!E)
|
||||
fatal(Prefix + ": " + toString(E.takeError()));
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
@ -13,8 +13,13 @@
|
||||
|
||||
#include "Filesystem.h"
|
||||
#include "Config.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#if LLVM_ON_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <thread>
|
||||
|
||||
using namespace llvm;
|
||||
@ -35,27 +40,29 @@ using namespace lld::elf;
|
||||
// Since LLD can link a 1 GB binary in about 5 seconds, that waste
|
||||
// actually counts.
|
||||
//
|
||||
// This function spawns a background thread to call unlink.
|
||||
// This function spawns a background thread to remove the file.
|
||||
// The calling thread returns almost immediately.
|
||||
void elf::unlinkAsync(StringRef Path) {
|
||||
if (!Config->Threads || !sys::fs::exists(Config->OutputFile) ||
|
||||
!sys::fs::is_regular_file(Config->OutputFile))
|
||||
// Removing a file is async on windows.
|
||||
#if defined(LLVM_ON_WIN32)
|
||||
sys::fs::remove(Path);
|
||||
#else
|
||||
if (!ThreadsEnabled || !sys::fs::exists(Path) ||
|
||||
!sys::fs::is_regular_file(Path))
|
||||
return;
|
||||
|
||||
// First, rename Path to avoid race condition. We cannot remove
|
||||
// Path from a different thread because we are now going to create
|
||||
// Path as a new file. If we do that in a different thread, the new
|
||||
// thread can remove the new file.
|
||||
SmallString<128> TempPath;
|
||||
if (sys::fs::createUniqueFile(Path + "tmp%%%%%%%%", TempPath))
|
||||
return;
|
||||
if (sys::fs::rename(Path, TempPath)) {
|
||||
sys::fs::remove(TempPath);
|
||||
return;
|
||||
}
|
||||
// We cannot just remove path from a different thread because we are now going
|
||||
// to create path as a new file.
|
||||
// Instead we open the file and unlink it on this thread. The unlink is fast
|
||||
// since the open fd guarantees that it is not removing the last reference.
|
||||
int FD;
|
||||
std::error_code EC = sys::fs::openFileForRead(Path, FD);
|
||||
sys::fs::remove(Path);
|
||||
|
||||
// Remove TempPath in background.
|
||||
std::thread([=] { ::remove(TempPath.str().str().c_str()); }).detach();
|
||||
// close and therefore remove TempPath in background.
|
||||
if (!EC)
|
||||
std::thread([=] { ::close(FD); }).detach();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Simulate file creation to see if Path is writable.
|
||||
@ -73,5 +80,7 @@ void elf::unlinkAsync(StringRef Path) {
|
||||
std::error_code elf::tryCreateFile(StringRef Path) {
|
||||
if (Path.empty())
|
||||
return std::error_code();
|
||||
return FileOutputBuffer::create(Path, 1).getError();
|
||||
if (Path == "-")
|
||||
return std::error_code();
|
||||
return errorToErrorCode(FileOutputBuffer::create(Path, 1).takeError());
|
||||
}
|
||||
|
@ -10,7 +10,8 @@
|
||||
#ifndef LLD_ELF_FILESYSTEM_H
|
||||
#define LLD_ELF_FILESYSTEM_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include <system_error>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
@ -15,7 +15,8 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "GdbIndex.h"
|
||||
#include "Memory.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
|
||||
@ -24,26 +25,77 @@ using namespace llvm::object;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
std::pair<bool, GdbSymbol *> GdbHashTab::add(uint32_t Hash, size_t Offset) {
|
||||
GdbSymbol *&Sym = Map[Offset];
|
||||
if (Sym)
|
||||
return {false, Sym};
|
||||
Sym = make<GdbSymbol>(Hash, Offset);
|
||||
return {true, Sym};
|
||||
}
|
||||
|
||||
void GdbHashTab::finalizeContents() {
|
||||
uint32_t Size = std::max<uint32_t>(1024, NextPowerOf2(Map.size() * 4 / 3));
|
||||
uint32_t Mask = Size - 1;
|
||||
Table.resize(Size);
|
||||
|
||||
for (auto &P : Map) {
|
||||
GdbSymbol *Sym = P.second;
|
||||
uint32_t I = Sym->NameHash & Mask;
|
||||
uint32_t Step = ((Sym->NameHash * 17) & Mask) | 1;
|
||||
|
||||
while (Table[I])
|
||||
I = (I + Step) & Mask;
|
||||
Table[I] = Sym;
|
||||
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->maybeUncompress();
|
||||
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);
|
||||
else if (Sec->Name == ".debug_str")
|
||||
StrSection = toStringRef(Sec->Data);
|
||||
}
|
||||
}
|
||||
|
||||
// Find if there is a relocation at Pos in Sec. The code is a bit
|
||||
// more complicated than usual because we need to pass a section index
|
||||
// to llvm since it has no idea about InputSection.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
Optional<RelocAddrEntry>
|
||||
LLDDwarfObj<ELFT>::findAux(const InputSectionBase &Sec, uint64_t Pos,
|
||||
ArrayRef<RelTy> Rels) const {
|
||||
auto It = std::lower_bound(
|
||||
Rels.begin(), Rels.end(), Pos,
|
||||
[](const RelTy &A, uint64_t B) { return A.r_offset < B; });
|
||||
if (It == Rels.end() || It->r_offset != Pos)
|
||||
return None;
|
||||
const RelTy &Rel = *It;
|
||||
|
||||
const ObjFile<ELFT> *File = Sec.getFile<ELFT>();
|
||||
uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
|
||||
const typename ELFT::Sym &Sym = File->getELFSyms()[SymIndex];
|
||||
uint32_t SecIndex = File->getSectionIndex(Sym);
|
||||
|
||||
// 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");
|
||||
return None;
|
||||
}
|
||||
uint64_t Val = DR->Value + getAddend<ELFT>(Rel);
|
||||
|
||||
// FIXME: We should be consistent about always adding the file
|
||||
// offset or not.
|
||||
if (DR->Section->Flags & ELF::SHF_ALLOC)
|
||||
Val += cast<InputSection>(DR->Section)->getOffsetInFile();
|
||||
|
||||
return RelocAddrEntry{SecIndex, Val};
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &S,
|
||||
uint64_t Pos) const {
|
||||
auto &Sec = static_cast<const LLDDWARFSection &>(S);
|
||||
if (Sec.Sec->AreRelocsRela)
|
||||
return findAux(*Sec.Sec, Pos, Sec.Sec->template relas<ELFT>());
|
||||
return findAux(*Sec.Sec, Pos, Sec.Sec->template rels<ELFT>());
|
||||
}
|
||||
|
||||
template class elf::LLDDwarfObj<ELF32LE>;
|
||||
template class elf::LLDDwarfObj<ELF32BE>;
|
||||
template class elf::LLDDwarfObj<ELF64LE>;
|
||||
template class elf::LLDDwarfObj<ELF64BE>;
|
||||
|
@ -19,61 +19,50 @@ namespace elf {
|
||||
|
||||
class InputSection;
|
||||
|
||||
// Struct represents single entry of address area of gdb index.
|
||||
struct AddressEntry {
|
||||
InputSection *Section;
|
||||
uint64_t LowAddress;
|
||||
uint64_t HighAddress;
|
||||
uint32_t CuIndex;
|
||||
struct LLDDWARFSection final : public llvm::DWARFSection {
|
||||
InputSectionBase *Sec = nullptr;
|
||||
};
|
||||
|
||||
// Struct represents single entry of compilation units list area of gdb index.
|
||||
// It consist of CU offset in .debug_info section and it's size.
|
||||
struct CompilationUnitEntry {
|
||||
uint64_t CuOffset;
|
||||
uint64_t CuLength;
|
||||
};
|
||||
template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
|
||||
LLDDWARFSection InfoSection;
|
||||
LLDDWARFSection RangeSection;
|
||||
LLDDWARFSection LineSection;
|
||||
StringRef AbbrevSection;
|
||||
StringRef GnuPubNamesSection;
|
||||
StringRef GnuPubTypesSection;
|
||||
StringRef StrSection;
|
||||
|
||||
// Represents data about symbol and type names which are used
|
||||
// to build symbol table and constant pool area of gdb index.
|
||||
struct NameTypeEntry {
|
||||
StringRef Name;
|
||||
uint8_t Type;
|
||||
};
|
||||
template <class RelTy>
|
||||
llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec,
|
||||
uint64_t Pos,
|
||||
ArrayRef<RelTy> Rels) const;
|
||||
|
||||
// We fill one GdbIndexDataChunk for each object where scan of
|
||||
// debug information performed. That information futher used
|
||||
// for filling gdb index section areas.
|
||||
struct GdbIndexChunk {
|
||||
InputSection *DebugInfoSec;
|
||||
std::vector<AddressEntry> AddressArea;
|
||||
std::vector<CompilationUnitEntry> CompilationUnits;
|
||||
std::vector<NameTypeEntry> NamesAndTypes;
|
||||
};
|
||||
|
||||
// Element of GdbHashTab hash table.
|
||||
struct GdbSymbol {
|
||||
GdbSymbol(uint32_t Hash, size_t Offset)
|
||||
: NameHash(Hash), NameOffset(Offset) {}
|
||||
uint32_t NameHash;
|
||||
size_t NameOffset;
|
||||
size_t CuVectorIndex;
|
||||
};
|
||||
|
||||
// This class manages the hashed symbol table for the .gdb_index section.
|
||||
// The hash value for a table entry is computed by applying an iterative hash
|
||||
// function to the symbol's name.
|
||||
class GdbHashTab final {
|
||||
public:
|
||||
std::pair<bool, GdbSymbol *> add(uint32_t Hash, size_t Offset);
|
||||
|
||||
void finalizeContents();
|
||||
size_t getCapacity() { return Table.size(); }
|
||||
GdbSymbol *getSymbol(size_t I) { return Table[I]; }
|
||||
|
||||
private:
|
||||
llvm::DenseMap<size_t, GdbSymbol *> Map;
|
||||
std::vector<GdbSymbol *> Table;
|
||||
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 getCUIndexSection() 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;
|
||||
};
|
||||
|
||||
} // namespace elf
|
||||
|
103
ELF/ICF.cpp
103
ELF/ICF.cpp
@ -76,7 +76,8 @@
|
||||
#include "ICF.h"
|
||||
#include "Config.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Threads.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
@ -155,16 +156,20 @@ private:
|
||||
// Returns a hash value for S. Note that the information about
|
||||
// relocation targets is not included in the hash value.
|
||||
template <class ELFT> static uint32_t getHash(InputSection *S) {
|
||||
return hash_combine(S->Flags, S->getSize(), S->NumRelocations);
|
||||
return hash_combine(S->Flags, S->getSize(), S->NumRelocations, S->Data);
|
||||
}
|
||||
|
||||
// Returns true if section S is subject of ICF.
|
||||
static bool isEligible(InputSection *S) {
|
||||
// Don't merge read only data sections unless --icf-data was passed.
|
||||
if (!(S->Flags & SHF_EXECINSTR) && !Config->ICFData)
|
||||
return false;
|
||||
|
||||
// .init and .fini contains instructions that must be executed to
|
||||
// initialize and finalize the process. They cannot and should not
|
||||
// be merged.
|
||||
return S->Live && (S->Flags & SHF_ALLOC) && (S->Flags & SHF_EXECINSTR) &&
|
||||
!(S->Flags & SHF_WRITE) && S->Name != ".init" && S->Name != ".fini";
|
||||
return S->Live && (S->Flags & SHF_ALLOC) && !(S->Flags & SHF_WRITE) &&
|
||||
S->Name != ".init" && S->Name != ".fini";
|
||||
}
|
||||
|
||||
// Split an equivalence class into smaller classes.
|
||||
@ -207,38 +212,49 @@ void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) {
|
||||
// Compare two lists of relocations.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
bool ICF<ELFT>::constantEq(const InputSection *A, ArrayRef<RelTy> RelsA,
|
||||
const InputSection *B, ArrayRef<RelTy> RelsB) {
|
||||
auto Eq = [&](const RelTy &RA, const RelTy &RB) {
|
||||
if (RA.r_offset != RB.r_offset ||
|
||||
RA.getType(Config->IsMips64EL) != RB.getType(Config->IsMips64EL))
|
||||
bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
|
||||
const InputSection *SecB, ArrayRef<RelTy> RB) {
|
||||
if (RA.size() != RB.size())
|
||||
return false;
|
||||
|
||||
for (size_t I = 0; I < RA.size(); ++I) {
|
||||
if (RA[I].r_offset != RB[I].r_offset ||
|
||||
RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL))
|
||||
return false;
|
||||
uint64_t AddA = getAddend<ELFT>(RA);
|
||||
uint64_t AddB = getAddend<ELFT>(RB);
|
||||
|
||||
SymbolBody &SA = A->template getFile<ELFT>()->getRelocTargetSym(RA);
|
||||
SymbolBody &SB = B->template getFile<ELFT>()->getRelocTargetSym(RB);
|
||||
if (&SA == &SB)
|
||||
return AddA == AddB;
|
||||
uint64_t AddA = getAddend<ELFT>(RA[I]);
|
||||
uint64_t AddB = getAddend<ELFT>(RB[I]);
|
||||
|
||||
auto *DA = dyn_cast<DefinedRegular>(&SA);
|
||||
auto *DB = dyn_cast<DefinedRegular>(&SB);
|
||||
Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
|
||||
Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
|
||||
if (&SA == &SB) {
|
||||
if (AddA == AddB)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *DA = dyn_cast<Defined>(&SA);
|
||||
auto *DB = dyn_cast<Defined>(&SB);
|
||||
if (!DA || !DB)
|
||||
return false;
|
||||
|
||||
// Relocations referring to absolute symbols are constant-equal if their
|
||||
// values are equal.
|
||||
if (!DA->Section && !DB->Section && DA->Value + AddA == DB->Value + AddB)
|
||||
continue;
|
||||
if (!DA->Section || !DB->Section)
|
||||
return !DA->Section && !DB->Section &&
|
||||
DA->Value + AddA == DB->Value + AddB;
|
||||
return false;
|
||||
|
||||
if (DA->Section->kind() != DB->Section->kind())
|
||||
return false;
|
||||
|
||||
// Relocations referring to InputSections are constant-equal if their
|
||||
// section offsets are equal.
|
||||
if (isa<InputSection>(DA->Section))
|
||||
return DA->Value + AddA == DB->Value + AddB;
|
||||
if (isa<InputSection>(DA->Section)) {
|
||||
if (DA->Value + AddA == DB->Value + AddB)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Relocations referring to MergeInputSections are constant-equal if their
|
||||
// offsets in the output section are equal.
|
||||
@ -253,11 +269,11 @@ bool ICF<ELFT>::constantEq(const InputSection *A, ArrayRef<RelTy> RelsA,
|
||||
SA.isSection() ? X->getOffset(AddA) : X->getOffset(DA->Value) + AddA;
|
||||
uint64_t OffsetB =
|
||||
SB.isSection() ? Y->getOffset(AddB) : Y->getOffset(DB->Value) + AddB;
|
||||
return OffsetA == OffsetB;
|
||||
};
|
||||
if (OffsetA != OffsetB)
|
||||
return false;
|
||||
}
|
||||
|
||||
return RelsA.size() == RelsB.size() &&
|
||||
std::equal(RelsA.begin(), RelsA.end(), RelsB.begin(), Eq);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare "non-moving" part of two InputSections, namely everything
|
||||
@ -278,37 +294,39 @@ bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) {
|
||||
// relocations point to the same section in terms of ICF.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
bool ICF<ELFT>::variableEq(const InputSection *A, ArrayRef<RelTy> RelsA,
|
||||
const InputSection *B, ArrayRef<RelTy> RelsB) {
|
||||
auto Eq = [&](const RelTy &RA, const RelTy &RB) {
|
||||
// The two sections must be identical.
|
||||
SymbolBody &SA = A->template getFile<ELFT>()->getRelocTargetSym(RA);
|
||||
SymbolBody &SB = B->template getFile<ELFT>()->getRelocTargetSym(RB);
|
||||
if (&SA == &SB)
|
||||
return true;
|
||||
bool ICF<ELFT>::variableEq(const InputSection *SecA, ArrayRef<RelTy> RA,
|
||||
const InputSection *SecB, ArrayRef<RelTy> RB) {
|
||||
assert(RA.size() == RB.size());
|
||||
|
||||
auto *DA = cast<DefinedRegular>(&SA);
|
||||
auto *DB = cast<DefinedRegular>(&SB);
|
||||
for (size_t I = 0; I < RA.size(); ++I) {
|
||||
// The two sections must be identical.
|
||||
Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
|
||||
Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
|
||||
if (&SA == &SB)
|
||||
continue;
|
||||
|
||||
auto *DA = cast<Defined>(&SA);
|
||||
auto *DB = cast<Defined>(&SB);
|
||||
|
||||
// We already dealt with absolute and non-InputSection symbols in
|
||||
// constantEq, and for InputSections we have already checked everything
|
||||
// except the equivalence class.
|
||||
if (!DA->Section)
|
||||
return true;
|
||||
continue;
|
||||
auto *X = dyn_cast<InputSection>(DA->Section);
|
||||
if (!X)
|
||||
return true;
|
||||
continue;
|
||||
auto *Y = cast<InputSection>(DB->Section);
|
||||
|
||||
// Ineligible sections are in the special equivalence class 0.
|
||||
// They can never be the same in terms of the equivalence class.
|
||||
if (X->Class[Current] == 0)
|
||||
return false;
|
||||
|
||||
return X->Class[Current] == Y->Class[Current];
|
||||
if (X->Class[Current] != Y->Class[Current])
|
||||
return false;
|
||||
};
|
||||
|
||||
return std::equal(RelsA.begin(), RelsA.end(), RelsB.begin(), Eq);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare "moving" part of two InputSections, namely relocation targets.
|
||||
@ -353,7 +371,7 @@ template <class ELFT>
|
||||
void ICF<ELFT>::forEachClass(std::function<void(size_t, size_t)> Fn) {
|
||||
// If threading is disabled or the number of sections are
|
||||
// too small to use threading, call Fn sequentially.
|
||||
if (!Config->Threads || Sections.size() < 1024) {
|
||||
if (!ThreadsEnabled || Sections.size() < 1024) {
|
||||
forEachClassRange(0, Sections.size(), Fn);
|
||||
++Cnt;
|
||||
return;
|
||||
@ -381,9 +399,10 @@ template <class ELFT> void ICF<ELFT>::run() {
|
||||
Sections.push_back(S);
|
||||
|
||||
// Initially, we use hash values to partition sections.
|
||||
for (InputSection *S : Sections)
|
||||
parallelForEach(Sections, [&](InputSection *S) {
|
||||
// Set MSB to 1 to avoid collisions with non-hash IDs.
|
||||
S->Class[0] = getHash<ELFT>(S) | (1 << 31);
|
||||
});
|
||||
|
||||
// From now on, sections in Sections vector are ordered so that sections
|
||||
// in the same equivalence class are consecutive in the vector.
|
||||
|
@ -8,13 +8,13 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "Error.h"
|
||||
#include "InputSection.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/CodeGen/Analysis.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
@ -23,6 +23,8 @@
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
#include "llvm/Support/ARMAttributeParser.h"
|
||||
#include "llvm/Support/ARMBuildAttributes.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/TarWriter.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@ -35,26 +37,23 @@ using namespace llvm::sys::fs;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
std::vector<BinaryFile *> elf::BinaryFiles;
|
||||
std::vector<BitcodeFile *> elf::BitcodeFiles;
|
||||
std::vector<InputFile *> elf::ObjectFiles;
|
||||
std::vector<InputFile *> elf::SharedFiles;
|
||||
|
||||
TarWriter *elf::Tar;
|
||||
|
||||
InputFile::InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
|
||||
|
||||
namespace {
|
||||
// In ELF object file all section addresses are zero. If we have multiple
|
||||
// .text sections (when using -ffunction-section or comdat group) then
|
||||
// LLVM DWARF parser will not be able to parse .debug_line correctly, unless
|
||||
// we assign each section some unique address. This callback method assigns
|
||||
// each section an address equal to its offset in ELF object file.
|
||||
class ObjectInfo : public LoadedObjectInfoHelper<ObjectInfo> {
|
||||
public:
|
||||
uint64_t getSectionLoadAddress(const object::SectionRef &Sec) const override {
|
||||
return static_cast<const ELFSectionRef &>(Sec).getOffset();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
|
||||
// The --chroot option changes our virtual root directory.
|
||||
// This is useful when you are dealing with files created by --reproduce.
|
||||
if (!Config->Chroot.empty() && Path.startswith("/"))
|
||||
Path = Saver.save(Config->Chroot + Path);
|
||||
|
||||
log(Path);
|
||||
|
||||
auto MBOrErr = MemoryBuffer::getFile(Path);
|
||||
if (auto EC = MBOrErr.getError()) {
|
||||
error("cannot open " + Path + ": " + EC.message());
|
||||
@ -70,28 +69,88 @@ Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
|
||||
return MBRef;
|
||||
}
|
||||
|
||||
template <class ELFT> void elf::ObjectFile<ELFT>::initializeDwarfLine() {
|
||||
std::unique_ptr<object::ObjectFile> Obj =
|
||||
check(object::ObjectFile::createObjectFile(this->MB), toString(this));
|
||||
|
||||
ObjectInfo ObjInfo;
|
||||
DWARFContextInMemory Dwarf(*Obj, &ObjInfo);
|
||||
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
|
||||
DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(this));
|
||||
const DWARFObject &Obj = Dwarf.getDWARFObj();
|
||||
DwarfLine.reset(new DWARFDebugLine);
|
||||
DWARFDataExtractor LineData(Dwarf.getLineSection(), Config->IsLE,
|
||||
DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE,
|
||||
Config->Wordsize);
|
||||
|
||||
// The second parameter is offset in .debug_line section
|
||||
// for compilation unit (CU) of interest. We have only one
|
||||
// CU (object file), so offset is always 0.
|
||||
DwarfLine->getOrParseLineTable(LineData, 0);
|
||||
// FIXME: Provide the associated DWARFUnit if there is one. DWARF v5
|
||||
// needs it in order to find indirect strings.
|
||||
const DWARFDebugLine::LineTable *LT =
|
||||
DwarfLine->getOrParseLineTable(LineData, 0, nullptr);
|
||||
|
||||
// Return if there is no debug information about CU available.
|
||||
if (!Dwarf.getNumCompileUnits())
|
||||
return;
|
||||
|
||||
// Loop over variable records and insert them to VariableLoc.
|
||||
DWARFCompileUnit *CU = Dwarf.getCompileUnitAtIndex(0);
|
||||
for (const auto &Entry : CU->dies()) {
|
||||
DWARFDie Die(CU, &Entry);
|
||||
// Skip all tags that are not variables.
|
||||
if (Die.getTag() != dwarf::DW_TAG_variable)
|
||||
continue;
|
||||
|
||||
// Skip if a local variable because we don't need them for generating error
|
||||
// messages. In general, only non-local symbols can fail to be linked.
|
||||
if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0))
|
||||
continue;
|
||||
|
||||
// Get the source filename index for the variable.
|
||||
unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
|
||||
if (!LT->hasFileAtIndex(File))
|
||||
continue;
|
||||
|
||||
// Get the line number on which the variable is declared.
|
||||
unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
|
||||
|
||||
// Get the name of the variable and add the collected information to
|
||||
// VariableLoc. Usually Name is non-empty, but it can be empty if the input
|
||||
// object file lacks some debug info.
|
||||
StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), "");
|
||||
if (!Name.empty())
|
||||
VariableLoc.insert({Name, {File, Line}});
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the pair of file name and line number describing location of data
|
||||
// object (variable, array, etc) definition.
|
||||
template <class ELFT>
|
||||
Optional<std::pair<std::string, unsigned>>
|
||||
ObjFile<ELFT>::getVariableLoc(StringRef Name) {
|
||||
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
|
||||
|
||||
// There is always only one CU so it's offset is 0.
|
||||
const DWARFDebugLine::LineTable *LT = DwarfLine->getLineTable(0);
|
||||
if (!LT)
|
||||
return None;
|
||||
|
||||
// Return if we have no debug information about data object.
|
||||
auto It = VariableLoc.find(Name);
|
||||
if (It == VariableLoc.end())
|
||||
return None;
|
||||
|
||||
// Take file name string from line table.
|
||||
std::string FileName;
|
||||
if (!LT->getFileNameByIndex(
|
||||
It->second.first /* File */, nullptr,
|
||||
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName))
|
||||
return None;
|
||||
|
||||
return std::make_pair(FileName, It->second.second /*Line*/);
|
||||
}
|
||||
|
||||
// Returns source line information for a given offset
|
||||
// using DWARF debug info.
|
||||
template <class ELFT>
|
||||
Optional<DILineInfo> elf::ObjectFile<ELFT>::getDILineInfo(InputSectionBase *S,
|
||||
uint64_t Offset) {
|
||||
llvm::call_once(InitDwarfLine, [this]() { initializeDwarfLine(); });
|
||||
Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S,
|
||||
uint64_t Offset) {
|
||||
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
|
||||
|
||||
// The offset to CU is 0.
|
||||
const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0);
|
||||
@ -112,8 +171,7 @@ Optional<DILineInfo> elf::ObjectFile<ELFT>::getDILineInfo(InputSectionBase *S,
|
||||
// Returns source line information for a given offset
|
||||
// using DWARF debug info.
|
||||
template <class ELFT>
|
||||
std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase *S,
|
||||
uint64_t Offset) {
|
||||
std::string ObjFile<ELFT>::getLineInfo(InputSectionBase *S, uint64_t Offset) {
|
||||
if (Optional<DILineInfo> Info = getDILineInfo(S, Offset))
|
||||
return Info->FileName + ":" + std::to_string(Info->Line);
|
||||
return "";
|
||||
@ -145,50 +203,41 @@ ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) {
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalSymbols() {
|
||||
return makeArrayRef(Symbols.begin() + FirstNonLocal, Symbols.end());
|
||||
typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalELFSyms() {
|
||||
return makeArrayRef(ELFSyms.begin() + FirstNonLocal, ELFSyms.end());
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
|
||||
return check(getObj().getSectionIndex(&Sym, Symbols, SymtabSHNDX),
|
||||
toString(this));
|
||||
return CHECK(getObj().getSectionIndex(&Sym, ELFSyms, SymtabSHNDX), this);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
|
||||
const Elf_Shdr *Symtab) {
|
||||
FirstNonLocal = Symtab->sh_info;
|
||||
Symbols = check(getObj().symbols(Symtab), toString(this));
|
||||
if (FirstNonLocal == 0 || FirstNonLocal > Symbols.size())
|
||||
ELFSyms = CHECK(getObj().symbols(Symtab), this);
|
||||
if (FirstNonLocal == 0 || FirstNonLocal > ELFSyms.size())
|
||||
fatal(toString(this) + ": invalid sh_info in symbol table");
|
||||
|
||||
StringTable = check(getObj().getStringTableForSymtab(*Symtab, Sections),
|
||||
toString(this));
|
||||
StringTable =
|
||||
CHECK(getObj().getStringTableForSymtab(*Symtab, Sections), this);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M, StringRef ArchiveName)
|
||||
: ELFFileBase<ELFT>(Base::ObjectKind, M) {
|
||||
ObjFile<ELFT>::ObjFile(MemoryBufferRef M, StringRef ArchiveName)
|
||||
: ELFFileBase<ELFT>(Base::ObjKind, M) {
|
||||
this->ArchiveName = ArchiveName;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getLocalSymbols() {
|
||||
if (this->SymbolBodies.empty())
|
||||
return this->SymbolBodies;
|
||||
return makeArrayRef(this->SymbolBodies).slice(1, this->FirstNonLocal - 1);
|
||||
template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getLocalSymbols() {
|
||||
if (this->Symbols.empty())
|
||||
return {};
|
||||
return makeArrayRef(this->Symbols).slice(1, this->FirstNonLocal - 1);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getSymbols() {
|
||||
if (this->SymbolBodies.empty())
|
||||
return this->SymbolBodies;
|
||||
return makeArrayRef(this->SymbolBodies).slice(1);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void elf::ObjectFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
|
||||
void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
|
||||
// Read section and symbol tables.
|
||||
initializeSections(ComdatGroups);
|
||||
initializeSymbols();
|
||||
@ -198,19 +247,17 @@ void elf::ObjectFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
|
||||
// They are identified and deduplicated by group name. This function
|
||||
// returns a group name.
|
||||
template <class ELFT>
|
||||
StringRef
|
||||
elf::ObjectFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
|
||||
const Elf_Shdr &Sec) {
|
||||
StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
|
||||
const Elf_Shdr &Sec) {
|
||||
// Group signatures are stored as symbol names in object files.
|
||||
// sh_info contains a symbol index, so we fetch a symbol and read its name.
|
||||
if (this->Symbols.empty())
|
||||
if (this->ELFSyms.empty())
|
||||
this->initSymtab(
|
||||
Sections,
|
||||
check(object::getSection<ELFT>(Sections, Sec.sh_link), toString(this)));
|
||||
Sections, CHECK(object::getSection<ELFT>(Sections, Sec.sh_link), this));
|
||||
|
||||
const Elf_Sym *Sym = check(
|
||||
object::getSymbol<ELFT>(this->Symbols, Sec.sh_info), toString(this));
|
||||
StringRef Signature = check(Sym->getName(this->StringTable), toString(this));
|
||||
const Elf_Sym *Sym =
|
||||
CHECK(object::getSymbol<ELFT>(this->ELFSyms, Sec.sh_info), this);
|
||||
StringRef Signature = CHECK(Sym->getName(this->StringTable), this);
|
||||
|
||||
// As a special case, if a symbol is a section symbol and has no name,
|
||||
// we use a section name as a signature.
|
||||
@ -225,32 +272,22 @@ elf::ObjectFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
ArrayRef<typename elf::ObjectFile<ELFT>::Elf_Word>
|
||||
elf::ObjectFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
|
||||
ArrayRef<typename ObjFile<ELFT>::Elf_Word>
|
||||
ObjFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
|
||||
const ELFFile<ELFT> &Obj = this->getObj();
|
||||
ArrayRef<Elf_Word> Entries = check(
|
||||
Obj.template getSectionContentsAsArray<Elf_Word>(&Sec), toString(this));
|
||||
ArrayRef<Elf_Word> Entries =
|
||||
CHECK(Obj.template getSectionContentsAsArray<Elf_Word>(&Sec), this);
|
||||
if (Entries.empty() || Entries[0] != GRP_COMDAT)
|
||||
fatal(toString(this) + ": unsupported SHT_GROUP format");
|
||||
return Entries.slice(1);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
|
||||
template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
|
||||
// We don't merge sections if -O0 (default is -O1). This makes sometimes
|
||||
// the linker significantly faster, although the output will be bigger.
|
||||
if (Config->Optimize == 0)
|
||||
return false;
|
||||
|
||||
// Do not merge sections if generating a relocatable object. It makes
|
||||
// the code simpler because we do not need to update relocation addends
|
||||
// to reflect changes introduced by merging. Instead of that we write
|
||||
// such "merge" sections into separate OutputSections and keep SHF_MERGE
|
||||
// / SHF_STRINGS flags and sh_entsize value to be able to perform merging
|
||||
// later during a final linking.
|
||||
if (Config->Relocatable)
|
||||
return false;
|
||||
|
||||
// A mergeable section with size 0 is useless because they don't have
|
||||
// any data to merge. A mergeable string section with size 0 can be
|
||||
// argued as invalid because it doesn't end with a null character.
|
||||
@ -276,29 +313,19 @@ bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
|
||||
if (Flags & SHF_WRITE)
|
||||
fatal(toString(this) + ": writable SHF_MERGE section is not supported");
|
||||
|
||||
// Don't try to merge if the alignment is larger than the sh_entsize and this
|
||||
// is not SHF_STRINGS.
|
||||
//
|
||||
// Since this is not a SHF_STRINGS, we would need to pad after every entity.
|
||||
// It would be equivalent for the producer of the .o to just set a larger
|
||||
// sh_entsize.
|
||||
if (Flags & SHF_STRINGS)
|
||||
return true;
|
||||
|
||||
return Sec.sh_addralign <= EntSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void elf::ObjectFile<ELFT>::initializeSections(
|
||||
void ObjFile<ELFT>::initializeSections(
|
||||
DenseSet<CachedHashStringRef> &ComdatGroups) {
|
||||
const ELFFile<ELFT> &Obj = this->getObj();
|
||||
|
||||
ArrayRef<Elf_Shdr> ObjSections =
|
||||
check(this->getObj().sections(), toString(this));
|
||||
ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this);
|
||||
uint64_t Size = ObjSections.size();
|
||||
this->Sections.resize(Size);
|
||||
this->SectionStringTable =
|
||||
check(Obj.getSectionStringTable(ObjSections), toString(this));
|
||||
CHECK(Obj.getSectionStringTable(ObjSections), this);
|
||||
|
||||
for (size_t I = 0, E = ObjSections.size(); I < E; I++) {
|
||||
if (this->Sections[I] == &InputSection::Discarded)
|
||||
@ -344,8 +371,7 @@ void elf::ObjectFile<ELFT>::initializeSections(
|
||||
this->initSymtab(ObjSections, &Sec);
|
||||
break;
|
||||
case SHT_SYMTAB_SHNDX:
|
||||
this->SymtabSHNDX =
|
||||
check(Obj.getSHNDXTable(Sec, ObjSections), toString(this));
|
||||
this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, ObjSections), this);
|
||||
break;
|
||||
case SHT_STRTAB:
|
||||
case SHT_NULL:
|
||||
@ -361,13 +387,55 @@ void elf::ObjectFile<ELFT>::initializeSections(
|
||||
fatal(toString(this) + ": invalid sh_link index: " +
|
||||
Twine(Sec.sh_link));
|
||||
this->Sections[Sec.sh_link]->DependentSections.push_back(
|
||||
this->Sections[I]);
|
||||
cast<InputSection>(this->Sections[I]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The ARM support in lld makes some use of instructions that are not available
|
||||
// on all ARM architectures. Namely:
|
||||
// - Use of BLX instruction for interworking between ARM and Thumb state.
|
||||
// - Use of the extended Thumb branch encoding in relocation.
|
||||
// - Use of the MOVT/MOVW instructions in Thumb Thunks.
|
||||
// The ARM Attributes section contains information about the architecture chosen
|
||||
// at compile time. We follow the convention that if at least one input object
|
||||
// is compiled with an architecture that supports these features then lld is
|
||||
// permitted to use them.
|
||||
static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) {
|
||||
if (!Attributes.hasAttribute(ARMBuildAttrs::CPU_arch))
|
||||
return;
|
||||
auto Arch = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
|
||||
switch (Arch) {
|
||||
case ARMBuildAttrs::Pre_v4:
|
||||
case ARMBuildAttrs::v4:
|
||||
case ARMBuildAttrs::v4T:
|
||||
// Architectures prior to v5 do not support BLX instruction
|
||||
break;
|
||||
case ARMBuildAttrs::v5T:
|
||||
case ARMBuildAttrs::v5TE:
|
||||
case ARMBuildAttrs::v5TEJ:
|
||||
case ARMBuildAttrs::v6:
|
||||
case ARMBuildAttrs::v6KZ:
|
||||
case ARMBuildAttrs::v6K:
|
||||
Config->ARMHasBlx = true;
|
||||
// Architectures used in pre-Cortex processors do not support
|
||||
// The J1 = 1 J2 = 1 Thumb branch range extension, with the exception
|
||||
// of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do.
|
||||
break;
|
||||
default:
|
||||
// All other Architectures have BLX and extended branch encoding
|
||||
Config->ARMHasBlx = true;
|
||||
Config->ARMJ1J2BranchEncoding = true;
|
||||
if (Arch != ARMBuildAttrs::v6_M && Arch != ARMBuildAttrs::v6S_M)
|
||||
// All Architectures used in Cortex processors with the exception
|
||||
// of v6-M and v6S-M have the MOVT and MOVW instructions.
|
||||
Config->ARMHasMovtMovw = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase *elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
|
||||
InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
|
||||
uint32_t Idx = Sec.sh_info;
|
||||
if (Idx >= this->Sections.size())
|
||||
fatal(toString(this) + ": invalid relocated section index: " + Twine(Idx));
|
||||
@ -394,21 +462,26 @@ InputSectionBase *toRegularSection(MergeInputSection *Sec) {
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase *
|
||||
elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
|
||||
InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
|
||||
StringRef Name = getSectionName(Sec);
|
||||
|
||||
switch (Sec.sh_type) {
|
||||
case SHT_ARM_ATTRIBUTES:
|
||||
// FIXME: ARM meta-data section. 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.
|
||||
case SHT_ARM_ATTRIBUTES: {
|
||||
if (Config->EMachine != EM_ARM)
|
||||
break;
|
||||
ARMAttributeParser Attributes;
|
||||
ArrayRef<uint8_t> Contents = check(this->getObj().getSectionContents(&Sec));
|
||||
Attributes.Parse(Contents, /*isLittle*/Config->EKind == ELF32LEKind);
|
||||
updateSupportedARMFeatures(Attributes);
|
||||
// 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;
|
||||
}
|
||||
return &InputSection::Discarded;
|
||||
}
|
||||
case SHT_RELA:
|
||||
case SHT_REL: {
|
||||
// Find the relocation target section and associate this
|
||||
@ -443,13 +516,12 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
|
||||
|
||||
size_t NumRelocations;
|
||||
if (Sec.sh_type == SHT_RELA) {
|
||||
ArrayRef<Elf_Rela> Rels =
|
||||
check(this->getObj().relas(&Sec), toString(this));
|
||||
ArrayRef<Elf_Rela> Rels = CHECK(this->getObj().relas(&Sec), this);
|
||||
Target->FirstRelocation = Rels.begin();
|
||||
NumRelocations = Rels.size();
|
||||
Target->AreRelocsRela = true;
|
||||
} else {
|
||||
ArrayRef<Elf_Rel> Rels = check(this->getObj().rels(&Sec), toString(this));
|
||||
ArrayRef<Elf_Rel> Rels = CHECK(this->getObj().rels(&Sec), this);
|
||||
Target->FirstRelocation = Rels.begin();
|
||||
NumRelocations = Rels.size();
|
||||
Target->AreRelocsRela = false;
|
||||
@ -497,18 +569,6 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
|
||||
return &InputSection::Discarded;
|
||||
}
|
||||
|
||||
if (Config->Strip != StripPolicy::None && Name.startswith(".debug"))
|
||||
return &InputSection::Discarded;
|
||||
|
||||
// If -gdb-index is given, LLD creates .gdb_index section, and that
|
||||
// section serves the same purpose as .debug_gnu_pub{names,types} sections.
|
||||
// If that's the case, we want to eliminate .debug_gnu_pub{names,types}
|
||||
// because they are redundant and can waste large amount of disk space
|
||||
// (for example, they are about 400 MiB in total for a clang debug build.)
|
||||
if (Config->GdbIndex &&
|
||||
(Name == ".debug_gnu_pubnames" || Name == ".debug_gnu_pubtypes"))
|
||||
return &InputSection::Discarded;
|
||||
|
||||
// The linkonce feature is a sort of proto-comdat. Some glibc i386 object
|
||||
// files contain definitions of symbol "__x86.get_pc_thunk.bx" in linkonce
|
||||
// sections. Drop those sections to avoid duplicate symbol errors.
|
||||
@ -529,46 +589,24 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
StringRef elf::ObjectFile<ELFT>::getSectionName(const Elf_Shdr &Sec) {
|
||||
return check(this->getObj().getSectionName(&Sec, SectionStringTable),
|
||||
toString(this));
|
||||
StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &Sec) {
|
||||
return CHECK(this->getObj().getSectionName(&Sec, SectionStringTable), this);
|
||||
}
|
||||
|
||||
template <class ELFT> void elf::ObjectFile<ELFT>::initializeSymbols() {
|
||||
SymbolBodies.reserve(this->Symbols.size());
|
||||
for (const Elf_Sym &Sym : this->Symbols)
|
||||
SymbolBodies.push_back(createSymbolBody(&Sym));
|
||||
template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
|
||||
this->Symbols.reserve(this->ELFSyms.size());
|
||||
for (const Elf_Sym &Sym : this->ELFSyms)
|
||||
this->Symbols.push_back(createSymbol(&Sym));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase *elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
|
||||
uint32_t Index = this->getSectionIndex(Sym);
|
||||
if (Index >= this->Sections.size())
|
||||
fatal(toString(this) + ": invalid section index: " + Twine(Index));
|
||||
InputSectionBase *S = this->Sections[Index];
|
||||
|
||||
// We found that GNU assembler 2.17.50 [FreeBSD] 2007-07-03 could
|
||||
// generate broken objects. STT_SECTION/STT_NOTYPE symbols can be
|
||||
// associated with SHT_REL[A]/SHT_SYMTAB/SHT_STRTAB sections.
|
||||
// In this case it is fine for section to be null here as we do not
|
||||
// allocate sections of these types.
|
||||
if (!S) {
|
||||
if (Index == 0 || Sym.getType() == STT_SECTION ||
|
||||
Sym.getType() == STT_NOTYPE)
|
||||
return nullptr;
|
||||
fatal(toString(this) + ": invalid section index: " + Twine(Index));
|
||||
}
|
||||
|
||||
if (S == &InputSection::Discarded)
|
||||
return S;
|
||||
return S->Repl;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
|
||||
template <class ELFT> Symbol *ObjFile<ELFT>::createSymbol(const Elf_Sym *Sym) {
|
||||
int Binding = Sym->getBinding();
|
||||
InputSectionBase *Sec = getSection(*Sym);
|
||||
|
||||
uint32_t SecIdx = this->getSectionIndex(*Sym);
|
||||
if (SecIdx >= this->Sections.size())
|
||||
fatal(toString(this) + ": invalid section index: " + Twine(SecIdx));
|
||||
|
||||
InputSectionBase *Sec = this->Sections[SecIdx];
|
||||
uint8_t StOther = Sym->st_other;
|
||||
uint8_t Type = Sym->getType();
|
||||
uint64_t Value = Sym->st_value;
|
||||
@ -576,34 +614,29 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
|
||||
|
||||
if (Binding == STB_LOCAL) {
|
||||
if (Sym->getType() == STT_FILE)
|
||||
SourceFile = check(Sym->getName(this->StringTable), toString(this));
|
||||
SourceFile = CHECK(Sym->getName(this->StringTable), this);
|
||||
|
||||
if (this->StringTable.size() <= Sym->st_name)
|
||||
fatal(toString(this) + ": invalid symbol name offset");
|
||||
|
||||
StringRefZ Name = this->StringTable.data() + Sym->st_name;
|
||||
if (Sym->st_shndx == SHN_UNDEF)
|
||||
return make<Undefined>(Name, /*IsLocal=*/true, StOther, Type, this);
|
||||
return make<Undefined>(this, Name, Binding, StOther, Type);
|
||||
|
||||
return make<DefinedRegular>(Name, /*IsLocal=*/true, StOther, Type, Value,
|
||||
Size, Sec, this);
|
||||
return make<Defined>(this, Name, Binding, StOther, Type, Value, Size, Sec);
|
||||
}
|
||||
|
||||
StringRef Name = check(Sym->getName(this->StringTable), toString(this));
|
||||
StringRef Name = CHECK(Sym->getName(this->StringTable), this);
|
||||
|
||||
switch (Sym->st_shndx) {
|
||||
case SHN_UNDEF:
|
||||
return elf::Symtab<ELFT>::X
|
||||
->addUndefined(Name, /*IsLocal=*/false, Binding, StOther, Type,
|
||||
/*CanOmitFromDynSym=*/false, this)
|
||||
->body();
|
||||
return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
|
||||
/*CanOmitFromDynSym=*/false, this);
|
||||
case SHN_COMMON:
|
||||
if (Value == 0 || Value >= UINT32_MAX)
|
||||
fatal(toString(this) + ": common symbol '" + Name +
|
||||
"' has invalid alignment: " + Twine(Value));
|
||||
return elf::Symtab<ELFT>::X
|
||||
->addCommon(Name, Size, Value, Binding, StOther, Type, this)
|
||||
->body();
|
||||
return Symtab->addCommon(Name, Size, Value, Binding, StOther, Type, this);
|
||||
}
|
||||
|
||||
switch (Binding) {
|
||||
@ -613,13 +646,10 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
|
||||
case STB_WEAK:
|
||||
case STB_GNU_UNIQUE:
|
||||
if (Sec == &InputSection::Discarded)
|
||||
return elf::Symtab<ELFT>::X
|
||||
->addUndefined(Name, /*IsLocal=*/false, Binding, StOther, Type,
|
||||
/*CanOmitFromDynSym=*/false, this)
|
||||
->body();
|
||||
return elf::Symtab<ELFT>::X
|
||||
->addRegular(Name, StOther, Type, Value, Size, Binding, Sec, this)
|
||||
->body();
|
||||
return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
|
||||
/*CanOmitFromDynSym=*/false, this);
|
||||
return Symtab->addRegular<ELFT>(Name, StOther, Type, Value, Size, Binding,
|
||||
Sec, this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -630,14 +660,14 @@ ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File)
|
||||
template <class ELFT> void ArchiveFile::parse() {
|
||||
Symbols.reserve(File->getNumberOfSymbols());
|
||||
for (const Archive::Symbol &Sym : File->symbols())
|
||||
Symbols.push_back(Symtab<ELFT>::X->addLazyArchive(this, Sym));
|
||||
Symbols.push_back(Symtab->addLazyArchive<ELFT>(Sym.getName(), this, Sym));
|
||||
}
|
||||
|
||||
// Returns a buffer pointing to a member file containing a given symbol.
|
||||
std::pair<MemoryBufferRef, uint64_t>
|
||||
ArchiveFile::getMember(const Archive::Symbol *Sym) {
|
||||
Archive::Child C =
|
||||
check(Sym->getMember(), toString(this) +
|
||||
CHECK(Sym->getMember(), toString(this) +
|
||||
": could not get the member for symbol " +
|
||||
Sym->getName());
|
||||
|
||||
@ -645,14 +675,13 @@ ArchiveFile::getMember(const Archive::Symbol *Sym) {
|
||||
return {MemoryBufferRef(), 0};
|
||||
|
||||
MemoryBufferRef Ret =
|
||||
check(C.getMemoryBufferRef(),
|
||||
CHECK(C.getMemoryBufferRef(),
|
||||
toString(this) +
|
||||
": could not get the buffer for the member defining symbol " +
|
||||
Sym->getName());
|
||||
|
||||
if (C.getParent()->isThin() && Tar)
|
||||
Tar->append(relativeToRoot(check(C.getFullName(), toString(this))),
|
||||
Ret.getBuffer());
|
||||
Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), Ret.getBuffer());
|
||||
if (C.getParent()->isThin())
|
||||
return {Ret, 0};
|
||||
return {Ret, C.getChildOffset()};
|
||||
@ -661,22 +690,14 @@ ArchiveFile::getMember(const Archive::Symbol *Sym) {
|
||||
template <class ELFT>
|
||||
SharedFile<ELFT>::SharedFile(MemoryBufferRef M, StringRef DefaultSoName)
|
||||
: ELFFileBase<ELFT>(Base::SharedKind, M), SoName(DefaultSoName),
|
||||
AsNeeded(Config->AsNeeded) {}
|
||||
|
||||
template <class ELFT>
|
||||
const typename ELFT::Shdr *
|
||||
SharedFile<ELFT>::getSection(const Elf_Sym &Sym) const {
|
||||
return check(
|
||||
this->getObj().getSection(&Sym, this->Symbols, this->SymtabSHNDX),
|
||||
toString(this));
|
||||
}
|
||||
IsNeeded(!Config->AsNeeded) {}
|
||||
|
||||
// Partially parse the shared object file so that we can call
|
||||
// getSoName on this object.
|
||||
template <class ELFT> void SharedFile<ELFT>::parseSoName() {
|
||||
const Elf_Shdr *DynamicSec = nullptr;
|
||||
const ELFFile<ELFT> Obj = this->getObj();
|
||||
ArrayRef<Elf_Shdr> Sections = check(Obj.sections(), toString(this));
|
||||
ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this);
|
||||
|
||||
// Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d.
|
||||
for (const Elf_Shdr &Sec : Sections) {
|
||||
@ -690,8 +711,7 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
|
||||
DynamicSec = &Sec;
|
||||
break;
|
||||
case SHT_SYMTAB_SHNDX:
|
||||
this->SymtabSHNDX =
|
||||
check(Obj.getSHNDXTable(Sec, Sections), toString(this));
|
||||
this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, Sections), this);
|
||||
break;
|
||||
case SHT_GNU_versym:
|
||||
this->VersymSec = &Sec;
|
||||
@ -702,15 +722,14 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
|
||||
}
|
||||
}
|
||||
|
||||
if (this->VersymSec && this->Symbols.empty())
|
||||
if (this->VersymSec && this->ELFSyms.empty())
|
||||
error("SHT_GNU_versym should be associated with symbol table");
|
||||
|
||||
// Search for a DT_SONAME tag to initialize this->SoName.
|
||||
if (!DynamicSec)
|
||||
return;
|
||||
ArrayRef<Elf_Dyn> Arr =
|
||||
check(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec),
|
||||
toString(this));
|
||||
CHECK(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec), this);
|
||||
for (const Elf_Dyn &Dyn : Arr) {
|
||||
if (Dyn.d_tag == DT_SONAME) {
|
||||
uint64_t Val = Dyn.getVal();
|
||||
@ -767,11 +786,14 @@ SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
|
||||
template <class ELFT> void SharedFile<ELFT>::parseRest() {
|
||||
// Create mapping from version identifiers to Elf_Verdef entries.
|
||||
const Elf_Versym *Versym = nullptr;
|
||||
std::vector<const Elf_Verdef *> Verdefs = parseVerdefs(Versym);
|
||||
Verdefs = parseVerdefs(Versym);
|
||||
|
||||
Elf_Sym_Range Syms = this->getGlobalSymbols();
|
||||
ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj().sections(), this);
|
||||
|
||||
// Add symbols to the symbol table.
|
||||
Elf_Sym_Range Syms = this->getGlobalELFSyms();
|
||||
for (const Elf_Sym &Sym : Syms) {
|
||||
unsigned VersymIndex = 0;
|
||||
unsigned VersymIndex = VER_NDX_GLOBAL;
|
||||
if (Versym) {
|
||||
VersymIndex = Versym->vs_index;
|
||||
++Versym;
|
||||
@ -779,28 +801,54 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
|
||||
bool Hidden = VersymIndex & VERSYM_HIDDEN;
|
||||
VersymIndex = VersymIndex & ~VERSYM_HIDDEN;
|
||||
|
||||
StringRef Name = check(Sym.getName(this->StringTable), toString(this));
|
||||
StringRef Name = CHECK(Sym.getName(this->StringTable), this);
|
||||
if (Sym.isUndefined()) {
|
||||
Undefs.push_back(Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore local symbols.
|
||||
if (Versym && VersymIndex == VER_NDX_LOCAL)
|
||||
if (Sym.getBinding() == STB_LOCAL) {
|
||||
warn("found local symbol '" + Name +
|
||||
"' in global part of symbol table in file " + toString(this));
|
||||
continue;
|
||||
}
|
||||
|
||||
const Elf_Verdef *V =
|
||||
VersymIndex == VER_NDX_GLOBAL ? nullptr : Verdefs[VersymIndex];
|
||||
const Elf_Verdef *Ver = nullptr;
|
||||
if (VersymIndex != VER_NDX_GLOBAL) {
|
||||
if (VersymIndex >= Verdefs.size() || VersymIndex == VER_NDX_LOCAL) {
|
||||
error("corrupt input file: version definition index " +
|
||||
Twine(VersymIndex) + " for symbol " + Name +
|
||||
" is out of bounds\n>>> defined in " + toString(this));
|
||||
continue;
|
||||
}
|
||||
Ver = Verdefs[VersymIndex];
|
||||
} else {
|
||||
VersymIndex = 0;
|
||||
}
|
||||
|
||||
// We do not usually care about alignments of data in shared object
|
||||
// files because the loader takes care of it. However, if we promote a
|
||||
// DSO symbol to point to .bss due to copy relocation, we need to keep
|
||||
// the original alignment requirements. We infer it here.
|
||||
uint64_t Alignment = 1;
|
||||
if (Sym.st_value)
|
||||
Alignment = 1ULL << countTrailingZeros((uint64_t)Sym.st_value);
|
||||
if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) {
|
||||
uint64_t SecAlign = Sections[Sym.st_shndx].sh_addralign;
|
||||
Alignment = std::min(Alignment, SecAlign);
|
||||
}
|
||||
if (Alignment > UINT32_MAX)
|
||||
error(toString(this) + ": alignment too large: " + Name);
|
||||
|
||||
if (!Hidden)
|
||||
elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V);
|
||||
Symtab->addShared(Name, this, Sym, Alignment, VersymIndex);
|
||||
|
||||
// Also add the symbol with the versioned name to handle undefined symbols
|
||||
// with explicit versions.
|
||||
if (V) {
|
||||
StringRef VerName = this->StringTable.data() + V->getAux()->vda_name;
|
||||
if (Ver) {
|
||||
StringRef VerName = this->StringTable.data() + Ver->getAux()->vda_name;
|
||||
Name = Saver.save(Name + "@" + VerName);
|
||||
elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V);
|
||||
Symtab->addShared(Name, this, Sym, Alignment, VersymIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -855,7 +903,7 @@ BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName,
|
||||
MemoryBufferRef MBRef(MB.getBuffer(),
|
||||
Saver.save(ArchiveName + MB.getBufferIdentifier() +
|
||||
utostr(OffsetInArchive)));
|
||||
Obj = check(lto::InputFile::create(MBRef), toString(this));
|
||||
Obj = CHECK(lto::InputFile::create(MBRef), this);
|
||||
|
||||
Triple T(Obj->getTargetTriple());
|
||||
EKind = getBitcodeELFKind(T);
|
||||
@ -887,22 +935,20 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
|
||||
|
||||
int C = ObjSym.getComdatIndex();
|
||||
if (C != -1 && !KeptComdats[C])
|
||||
return Symtab<ELFT>::X->addUndefined(NameRef, /*IsLocal=*/false, Binding,
|
||||
Visibility, Type, CanOmitFromDynSym,
|
||||
F);
|
||||
return Symtab->addUndefined<ELFT>(NameRef, Binding, Visibility, Type,
|
||||
CanOmitFromDynSym, F);
|
||||
|
||||
if (ObjSym.isUndefined())
|
||||
return Symtab<ELFT>::X->addUndefined(NameRef, /*IsLocal=*/false, Binding,
|
||||
Visibility, Type, CanOmitFromDynSym,
|
||||
F);
|
||||
return Symtab->addUndefined<ELFT>(NameRef, Binding, Visibility, Type,
|
||||
CanOmitFromDynSym, F);
|
||||
|
||||
if (ObjSym.isCommon())
|
||||
return Symtab<ELFT>::X->addCommon(NameRef, ObjSym.getCommonSize(),
|
||||
ObjSym.getCommonAlignment(), Binding,
|
||||
Visibility, STT_OBJECT, F);
|
||||
return Symtab->addCommon(NameRef, ObjSym.getCommonSize(),
|
||||
ObjSym.getCommonAlignment(), Binding, Visibility,
|
||||
STT_OBJECT, F);
|
||||
|
||||
return Symtab<ELFT>::X->addBitcode(NameRef, Binding, Visibility, Type,
|
||||
CanOmitFromDynSym, F);
|
||||
return Symtab->addBitcode(NameRef, Binding, Visibility, Type,
|
||||
CanOmitFromDynSym, F);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
@ -947,18 +993,15 @@ template <class ELFT> void BinaryFile::parse() {
|
||||
// characters in a filename are replaced with underscore.
|
||||
std::string S = "_binary_" + MB.getBufferIdentifier().str();
|
||||
for (size_t I = 0; I < S.size(); ++I)
|
||||
if (!isalnum(S[I]))
|
||||
if (!isAlnum(S[I]))
|
||||
S[I] = '_';
|
||||
|
||||
elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_start"), STV_DEFAULT,
|
||||
STT_OBJECT, 0, 0, STB_GLOBAL, Section,
|
||||
nullptr);
|
||||
elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_end"), STV_DEFAULT,
|
||||
STT_OBJECT, Data.size(), 0, STB_GLOBAL,
|
||||
Section, nullptr);
|
||||
elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_size"), STV_DEFAULT,
|
||||
STT_OBJECT, Data.size(), 0, STB_GLOBAL,
|
||||
nullptr, nullptr);
|
||||
Symtab->addRegular<ELFT>(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT,
|
||||
0, 0, STB_GLOBAL, Section, nullptr);
|
||||
Symtab->addRegular<ELFT>(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT,
|
||||
Data.size(), 0, STB_GLOBAL, Section, nullptr);
|
||||
Symtab->addRegular<ELFT>(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT,
|
||||
Data.size(), 0, STB_GLOBAL, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static bool isBitcode(MemoryBufferRef MB) {
|
||||
@ -973,13 +1016,13 @@ InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName,
|
||||
|
||||
switch (getELFKind(MB)) {
|
||||
case ELF32LEKind:
|
||||
return make<ObjectFile<ELF32LE>>(MB, ArchiveName);
|
||||
return make<ObjFile<ELF32LE>>(MB, ArchiveName);
|
||||
case ELF32BEKind:
|
||||
return make<ObjectFile<ELF32BE>>(MB, ArchiveName);
|
||||
return make<ObjFile<ELF32BE>>(MB, ArchiveName);
|
||||
case ELF64LEKind:
|
||||
return make<ObjectFile<ELF64LE>>(MB, ArchiveName);
|
||||
return make<ObjFile<ELF64LE>>(MB, ArchiveName);
|
||||
case ELF64BEKind:
|
||||
return make<ObjectFile<ELF64BE>>(MB, ArchiveName);
|
||||
return make<ObjFile<ELF64BE>>(MB, ArchiveName);
|
||||
default:
|
||||
llvm_unreachable("getELFKind");
|
||||
}
|
||||
@ -1000,53 +1043,53 @@ InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) {
|
||||
}
|
||||
}
|
||||
|
||||
MemoryBufferRef LazyObjectFile::getBuffer() {
|
||||
MemoryBufferRef LazyObjFile::getBuffer() {
|
||||
if (Seen)
|
||||
return MemoryBufferRef();
|
||||
Seen = true;
|
||||
return MB;
|
||||
}
|
||||
|
||||
InputFile *LazyObjectFile::fetch() {
|
||||
InputFile *LazyObjFile::fetch() {
|
||||
MemoryBufferRef MBRef = getBuffer();
|
||||
if (MBRef.getBuffer().empty())
|
||||
return nullptr;
|
||||
return createObjectFile(MBRef, ArchiveName, OffsetInArchive);
|
||||
}
|
||||
|
||||
template <class ELFT> void LazyObjectFile::parse() {
|
||||
for (StringRef Sym : getSymbols())
|
||||
Symtab<ELFT>::X->addLazyObject(Sym, *this);
|
||||
template <class ELFT> void LazyObjFile::parse() {
|
||||
for (StringRef Sym : getSymbolNames())
|
||||
Symtab->addLazyObject<ELFT>(Sym, *this);
|
||||
}
|
||||
|
||||
template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() {
|
||||
template <class ELFT> std::vector<StringRef> LazyObjFile::getElfSymbols() {
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::SymRange Elf_Sym_Range;
|
||||
|
||||
const ELFFile<ELFT> Obj(this->MB.getBuffer());
|
||||
ArrayRef<Elf_Shdr> Sections = check(Obj.sections(), toString(this));
|
||||
ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(this->MB.getBuffer()));
|
||||
ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this);
|
||||
for (const Elf_Shdr &Sec : Sections) {
|
||||
if (Sec.sh_type != SHT_SYMTAB)
|
||||
continue;
|
||||
|
||||
Elf_Sym_Range Syms = check(Obj.symbols(&Sec), toString(this));
|
||||
Elf_Sym_Range Syms = CHECK(Obj.symbols(&Sec), this);
|
||||
uint32_t FirstNonLocal = Sec.sh_info;
|
||||
StringRef StringTable =
|
||||
check(Obj.getStringTableForSymtab(Sec, Sections), toString(this));
|
||||
CHECK(Obj.getStringTableForSymtab(Sec, Sections), this);
|
||||
std::vector<StringRef> V;
|
||||
|
||||
for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal))
|
||||
if (Sym.st_shndx != SHN_UNDEF)
|
||||
V.push_back(check(Sym.getName(StringTable), toString(this)));
|
||||
V.push_back(CHECK(Sym.getName(StringTable), this));
|
||||
return V;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() {
|
||||
std::vector<StringRef> LazyObjFile::getBitcodeSymbols() {
|
||||
std::unique_ptr<lto::InputFile> Obj =
|
||||
check(lto::InputFile::create(this->MB), toString(this));
|
||||
CHECK(lto::InputFile::create(this->MB), this);
|
||||
std::vector<StringRef> V;
|
||||
for (const lto::InputFile::Symbol &Sym : Obj->symbols())
|
||||
if (!Sym.isUndefined())
|
||||
@ -1055,7 +1098,7 @@ std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() {
|
||||
}
|
||||
|
||||
// Returns a vector of globally-visible defined symbol names.
|
||||
std::vector<StringRef> LazyObjectFile::getSymbols() {
|
||||
std::vector<StringRef> LazyObjFile::getSymbolNames() {
|
||||
if (isBitcode(this->MB))
|
||||
return getBitcodeSymbols();
|
||||
|
||||
@ -1083,20 +1126,20 @@ template void BitcodeFile::parse<ELF32BE>(DenseSet<CachedHashStringRef> &);
|
||||
template void BitcodeFile::parse<ELF64LE>(DenseSet<CachedHashStringRef> &);
|
||||
template void BitcodeFile::parse<ELF64BE>(DenseSet<CachedHashStringRef> &);
|
||||
|
||||
template void LazyObjectFile::parse<ELF32LE>();
|
||||
template void LazyObjectFile::parse<ELF32BE>();
|
||||
template void LazyObjectFile::parse<ELF64LE>();
|
||||
template void LazyObjectFile::parse<ELF64BE>();
|
||||
template void LazyObjFile::parse<ELF32LE>();
|
||||
template void LazyObjFile::parse<ELF32BE>();
|
||||
template void LazyObjFile::parse<ELF64LE>();
|
||||
template void LazyObjFile::parse<ELF64BE>();
|
||||
|
||||
template class elf::ELFFileBase<ELF32LE>;
|
||||
template class elf::ELFFileBase<ELF32BE>;
|
||||
template class elf::ELFFileBase<ELF64LE>;
|
||||
template class elf::ELFFileBase<ELF64BE>;
|
||||
|
||||
template class elf::ObjectFile<ELF32LE>;
|
||||
template class elf::ObjectFile<ELF32BE>;
|
||||
template class elf::ObjectFile<ELF64LE>;
|
||||
template class elf::ObjectFile<ELF64BE>;
|
||||
template class elf::ObjFile<ELF32LE>;
|
||||
template class elf::ObjFile<ELF32BE>;
|
||||
template class elf::ObjFile<ELF64LE>;
|
||||
template class elf::ObjFile<ELF64BE>;
|
||||
|
||||
template class elf::SharedFile<ELF32LE>;
|
||||
template class elf::SharedFile<ELF32BE>;
|
||||
|
105
ELF/InputFiles.h
105
ELF/InputFiles.h
@ -11,12 +11,10 @@
|
||||
#define LLD_ELF_INPUT_FILES_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputSection.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/Reproduce.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Common/Reproduce.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
@ -40,9 +38,10 @@ class InputFile;
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
class InputSectionBase;
|
||||
}
|
||||
|
||||
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
|
||||
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
|
||||
std::string toString(const elf::InputFile *F);
|
||||
|
||||
namespace elf {
|
||||
@ -50,7 +49,7 @@ namespace elf {
|
||||
using llvm::object::Archive;
|
||||
|
||||
class Lazy;
|
||||
class SymbolBody;
|
||||
class Symbol;
|
||||
|
||||
// If -reproduce option is given, all input files are written
|
||||
// to this tar archive.
|
||||
@ -63,9 +62,9 @@ llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
|
||||
class InputFile {
|
||||
public:
|
||||
enum Kind {
|
||||
ObjectKind,
|
||||
ObjKind,
|
||||
SharedKind,
|
||||
LazyObjectKind,
|
||||
LazyObjKind,
|
||||
ArchiveKind,
|
||||
BitcodeKind,
|
||||
BinaryKind,
|
||||
@ -79,10 +78,18 @@ public:
|
||||
// Returns sections. It is a runtime error to call this function
|
||||
// on files that don't have the notion of sections.
|
||||
ArrayRef<InputSectionBase *> getSections() const {
|
||||
assert(FileKind == ObjectKind || FileKind == BinaryKind);
|
||||
assert(FileKind == ObjKind || FileKind == BinaryKind);
|
||||
return Sections;
|
||||
}
|
||||
|
||||
// Returns object file symbols. It is a runtime error to call this
|
||||
// function on files of other types.
|
||||
ArrayRef<Symbol *> getSymbols() {
|
||||
assert(FileKind == ObjKind || FileKind == BitcodeKind ||
|
||||
FileKind == ArchiveKind);
|
||||
return Symbols;
|
||||
}
|
||||
|
||||
// Filename of .a which contained this file. If this file was
|
||||
// not in an archive file, it is the empty string. We use this
|
||||
// string for creating error messages.
|
||||
@ -100,6 +107,7 @@ public:
|
||||
protected:
|
||||
InputFile(Kind K, MemoryBufferRef M);
|
||||
std::vector<InputSectionBase *> Sections;
|
||||
std::vector<Symbol *> Symbols;
|
||||
|
||||
private:
|
||||
const Kind FileKind;
|
||||
@ -115,21 +123,22 @@ public:
|
||||
ELFFileBase(Kind K, MemoryBufferRef M);
|
||||
static bool classof(const InputFile *F) {
|
||||
Kind K = F->kind();
|
||||
return K == ObjectKind || K == SharedKind;
|
||||
return K == ObjKind || K == SharedKind;
|
||||
}
|
||||
|
||||
llvm::object::ELFFile<ELFT> getObj() const {
|
||||
return llvm::object::ELFFile<ELFT>(MB.getBuffer());
|
||||
return check(llvm::object::ELFFile<ELFT>::create(MB.getBuffer()));
|
||||
}
|
||||
|
||||
StringRef getStringTable() const { return StringTable; }
|
||||
|
||||
uint32_t getSectionIndex(const Elf_Sym &Sym) const;
|
||||
|
||||
Elf_Sym_Range getGlobalSymbols();
|
||||
Elf_Sym_Range getGlobalELFSyms();
|
||||
Elf_Sym_Range getELFSyms() const { return ELFSyms; }
|
||||
|
||||
protected:
|
||||
ArrayRef<Elf_Sym> Symbols;
|
||||
ArrayRef<Elf_Sym> ELFSyms;
|
||||
uint32_t FirstNonLocal = 0;
|
||||
ArrayRef<Elf_Word> SymtabSHNDX;
|
||||
StringRef StringTable;
|
||||
@ -137,7 +146,7 @@ protected:
|
||||
};
|
||||
|
||||
// .o file.
|
||||
template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
|
||||
template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
|
||||
typedef ELFFileBase<ELFT> Base;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
@ -150,34 +159,29 @@ template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
|
||||
ArrayRef<Elf_Word> getShtGroupEntries(const Elf_Shdr &Sec);
|
||||
|
||||
public:
|
||||
static bool classof(const InputFile *F) {
|
||||
return F->kind() == Base::ObjectKind;
|
||||
}
|
||||
static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; }
|
||||
|
||||
ArrayRef<SymbolBody *> getSymbols();
|
||||
ArrayRef<SymbolBody *> getLocalSymbols();
|
||||
ArrayRef<Symbol *> getLocalSymbols();
|
||||
|
||||
ObjectFile(MemoryBufferRef M, StringRef ArchiveName);
|
||||
ObjFile(MemoryBufferRef M, StringRef ArchiveName);
|
||||
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
|
||||
InputSectionBase *getSection(const Elf_Sym &Sym) const;
|
||||
|
||||
SymbolBody &getSymbolBody(uint32_t SymbolIndex) const {
|
||||
if (SymbolIndex >= SymbolBodies.size())
|
||||
Symbol &getSymbol(uint32_t SymbolIndex) const {
|
||||
if (SymbolIndex >= this->Symbols.size())
|
||||
fatal(toString(this) + ": invalid symbol index");
|
||||
return *SymbolBodies[SymbolIndex];
|
||||
return *this->Symbols[SymbolIndex];
|
||||
}
|
||||
|
||||
template <typename RelT>
|
||||
SymbolBody &getRelocTargetSym(const RelT &Rel) const {
|
||||
template <typename RelT> Symbol &getRelocTargetSym(const RelT &Rel) const {
|
||||
uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
|
||||
return getSymbolBody(SymIndex);
|
||||
return getSymbol(SymIndex);
|
||||
}
|
||||
|
||||
// Returns source line information for a given offset.
|
||||
// If no information is available, returns "".
|
||||
std::string getLineInfo(InputSectionBase *S, uint64_t Offset);
|
||||
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
|
||||
llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef Name);
|
||||
|
||||
// MIPS GP0 value defined by this file. This value represents the gp value
|
||||
// used to create the relocatable object and required to support
|
||||
@ -193,16 +197,13 @@ private:
|
||||
void
|
||||
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
void initializeSymbols();
|
||||
void initializeDwarfLine();
|
||||
void initializeDwarf();
|
||||
InputSectionBase *getRelocTarget(const Elf_Shdr &Sec);
|
||||
InputSectionBase *createInputSection(const Elf_Shdr &Sec);
|
||||
StringRef getSectionName(const Elf_Shdr &Sec);
|
||||
|
||||
bool shouldMerge(const Elf_Shdr &Sec);
|
||||
SymbolBody *createSymbolBody(const Elf_Sym *Sym);
|
||||
|
||||
// List of all symbols referenced or defined by this file.
|
||||
std::vector<SymbolBody *> SymbolBodies;
|
||||
Symbol *createSymbol(const Elf_Sym *Sym);
|
||||
|
||||
// .shstrtab contents.
|
||||
StringRef SectionStringTable;
|
||||
@ -212,34 +213,33 @@ private:
|
||||
// single object file, so we cache debugging information in order to
|
||||
// parse it only once for each object file we link.
|
||||
std::unique_ptr<llvm::DWARFDebugLine> DwarfLine;
|
||||
llvm::DenseMap<StringRef, std::pair<unsigned, unsigned>> VariableLoc;
|
||||
llvm::once_flag InitDwarfLine;
|
||||
};
|
||||
|
||||
// LazyObjectFile is analogous to ArchiveFile in the sense that
|
||||
// LazyObjFile is analogous to ArchiveFile in the sense that
|
||||
// the file contains lazy symbols. The difference is that
|
||||
// LazyObjectFile wraps a single file instead of multiple files.
|
||||
// LazyObjFile wraps a single file instead of multiple files.
|
||||
//
|
||||
// This class is used for --start-lib and --end-lib options which
|
||||
// instruct the linker to link object files between them with the
|
||||
// archive file semantics.
|
||||
class LazyObjectFile : public InputFile {
|
||||
class LazyObjFile : public InputFile {
|
||||
public:
|
||||
LazyObjectFile(MemoryBufferRef M, StringRef ArchiveName,
|
||||
uint64_t OffsetInArchive)
|
||||
: InputFile(LazyObjectKind, M), OffsetInArchive(OffsetInArchive) {
|
||||
LazyObjFile(MemoryBufferRef M, StringRef ArchiveName,
|
||||
uint64_t OffsetInArchive)
|
||||
: InputFile(LazyObjKind, M), OffsetInArchive(OffsetInArchive) {
|
||||
this->ArchiveName = ArchiveName;
|
||||
}
|
||||
|
||||
static bool classof(const InputFile *F) {
|
||||
return F->kind() == LazyObjectKind;
|
||||
}
|
||||
static bool classof(const InputFile *F) { return F->kind() == LazyObjKind; }
|
||||
|
||||
template <class ELFT> void parse();
|
||||
MemoryBufferRef getBuffer();
|
||||
InputFile *fetch();
|
||||
|
||||
private:
|
||||
std::vector<StringRef> getSymbols();
|
||||
std::vector<StringRef> getSymbolNames();
|
||||
template <class ELFT> std::vector<StringRef> getElfSymbols();
|
||||
std::vector<StringRef> getBitcodeSymbols();
|
||||
|
||||
@ -253,7 +253,6 @@ public:
|
||||
explicit ArchiveFile(std::unique_ptr<Archive> &&File);
|
||||
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
|
||||
template <class ELFT> void parse();
|
||||
ArrayRef<Symbol *> getSymbols() { return Symbols; }
|
||||
|
||||
// Returns a memory buffer for a given symbol and the offset in the archive
|
||||
// for the member. An empty memory buffer and an offset of zero
|
||||
@ -264,7 +263,6 @@ public:
|
||||
private:
|
||||
std::unique_ptr<Archive> File;
|
||||
llvm::DenseSet<uint64_t> Seen;
|
||||
std::vector<Symbol *> Symbols;
|
||||
};
|
||||
|
||||
class BitcodeFile : public InputFile {
|
||||
@ -274,11 +272,7 @@ public:
|
||||
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
|
||||
template <class ELFT>
|
||||
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
ArrayRef<Symbol *> getSymbols() { return Symbols; }
|
||||
std::unique_ptr<llvm::lto::InputFile> Obj;
|
||||
|
||||
private:
|
||||
std::vector<Symbol *> Symbols;
|
||||
};
|
||||
|
||||
// .so file.
|
||||
@ -296,9 +290,9 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
|
||||
const Elf_Shdr *VerdefSec = nullptr;
|
||||
|
||||
public:
|
||||
std::vector<const Elf_Verdef *> Verdefs;
|
||||
std::string SoName;
|
||||
|
||||
const Elf_Shdr *getSection(const Elf_Sym &Sym) const;
|
||||
llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }
|
||||
|
||||
static bool classof(const InputFile *F) {
|
||||
@ -324,9 +318,7 @@ public:
|
||||
std::map<const Elf_Verdef *, NeededVer> VerdefMap;
|
||||
|
||||
// Used for --as-needed
|
||||
bool AsNeeded = false;
|
||||
bool IsUsed = false;
|
||||
bool isNeeded() const { return !AsNeeded || IsUsed; }
|
||||
bool IsNeeded;
|
||||
};
|
||||
|
||||
class BinaryFile : public InputFile {
|
||||
@ -340,6 +332,11 @@ InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "",
|
||||
uint64_t OffsetInArchive = 0);
|
||||
InputFile *createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName);
|
||||
|
||||
extern std::vector<BinaryFile *> BinaryFiles;
|
||||
extern std::vector<BitcodeFile *> BitcodeFiles;
|
||||
extern std::vector<InputFile *> ObjectFiles;
|
||||
extern std::vector<InputFile *> SharedFiles;
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
@ -10,21 +10,23 @@
|
||||
#include "InputSection.h"
|
||||
#include "Config.h"
|
||||
#include "EhFrame.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Relocations.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#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"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Threading.h"
|
||||
#include "llvm/Support/xxhash.h"
|
||||
#include <mutex>
|
||||
|
||||
using namespace llvm;
|
||||
@ -44,8 +46,34 @@ std::string lld::toString(const InputSectionBase *Sec) {
|
||||
return (toString(Sec->File) + ":(" + Sec->Name + ")").str();
|
||||
}
|
||||
|
||||
DenseMap<SectionBase *, int> elf::buildSectionOrder() {
|
||||
DenseMap<SectionBase *, int> SectionOrder;
|
||||
if (Config->SymbolOrderingFile.empty())
|
||||
return SectionOrder;
|
||||
|
||||
// Build a map from symbols to their priorities. Symbols that didn't
|
||||
// appear in the symbol ordering file have the lowest priority 0.
|
||||
// All explicitly mentioned symbols have negative (higher) priorities.
|
||||
DenseMap<StringRef, int> SymbolOrder;
|
||||
int Priority = -Config->SymbolOrderingFile.size();
|
||||
for (StringRef S : Config->SymbolOrderingFile)
|
||||
SymbolOrder.insert({S, Priority++});
|
||||
|
||||
// Build a map from sections to their priorities.
|
||||
for (InputFile *File : ObjectFiles) {
|
||||
for (Symbol *Sym : File->getSymbols()) {
|
||||
auto *D = dyn_cast<Defined>(Sym);
|
||||
if (!D || !D->Section)
|
||||
continue;
|
||||
int &Priority = SectionOrder[D->Section];
|
||||
Priority = std::min(Priority, SymbolOrder.lookup(D->getName()));
|
||||
}
|
||||
}
|
||||
return SectionOrder;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static ArrayRef<uint8_t> getSectionContents(elf::ObjectFile<ELFT> *File,
|
||||
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> *File,
|
||||
const typename ELFT::Shdr *Hdr) {
|
||||
if (!File || Hdr->sh_type == SHT_NOBITS)
|
||||
return makeArrayRef<uint8_t>(nullptr, Hdr->sh_size);
|
||||
@ -59,9 +87,7 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
|
||||
StringRef Name, Kind SectionKind)
|
||||
: SectionBase(SectionKind, Name, Flags, Entsize, Alignment, Type, Info,
|
||||
Link),
|
||||
File(File), Data(Data), Repl(this) {
|
||||
Live = !Config->GcSections || !(Flags & SHF_ALLOC);
|
||||
Assigned = false;
|
||||
File(File), Data(Data) {
|
||||
NumRelocations = 0;
|
||||
AreRelocsRela = false;
|
||||
|
||||
@ -102,7 +128,7 @@ static uint64_t getType(uint64_t Type, StringRef Name) {
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase::InputSectionBase(elf::ObjectFile<ELFT> *File,
|
||||
InputSectionBase::InputSectionBase(ObjFile<ELFT> *File,
|
||||
const typename ELFT::Shdr *Hdr,
|
||||
StringRef Name, Kind SectionKind)
|
||||
: InputSectionBase(File, getFlags(Hdr->sh_flags),
|
||||
@ -160,7 +186,7 @@ uint64_t SectionBase::getOffset(uint64_t Offset) const {
|
||||
OutputSection *SectionBase::getOutputSection() {
|
||||
InputSection *Sec;
|
||||
if (auto *IS = dyn_cast<InputSection>(this))
|
||||
Sec = IS;
|
||||
return IS->getParent();
|
||||
else if (auto *MS = dyn_cast<MergeInputSection>(this))
|
||||
Sec = MS->getParent();
|
||||
else if (auto *EH = dyn_cast<EhInputSection>(this))
|
||||
@ -170,29 +196,23 @@ OutputSection *SectionBase::getOutputSection() {
|
||||
return Sec ? Sec->getParent() : nullptr;
|
||||
}
|
||||
|
||||
// Uncompress section contents. Note that this function is called
|
||||
// from parallel_for_each, so it must be thread-safe.
|
||||
void InputSectionBase::uncompress() {
|
||||
// Uncompress section contents if required. Note that this function
|
||||
// is called from parallelForEach, so it must be thread-safe.
|
||||
void InputSectionBase::maybeUncompress() {
|
||||
if (UncompressBuf || !Decompressor::isCompressedELFSection(Flags, Name))
|
||||
return;
|
||||
|
||||
Decompressor Dec = check(Decompressor::create(Name, toStringRef(Data),
|
||||
Config->IsLE, Config->Is64));
|
||||
|
||||
size_t Size = Dec.getDecompressedSize();
|
||||
char *OutputBuf;
|
||||
{
|
||||
static std::mutex Mu;
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
OutputBuf = BAlloc.Allocate<char>(Size);
|
||||
}
|
||||
|
||||
if (Error E = Dec.decompress({OutputBuf, Size}))
|
||||
UncompressBuf.reset(new char[Size]());
|
||||
if (Error E = Dec.decompress({UncompressBuf.get(), Size}))
|
||||
fatal(toString(this) +
|
||||
": decompress failed: " + llvm::toString(std::move(E)));
|
||||
this->Data = ArrayRef<uint8_t>((uint8_t *)OutputBuf, Size);
|
||||
this->Flags &= ~(uint64_t)SHF_COMPRESSED;
|
||||
}
|
||||
|
||||
uint64_t SectionBase::getOffset(const DefinedRegular &Sym) const {
|
||||
return getOffset(Sym.Value);
|
||||
this->Data = makeArrayRef((uint8_t *)UncompressBuf.get(), Size);
|
||||
this->Flags &= ~(uint64_t)SHF_COMPRESSED;
|
||||
}
|
||||
|
||||
InputSection *InputSectionBase::getLinkOrderDep() const {
|
||||
@ -200,9 +220,9 @@ InputSection *InputSectionBase::getLinkOrderDep() const {
|
||||
InputSectionBase *L = File->getSections()[Link];
|
||||
if (auto *IS = dyn_cast<InputSection>(L))
|
||||
return IS;
|
||||
error(
|
||||
"Merge and .eh_frame sections are not supported with SHF_LINK_ORDER " +
|
||||
toString(L));
|
||||
error("a section with SHF_LINK_ORDER should not refer a non-regular "
|
||||
"section: " +
|
||||
toString(L));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -227,8 +247,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
|
||||
SrcFile = toString(File);
|
||||
|
||||
// Find a function symbol that encloses a given location.
|
||||
for (SymbolBody *B : getFile<ELFT>()->getSymbols())
|
||||
if (auto *D = dyn_cast<DefinedRegular>(B))
|
||||
for (Symbol *B : File->getSymbols())
|
||||
if (auto *D = dyn_cast<Defined>(B))
|
||||
if (D->Section == this && D->Type == STT_FUNC)
|
||||
if (D->Value <= Offset && Offset < D->Value + D->Size)
|
||||
return SrcFile + ":(function " + toString(*D) + ")";
|
||||
@ -237,31 +257,40 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
|
||||
return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str();
|
||||
}
|
||||
|
||||
// Returns a source location string. This function is intended to be
|
||||
// used for constructing an error message. The returned message looks
|
||||
// like this:
|
||||
// Concatenates arguments to construct a string representing an error location.
|
||||
static std::string createFileLineMsg(StringRef Path, unsigned Line) {
|
||||
std::string Filename = path::filename(Path);
|
||||
std::string Lineno = ":" + std::to_string(Line);
|
||||
if (Filename == Path)
|
||||
return Filename + Lineno;
|
||||
return Filename + Lineno + " (" + Path.str() + Lineno + ")";
|
||||
}
|
||||
|
||||
// This function is intended to be used for constructing an error message.
|
||||
// The returned message looks like this:
|
||||
//
|
||||
// foo.c:42 (/home/alice/possibly/very/long/path/foo.c:42)
|
||||
//
|
||||
// Returns an empty string if there's no way to get line info.
|
||||
template <class ELFT> std::string InputSectionBase::getSrcMsg(uint64_t Offset) {
|
||||
// Returns an empty string if there's no way to get line info.
|
||||
template <class ELFT>
|
||||
std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
|
||||
// Synthetic sections don't have input files.
|
||||
elf::ObjectFile<ELFT> *File = getFile<ELFT>();
|
||||
ObjFile<ELFT> *File = getFile<ELFT>();
|
||||
if (!File)
|
||||
return "";
|
||||
|
||||
Optional<DILineInfo> Info = File->getDILineInfo(this, Offset);
|
||||
// In DWARF, functions and variables are stored to different places.
|
||||
// First, lookup a function for a given offset.
|
||||
if (Optional<DILineInfo> Info = File->getDILineInfo(this, Offset))
|
||||
return createFileLineMsg(Info->FileName, Info->Line);
|
||||
|
||||
// If it failed, lookup again as a variable.
|
||||
if (Optional<std::pair<std::string, unsigned>> FileLine =
|
||||
File->getVariableLoc(Sym.getName()))
|
||||
return createFileLineMsg(FileLine->first, FileLine->second);
|
||||
|
||||
// File->SourceFile contains STT_FILE symbol, and that is a last resort.
|
||||
if (!Info)
|
||||
return File->SourceFile;
|
||||
|
||||
std::string Path = Info->FileName;
|
||||
std::string Filename = path::filename(Path);
|
||||
std::string Lineno = ":" + std::to_string(Info->Line);
|
||||
if (Filename == Path)
|
||||
return Filename + Lineno;
|
||||
return Filename + Lineno + " (" + Path + Lineno + ")";
|
||||
return File->SourceFile;
|
||||
}
|
||||
|
||||
// Returns a filename string along with an optional section name. This
|
||||
@ -273,11 +302,10 @@ template <class ELFT> std::string InputSectionBase::getSrcMsg(uint64_t Offset) {
|
||||
// or
|
||||
//
|
||||
// path/to/foo.o:(function bar) in archive path/to/bar.a
|
||||
template <class ELFT> std::string InputSectionBase::getObjMsg(uint64_t Off) {
|
||||
std::string InputSectionBase::getObjMsg(uint64_t Off) {
|
||||
// Synthetic sections don't have input files.
|
||||
elf::ObjectFile<ELFT> *File = getFile<ELFT>();
|
||||
if (!File)
|
||||
return ("(internal):(" + Name + "+0x" + utohexstr(Off) + ")").str();
|
||||
return ("<internal>:(" + Name + "+0x" + utohexstr(Off) + ")").str();
|
||||
std::string Filename = File->getName();
|
||||
|
||||
std::string Archive;
|
||||
@ -285,8 +313,8 @@ template <class ELFT> std::string InputSectionBase::getObjMsg(uint64_t Off) {
|
||||
Archive = (" in archive " + File->ArchiveName).str();
|
||||
|
||||
// Find a symbol that encloses a given location.
|
||||
for (SymbolBody *B : getFile<ELFT>()->getSymbols())
|
||||
if (auto *D = dyn_cast<DefinedRegular>(B))
|
||||
for (Symbol *B : File->getSymbols())
|
||||
if (auto *D = dyn_cast<Defined>(B))
|
||||
if (D->Section == this && D->Value <= Off && Off < D->Value + D->Size)
|
||||
return Filename + ":(" + toString(*D) + ")" + Archive;
|
||||
|
||||
@ -295,7 +323,7 @@ template <class ELFT> std::string InputSectionBase::getObjMsg(uint64_t Off) {
|
||||
.str();
|
||||
}
|
||||
|
||||
InputSectionBase InputSectionBase::Discarded;
|
||||
InputSection InputSection::Discarded(0, 0, 0, ArrayRef<uint8_t>(), "");
|
||||
|
||||
InputSection::InputSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
|
||||
ArrayRef<uint8_t> Data, StringRef Name, Kind K)
|
||||
@ -304,8 +332,8 @@ InputSection::InputSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
|
||||
Name, K) {}
|
||||
|
||||
template <class ELFT>
|
||||
InputSection::InputSection(elf::ObjectFile<ELFT> *F,
|
||||
const typename ELFT::Shdr *Header, StringRef Name)
|
||||
InputSection::InputSection(ObjFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
StringRef Name)
|
||||
: InputSectionBase(F, Header, Name, InputSectionBase::Regular) {}
|
||||
|
||||
bool InputSection::classof(const SectionBase *S) {
|
||||
@ -313,10 +341,6 @@ bool InputSection::classof(const SectionBase *S) {
|
||||
S->kind() == SectionBase::Synthetic;
|
||||
}
|
||||
|
||||
bool InputSectionBase::classof(const SectionBase *S) {
|
||||
return S->kind() != Output;
|
||||
}
|
||||
|
||||
OutputSection *InputSection::getParent() const {
|
||||
return cast_or_null<OutputSection>(Parent);
|
||||
}
|
||||
@ -349,15 +373,11 @@ InputSectionBase *InputSection::getRelocatedSection() {
|
||||
// for each relocation. So we copy relocations one by one.
|
||||
template <class ELFT, class RelTy>
|
||||
void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
|
||||
InputSectionBase *RelocatedSection = getRelocatedSection();
|
||||
InputSectionBase *Sec = getRelocatedSection();
|
||||
|
||||
// Loop is slow and have complexity O(N*M), where N - amount of
|
||||
// relocations and M - amount of symbols in symbol table.
|
||||
// That happens because getSymbolIndex(...) call below performs
|
||||
// simple linear search.
|
||||
for (const RelTy &Rel : Rels) {
|
||||
uint32_t Type = Rel.getType(Config->IsMips64EL);
|
||||
SymbolBody &Body = this->getFile<ELFT>()->getRelocTargetSym(Rel);
|
||||
RelType Type = Rel.getType(Config->IsMips64EL);
|
||||
Symbol &Sym = this->getFile<ELFT>()->getRelocTargetSym(Rel);
|
||||
|
||||
auto *P = reinterpret_cast<typename ELFT::Rela *>(Buf);
|
||||
Buf += sizeof(RelTy);
|
||||
@ -367,12 +387,11 @@ 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 = RelocatedSection->getOutputSection()->Addr +
|
||||
RelocatedSection->getOffset(Rel.r_offset);
|
||||
P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Body), Type,
|
||||
P->r_offset = Sec->getOutputSection()->Addr + Sec->getOffset(Rel.r_offset);
|
||||
P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type,
|
||||
Config->IsMips64EL);
|
||||
|
||||
if (Body.Type == STT_SECTION) {
|
||||
if (Sym.Type == STT_SECTION) {
|
||||
// We combine multiple section symbols into only one per
|
||||
// section. This means we have to update the addend. That is
|
||||
// trivial for Elf_Rela, but for Elf_Rel we have to write to the
|
||||
@ -382,19 +401,25 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
|
||||
// avoid having to parse and recreate .eh_frame, we just replace any
|
||||
// relocation in it pointing to discarded sections with R_*_NONE, which
|
||||
// hopefully creates a frame that is ignored at runtime.
|
||||
SectionBase *Section = cast<DefinedRegular>(Body).Section;
|
||||
auto *D = dyn_cast<Defined>(&Sym);
|
||||
if (!D) {
|
||||
error("STT_SECTION symbol should be defined");
|
||||
continue;
|
||||
}
|
||||
SectionBase *Section = D->Section;
|
||||
if (Section == &InputSection::Discarded) {
|
||||
P->setSymbolAndType(0, 0, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Config->IsRela) {
|
||||
P->r_addend += Body.getVA() - Section->getOutputSection()->Addr;
|
||||
P->r_addend =
|
||||
Sym.getVA(getAddend<ELFT>(Rel)) - Section->getOutputSection()->Addr;
|
||||
} else if (Config->Relocatable) {
|
||||
const uint8_t *BufLoc = RelocatedSection->Data.begin() + Rel.r_offset;
|
||||
RelocatedSection->Relocations.push_back(
|
||||
{R_ABS, Type, Rel.r_offset, Target->getImplicitAddend(BufLoc, Type),
|
||||
&Body});
|
||||
const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset;
|
||||
Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset,
|
||||
Target->getImplicitAddend(BufLoc, Type),
|
||||
&Sym});
|
||||
}
|
||||
}
|
||||
|
||||
@ -406,7 +431,7 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
|
||||
// this context is the address of the place P. A further special case is that
|
||||
// branch relocations to an undefined weak reference resolve to the next
|
||||
// instruction.
|
||||
static uint32_t getARMUndefinedRelativeWeakVA(uint32_t Type, uint32_t A,
|
||||
static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A,
|
||||
uint32_t P) {
|
||||
switch (Type) {
|
||||
// Unresolved branch relocations to weak references resolve to next
|
||||
@ -453,6 +478,7 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
|
||||
case R_AARCH64_PREL32:
|
||||
case R_AARCH64_PREL64:
|
||||
case R_AARCH64_ADR_PREL_LO21:
|
||||
case R_AARCH64_LD_PREL_LO19:
|
||||
return P + A;
|
||||
}
|
||||
llvm_unreachable("AArch64 pc-relative relocation expected\n");
|
||||
@ -461,53 +487,55 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
|
||||
// ARM SBREL relocations are of the form S + A - B where B is the static base
|
||||
// The ARM ABI defines base to be "addressing origin of the output segment
|
||||
// defining the symbol S". We defined the "addressing origin"/static base to be
|
||||
// the base of the PT_LOAD segment containing the Body.
|
||||
// the base of the PT_LOAD segment containing the Sym.
|
||||
// The procedure call standard only defines a Read Write Position Independent
|
||||
// RWPI variant so in practice we should expect the static base to be the base
|
||||
// of the RW segment.
|
||||
static uint64_t getARMStaticBase(const SymbolBody &Body) {
|
||||
OutputSection *OS = Body.getOutputSection();
|
||||
if (!OS || !OS->FirstInPtLoad)
|
||||
fatal("SBREL relocation to " + Body.getName() + " without static base");
|
||||
return OS->FirstInPtLoad->Addr;
|
||||
static uint64_t getARMStaticBase(const Symbol &Sym) {
|
||||
OutputSection *OS = Sym.getOutputSection();
|
||||
if (!OS || !OS->PtLoad || !OS->PtLoad->FirstSec)
|
||||
fatal("SBREL relocation to " + Sym.getName() + " without static base");
|
||||
return OS->PtLoad->FirstSec->Addr;
|
||||
}
|
||||
|
||||
static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
|
||||
const SymbolBody &Body, RelExpr Expr) {
|
||||
static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
|
||||
const Symbol &Sym, RelExpr Expr) {
|
||||
switch (Expr) {
|
||||
case R_INVALID:
|
||||
return 0;
|
||||
case R_ABS:
|
||||
case R_RELAX_GOT_PC_NOPIC:
|
||||
return Body.getVA(A);
|
||||
return Sym.getVA(A);
|
||||
case R_ARM_SBREL:
|
||||
return Body.getVA(A) - getARMStaticBase(Body);
|
||||
return Sym.getVA(A) - getARMStaticBase(Sym);
|
||||
case R_GOT:
|
||||
case R_RELAX_TLS_GD_TO_IE_ABS:
|
||||
return Body.getGotVA() + A;
|
||||
return Sym.getGotVA() + A;
|
||||
case R_GOTONLY_PC:
|
||||
return InX::Got->getVA() + A - P;
|
||||
case R_GOTONLY_PC_FROM_END:
|
||||
return InX::Got->getVA() + A - P + InX::Got->getSize();
|
||||
case R_GOTREL:
|
||||
return Body.getVA(A) - InX::Got->getVA();
|
||||
return Sym.getVA(A) - InX::Got->getVA();
|
||||
case R_GOTREL_FROM_END:
|
||||
return Body.getVA(A) - InX::Got->getVA() - InX::Got->getSize();
|
||||
return Sym.getVA(A) - InX::Got->getVA() - InX::Got->getSize();
|
||||
case R_GOT_FROM_END:
|
||||
case R_RELAX_TLS_GD_TO_IE_END:
|
||||
return Body.getGotOffset() + A - InX::Got->getSize();
|
||||
return Sym.getGotOffset() + A - InX::Got->getSize();
|
||||
case R_GOT_OFF:
|
||||
return Body.getGotOffset() + A;
|
||||
return Sym.getGotOffset() + A;
|
||||
case R_GOT_PAGE_PC:
|
||||
case R_RELAX_TLS_GD_TO_IE_PAGE_PC:
|
||||
return getAArch64Page(Body.getGotVA() + A) - getAArch64Page(P);
|
||||
return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P);
|
||||
case R_GOT_PC:
|
||||
case R_RELAX_TLS_GD_TO_IE:
|
||||
return Body.getGotVA() + A - P;
|
||||
return Sym.getGotVA() + A - P;
|
||||
case R_HINT:
|
||||
case R_NONE:
|
||||
case R_TLSDESC_CALL:
|
||||
llvm_unreachable("cannot relocate hint relocs");
|
||||
case R_MIPS_GOTREL:
|
||||
return Body.getVA(A) - InX::MipsGot->getGp();
|
||||
return Sym.getVA(A) - InX::MipsGot->getGp();
|
||||
case R_MIPS_GOT_GP:
|
||||
return InX::MipsGot->getGp() + A;
|
||||
case R_MIPS_GOT_GP_PC: {
|
||||
@ -515,42 +543,47 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
|
||||
// is _gp_disp symbol. In that case we should use the following
|
||||
// formula for calculation "AHL + GP - P + 4". For details see p. 4-19 at
|
||||
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
|
||||
// microMIPS variants of these relocations use slightly different
|
||||
// expressions: AHL + GP - P + 3 for %lo() and AHL + GP - P - 1 for %hi()
|
||||
// to correctly handle less-sugnificant bit of the microMIPS symbol.
|
||||
uint64_t V = InX::MipsGot->getGp() + A - P;
|
||||
if (Type == R_MIPS_LO16)
|
||||
if (Type == R_MIPS_LO16 || Type == R_MICROMIPS_LO16)
|
||||
V += 4;
|
||||
if (Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_HI16)
|
||||
V -= 1;
|
||||
return V;
|
||||
}
|
||||
case R_MIPS_GOT_LOCAL_PAGE:
|
||||
// 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(Body, A) -
|
||||
return InX::MipsGot->getVA() + InX::MipsGot->getPageEntryOffset(Sym, A) -
|
||||
InX::MipsGot->getGp();
|
||||
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->getBodyEntryOffset(Body, A) -
|
||||
return InX::MipsGot->getVA() + InX::MipsGot->getSymEntryOffset(Sym, A) -
|
||||
InX::MipsGot->getGp();
|
||||
case R_MIPS_TLSGD:
|
||||
return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() +
|
||||
InX::MipsGot->getGlobalDynOffset(Body) - InX::MipsGot->getGp();
|
||||
InX::MipsGot->getGlobalDynOffset(Sym) - InX::MipsGot->getGp();
|
||||
case R_MIPS_TLSLD:
|
||||
return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() +
|
||||
InX::MipsGot->getTlsIndexOff() - InX::MipsGot->getGp();
|
||||
case R_PAGE_PC:
|
||||
case R_PLT_PAGE_PC: {
|
||||
uint64_t Dest;
|
||||
if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak())
|
||||
if (Sym.isUndefWeak())
|
||||
Dest = getAArch64Page(A);
|
||||
else
|
||||
Dest = getAArch64Page(Body.getVA(A));
|
||||
Dest = getAArch64Page(Sym.getVA(A));
|
||||
return Dest - getAArch64Page(P);
|
||||
}
|
||||
case R_PC: {
|
||||
uint64_t Dest;
|
||||
if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak()) {
|
||||
if (Sym.isUndefWeak()) {
|
||||
// On ARM and AArch64 a branch to an undefined weak resolves to the
|
||||
// next instruction, otherwise the place.
|
||||
if (Config->EMachine == EM_ARM)
|
||||
@ -558,19 +591,19 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
|
||||
else if (Config->EMachine == EM_AARCH64)
|
||||
Dest = getAArch64UndefinedRelativeWeakVA(Type, A, P);
|
||||
else
|
||||
Dest = Body.getVA(A);
|
||||
Dest = Sym.getVA(A);
|
||||
} else {
|
||||
Dest = Body.getVA(A);
|
||||
Dest = Sym.getVA(A);
|
||||
}
|
||||
return Dest - P;
|
||||
}
|
||||
case R_PLT:
|
||||
return Body.getPltVA() + A;
|
||||
return Sym.getPltVA() + A;
|
||||
case R_PLT_PC:
|
||||
case R_PPC_PLT_OPD:
|
||||
return Body.getPltVA() + A - P;
|
||||
return Sym.getPltVA() + A - P;
|
||||
case R_PPC_OPD: {
|
||||
uint64_t SymVA = Body.getVA(A);
|
||||
uint64_t SymVA = Sym.getVA(A);
|
||||
// If we have an undefined weak symbol, we might get here with a symbol
|
||||
// address of zero. That could overflow, but the code must be unreachable,
|
||||
// so don't bother doing anything at all.
|
||||
@ -590,7 +623,7 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
|
||||
case R_PPC_TOC:
|
||||
return getPPC64TocBase() + A;
|
||||
case R_RELAX_GOT_PC:
|
||||
return Body.getVA(A) - P;
|
||||
return Sym.getVA(A) - P;
|
||||
case R_RELAX_TLS_GD_TO_LE:
|
||||
case R_RELAX_TLS_IE_TO_LE:
|
||||
case R_RELAX_TLS_LD_TO_LE:
|
||||
@ -600,26 +633,25 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
|
||||
// lld and .tbss is not referenced, it gets reclaimed and we don't
|
||||
// create a TLS program header. Therefore, we resolve this
|
||||
// statically to zero.
|
||||
if (Body.isTls() && (Body.isLazy() || Body.isUndefined()) &&
|
||||
Body.symbol()->isWeak())
|
||||
if (Sym.isTls() && Sym.isUndefWeak())
|
||||
return 0;
|
||||
if (Target->TcbSize)
|
||||
return Body.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align);
|
||||
return Body.getVA(A) - Out::TlsPhdr->p_memsz;
|
||||
return Sym.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align);
|
||||
return Sym.getVA(A) - Out::TlsPhdr->p_memsz;
|
||||
case R_RELAX_TLS_GD_TO_LE_NEG:
|
||||
case R_NEG_TLS:
|
||||
return Out::TlsPhdr->p_memsz - Body.getVA(A);
|
||||
return Out::TlsPhdr->p_memsz - Sym.getVA(A);
|
||||
case R_SIZE:
|
||||
return A; // Body.getSize was already folded into the addend.
|
||||
return A; // Sym.getSize was already folded into the addend.
|
||||
case R_TLSDESC:
|
||||
return InX::Got->getGlobalDynAddr(Body) + A;
|
||||
return InX::Got->getGlobalDynAddr(Sym) + A;
|
||||
case R_TLSDESC_PAGE:
|
||||
return getAArch64Page(InX::Got->getGlobalDynAddr(Body) + A) -
|
||||
return getAArch64Page(InX::Got->getGlobalDynAddr(Sym) + A) -
|
||||
getAArch64Page(P);
|
||||
case R_TLSGD:
|
||||
return InX::Got->getGlobalDynOffset(Body) + A - InX::Got->getSize();
|
||||
return InX::Got->getGlobalDynOffset(Sym) + A - InX::Got->getSize();
|
||||
case R_TLSGD_PC:
|
||||
return InX::Got->getGlobalDynAddr(Body) + A - P;
|
||||
return InX::Got->getGlobalDynAddr(Sym) + A - P;
|
||||
case R_TLSLD:
|
||||
return InX::Got->getTlsIndexOff() + A - InX::Got->getSize();
|
||||
case R_TLSLD_PC:
|
||||
@ -637,64 +669,62 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
|
||||
// function as a performance optimization.
|
||||
template <class ELFT, class RelTy>
|
||||
void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
|
||||
const unsigned Bits = sizeof(typename ELFT::uint) * 8;
|
||||
|
||||
for (const RelTy &Rel : Rels) {
|
||||
uint32_t Type = Rel.getType(Config->IsMips64EL);
|
||||
RelType Type = Rel.getType(Config->IsMips64EL);
|
||||
uint64_t Offset = getOffset(Rel.r_offset);
|
||||
uint8_t *BufLoc = Buf + Offset;
|
||||
int64_t Addend = getAddend<ELFT>(Rel);
|
||||
if (!RelTy::IsRela)
|
||||
Addend += Target->getImplicitAddend(BufLoc, Type);
|
||||
|
||||
SymbolBody &Sym = this->getFile<ELFT>()->getRelocTargetSym(Rel);
|
||||
Symbol &Sym = this->getFile<ELFT>()->getRelocTargetSym(Rel);
|
||||
RelExpr Expr = Target->getRelExpr(Type, Sym, BufLoc);
|
||||
if (Expr == R_NONE)
|
||||
continue;
|
||||
if (Expr != R_ABS) {
|
||||
error(this->getLocation<ELFT>(Offset) + ": has non-ABS reloc");
|
||||
// GCC 8.0 or earlier have a bug that it emits R_386_GOTPC relocations
|
||||
// against _GLOBAL_OFFSET_TABLE for .debug_info. The bug seems to have
|
||||
// been fixed in 2017: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630,
|
||||
// but we need to keep this bug-compatible code for a while.
|
||||
if (Config->EMachine == EM_386 && Type == R_386_GOTPC)
|
||||
continue;
|
||||
|
||||
error(this->getLocation<ELFT>(Offset) + ": has non-ABS relocation " +
|
||||
toString(Type) + " against symbol '" + toString(Sym) + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t AddrLoc = getParent()->Addr + Offset;
|
||||
uint64_t SymVA = 0;
|
||||
if (!Sym.isTls() || Out::TlsPhdr)
|
||||
SymVA = SignExtend64<sizeof(typename ELFT::uint) * 8>(
|
||||
getRelocTargetVA(Type, Addend, AddrLoc, Sym, R_ABS));
|
||||
Target->relocateOne(BufLoc, Type, SymVA);
|
||||
if (Sym.isTls() && !Out::TlsPhdr)
|
||||
Target->relocateOne(BufLoc, Type, 0);
|
||||
else
|
||||
Target->relocateOne(BufLoc, Type, SignExtend64<Bits>(Sym.getVA(Addend)));
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> elf::ObjectFile<ELFT> *InputSectionBase::getFile() const {
|
||||
return cast_or_null<elf::ObjectFile<ELFT>>(File);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void InputSectionBase::relocate(uint8_t *Buf, uint8_t *BufEnd) {
|
||||
if (Flags & SHF_ALLOC)
|
||||
if (Flags & SHF_ALLOC) {
|
||||
relocateAlloc(Buf, BufEnd);
|
||||
else
|
||||
relocateNonAlloc<ELFT>(Buf, BufEnd);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void InputSectionBase::relocateNonAlloc(uint8_t *Buf, uint8_t *BufEnd) {
|
||||
// scanReloc function in Writer.cpp constructs Relocations
|
||||
// vector only for SHF_ALLOC'ed sections. For other sections,
|
||||
// we handle relocations directly here.
|
||||
auto *IS = cast<InputSection>(this);
|
||||
assert(!(IS->Flags & SHF_ALLOC));
|
||||
if (IS->AreRelocsRela)
|
||||
IS->relocateNonAlloc<ELFT>(Buf, IS->template relas<ELFT>());
|
||||
auto *Sec = cast<InputSection>(this);
|
||||
if (Sec->AreRelocsRela)
|
||||
Sec->relocateNonAlloc<ELFT>(Buf, Sec->template relas<ELFT>());
|
||||
else
|
||||
IS->relocateNonAlloc<ELFT>(Buf, IS->template rels<ELFT>());
|
||||
Sec->relocateNonAlloc<ELFT>(Buf, Sec->template rels<ELFT>());
|
||||
}
|
||||
|
||||
void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
|
||||
assert(Flags & SHF_ALLOC);
|
||||
const unsigned Bits = Config->Wordsize * 8;
|
||||
|
||||
for (const Relocation &Rel : Relocations) {
|
||||
uint64_t Offset = getOffset(Rel.Offset);
|
||||
uint8_t *BufLoc = Buf + Offset;
|
||||
uint32_t Type = Rel.Type;
|
||||
RelType Type = Rel.Type;
|
||||
|
||||
uint64_t AddrLoc = getOutputSection()->Addr + Offset;
|
||||
RelExpr Expr = Rel.Expr;
|
||||
@ -776,24 +806,15 @@ void InputSection::replace(InputSection *Other) {
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
EhInputSection::EhInputSection(elf::ObjectFile<ELFT> *F,
|
||||
EhInputSection::EhInputSection(ObjFile<ELFT> *F,
|
||||
const typename ELFT::Shdr *Header,
|
||||
StringRef Name)
|
||||
: InputSectionBase(F, Header, Name, InputSectionBase::EHFrame) {
|
||||
// Mark .eh_frame sections as live by default because there are
|
||||
// usually no relocations that point to .eh_frames. Otherwise,
|
||||
// the garbage collector would drop all .eh_frame sections.
|
||||
this->Live = true;
|
||||
}
|
||||
: InputSectionBase(F, Header, Name, InputSectionBase::EHFrame) {}
|
||||
|
||||
SyntheticSection *EhInputSection::getParent() const {
|
||||
return cast_or_null<SyntheticSection>(Parent);
|
||||
}
|
||||
|
||||
bool EhInputSection::classof(const SectionBase *S) {
|
||||
return S->kind() == InputSectionBase::EHFrame;
|
||||
}
|
||||
|
||||
// Returns the index of the first relocation that points to a region between
|
||||
// Begin and Begin+Size.
|
||||
template <class IntTy, class RelTy>
|
||||
@ -820,14 +841,10 @@ template <class ELFT> void EhInputSection::split() {
|
||||
if (!this->Pieces.empty())
|
||||
return;
|
||||
|
||||
if (this->NumRelocations) {
|
||||
if (this->AreRelocsRela)
|
||||
split<ELFT>(this->relas<ELFT>());
|
||||
else
|
||||
split<ELFT>(this->rels<ELFT>());
|
||||
return;
|
||||
}
|
||||
split<ELFT>(makeArrayRef<typename ELFT::Rela>(nullptr, nullptr));
|
||||
if (this->AreRelocsRela)
|
||||
split<ELFT>(this->relas<ELFT>());
|
||||
else
|
||||
split<ELFT>(this->rels<ELFT>());
|
||||
}
|
||||
|
||||
template <class ELFT, class RelTy>
|
||||
@ -835,7 +852,7 @@ void EhInputSection::split(ArrayRef<RelTy> Rels) {
|
||||
ArrayRef<uint8_t> Data = this->Data;
|
||||
unsigned RelI = 0;
|
||||
for (size_t Off = 0, End = Data.size(); Off != End;) {
|
||||
size_t Size = readEhRecordSize<ELFT>(this, Off);
|
||||
size_t Size = readEhRecordSize(this, Off);
|
||||
this->Pieces.emplace_back(Off, this, Size, getReloc(Off, Size, Rels, RelI));
|
||||
// The empty record is the end marker.
|
||||
if (Size == 4)
|
||||
@ -844,9 +861,8 @@ void EhInputSection::split(ArrayRef<RelTy> Rels) {
|
||||
}
|
||||
}
|
||||
|
||||
static size_t findNull(ArrayRef<uint8_t> A, size_t EntSize) {
|
||||
static size_t findNull(StringRef S, size_t EntSize) {
|
||||
// Optimize the common case.
|
||||
StringRef S((const char *)A.data(), A.size());
|
||||
if (EntSize == 1)
|
||||
return S.find(0);
|
||||
|
||||
@ -867,14 +883,16 @@ SyntheticSection *MergeInputSection::getParent() const {
|
||||
void MergeInputSection::splitStrings(ArrayRef<uint8_t> Data, size_t EntSize) {
|
||||
size_t Off = 0;
|
||||
bool IsAlloc = this->Flags & SHF_ALLOC;
|
||||
while (!Data.empty()) {
|
||||
size_t End = findNull(Data, EntSize);
|
||||
StringRef S = toStringRef(Data);
|
||||
|
||||
while (!S.empty()) {
|
||||
size_t End = findNull(S, EntSize);
|
||||
if (End == StringRef::npos)
|
||||
fatal(toString(this) + ": string is not null terminated");
|
||||
size_t Size = End + EntSize;
|
||||
Pieces.emplace_back(Off, !IsAlloc);
|
||||
Hashes.push_back(hash_value(toStringRef(Data.slice(0, Size))));
|
||||
Data = Data.slice(Size);
|
||||
|
||||
Pieces.emplace_back(Off, xxHash64(S.substr(0, Size)), !IsAlloc);
|
||||
S = S.substr(Size);
|
||||
Off += Size;
|
||||
}
|
||||
}
|
||||
@ -886,41 +904,43 @@ void MergeInputSection::splitNonStrings(ArrayRef<uint8_t> Data,
|
||||
size_t Size = Data.size();
|
||||
assert((Size % EntSize) == 0);
|
||||
bool IsAlloc = this->Flags & SHF_ALLOC;
|
||||
for (unsigned I = 0, N = Size; I != N; I += EntSize) {
|
||||
Hashes.push_back(hash_value(toStringRef(Data.slice(I, EntSize))));
|
||||
Pieces.emplace_back(I, !IsAlloc);
|
||||
}
|
||||
|
||||
for (size_t I = 0; I != Size; I += EntSize)
|
||||
Pieces.emplace_back(I, xxHash64(toStringRef(Data.slice(I, EntSize))),
|
||||
!IsAlloc);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
MergeInputSection::MergeInputSection(elf::ObjectFile<ELFT> *F,
|
||||
MergeInputSection::MergeInputSection(ObjFile<ELFT> *F,
|
||||
const typename ELFT::Shdr *Header,
|
||||
StringRef Name)
|
||||
: InputSectionBase(F, Header, Name, InputSectionBase::Merge) {}
|
||||
: InputSectionBase(F, Header, Name, InputSectionBase::Merge) {
|
||||
// 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 (Data.size() > UINT32_MAX)
|
||||
error(toString(this) + ": section too large");
|
||||
}
|
||||
|
||||
// This function is called after we obtain a complete list of input sections
|
||||
// that need to be linked. This is responsible to split section contents
|
||||
// into small chunks for further processing.
|
||||
//
|
||||
// Note that this function is called from parallel_for_each. This must be
|
||||
// Note that this function is called from parallelForEach. This must be
|
||||
// thread-safe (i.e. no memory allocation from the pools).
|
||||
void MergeInputSection::splitIntoPieces() {
|
||||
ArrayRef<uint8_t> Data = this->Data;
|
||||
uint64_t EntSize = this->Entsize;
|
||||
assert(Pieces.empty());
|
||||
|
||||
if (this->Flags & SHF_STRINGS)
|
||||
splitStrings(Data, EntSize);
|
||||
splitStrings(Data, Entsize);
|
||||
else
|
||||
splitNonStrings(Data, EntSize);
|
||||
splitNonStrings(Data, Entsize);
|
||||
|
||||
if (Config->GcSections && (this->Flags & SHF_ALLOC))
|
||||
for (uint64_t Off : LiveOffsets)
|
||||
this->getSectionPiece(Off)->Live = true;
|
||||
}
|
||||
|
||||
bool MergeInputSection::classof(const SectionBase *S) {
|
||||
return S->kind() == InputSectionBase::Merge;
|
||||
}
|
||||
|
||||
// Do binary search to get a section piece at a given input offset.
|
||||
SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
|
||||
auto *This = static_cast<const MergeInputSection *>(this);
|
||||
@ -941,8 +961,7 @@ static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) {
|
||||
}
|
||||
|
||||
const SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) const {
|
||||
uint64_t Size = this->Data.size();
|
||||
if (Offset >= Size)
|
||||
if (Data.size() <= Offset)
|
||||
fatal(toString(this) + ": entry is past the end of the section");
|
||||
|
||||
// Find the element this offset points to.
|
||||
@ -957,20 +976,20 @@ const SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) const {
|
||||
// Because contents of a mergeable section is not contiguous in output,
|
||||
// it is not just an addition to a base output offset.
|
||||
uint64_t MergeInputSection::getOffset(uint64_t Offset) const {
|
||||
if (!Live)
|
||||
return 0;
|
||||
|
||||
// Initialize OffsetMap lazily.
|
||||
llvm::call_once(InitOffsetMap, [&] {
|
||||
OffsetMap.reserve(Pieces.size());
|
||||
for (const SectionPiece &Piece : Pieces)
|
||||
OffsetMap[Piece.InputOff] = Piece.OutputOff;
|
||||
for (size_t I = 0; I < Pieces.size(); ++I)
|
||||
OffsetMap[Pieces[I].InputOff] = I;
|
||||
});
|
||||
|
||||
// Find a string starting at a given offset.
|
||||
auto It = OffsetMap.find(Offset);
|
||||
if (It != OffsetMap.end())
|
||||
return It->second;
|
||||
|
||||
if (!this->Live)
|
||||
return 0;
|
||||
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.
|
||||
@ -982,56 +1001,50 @@ uint64_t MergeInputSection::getOffset(uint64_t Offset) const {
|
||||
return Piece.OutputOff + Addend;
|
||||
}
|
||||
|
||||
template InputSection::InputSection(elf::ObjectFile<ELF32LE> *,
|
||||
const ELF32LE::Shdr *, StringRef);
|
||||
template InputSection::InputSection(elf::ObjectFile<ELF32BE> *,
|
||||
const ELF32BE::Shdr *, StringRef);
|
||||
template InputSection::InputSection(elf::ObjectFile<ELF64LE> *,
|
||||
const ELF64LE::Shdr *, StringRef);
|
||||
template InputSection::InputSection(elf::ObjectFile<ELF64BE> *,
|
||||
const ELF64BE::Shdr *, StringRef);
|
||||
template InputSection::InputSection(ObjFile<ELF32LE> *, const ELF32LE::Shdr *,
|
||||
StringRef);
|
||||
template InputSection::InputSection(ObjFile<ELF32BE> *, const ELF32BE::Shdr *,
|
||||
StringRef);
|
||||
template InputSection::InputSection(ObjFile<ELF64LE> *, const ELF64LE::Shdr *,
|
||||
StringRef);
|
||||
template InputSection::InputSection(ObjFile<ELF64BE> *, const ELF64BE::Shdr *,
|
||||
StringRef);
|
||||
|
||||
template std::string InputSectionBase::getLocation<ELF32LE>(uint64_t);
|
||||
template std::string InputSectionBase::getLocation<ELF32BE>(uint64_t);
|
||||
template std::string InputSectionBase::getLocation<ELF64LE>(uint64_t);
|
||||
template std::string InputSectionBase::getLocation<ELF64BE>(uint64_t);
|
||||
|
||||
template std::string InputSectionBase::getSrcMsg<ELF32LE>(uint64_t);
|
||||
template std::string InputSectionBase::getSrcMsg<ELF32BE>(uint64_t);
|
||||
template std::string InputSectionBase::getSrcMsg<ELF64LE>(uint64_t);
|
||||
template std::string InputSectionBase::getSrcMsg<ELF64BE>(uint64_t);
|
||||
|
||||
template std::string InputSectionBase::getObjMsg<ELF32LE>(uint64_t);
|
||||
template std::string InputSectionBase::getObjMsg<ELF32BE>(uint64_t);
|
||||
template std::string InputSectionBase::getObjMsg<ELF64LE>(uint64_t);
|
||||
template std::string InputSectionBase::getObjMsg<ELF64BE>(uint64_t);
|
||||
template std::string InputSectionBase::getSrcMsg<ELF32LE>(const Symbol &,
|
||||
uint64_t);
|
||||
template std::string InputSectionBase::getSrcMsg<ELF32BE>(const Symbol &,
|
||||
uint64_t);
|
||||
template std::string InputSectionBase::getSrcMsg<ELF64LE>(const Symbol &,
|
||||
uint64_t);
|
||||
template std::string InputSectionBase::getSrcMsg<ELF64BE>(const Symbol &,
|
||||
uint64_t);
|
||||
|
||||
template void InputSection::writeTo<ELF32LE>(uint8_t *);
|
||||
template void InputSection::writeTo<ELF32BE>(uint8_t *);
|
||||
template void InputSection::writeTo<ELF64LE>(uint8_t *);
|
||||
template void InputSection::writeTo<ELF64BE>(uint8_t *);
|
||||
|
||||
template elf::ObjectFile<ELF32LE> *InputSectionBase::getFile<ELF32LE>() const;
|
||||
template elf::ObjectFile<ELF32BE> *InputSectionBase::getFile<ELF32BE>() const;
|
||||
template elf::ObjectFile<ELF64LE> *InputSectionBase::getFile<ELF64LE>() const;
|
||||
template elf::ObjectFile<ELF64BE> *InputSectionBase::getFile<ELF64BE>() const;
|
||||
|
||||
template MergeInputSection::MergeInputSection(elf::ObjectFile<ELF32LE> *,
|
||||
template MergeInputSection::MergeInputSection(ObjFile<ELF32LE> *,
|
||||
const ELF32LE::Shdr *, StringRef);
|
||||
template MergeInputSection::MergeInputSection(elf::ObjectFile<ELF32BE> *,
|
||||
template MergeInputSection::MergeInputSection(ObjFile<ELF32BE> *,
|
||||
const ELF32BE::Shdr *, StringRef);
|
||||
template MergeInputSection::MergeInputSection(elf::ObjectFile<ELF64LE> *,
|
||||
template MergeInputSection::MergeInputSection(ObjFile<ELF64LE> *,
|
||||
const ELF64LE::Shdr *, StringRef);
|
||||
template MergeInputSection::MergeInputSection(elf::ObjectFile<ELF64BE> *,
|
||||
template MergeInputSection::MergeInputSection(ObjFile<ELF64BE> *,
|
||||
const ELF64BE::Shdr *, StringRef);
|
||||
|
||||
template EhInputSection::EhInputSection(elf::ObjectFile<ELF32LE> *,
|
||||
template EhInputSection::EhInputSection(ObjFile<ELF32LE> *,
|
||||
const ELF32LE::Shdr *, StringRef);
|
||||
template EhInputSection::EhInputSection(elf::ObjectFile<ELF32BE> *,
|
||||
template EhInputSection::EhInputSection(ObjFile<ELF32BE> *,
|
||||
const ELF32BE::Shdr *, StringRef);
|
||||
template EhInputSection::EhInputSection(elf::ObjectFile<ELF64LE> *,
|
||||
template EhInputSection::EhInputSection(ObjFile<ELF64LE> *,
|
||||
const ELF64LE::Shdr *, StringRef);
|
||||
template EhInputSection::EhInputSection(elf::ObjectFile<ELF64BE> *,
|
||||
template EhInputSection::EhInputSection(ObjFile<ELF64BE> *,
|
||||
const ELF64BE::Shdr *, StringRef);
|
||||
|
||||
template void EhInputSection::split<ELF32LE>();
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "Config.h"
|
||||
#include "Relocations.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
@ -24,15 +24,13 @@
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class DefinedCommon;
|
||||
class SymbolBody;
|
||||
class Symbol;
|
||||
struct SectionPiece;
|
||||
|
||||
class DefinedRegular;
|
||||
class Defined;
|
||||
class SyntheticSection;
|
||||
template <class ELFT> class EhFrameSection;
|
||||
class MergeSyntheticSection;
|
||||
template <class ELFT> class ObjectFile;
|
||||
template <class ELFT> class ObjFile;
|
||||
class OutputSection;
|
||||
|
||||
// This is the base class of all sections that lld handles. Some are sections in
|
||||
@ -47,6 +45,13 @@ public:
|
||||
|
||||
StringRef Name;
|
||||
|
||||
// This pointer points to the "real" instance of this instance.
|
||||
// Usually Repl == this. However, if ICF merges two sections,
|
||||
// Repl pointer of one section points to another section. So,
|
||||
// if you need to get a pointer to this instance, do not use
|
||||
// this but instead this->Repl.
|
||||
SectionBase *Repl;
|
||||
|
||||
unsigned SectionKind : 3;
|
||||
|
||||
// The next two bit fields are only used by InputSectionBase, but we
|
||||
@ -54,12 +59,12 @@ public:
|
||||
|
||||
// The garbage collector sets sections' Live bits.
|
||||
// If GC is disabled, all sections are considered live by default.
|
||||
unsigned Live : 1; // for garbage collection
|
||||
unsigned Assigned : 1; // for linker script
|
||||
unsigned Live : 1;
|
||||
|
||||
uint32_t Alignment;
|
||||
unsigned Bss : 1;
|
||||
|
||||
// These corresponds to the fields in Elf_Shdr.
|
||||
uint32_t Alignment;
|
||||
uint64_t Flags;
|
||||
uint64_t Entsize;
|
||||
uint32_t Type;
|
||||
@ -75,45 +80,20 @@ public:
|
||||
// section.
|
||||
uint64_t getOffset(uint64_t Offset) const;
|
||||
|
||||
uint64_t getOffset(const DefinedRegular &Sym) const;
|
||||
|
||||
protected:
|
||||
SectionBase(Kind SectionKind, StringRef Name, uint64_t Flags,
|
||||
uint64_t Entsize, uint64_t Alignment, uint32_t Type,
|
||||
uint32_t Info, uint32_t Link)
|
||||
: Name(Name), SectionKind(SectionKind), Alignment(Alignment),
|
||||
Flags(Flags), Entsize(Entsize), Type(Type), Link(Link), Info(Info) {
|
||||
Live = false;
|
||||
Assigned = false;
|
||||
}
|
||||
: Name(Name), Repl(this), SectionKind(SectionKind), Live(false),
|
||||
Bss(false), Alignment(Alignment), Flags(Flags), Entsize(Entsize),
|
||||
Type(Type), Link(Link), Info(Info) {}
|
||||
};
|
||||
|
||||
// This corresponds to a section of an input file.
|
||||
class InputSectionBase : public SectionBase {
|
||||
public:
|
||||
static bool classof(const SectionBase *S);
|
||||
|
||||
// The file this section is from.
|
||||
InputFile *File;
|
||||
|
||||
ArrayRef<uint8_t> Data;
|
||||
uint64_t getOffsetInFile() const;
|
||||
|
||||
static InputSectionBase Discarded;
|
||||
|
||||
InputSectionBase()
|
||||
: SectionBase(Regular, "", /*Flags*/ 0, /*Entsize*/ 0, /*Alignment*/ 0,
|
||||
/*Type*/ 0,
|
||||
/*Info*/ 0, /*Link*/ 0),
|
||||
Repl(this) {
|
||||
Live = false;
|
||||
Assigned = false;
|
||||
NumRelocations = 0;
|
||||
AreRelocsRela = false;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase(ObjectFile<ELFT> *File, const typename ELFT::Shdr *Header,
|
||||
InputSectionBase(ObjFile<ELFT> *File, const typename ELFT::Shdr *Header,
|
||||
StringRef Name, Kind SectionKind);
|
||||
|
||||
InputSectionBase(InputFile *File, uint64_t Flags, uint32_t Type,
|
||||
@ -121,6 +101,33 @@ public:
|
||||
uint32_t Alignment, ArrayRef<uint8_t> Data, StringRef Name,
|
||||
Kind SectionKind);
|
||||
|
||||
static bool classof(const SectionBase *S) { return S->kind() != Output; }
|
||||
|
||||
// The file which contains this section. It's dynamic type is always
|
||||
// ObjFile<ELFT>, but in order to avoid ELFT, we use InputFile as
|
||||
// its static type.
|
||||
InputFile *File;
|
||||
|
||||
template <class ELFT> ObjFile<ELFT> *getFile() const {
|
||||
return cast_or_null<ObjFile<ELFT>>(File);
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> Data;
|
||||
uint64_t getOffsetInFile() const;
|
||||
|
||||
// True if this section has already been placed to a linker script
|
||||
// output section. This is needed because, in a linker script, you
|
||||
// can refer to the same section more than once. For example, in
|
||||
// the following linker script,
|
||||
//
|
||||
// .foo : { *(.text) }
|
||||
// .bar : { *(.text) }
|
||||
//
|
||||
// .foo takes all .text sections, and .bar becomes empty. To achieve
|
||||
// this, we need to memorize whether a section has been placed or
|
||||
// not for each input section.
|
||||
bool Assigned = false;
|
||||
|
||||
// Input sections are part of an output section. Special sections
|
||||
// like .eh_frame and merge sections are first combined into a
|
||||
// synthetic section that is then added to an output section. In all
|
||||
@ -131,12 +138,14 @@ public:
|
||||
const void *FirstRelocation = nullptr;
|
||||
unsigned NumRelocations : 31;
|
||||
unsigned AreRelocsRela : 1;
|
||||
|
||||
template <class ELFT> ArrayRef<typename ELFT::Rel> rels() const {
|
||||
assert(!AreRelocsRela);
|
||||
return llvm::makeArrayRef(
|
||||
static_cast<const typename ELFT::Rel *>(FirstRelocation),
|
||||
NumRelocations);
|
||||
}
|
||||
|
||||
template <class ELFT> ArrayRef<typename ELFT::Rela> relas() const {
|
||||
assert(AreRelocsRela);
|
||||
return llvm::makeArrayRef(
|
||||
@ -144,38 +153,34 @@ public:
|
||||
NumRelocations);
|
||||
}
|
||||
|
||||
// This pointer points to the "real" instance of this instance.
|
||||
// Usually Repl == this. However, if ICF merges two sections,
|
||||
// Repl pointer of one section points to another section. So,
|
||||
// if you need to get a pointer to this instance, do not use
|
||||
// this but instead this->Repl.
|
||||
InputSectionBase *Repl;
|
||||
|
||||
// InputSections that are dependent on us (reverse dependency for GC)
|
||||
llvm::TinyPtrVector<InputSectionBase *> DependentSections;
|
||||
llvm::TinyPtrVector<InputSection *> DependentSections;
|
||||
|
||||
// Returns the size of this section (even if this is a common or BSS.)
|
||||
size_t getSize() const;
|
||||
|
||||
template <class ELFT> ObjectFile<ELFT> *getFile() const;
|
||||
|
||||
template <class ELFT> llvm::object::ELFFile<ELFT> getObj() const {
|
||||
return getFile<ELFT>()->getObj();
|
||||
}
|
||||
|
||||
InputSection *getLinkOrderDep() const;
|
||||
|
||||
void uncompress();
|
||||
// Compilers emit zlib-compressed debug sections if the -gz option
|
||||
// is given. This function checks if this section is compressed, and
|
||||
// if so, decompress in memory.
|
||||
void maybeUncompress();
|
||||
|
||||
// Returns a source location string. Used to construct an error message.
|
||||
template <class ELFT> std::string getLocation(uint64_t Offset);
|
||||
template <class ELFT> std::string getSrcMsg(uint64_t Offset);
|
||||
template <class ELFT> std::string getObjMsg(uint64_t Offset);
|
||||
template <class ELFT>
|
||||
std::string getSrcMsg(const Symbol &Sym, uint64_t Offset);
|
||||
std::string getObjMsg(uint64_t Offset);
|
||||
|
||||
// Each section knows how to relocate itself. These functions apply
|
||||
// relocations, assuming that Buf points to this section's copy in
|
||||
// the mmap'ed output buffer.
|
||||
template <class ELFT> void relocate(uint8_t *Buf, uint8_t *BufEnd);
|
||||
void relocateAlloc(uint8_t *Buf, uint8_t *BufEnd);
|
||||
template <class ELFT> void relocateNonAlloc(uint8_t *Buf, uint8_t *BufEnd);
|
||||
|
||||
// The native ELF reloc data type is not very convenient to handle.
|
||||
// So we convert ELF reloc records to our own records in Relocations.cpp.
|
||||
// This vector contains such "cooked" relocations.
|
||||
std::vector<Relocation> Relocations;
|
||||
|
||||
template <typename T> llvm::ArrayRef<T> getDataAs() const {
|
||||
@ -183,36 +188,43 @@ public:
|
||||
assert(S % sizeof(T) == 0);
|
||||
return llvm::makeArrayRef<T>((const T *)Data.data(), S / sizeof(T));
|
||||
}
|
||||
|
||||
private:
|
||||
// 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[]> UncompressBuf;
|
||||
};
|
||||
|
||||
// SectionPiece represents a piece of splittable section contents.
|
||||
// We allocate a lot of these and binary search on them. This means that they
|
||||
// have to be as compact as possible, which is why we don't store the size (can
|
||||
// be found by looking at the next one) and put the hash in a side table.
|
||||
// be found by looking at the next one).
|
||||
struct SectionPiece {
|
||||
SectionPiece(size_t Off, bool Live = false)
|
||||
: InputOff(Off), OutputOff(-1), Live(Live || !Config->GcSections) {}
|
||||
SectionPiece(size_t Off, uint32_t Hash, bool Live)
|
||||
: InputOff(Off), Hash(Hash), OutputOff(-1),
|
||||
Live(Live || !Config->GcSections) {}
|
||||
|
||||
size_t InputOff;
|
||||
ssize_t OutputOff : 8 * sizeof(ssize_t) - 1;
|
||||
size_t Live : 1;
|
||||
uint32_t InputOff;
|
||||
uint32_t Hash;
|
||||
int64_t OutputOff : 63;
|
||||
uint64_t Live : 1;
|
||||
};
|
||||
static_assert(sizeof(SectionPiece) == 2 * sizeof(size_t),
|
||||
"SectionPiece is too big");
|
||||
|
||||
static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big");
|
||||
|
||||
// This corresponds to a SHF_MERGE section of an input file.
|
||||
class MergeInputSection : public InputSectionBase {
|
||||
public:
|
||||
template <class ELFT>
|
||||
MergeInputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
MergeInputSection(ObjFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
StringRef Name);
|
||||
static bool classof(const SectionBase *S);
|
||||
static bool classof(const SectionBase *S) { return S->kind() == Merge; }
|
||||
void splitIntoPieces();
|
||||
|
||||
// Mark the piece at a given offset live. Used by GC.
|
||||
void markLiveAt(uint64_t Offset) {
|
||||
assert(this->Flags & llvm::ELF::SHF_ALLOC);
|
||||
LiveOffsets.insert(Offset);
|
||||
if (this->Flags & llvm::ELF::SHF_ALLOC)
|
||||
LiveOffsets.insert(Offset);
|
||||
}
|
||||
|
||||
// Translate an offset in the input section to an offset
|
||||
@ -228,14 +240,9 @@ public:
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
llvm::CachedHashStringRef getData(size_t I) const {
|
||||
size_t Begin = Pieces[I].InputOff;
|
||||
size_t End;
|
||||
if (Pieces.size() - 1 == I)
|
||||
End = this->Data.size();
|
||||
else
|
||||
End = Pieces[I + 1].InputOff;
|
||||
|
||||
StringRef S = {(const char *)(this->Data.data() + Begin), End - Begin};
|
||||
return {S, Hashes[I]};
|
||||
size_t End =
|
||||
(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.
|
||||
@ -248,24 +255,23 @@ private:
|
||||
void splitStrings(ArrayRef<uint8_t> A, size_t Size);
|
||||
void splitNonStrings(ArrayRef<uint8_t> A, size_t Size);
|
||||
|
||||
std::vector<uint32_t> Hashes;
|
||||
|
||||
mutable llvm::DenseMap<uint64_t, uint64_t> OffsetMap;
|
||||
mutable llvm::DenseMap<uint32_t, uint32_t> OffsetMap;
|
||||
mutable llvm::once_flag InitOffsetMap;
|
||||
|
||||
llvm::DenseSet<uint64_t> LiveOffsets;
|
||||
};
|
||||
|
||||
struct EhSectionPiece : public SectionPiece {
|
||||
EhSectionPiece(size_t Off, InputSectionBase *ID, uint32_t Size,
|
||||
struct EhSectionPiece {
|
||||
EhSectionPiece(size_t Off, InputSectionBase *Sec, uint32_t Size,
|
||||
unsigned FirstRelocation)
|
||||
: SectionPiece(Off, false), ID(ID), Size(Size),
|
||||
FirstRelocation(FirstRelocation) {}
|
||||
InputSectionBase *ID;
|
||||
uint32_t Size;
|
||||
uint32_t size() const { return Size; }
|
||||
: InputOff(Off), Sec(Sec), Size(Size), FirstRelocation(FirstRelocation) {}
|
||||
|
||||
ArrayRef<uint8_t> data() { return {ID->Data.data() + this->InputOff, Size}; }
|
||||
ArrayRef<uint8_t> data() { return {Sec->Data.data() + this->InputOff, Size}; }
|
||||
|
||||
size_t InputOff;
|
||||
ssize_t OutputOff = -1;
|
||||
InputSectionBase *Sec;
|
||||
uint32_t Size;
|
||||
unsigned FirstRelocation;
|
||||
};
|
||||
|
||||
@ -273,9 +279,9 @@ struct EhSectionPiece : public SectionPiece {
|
||||
class EhInputSection : public InputSectionBase {
|
||||
public:
|
||||
template <class ELFT>
|
||||
EhInputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
EhInputSection(ObjFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
StringRef Name);
|
||||
static bool classof(const SectionBase *S);
|
||||
static bool classof(const SectionBase *S) { return S->kind() == EHFrame; }
|
||||
template <class ELFT> void split();
|
||||
template <class ELFT, class RelTy> void split(ArrayRef<RelTy> Rels);
|
||||
|
||||
@ -295,7 +301,7 @@ public:
|
||||
InputSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
|
||||
ArrayRef<uint8_t> Data, StringRef Name, Kind K = Regular);
|
||||
template <class ELFT>
|
||||
InputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
InputSection(ObjFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
StringRef Name);
|
||||
|
||||
// Write this section to a mmap'ed file, assuming Buf is pointing to
|
||||
@ -304,8 +310,10 @@ public:
|
||||
|
||||
OutputSection *getParent() const;
|
||||
|
||||
// The offset from beginning of the output sections this section was assigned
|
||||
// to. The writer sets a value.
|
||||
// This variable has two usages. Initially, it represents an index in the
|
||||
// OutputSection's InputSection list, and is used when ordering SHF_LINK_ORDER
|
||||
// sections. After assignAddresses is called, it represents the offset from
|
||||
// the beginning of the output section this section was assigned to.
|
||||
uint64_t OutSecOff = 0;
|
||||
|
||||
static bool classof(const SectionBase *S);
|
||||
@ -321,6 +329,8 @@ public:
|
||||
// Called by ICF to merge two input sections.
|
||||
void replace(InputSection *Other);
|
||||
|
||||
static InputSection Discarded;
|
||||
|
||||
private:
|
||||
template <class ELFT, class RelTy>
|
||||
void copyRelocations(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
|
||||
@ -331,6 +341,9 @@ private:
|
||||
// The list of all input sections.
|
||||
extern std::vector<InputSectionBase *> InputSections;
|
||||
|
||||
// Builds section order for handling --symbol-ordering-file.
|
||||
llvm::DenseMap<SectionBase *, int> buildSectionOrder();
|
||||
|
||||
} // namespace elf
|
||||
|
||||
std::string toString(const elf::InputSectionBase *);
|
||||
|
61
ELF/LTO.cpp
61
ELF/LTO.cpp
@ -9,10 +9,12 @@
|
||||
|
||||
#include "LTO.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Core/TargetOptionsCommandFlags.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/TargetOptionsCommandFlags.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
@ -60,10 +62,8 @@ static void diagnosticHandler(const DiagnosticInfo &DI) {
|
||||
}
|
||||
|
||||
static void checkError(Error E) {
|
||||
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
|
||||
error(EIB.message());
|
||||
return Error::success();
|
||||
});
|
||||
handleAllErrors(std::move(E),
|
||||
[&](ErrorInfoBase &EIB) { error(EIB.message()); });
|
||||
}
|
||||
|
||||
static std::unique_ptr<lto::LTO> createLTO() {
|
||||
@ -73,6 +73,10 @@ static std::unique_ptr<lto::LTO> createLTO() {
|
||||
Conf.Options = InitTargetOptionsFromCodeGenFlags();
|
||||
Conf.Options.RelaxELFRelocations = true;
|
||||
|
||||
// Always emit a section per function/datum with LTO.
|
||||
Conf.Options.FunctionSections = true;
|
||||
Conf.Options.DataSections = true;
|
||||
|
||||
if (Config->Relocatable)
|
||||
Conf.RelocModel = None;
|
||||
else if (Config->Pic)
|
||||
@ -103,13 +107,20 @@ static std::unique_ptr<lto::LTO> createLTO() {
|
||||
Config->LTOPartitions);
|
||||
}
|
||||
|
||||
BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
|
||||
BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {
|
||||
for (Symbol *Sym : Symtab->getSymbols()) {
|
||||
StringRef Name = Sym->getName();
|
||||
for (StringRef Prefix : {"__start_", "__stop_"})
|
||||
if (Name.startswith(Prefix))
|
||||
UsedStartStop.insert(Name.substr(Prefix.size()));
|
||||
}
|
||||
}
|
||||
|
||||
BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
|
||||
static void undefine(Symbol *S) {
|
||||
replaceBody<Undefined>(S, S->body()->getName(), /*IsLocal=*/false,
|
||||
STV_DEFAULT, S->body()->Type, nullptr);
|
||||
replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_GLOBAL, STV_DEFAULT,
|
||||
S->Type);
|
||||
}
|
||||
|
||||
void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
@ -118,25 +129,42 @@ void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
std::vector<Symbol *> Syms = F.getSymbols();
|
||||
std::vector<lto::SymbolResolution> Resols(Syms.size());
|
||||
|
||||
DenseSet<StringRef> ScriptSymbols;
|
||||
for (BaseCommand *Base : Script->SectionCommands)
|
||||
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base))
|
||||
ScriptSymbols.insert(Cmd->Name);
|
||||
|
||||
// Provide a resolution to the LTO API for each symbol.
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
|
||||
Symbol *Sym = Syms[SymNum];
|
||||
lto::SymbolResolution &R = Resols[SymNum];
|
||||
++SymNum;
|
||||
SymbolBody *B = Sym->body();
|
||||
|
||||
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
|
||||
// reports two symbols for module ASM defined. Without this check, lld
|
||||
// flags an undefined in IR with a definition in ASM as prevailing.
|
||||
// Once IRObjectFile is fixed to report only one symbol this hack can
|
||||
// be removed.
|
||||
R.Prevailing = !ObjSym.isUndefined() && B->File == &F;
|
||||
R.Prevailing = !ObjSym.isUndefined() && Sym->File == &F;
|
||||
|
||||
R.VisibleToRegularObj =
|
||||
Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym());
|
||||
// We ask LTO to preserve following global symbols:
|
||||
// 1) All symbols when doing relocatable link, so that them can be used
|
||||
// for doing final link.
|
||||
// 2) Symbols that are used in regular objects.
|
||||
// 3) C named sections if we have corresponding __start_/__stop_ symbol.
|
||||
// 4) Symbols that are defined in bitcode files and used for dynamic linking.
|
||||
R.VisibleToRegularObj = Config->Relocatable || Sym->IsUsedInRegularObj ||
|
||||
(R.Prevailing && Sym->includeInDynsym()) ||
|
||||
UsedStartStop.count(ObjSym.getSectionName());
|
||||
if (R.Prevailing)
|
||||
undefine(Sym);
|
||||
R.LinkerRedefined = Config->RenamedSymbols.count(Sym);
|
||||
|
||||
// We tell LTO to not apply interprocedural optimization for following
|
||||
// symbols because otherwise LTO would inline them while their values are
|
||||
// still not final:
|
||||
// 1) Aliased (with --defsym) or wrapped (with --wrap) symbols.
|
||||
// 2) Symbols redefined in linker script.
|
||||
R.LinkerRedefined = !Sym->CanInline || ScriptSymbols.count(Sym->getName());
|
||||
}
|
||||
checkError(LTOObj->add(std::move(F.Obj), Resols));
|
||||
}
|
||||
@ -156,9 +184,8 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||
if (!Config->ThinLTOCacheDir.empty())
|
||||
Cache = check(
|
||||
lto::localCache(Config->ThinLTOCacheDir,
|
||||
[&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
|
||||
Files[Task] = std::move(MB);
|
||||
}));
|
||||
[&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
|
||||
StringRef Path) { Files[Task] = std::move(MB); }));
|
||||
|
||||
checkError(LTOObj->run(
|
||||
[&](size_t Task) {
|
||||
|
@ -21,7 +21,8 @@
|
||||
#ifndef LLD_ELF_LTO_H
|
||||
#define LLD_ELF_LTO_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -50,6 +51,7 @@ private:
|
||||
std::unique_ptr<llvm::lto::LTO> LTOObj;
|
||||
std::vector<SmallString<0>> Buff;
|
||||
std::vector<std::unique_ptr<MemoryBuffer>> Files;
|
||||
llvm::DenseSet<StringRef> UsedStartStop;
|
||||
};
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
1399
ELF/LinkerScript.cpp
1399
ELF/LinkerScript.cpp
File diff suppressed because it is too large
Load Diff
@ -13,10 +13,11 @@
|
||||
#include "Config.h"
|
||||
#include "Strings.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <cstddef>
|
||||
@ -28,31 +29,39 @@
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class DefinedCommon;
|
||||
class SymbolBody;
|
||||
class Defined;
|
||||
class Symbol;
|
||||
class InputSectionBase;
|
||||
class InputSection;
|
||||
class OutputSection;
|
||||
class OutputSectionFactory;
|
||||
class InputSectionBase;
|
||||
class SectionBase;
|
||||
|
||||
// This represents an r-value in the linker script.
|
||||
struct ExprValue {
|
||||
SectionBase *Sec;
|
||||
uint64_t Val;
|
||||
bool ForceAbsolute;
|
||||
uint64_t Alignment = 1;
|
||||
std::string Loc;
|
||||
|
||||
ExprValue(SectionBase *Sec, bool ForceAbsolute, uint64_t Val,
|
||||
const Twine &Loc)
|
||||
: Sec(Sec), Val(Val), ForceAbsolute(ForceAbsolute), Loc(Loc.str()) {}
|
||||
ExprValue(SectionBase *Sec, uint64_t Val, const Twine &Loc)
|
||||
: ExprValue(Sec, false, Val, Loc) {}
|
||||
ExprValue(uint64_t Val) : ExprValue(nullptr, Val, "") {}
|
||||
: Sec(Sec), ForceAbsolute(ForceAbsolute), Val(Val), Loc(Loc.str()) {}
|
||||
|
||||
ExprValue(uint64_t Val) : ExprValue(nullptr, false, Val, "") {}
|
||||
|
||||
bool isAbsolute() const { return ForceAbsolute || Sec == nullptr; }
|
||||
uint64_t getValue() const;
|
||||
uint64_t getSecAddr() const;
|
||||
uint64_t getSectionOffset() const;
|
||||
|
||||
// If a value is relative to a section, it has a non-null Sec.
|
||||
SectionBase *Sec;
|
||||
|
||||
// True if this expression is enclosed in ABSOLUTE().
|
||||
// This flag affects the return value of getValue().
|
||||
bool ForceAbsolute;
|
||||
|
||||
uint64_t Val;
|
||||
uint64_t Alignment = 1;
|
||||
|
||||
// Original source location. Used for error messages.
|
||||
std::string Loc;
|
||||
};
|
||||
|
||||
// This represents an expression in the linker script.
|
||||
@ -66,8 +75,8 @@ enum SectionsCommandKind {
|
||||
AssignmentKind, // . = expr or <sym> = expr
|
||||
OutputSectionKind,
|
||||
InputSectionKind,
|
||||
AssertKind, // ASSERT(expr)
|
||||
BytesDataKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
|
||||
AssertKind, // ASSERT(expr)
|
||||
ByteKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
|
||||
};
|
||||
|
||||
struct BaseCommand {
|
||||
@ -80,11 +89,13 @@ struct SymbolAssignment : BaseCommand {
|
||||
SymbolAssignment(StringRef Name, Expr E, std::string Loc)
|
||||
: BaseCommand(AssignmentKind), Name(Name), Expression(E), Location(Loc) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
static bool classof(const BaseCommand *C) {
|
||||
return C->Kind == AssignmentKind;
|
||||
}
|
||||
|
||||
// The LHS of an expression. Name is either a symbol name or ".".
|
||||
StringRef Name;
|
||||
SymbolBody *Sym = nullptr;
|
||||
Defined *Sym = nullptr;
|
||||
|
||||
// The RHS of an expression.
|
||||
Expr Expression;
|
||||
@ -114,37 +125,6 @@ struct MemoryRegion {
|
||||
uint32_t NegFlags;
|
||||
};
|
||||
|
||||
struct OutputSectionCommand : BaseCommand {
|
||||
OutputSectionCommand(StringRef Name)
|
||||
: BaseCommand(OutputSectionKind), Name(Name) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
|
||||
OutputSection *Sec = nullptr;
|
||||
MemoryRegion *MemRegion = nullptr;
|
||||
StringRef Name;
|
||||
Expr AddrExpr;
|
||||
Expr AlignExpr;
|
||||
Expr LMAExpr;
|
||||
Expr SubalignExpr;
|
||||
std::vector<BaseCommand *> Commands;
|
||||
std::vector<StringRef> Phdrs;
|
||||
llvm::Optional<uint32_t> Filler;
|
||||
ConstraintKind Constraint = ConstraintKind::NoConstraint;
|
||||
std::string Location;
|
||||
std::string MemoryRegionName;
|
||||
bool Noload = false;
|
||||
|
||||
template <class ELFT> void finalize();
|
||||
template <class ELFT> void writeTo(uint8_t *Buf);
|
||||
template <class ELFT> void maybeCompress();
|
||||
uint32_t getFiller();
|
||||
|
||||
void sort(std::function<int(InputSectionBase *S)> Order);
|
||||
void sortInitFini();
|
||||
void sortCtorsDtors();
|
||||
};
|
||||
|
||||
// This struct represents one section match pattern in SECTIONS() command.
|
||||
// It can optionally have negative match pattern for EXCLUDED_FILE command.
|
||||
// Also it may be surrounded with SORT() command, so contains sorting rules.
|
||||
@ -158,11 +138,14 @@ struct SectionPattern {
|
||||
SortSectionPolicy SortInner;
|
||||
};
|
||||
|
||||
class ThunkSection;
|
||||
struct InputSectionDescription : BaseCommand {
|
||||
InputSectionDescription(StringRef FilePattern)
|
||||
: BaseCommand(InputSectionKind), FilePat(FilePattern) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
static bool classof(const BaseCommand *C) {
|
||||
return C->Kind == InputSectionKind;
|
||||
}
|
||||
|
||||
StringMatcher FilePat;
|
||||
|
||||
@ -171,23 +154,28 @@ struct InputSectionDescription : BaseCommand {
|
||||
std::vector<SectionPattern> SectionPatterns;
|
||||
|
||||
std::vector<InputSection *> Sections;
|
||||
|
||||
// Temporary record of synthetic ThunkSection instances and the pass that
|
||||
// they were created in. This is used to insert newly created ThunkSections
|
||||
// into Sections at the end of a createThunks() pass.
|
||||
std::vector<std::pair<ThunkSection *, uint32_t>> ThunkSections;
|
||||
};
|
||||
|
||||
// Represents an ASSERT().
|
||||
struct AssertCommand : BaseCommand {
|
||||
AssertCommand(Expr E) : BaseCommand(AssertKind), Expression(E) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
static bool classof(const BaseCommand *C) { return C->Kind == AssertKind; }
|
||||
|
||||
Expr Expression;
|
||||
};
|
||||
|
||||
// Represents BYTE(), SHORT(), LONG(), or QUAD().
|
||||
struct BytesDataCommand : BaseCommand {
|
||||
BytesDataCommand(Expr E, unsigned Size)
|
||||
: BaseCommand(BytesDataKind), Expression(E), Size(Size) {}
|
||||
struct ByteCommand : BaseCommand {
|
||||
ByteCommand(Expr E, unsigned Size)
|
||||
: BaseCommand(ByteKind), Expression(E), Size(Size) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
static bool classof(const BaseCommand *C) { return C->Kind == ByteKind; }
|
||||
|
||||
Expr Expression;
|
||||
unsigned Offset;
|
||||
@ -196,108 +184,105 @@ struct BytesDataCommand : BaseCommand {
|
||||
|
||||
struct PhdrsCommand {
|
||||
StringRef Name;
|
||||
unsigned Type;
|
||||
bool HasFilehdr;
|
||||
bool HasPhdrs;
|
||||
unsigned Flags;
|
||||
Expr LMAExpr;
|
||||
unsigned Type = llvm::ELF::PT_NULL;
|
||||
bool HasFilehdr = false;
|
||||
bool HasPhdrs = false;
|
||||
llvm::Optional<unsigned> Flags;
|
||||
Expr LMAExpr = nullptr;
|
||||
};
|
||||
|
||||
// ScriptConfiguration holds linker script parse results.
|
||||
struct ScriptConfiguration {
|
||||
// Used to assign addresses to sections.
|
||||
std::vector<BaseCommand *> Commands;
|
||||
class LinkerScript final {
|
||||
// Temporary state used in processSectionCommands() and assignAddresses()
|
||||
// that must be reinitialized for each call to the above functions, and must
|
||||
// not be used outside of the scope of a call to the above functions.
|
||||
struct AddressState {
|
||||
AddressState();
|
||||
uint64_t ThreadBssOffset = 0;
|
||||
OutputSection *OutSec = nullptr;
|
||||
MemoryRegion *MemRegion = nullptr;
|
||||
llvm::DenseMap<const MemoryRegion *, uint64_t> MemRegionOffset;
|
||||
std::function<uint64_t()> LMAOffset;
|
||||
};
|
||||
|
||||
// Used to assign sections to headers.
|
||||
llvm::DenseMap<StringRef, OutputSection *> NameToOutputSection;
|
||||
|
||||
void addSymbol(SymbolAssignment *Cmd);
|
||||
void assignSymbol(SymbolAssignment *Cmd, bool InSec);
|
||||
void setDot(Expr E, const Twine &Loc, bool InSec);
|
||||
|
||||
std::vector<InputSection *>
|
||||
computeInputSections(const InputSectionDescription *,
|
||||
const llvm::DenseMap<SectionBase *, int> &Order);
|
||||
|
||||
std::vector<InputSection *>
|
||||
createInputSectionList(OutputSection &Cmd,
|
||||
const llvm::DenseMap<SectionBase *, int> &Order);
|
||||
|
||||
std::vector<size_t> getPhdrIndices(OutputSection *Sec);
|
||||
|
||||
MemoryRegion *findMemoryRegion(OutputSection *Sec);
|
||||
|
||||
void switchTo(OutputSection *Sec);
|
||||
uint64_t advance(uint64_t Size, unsigned Align);
|
||||
void output(InputSection *Sec);
|
||||
|
||||
void assignOffsets(OutputSection *Sec);
|
||||
|
||||
// Ctx captures the local AddressState and makes it accessible
|
||||
// deliberately. This is needed as there are some cases where we cannot just
|
||||
// thread the current state through to a lambda function created by the
|
||||
// script parser.
|
||||
// This should remain a plain pointer as its lifetime is smaller than
|
||||
// LinkerScript.
|
||||
AddressState *Ctx = nullptr;
|
||||
|
||||
OutputSection *Aether;
|
||||
|
||||
uint64_t Dot;
|
||||
|
||||
public:
|
||||
OutputSection *createOutputSection(StringRef Name, StringRef Location);
|
||||
OutputSection *getOrCreateOutputSection(StringRef Name);
|
||||
|
||||
bool hasPhdrsCommands() { return !PhdrsCommands.empty(); }
|
||||
uint64_t getDot() { return Dot; }
|
||||
void discard(ArrayRef<InputSection *> V);
|
||||
|
||||
ExprValue getSymbolValue(StringRef Name, const Twine &Loc);
|
||||
|
||||
void addOrphanSections();
|
||||
void removeEmptyCommands();
|
||||
void adjustSectionsBeforeSorting();
|
||||
void adjustSectionsAfterSorting();
|
||||
|
||||
std::vector<PhdrEntry *> createPhdrs();
|
||||
bool needsInterpSection();
|
||||
|
||||
bool shouldKeep(InputSectionBase *S);
|
||||
void assignAddresses();
|
||||
void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
|
||||
void processSectionCommands();
|
||||
|
||||
// SECTIONS command list.
|
||||
std::vector<BaseCommand *> SectionCommands;
|
||||
|
||||
// PHDRS command list.
|
||||
std::vector<PhdrsCommand> PhdrsCommands;
|
||||
|
||||
bool HasSections = false;
|
||||
bool HasSectionsCommand = false;
|
||||
bool ErrorOnMissingSection = false;
|
||||
|
||||
// List of section patterns specified with KEEP commands. They will
|
||||
// be kept even if they are unused and --gc-sections is specified.
|
||||
std::vector<InputSectionDescription *> KeptSections;
|
||||
|
||||
// A map from memory region name to a memory region descriptor.
|
||||
llvm::DenseMap<llvm::StringRef, MemoryRegion> MemoryRegions;
|
||||
llvm::MapVector<llvm::StringRef, MemoryRegion *> MemoryRegions;
|
||||
|
||||
// A list of symbols referenced by the script.
|
||||
std::vector<llvm::StringRef> ReferencedSymbols;
|
||||
};
|
||||
|
||||
class LinkerScript final {
|
||||
// Temporary state used in processCommands() and assignAddresses()
|
||||
// that must be reinitialized for each call to the above functions, and must
|
||||
// not be used outside of the scope of a call to the above functions.
|
||||
struct AddressState {
|
||||
uint64_t ThreadBssOffset = 0;
|
||||
OutputSection *OutSec = nullptr;
|
||||
MemoryRegion *MemRegion = nullptr;
|
||||
llvm::DenseMap<const MemoryRegion *, uint64_t> MemRegionOffset;
|
||||
std::function<uint64_t()> LMAOffset;
|
||||
AddressState(const ScriptConfiguration &Opt);
|
||||
};
|
||||
llvm::DenseMap<OutputSection *, OutputSectionCommand *> SecToCommand;
|
||||
llvm::DenseMap<StringRef, OutputSectionCommand *> NameToOutputSectionCommand;
|
||||
|
||||
void assignSymbol(SymbolAssignment *Cmd, bool InSec);
|
||||
void setDot(Expr E, const Twine &Loc, bool InSec);
|
||||
|
||||
std::vector<InputSection *>
|
||||
computeInputSections(const InputSectionDescription *);
|
||||
|
||||
std::vector<InputSectionBase *>
|
||||
createInputSectionList(OutputSectionCommand &Cmd);
|
||||
|
||||
std::vector<size_t> getPhdrIndices(OutputSectionCommand *Cmd);
|
||||
size_t getPhdrIndex(const Twine &Loc, StringRef PhdrName);
|
||||
|
||||
MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd);
|
||||
|
||||
void switchTo(OutputSection *Sec);
|
||||
uint64_t advance(uint64_t Size, unsigned Align);
|
||||
void output(InputSection *Sec);
|
||||
void process(BaseCommand &Base);
|
||||
|
||||
AddressState *CurAddressState = nullptr;
|
||||
OutputSection *Aether;
|
||||
|
||||
uint64_t Dot;
|
||||
|
||||
public:
|
||||
bool ErrorOnMissingSection = false;
|
||||
OutputSectionCommand *createOutputSectionCommand(StringRef Name,
|
||||
StringRef Location);
|
||||
OutputSectionCommand *getOrCreateOutputSectionCommand(StringRef Name);
|
||||
|
||||
OutputSectionCommand *getCmd(OutputSection *Sec) const;
|
||||
bool hasPhdrsCommands() { return !Opt.PhdrsCommands.empty(); }
|
||||
uint64_t getDot() { return Dot; }
|
||||
void discard(ArrayRef<InputSectionBase *> V);
|
||||
|
||||
ExprValue getSymbolValue(const Twine &Loc, StringRef S);
|
||||
bool isDefined(StringRef S);
|
||||
|
||||
void fabricateDefaultCommands();
|
||||
void addOrphanSections(OutputSectionFactory &Factory);
|
||||
void removeEmptyCommands();
|
||||
void adjustSectionsBeforeSorting();
|
||||
void adjustSectionsAfterSorting();
|
||||
|
||||
std::vector<PhdrEntry> createPhdrs();
|
||||
bool ignoreInterpSection();
|
||||
|
||||
bool shouldKeep(InputSectionBase *S);
|
||||
void assignOffsets(OutputSectionCommand *Cmd);
|
||||
void processNonSectionCommands();
|
||||
void assignAddresses();
|
||||
void allocateHeaders(std::vector<PhdrEntry> &Phdrs);
|
||||
void addSymbol(SymbolAssignment *Cmd);
|
||||
void processCommands(OutputSectionFactory &Factory);
|
||||
|
||||
// Parsed linker script configurations are set to this struct.
|
||||
ScriptConfiguration Opt;
|
||||
};
|
||||
|
||||
extern LinkerScript *Script;
|
||||
|
||||
} // end namespace elf
|
||||
|
@ -25,8 +25,9 @@
|
||||
#include "OutputSections.h"
|
||||
#include "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Threads.h"
|
||||
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -35,46 +36,57 @@ using namespace llvm::object;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
typedef DenseMap<const SectionBase *, SmallVector<DefinedRegular *, 4>>
|
||||
SymbolMapTy;
|
||||
typedef DenseMap<const SectionBase *, SmallVector<Symbol *, 4>> SymbolMapTy;
|
||||
|
||||
// Print out the first three columns of a line.
|
||||
template <class ELFT>
|
||||
static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
|
||||
uint64_t Align) {
|
||||
int W = ELFT::Is64Bits ? 16 : 8;
|
||||
int W = Config->Is64 ? 16 : 8;
|
||||
OS << format("%0*llx %0*llx %5lld ", W, Addr, W, Size, Align);
|
||||
}
|
||||
|
||||
static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
|
||||
|
||||
// Returns a list of all symbols that we want to print out.
|
||||
template <class ELFT> std::vector<DefinedRegular *> getSymbols() {
|
||||
std::vector<DefinedRegular *> V;
|
||||
for (elf::ObjectFile<ELFT> *File : Symtab<ELFT>::X->getObjectFiles())
|
||||
for (SymbolBody *B : File->getSymbols())
|
||||
if (B->File == File && !B->isSection())
|
||||
if (auto *Sym = dyn_cast<DefinedRegular>(B))
|
||||
if (Sym->Section && Sym->Section->Live)
|
||||
V.push_back(Sym);
|
||||
static std::vector<Symbol *> getSymbols() {
|
||||
std::vector<Symbol *> V;
|
||||
for (InputFile *File : ObjectFiles) {
|
||||
for (Symbol *B : File->getSymbols()) {
|
||||
if (auto *SS = dyn_cast<SharedSymbol>(B))
|
||||
if (SS->CopyRelSec || SS->NeedsPltAddr)
|
||||
V.push_back(SS);
|
||||
if (auto *DR = dyn_cast<Defined>(B))
|
||||
if (DR->File == File && !DR->isSection() && DR->Section &&
|
||||
DR->Section->Live)
|
||||
V.push_back(DR);
|
||||
}
|
||||
}
|
||||
return V;
|
||||
}
|
||||
|
||||
// Returns a map from sections to their symbols.
|
||||
template <class ELFT>
|
||||
SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
|
||||
static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> Syms) {
|
||||
SymbolMapTy Ret;
|
||||
for (DefinedRegular *S : Syms)
|
||||
Ret[S->Section].push_back(S);
|
||||
for (Symbol *S : Syms) {
|
||||
if (auto *DR = dyn_cast<Defined>(S)) {
|
||||
Ret[DR->Section].push_back(S);
|
||||
continue;
|
||||
}
|
||||
|
||||
SharedSymbol *SS = cast<SharedSymbol>(S);
|
||||
if (SS->CopyRelSec)
|
||||
Ret[SS->CopyRelSec].push_back(S);
|
||||
else
|
||||
Ret[InX::Plt].push_back(S);
|
||||
}
|
||||
|
||||
// Sort symbols by address. We want to print out symbols in the
|
||||
// order in the output file rather than the order they appeared
|
||||
// in the input files.
|
||||
for (auto &It : Ret) {
|
||||
SmallVectorImpl<DefinedRegular *> &V = It.second;
|
||||
std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) {
|
||||
return A->getVA() < B->getVA();
|
||||
});
|
||||
SmallVectorImpl<Symbol *> &V = It.second;
|
||||
std::sort(V.begin(), V.end(),
|
||||
[](Symbol *A, Symbol *B) { return A->getVA() < B->getVA(); });
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
@ -82,25 +94,22 @@ SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
|
||||
// Construct a map from symbols to their stringified representations.
|
||||
// Demangling symbols (which is what toString() does) is slow, so
|
||||
// we do that in batch using parallel-for.
|
||||
template <class ELFT>
|
||||
DenseMap<DefinedRegular *, std::string>
|
||||
getSymbolStrings(ArrayRef<DefinedRegular *> Syms) {
|
||||
static DenseMap<Symbol *, std::string>
|
||||
getSymbolStrings(ArrayRef<Symbol *> Syms) {
|
||||
std::vector<std::string> Str(Syms.size());
|
||||
parallelForEachN(0, Syms.size(), [&](size_t I) {
|
||||
raw_string_ostream OS(Str[I]);
|
||||
writeHeader<ELFT>(OS, Syms[I]->getVA(), Syms[I]->template getSize<ELFT>(),
|
||||
0);
|
||||
writeHeader(OS, Syms[I]->getVA(), Syms[I]->getSize(), 0);
|
||||
OS << indent(2) << toString(*Syms[I]);
|
||||
});
|
||||
|
||||
DenseMap<DefinedRegular *, std::string> Ret;
|
||||
DenseMap<Symbol *, std::string> Ret;
|
||||
for (size_t I = 0, E = Syms.size(); I < E; ++I)
|
||||
Ret[Syms[I]] = std::move(Str[I]);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void elf::writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script) {
|
||||
void elf::writeMapFile() {
|
||||
if (Config->MapFile.empty())
|
||||
return;
|
||||
|
||||
@ -113,38 +122,32 @@ void elf::writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script) {
|
||||
}
|
||||
|
||||
// Collect symbol info that we want to print out.
|
||||
std::vector<DefinedRegular *> Syms = getSymbols<ELFT>();
|
||||
SymbolMapTy SectionSyms = getSectionSyms<ELFT>(Syms);
|
||||
DenseMap<DefinedRegular *, std::string> SymStr = getSymbolStrings<ELFT>(Syms);
|
||||
std::vector<Symbol *> Syms = getSymbols();
|
||||
SymbolMapTy SectionSyms = getSectionSyms(Syms);
|
||||
DenseMap<Symbol *, std::string> SymStr = getSymbolStrings(Syms);
|
||||
|
||||
// Print out the header line.
|
||||
int W = ELFT::Is64Bits ? 16 : 8;
|
||||
int W = Config->Is64 ? 16 : 8;
|
||||
OS << left_justify("Address", W) << ' ' << left_justify("Size", W)
|
||||
<< " Align Out In Symbol\n";
|
||||
|
||||
// Print out file contents.
|
||||
for (OutputSectionCommand *Cmd : Script) {
|
||||
OutputSection *OSec = Cmd->Sec;
|
||||
writeHeader<ELFT>(OS, OSec->Addr, OSec->Size, OSec->Alignment);
|
||||
for (OutputSection *OSec : OutputSections) {
|
||||
writeHeader(OS, OSec->Addr, OSec->Size, OSec->Alignment);
|
||||
OS << OSec->Name << '\n';
|
||||
|
||||
// Dump symbols for each input section.
|
||||
for (BaseCommand *Base : Cmd->Commands) {
|
||||
for (BaseCommand *Base : OSec->SectionCommands) {
|
||||
auto *ISD = dyn_cast<InputSectionDescription>(Base);
|
||||
if (!ISD)
|
||||
continue;
|
||||
for (InputSection *IS : ISD->Sections) {
|
||||
writeHeader<ELFT>(OS, OSec->Addr + IS->OutSecOff, IS->getSize(),
|
||||
IS->Alignment);
|
||||
writeHeader(OS, OSec->Addr + IS->OutSecOff, IS->getSize(),
|
||||
IS->Alignment);
|
||||
OS << indent(1) << toString(IS) << '\n';
|
||||
for (DefinedRegular *Sym : SectionSyms[IS])
|
||||
for (Symbol *Sym : SectionSyms[IS])
|
||||
OS << SymStr[Sym] << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void elf::writeMapFile<ELF32LE>(ArrayRef<OutputSectionCommand *>);
|
||||
template void elf::writeMapFile<ELF32BE>(ArrayRef<OutputSectionCommand *>);
|
||||
template void elf::writeMapFile<ELF64LE>(ArrayRef<OutputSectionCommand *>);
|
||||
template void elf::writeMapFile<ELF64BE>(ArrayRef<OutputSectionCommand *>);
|
||||
|
@ -10,13 +10,9 @@
|
||||
#ifndef LLD_ELF_MAPFILE_H
|
||||
#define LLD_ELF_MAPFILE_H
|
||||
|
||||
#include <llvm/ADT/ArrayRef.h>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
struct OutputSectionCommand;
|
||||
template <class ELFT>
|
||||
void writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script);
|
||||
void writeMapFile();
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
191
ELF/MarkLive.cpp
191
ELF/MarkLive.cpp
@ -22,13 +22,13 @@
|
||||
|
||||
#include "InputSection.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include <functional>
|
||||
@ -42,15 +42,6 @@ using namespace llvm::support::endian;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
// A resolved relocation. The Sec and Offset fields are set if the relocation
|
||||
// was resolved to an offset within a section.
|
||||
struct ResolvedReloc {
|
||||
InputSectionBase *Sec;
|
||||
uint64_t Offset;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
template <class ELFT>
|
||||
static typename ELFT::uint getAddend(InputSectionBase &Sec,
|
||||
const typename ELFT::Rel &Rel) {
|
||||
@ -70,25 +61,36 @@ static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections;
|
||||
|
||||
template <class ELFT, class RelT>
|
||||
static void resolveReloc(InputSectionBase &Sec, RelT &Rel,
|
||||
std::function<void(ResolvedReloc)> Fn) {
|
||||
SymbolBody &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
|
||||
if (auto *D = dyn_cast<DefinedRegular>(&B)) {
|
||||
if (!D->Section)
|
||||
std::function<void(InputSectionBase *, uint64_t)> Fn) {
|
||||
Symbol &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
|
||||
|
||||
// If a symbol is referenced in a live section, it is used.
|
||||
B.Used = true;
|
||||
if (auto *SS = dyn_cast<SharedSymbol>(&B))
|
||||
if (!SS->isWeak())
|
||||
SS->getFile<ELFT>()->IsNeeded = true;
|
||||
|
||||
if (auto *D = dyn_cast<Defined>(&B)) {
|
||||
auto *RelSec = dyn_cast_or_null<InputSectionBase>(D->Section);
|
||||
if (!RelSec)
|
||||
return;
|
||||
typename ELFT::uint Offset = D->Value;
|
||||
uint64_t Offset = D->Value;
|
||||
if (D->isSection())
|
||||
Offset += getAddend<ELFT>(Sec, Rel);
|
||||
Fn({cast<InputSectionBase>(D->Section), Offset});
|
||||
} else if (auto *U = dyn_cast<Undefined>(&B)) {
|
||||
for (InputSectionBase *Sec : CNamedSections.lookup(U->getName()))
|
||||
Fn({Sec, 0});
|
||||
Fn(RelSec, Offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!B.isDefined())
|
||||
for (InputSectionBase *Sec : CNamedSections.lookup(B.getName()))
|
||||
Fn(Sec, 0);
|
||||
}
|
||||
|
||||
// Calls Fn for each section that Sec refers to via relocations.
|
||||
template <class ELFT>
|
||||
static void forEachSuccessor(InputSection &Sec,
|
||||
std::function<void(ResolvedReloc)> Fn) {
|
||||
static void
|
||||
forEachSuccessor(InputSection &Sec,
|
||||
std::function<void(InputSectionBase *, uint64_t)> Fn) {
|
||||
if (Sec.AreRelocsRela) {
|
||||
for (const typename ELFT::Rela &Rel : Sec.template relas<ELFT>())
|
||||
resolveReloc<ELFT>(Sec, Rel, Fn);
|
||||
@ -96,8 +98,9 @@ static void forEachSuccessor(InputSection &Sec,
|
||||
for (const typename ELFT::Rel &Rel : Sec.template rels<ELFT>())
|
||||
resolveReloc<ELFT>(Sec, Rel, Fn);
|
||||
}
|
||||
|
||||
for (InputSectionBase *IS : Sec.DependentSections)
|
||||
Fn({IS, 0});
|
||||
Fn(IS, 0);
|
||||
}
|
||||
|
||||
// The .eh_frame section is an unfortunate special case.
|
||||
@ -115,9 +118,11 @@ static void forEachSuccessor(InputSection &Sec,
|
||||
// the gc pass. With that we would be able to also gc some sections holding
|
||||
// LSDAs and personality functions if we found that they were unused.
|
||||
template <class ELFT, class RelTy>
|
||||
static void scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
|
||||
std::function<void(ResolvedReloc)> Enqueue) {
|
||||
static void
|
||||
scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
|
||||
std::function<void(InputSectionBase *, uint64_t)> Fn) {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
|
||||
for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) {
|
||||
EhSectionPiece &Piece = EH.Pieces[I];
|
||||
unsigned FirstRelI = Piece.FirstRelocation;
|
||||
@ -126,31 +131,31 @@ static void scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
|
||||
if (read32<E>(Piece.data().data() + 4) == 0) {
|
||||
// This is a CIE, we only need to worry about the first relocation. It is
|
||||
// known to point to the personality function.
|
||||
resolveReloc<ELFT>(EH, Rels[FirstRelI], Enqueue);
|
||||
resolveReloc<ELFT>(EH, Rels[FirstRelI], Fn);
|
||||
continue;
|
||||
}
|
||||
// This is a FDE. The relocations point to the described function or to
|
||||
// a LSDA. We only need to keep the LSDA alive, so ignore anything that
|
||||
// points to executable sections.
|
||||
typename ELFT::uint PieceEnd = Piece.InputOff + Piece.size();
|
||||
typename ELFT::uint PieceEnd = Piece.InputOff + Piece.Size;
|
||||
for (unsigned I2 = FirstRelI, N2 = Rels.size(); I2 < N2; ++I2) {
|
||||
const RelTy &Rel = Rels[I2];
|
||||
if (Rel.r_offset >= PieceEnd)
|
||||
break;
|
||||
resolveReloc<ELFT>(EH, Rels[I2], [&](ResolvedReloc R) {
|
||||
if (!R.Sec || R.Sec == &InputSection::Discarded)
|
||||
return;
|
||||
if (R.Sec->Flags & SHF_EXECINSTR)
|
||||
return;
|
||||
Enqueue({R.Sec, 0});
|
||||
});
|
||||
resolveReloc<ELFT>(EH, Rels[I2],
|
||||
[&](InputSectionBase *Sec, uint64_t Offset) {
|
||||
if (Sec && Sec != &InputSection::Discarded &&
|
||||
!(Sec->Flags & SHF_EXECINSTR))
|
||||
Fn(Sec, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void scanEhFrameSection(EhInputSection &EH,
|
||||
std::function<void(ResolvedReloc)> Enqueue) {
|
||||
static void
|
||||
scanEhFrameSection(EhInputSection &EH,
|
||||
std::function<void(InputSectionBase *, uint64_t)> Fn) {
|
||||
if (!EH.NumRelocations)
|
||||
return;
|
||||
|
||||
@ -159,14 +164,14 @@ static void scanEhFrameSection(EhInputSection &EH,
|
||||
EH.split<ELFT>();
|
||||
|
||||
if (EH.AreRelocsRela)
|
||||
scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Enqueue);
|
||||
scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Fn);
|
||||
else
|
||||
scanEhFrameSection<ELFT>(EH, EH.template rels<ELFT>(), Enqueue);
|
||||
scanEhFrameSection<ELFT>(EH, EH.template rels<ELFT>(), Fn);
|
||||
}
|
||||
|
||||
// We do not garbage-collect two types of sections:
|
||||
// 1) Sections used by the loader (.init, .fini, .ctors, .dtors or .jcr)
|
||||
// 2) Non-allocatable sections which typically contain debugging information
|
||||
// Some sections are used directly by the loader, so they should never be
|
||||
// garbage-collected. This function returns true if a given section is such
|
||||
// section.
|
||||
template <class ELFT> static bool isReserved(InputSectionBase *Sec) {
|
||||
switch (Sec->Type) {
|
||||
case SHT_FINI_ARRAY:
|
||||
@ -175,9 +180,6 @@ template <class ELFT> static bool isReserved(InputSectionBase *Sec) {
|
||||
case SHT_PREINIT_ARRAY:
|
||||
return true;
|
||||
default:
|
||||
if (!(Sec->Flags & SHF_ALLOC))
|
||||
return true;
|
||||
|
||||
StringRef S = Sec->Name;
|
||||
return S.startswith(".ctors") || S.startswith(".dtors") ||
|
||||
S.startswith(".init") || S.startswith(".fini") ||
|
||||
@ -188,72 +190,74 @@ template <class ELFT> static bool isReserved(InputSectionBase *Sec) {
|
||||
// This is the main function of the garbage collector.
|
||||
// Starting from GC-root sections, this function visits all reachable
|
||||
// sections to set their "Live" bits.
|
||||
template <class ELFT> void elf::markLive() {
|
||||
template <class ELFT> static void doGcSections() {
|
||||
SmallVector<InputSection *, 256> Q;
|
||||
CNamedSections.clear();
|
||||
|
||||
auto Enqueue = [&](ResolvedReloc R) {
|
||||
auto Enqueue = [&](InputSectionBase *Sec, uint64_t Offset) {
|
||||
// Skip over discarded sections. This in theory shouldn't happen, because
|
||||
// the ELF spec doesn't allow a relocation to point to a deduplicated
|
||||
// COMDAT section directly. Unfortunately this happens in practice (e.g.
|
||||
// .eh_frame) so we need to add a check.
|
||||
if (R.Sec == &InputSection::Discarded)
|
||||
if (Sec == &InputSection::Discarded)
|
||||
return;
|
||||
|
||||
// We don't gc non alloc sections.
|
||||
if (!(R.Sec->Flags & SHF_ALLOC))
|
||||
return;
|
||||
|
||||
// Usually, a whole section is marked as live or dead, but in mergeable
|
||||
// (splittable) sections, each piece of data has independent liveness bit.
|
||||
// So we explicitly tell it which offset is in use.
|
||||
if (auto *MS = dyn_cast<MergeInputSection>(R.Sec))
|
||||
MS->markLiveAt(R.Offset);
|
||||
if (auto *MS = dyn_cast<MergeInputSection>(Sec))
|
||||
MS->markLiveAt(Offset);
|
||||
|
||||
if (R.Sec->Live)
|
||||
if (Sec->Live)
|
||||
return;
|
||||
R.Sec->Live = true;
|
||||
Sec->Live = true;
|
||||
|
||||
// Add input section to the queue.
|
||||
if (InputSection *S = dyn_cast<InputSection>(R.Sec))
|
||||
if (InputSection *S = dyn_cast<InputSection>(Sec))
|
||||
Q.push_back(S);
|
||||
};
|
||||
|
||||
auto MarkSymbol = [&](const SymbolBody *Sym) {
|
||||
if (auto *D = dyn_cast_or_null<DefinedRegular>(Sym))
|
||||
if (auto *IS = cast_or_null<InputSectionBase>(D->Section))
|
||||
Enqueue({IS, D->Value});
|
||||
auto MarkSymbol = [&](Symbol *Sym) {
|
||||
if (auto *D = dyn_cast_or_null<Defined>(Sym))
|
||||
if (auto *IS = dyn_cast_or_null<InputSectionBase>(D->Section))
|
||||
Enqueue(IS, D->Value);
|
||||
};
|
||||
|
||||
// Add GC root symbols.
|
||||
MarkSymbol(Symtab<ELFT>::X->find(Config->Entry));
|
||||
MarkSymbol(Symtab<ELFT>::X->find(Config->Init));
|
||||
MarkSymbol(Symtab<ELFT>::X->find(Config->Fini));
|
||||
MarkSymbol(Symtab->find(Config->Entry));
|
||||
MarkSymbol(Symtab->find(Config->Init));
|
||||
MarkSymbol(Symtab->find(Config->Fini));
|
||||
for (StringRef S : Config->Undefined)
|
||||
MarkSymbol(Symtab<ELFT>::X->find(S));
|
||||
for (StringRef S : Script->Opt.ReferencedSymbols)
|
||||
MarkSymbol(Symtab<ELFT>::X->find(S));
|
||||
MarkSymbol(Symtab->find(S));
|
||||
for (StringRef S : Script->ReferencedSymbols)
|
||||
MarkSymbol(Symtab->find(S));
|
||||
|
||||
// Preserve externally-visible symbols if the symbols defined by this
|
||||
// file can interrupt other ELF file's symbols at runtime.
|
||||
for (const Symbol *S : Symtab<ELFT>::X->getSymbols())
|
||||
for (Symbol *S : Symtab->getSymbols())
|
||||
if (S->includeInDynsym())
|
||||
MarkSymbol(S->body());
|
||||
MarkSymbol(S);
|
||||
|
||||
// Preserve special sections and those which are specified in linker
|
||||
// script KEEP command.
|
||||
for (InputSectionBase *Sec : InputSections) {
|
||||
// .eh_frame is always marked as live now, but also it can reference to
|
||||
// sections that contain personality. We preserve all non-text sections
|
||||
// referred by .eh_frame here.
|
||||
if (auto *EH = dyn_cast_or_null<EhInputSection>(Sec))
|
||||
// Mark .eh_frame sections as live because there are usually no relocations
|
||||
// that point to .eh_frames. Otherwise, the garbage collector would drop
|
||||
// all of them. We also want to preserve personality routines and LSDA
|
||||
// referenced by .eh_frame sections, so we scan them for that here.
|
||||
if (auto *EH = dyn_cast_or_null<EhInputSection>(Sec)) {
|
||||
EH->Live = true;
|
||||
scanEhFrameSection<ELFT>(*EH, Enqueue);
|
||||
}
|
||||
|
||||
if (Sec->Flags & SHF_LINK_ORDER)
|
||||
continue;
|
||||
if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec))
|
||||
Enqueue({Sec, 0});
|
||||
Enqueue(Sec, 0);
|
||||
else if (isValidCIdentifier(Sec->Name)) {
|
||||
CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec);
|
||||
CNamedSections[Saver.save("__end_" + Sec->Name)].push_back(Sec);
|
||||
CNamedSections[Saver.save("__stop_" + Sec->Name)].push_back(Sec);
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,6 +266,49 @@ template <class ELFT> void elf::markLive() {
|
||||
forEachSuccessor<ELFT>(*Q.pop_back_val(), Enqueue);
|
||||
}
|
||||
|
||||
// Before calling this function, Live bits are off for all
|
||||
// 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) {
|
||||
for (InputSectionBase *Sec : InputSections)
|
||||
Sec->Live = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// The -gc-sections option works only for SHF_ALLOC sections
|
||||
// (sections that are memory-mapped at runtime). So we can
|
||||
// unconditionally make non-SHF_ALLOC sections alive.
|
||||
//
|
||||
// Non SHF_ALLOC sections are not removed even if they are
|
||||
// unreachable through relocations because reachability is not
|
||||
// a good signal whether they are garbage or not (e.g. there is
|
||||
// usually no section referring to a .comment section, but we
|
||||
// want to keep it.)
|
||||
//
|
||||
// Note on SHF_REL{,A}: Such sections reach here only when -r
|
||||
// or -emit-reloc were given. And they are subject of garbage
|
||||
// collection because, if we remove a text section, we also
|
||||
// remove its relocation section.
|
||||
for (InputSectionBase *Sec : InputSections) {
|
||||
bool IsAlloc = (Sec->Flags & SHF_ALLOC);
|
||||
bool IsRel = (Sec->Type == SHT_REL || Sec->Type == SHT_RELA);
|
||||
if (!IsAlloc && !IsRel)
|
||||
Sec->Live = true;
|
||||
}
|
||||
|
||||
// Follow the graph to mark all live sections.
|
||||
doGcSections<ELFT>();
|
||||
|
||||
// Report garbage-collected sections.
|
||||
if (Config->PrintGcSections)
|
||||
for (InputSectionBase *Sec : InputSections)
|
||||
if (!Sec->Live)
|
||||
message("removing unused section from '" + Sec->Name + "' in file '" +
|
||||
Sec->File->getName() + "'");
|
||||
}
|
||||
|
||||
template void elf::markLive<ELF32LE>();
|
||||
template void elf::markLive<ELF32BE>();
|
||||
template void elf::markLive<ELF64LE>();
|
||||
|
177
ELF/Options.td
177
ELF/Options.td
@ -5,7 +5,11 @@ include "llvm/Option/OptParser.td"
|
||||
class F<string name>: Flag<["--", "-"], name>;
|
||||
class J<string name>: Joined<["--", "-"], name>;
|
||||
class S<string name>: Separate<["--", "-"], name>;
|
||||
class JS<string name>: JoinedOrSeparate<["--", "-"], name>;
|
||||
|
||||
multiclass Eq<string name> {
|
||||
def "": Separate<["--", "-"], name>;
|
||||
def _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
|
||||
}
|
||||
|
||||
def auxiliary: S<"auxiliary">, HelpText<"Set DT_AUXILIARY field to the specified name">;
|
||||
|
||||
@ -22,21 +26,24 @@ def build_id: F<"build-id">, HelpText<"Generate build ID note">;
|
||||
|
||||
def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">;
|
||||
|
||||
def compress_debug_sections : J<"compress-debug-sections=">,
|
||||
defm compress_debug_sections : Eq<"compress-debug-sections">,
|
||||
HelpText<"Compress DWARF debug sections">;
|
||||
|
||||
def defsym: J<"defsym=">, HelpText<"Define a symbol alias">;
|
||||
defm defsym: Eq<"defsym">, HelpText<"Define a symbol alias">;
|
||||
|
||||
def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
|
||||
HelpText<"Add a directory to the library search path">;
|
||||
defm library_path: Eq<"library-path">,
|
||||
HelpText<"Add a directory to the library search path">, MetaVarName<"<dir>">;
|
||||
|
||||
def O: Joined<["-"], "O">, HelpText<"Optimize output file size">;
|
||||
def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">;
|
||||
|
||||
def Tbss: S<"Tbss">, HelpText<"Same as --section-start with .bss as the sectionname">;
|
||||
defm Tbss: Eq<"Tbss">,
|
||||
HelpText<"Same as --section-start with .bss as the sectionname">;
|
||||
|
||||
def Tdata: S<"Tdata">, HelpText<"Same as --section-start with .data as the sectionname">;
|
||||
defm Tdata: Eq<"Tdata">,
|
||||
HelpText<"Same as --section-start with .data as the sectionname">;
|
||||
|
||||
def Ttext: S<"Ttext">, HelpText<"Same as --section-start with .text as the sectionname">;
|
||||
defm Ttext: Eq<"Ttext">,
|
||||
HelpText<"Same as --section-start with .text as the sectionname">;
|
||||
|
||||
def allow_multiple_definition: F<"allow-multiple-definition">,
|
||||
HelpText<"Allow multiple definitions">;
|
||||
@ -44,6 +51,9 @@ def allow_multiple_definition: F<"allow-multiple-definition">,
|
||||
def as_needed: F<"as-needed">,
|
||||
HelpText<"Only set DT_NEEDED for shared libraries if used">;
|
||||
|
||||
// -chroot doesn't have a help text because it is an internal option.
|
||||
def chroot: S<"chroot">;
|
||||
|
||||
def color_diagnostics: F<"color-diagnostics">,
|
||||
HelpText<"Use colors in diagnostics">;
|
||||
|
||||
@ -69,7 +79,7 @@ def discard_none: F<"discard-none">,
|
||||
def dynamic_linker: S<"dynamic-linker">,
|
||||
HelpText<"Which dynamic linker to use">;
|
||||
|
||||
def dynamic_list: S<"dynamic-list">,
|
||||
defm dynamic_list: Eq<"dynamic-list">,
|
||||
HelpText<"Read a list of dynamic symbols">;
|
||||
|
||||
def eh_frame_hdr: F<"eh-frame-hdr">,
|
||||
@ -83,37 +93,42 @@ def enable_new_dtags: F<"enable-new-dtags">,
|
||||
def end_lib: F<"end-lib">,
|
||||
HelpText<"End a grouping of objects that should be treated as if they were together in an archive">;
|
||||
|
||||
def entry: S<"entry">, MetaVarName<"<entry>">,
|
||||
HelpText<"Name of entry point symbol">;
|
||||
defm entry: Eq<"entry">, HelpText<"Name of entry point symbol">,
|
||||
MetaVarName<"<entry>">;
|
||||
|
||||
def error_limit: S<"error-limit">,
|
||||
defm error_limit: Eq<"error-limit">,
|
||||
HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
|
||||
|
||||
def error_unresolved_symbols: F<"error-unresolved-symbols">,
|
||||
HelpText<"Report unresolved symbols as errors">;
|
||||
|
||||
def exclude_libs: S<"exclude-libs">,
|
||||
defm exclude_libs: Eq<"exclude-libs">,
|
||||
HelpText<"Exclude static libraries from automatic export">;
|
||||
|
||||
def export_dynamic: F<"export-dynamic">,
|
||||
HelpText<"Put symbols in the dynamic symbol table">;
|
||||
|
||||
def export_dynamic_symbol: S<"export-dynamic-symbol">,
|
||||
defm export_dynamic_symbol: Eq<"export-dynamic-symbol">,
|
||||
HelpText<"Put a symbol in the dynamic symbol table">;
|
||||
|
||||
def fatal_warnings: F<"fatal-warnings">,
|
||||
HelpText<"Treat warnings as errors">;
|
||||
|
||||
def filter: J<"filter=">, HelpText<"Set DT_FILTER field to the specified name">;
|
||||
defm filter: Eq<"filter">,
|
||||
HelpText<"Set DT_FILTER field to the specified name">;
|
||||
|
||||
def fini: S<"fini">, MetaVarName<"<symbol>">,
|
||||
HelpText<"Specify a finalizer function">;
|
||||
defm fini: Eq<"fini">,
|
||||
HelpText<"Specify a finalizer function">, MetaVarName<"<symbol>">;
|
||||
|
||||
def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
|
||||
HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">;
|
||||
|
||||
def full_shutdown : F<"full-shutdown">,
|
||||
HelpText<"Perform a full shutdown instead of calling _exit">;
|
||||
|
||||
def format: J<"format=">, MetaVarName<"<input-format>">,
|
||||
HelpText<"Change the input format of the inputs following this option">;
|
||||
defm format: Eq<"format">,
|
||||
HelpText<"Change the input format of the inputs following this option">,
|
||||
MetaVarName<"<input-format>">;
|
||||
|
||||
def gc_sections: F<"gc-sections">,
|
||||
HelpText<"Enable garbage collection of unused sections">;
|
||||
@ -121,29 +136,35 @@ def gc_sections: F<"gc-sections">,
|
||||
def gdb_index: F<"gdb-index">,
|
||||
HelpText<"Generate .gdb_index section">;
|
||||
|
||||
def hash_style: S<"hash-style">,
|
||||
defm hash_style: Eq<"hash-style">,
|
||||
HelpText<"Specify hash style (sysv, gnu or both)">;
|
||||
|
||||
def help: F<"help">, HelpText<"Print option help">;
|
||||
|
||||
def icf_all: F<"icf=all">, HelpText<"Enable identical code folding">;
|
||||
|
||||
def icf_data: F<"icf-data">,
|
||||
HelpText<"Enable ICF to also fold identical read only data">;
|
||||
|
||||
def icf_none: F<"icf=none">, HelpText<"Disable identical code folding">;
|
||||
|
||||
def image_base : J<"image-base=">, HelpText<"Set the base address">;
|
||||
defm image_base : Eq<"image-base">, HelpText<"Set the base address">;
|
||||
|
||||
def init: S<"init">, MetaVarName<"<symbol>">,
|
||||
HelpText<"Specify an initializer function">;
|
||||
defm init: Eq<"init">, HelpText<"Specify an initializer function">,
|
||||
MetaVarName<"<symbol>">;
|
||||
|
||||
def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
|
||||
HelpText<"Root name of library to use">;
|
||||
defm library: Eq<"library">, HelpText<"Root name of library to use">,
|
||||
MetaVarName<"<libName>">;
|
||||
|
||||
def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
|
||||
HelpText<"Optimization level for LTO">;
|
||||
|
||||
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
|
||||
|
||||
def Map: JS<"Map">, HelpText<"Print a link map to the specified file">;
|
||||
defm Map: Eq<"Map">, HelpText<"Print a link map to the specified file">;
|
||||
|
||||
def merge_exidx_entries: F<"merge-exidx-entries">,
|
||||
HelpText<"Enable merging .ARM.exidx entries">;
|
||||
|
||||
def nostdlib: F<"nostdlib">,
|
||||
HelpText<"Only search directories specified on the command line">;
|
||||
@ -163,15 +184,24 @@ def no_demangle: F<"no-demangle">,
|
||||
def no_dynamic_linker: F<"no-dynamic-linker">,
|
||||
HelpText<"Inhibit output of .interp section">;
|
||||
|
||||
def no_eh_frame_hdr: F<"no-eh-frame-hdr">,
|
||||
HelpText<"Do not create .eh_frame_hdr section">;
|
||||
|
||||
def no_export_dynamic: F<"no-export-dynamic">;
|
||||
def no_fatal_warnings: F<"no-fatal-warnings">;
|
||||
|
||||
def no_gc_sections: F<"no-gc-sections">,
|
||||
HelpText<"Disable garbage collection of unused sections">;
|
||||
|
||||
def no_gdb_index: F<"no-gdb-index">,
|
||||
HelpText<"Do not generate .gdb_index section">;
|
||||
|
||||
def no_gnu_unique: F<"no-gnu-unique">,
|
||||
HelpText<"Disable STB_GNU_UNIQUE symbol binding">;
|
||||
|
||||
def no_merge_exidx_entries: F<"no-merge-exidx-entries">,
|
||||
HelpText<"Disable merging .ARM.exidx entries">;
|
||||
|
||||
def no_threads: F<"no-threads">,
|
||||
HelpText<"Do not run the linker multi-threaded">;
|
||||
|
||||
@ -183,7 +213,14 @@ def noinhibit_exec: F<"noinhibit-exec">,
|
||||
|
||||
def nopie: F<"nopie">, HelpText<"Do not create a position independent executable">;
|
||||
|
||||
def no_rosegment: F<"no-rosegment">, HelpText<"Do not put read-only non-executable sections in their own segment">;
|
||||
def no_omagic: Flag<["--"], "no-omagic">, MetaVarName<"<magic>">,
|
||||
HelpText<"Do not set the text data sections to be writable">;
|
||||
|
||||
def no_print_gc_sections: F<"no-print-gc-sections">,
|
||||
HelpText<"Do not list removed unused sections">;
|
||||
|
||||
def no_rosegment: F<"no-rosegment">,
|
||||
HelpText<"Do not put read-only non-executable sections in their own segment">;
|
||||
|
||||
def no_undefined: F<"no-undefined">,
|
||||
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
|
||||
@ -200,6 +237,12 @@ def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
|
||||
def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
|
||||
HelpText<"Set the text and data sections to be readable and writable">;
|
||||
|
||||
defm orphan_handling: Eq<"orphan-handling">,
|
||||
HelpText<"Control how orphan sections are handled when linker script used">;
|
||||
|
||||
def pack_dyn_relocs_eq: J<"pack-dyn-relocs=">, MetaVarName<"<format>">,
|
||||
HelpText<"Pack dynamic relocations in the given format (none or android)">;
|
||||
|
||||
def pie: F<"pie">, HelpText<"Create a position independent executable">;
|
||||
|
||||
def print_gc_sections: F<"print-gc-sections">,
|
||||
@ -208,26 +251,28 @@ def print_gc_sections: F<"print-gc-sections">,
|
||||
def print_map: F<"print-map">,
|
||||
HelpText<"Print a link map to the standard output">;
|
||||
|
||||
def reproduce: S<"reproduce">,
|
||||
defm reproduce: Eq<"reproduce">,
|
||||
HelpText<"Dump linker invocation and input files for debugging">;
|
||||
|
||||
def rpath: S<"rpath">, HelpText<"Add a DT_RUNPATH to the output">;
|
||||
defm rpath: Eq<"rpath">, HelpText<"Add a DT_RUNPATH to the output">;
|
||||
|
||||
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
|
||||
|
||||
def retain_symbols_file: J<"retain-symbols-file=">, MetaVarName<"<file>">,
|
||||
HelpText<"Retain only the symbols listed in the file">;
|
||||
defm retain_symbols_file: Eq<"retain-symbols-file">,
|
||||
HelpText<"Retain only the symbols listed in the file">,
|
||||
MetaVarName<"<file>">;
|
||||
|
||||
def script: S<"script">, HelpText<"Read linker script">;
|
||||
defm script: Eq<"script">, HelpText<"Read linker script">;
|
||||
|
||||
def section_start: S<"section-start">, MetaVarName<"<address>">,
|
||||
HelpText<"Set address of section">;
|
||||
|
||||
def shared: F<"shared">, HelpText<"Build a shared object">;
|
||||
|
||||
def soname: J<"soname=">, HelpText<"Set DT_SONAME">;
|
||||
defm soname: Eq<"soname">, HelpText<"Set DT_SONAME">;
|
||||
|
||||
def sort_section: S<"sort-section">, HelpText<"Specifies sections sorting rule when linkerscript is used">;
|
||||
defm sort_section: Eq<"sort-section">,
|
||||
HelpText<"Specifies sections sorting rule when linkerscript is used">;
|
||||
|
||||
def start_lib: F<"start-lib">,
|
||||
HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">;
|
||||
@ -239,27 +284,29 @@ def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
|
||||
def symbol_ordering_file: S<"symbol-ordering-file">,
|
||||
HelpText<"Layout sections in the order specified by symbol file">;
|
||||
|
||||
def sysroot: J<"sysroot=">, HelpText<"Set the system root">;
|
||||
defm sysroot: Eq<"sysroot">, HelpText<"Set the system root">;
|
||||
|
||||
def target1_rel: F<"target1-rel">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_REL32">;
|
||||
|
||||
def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32">;
|
||||
|
||||
def target2: J<"target2=">, MetaVarName<"<type>">, HelpText<"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">;
|
||||
defm target2: Eq<"target2">,
|
||||
HelpText<"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
|
||||
MetaVarName<"<type>">;
|
||||
|
||||
def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
|
||||
|
||||
def trace: F<"trace">, HelpText<"Print the names of the input files">;
|
||||
|
||||
def trace_symbol : S<"trace-symbol">, HelpText<"Trace references to symbols">;
|
||||
defm trace_symbol : Eq<"trace-symbol">, HelpText<"Trace references to symbols">;
|
||||
|
||||
def undefined: S<"undefined">,
|
||||
defm undefined: Eq<"undefined">,
|
||||
HelpText<"Force undefined symbol during linking">;
|
||||
|
||||
def unresolved_symbols: J<"unresolved-symbols=">,
|
||||
defm unresolved_symbols: Eq<"unresolved-symbols">,
|
||||
HelpText<"Determine how to handle unresolved symbols">;
|
||||
|
||||
def rsp_quoting: J<"rsp-quoting=">,
|
||||
defm rsp_quoting: Eq<"rsp-quoting">,
|
||||
HelpText<"Quoting style for response files. Values supported: windows|posix">;
|
||||
|
||||
def v: Flag<["-"], "v">, HelpText<"Display the version number">;
|
||||
@ -268,8 +315,7 @@ def verbose: F<"verbose">, HelpText<"Verbose mode">;
|
||||
|
||||
def version: F<"version">, HelpText<"Display the version number and exit">;
|
||||
|
||||
def version_script: S<"version-script">,
|
||||
HelpText<"Read a version script">;
|
||||
defm version_script: Eq<"version-script">, HelpText<"Read a version script">;
|
||||
|
||||
def warn_common: F<"warn-common">,
|
||||
HelpText<"Warn about duplicate common symbols">;
|
||||
@ -280,8 +326,8 @@ def warn_unresolved_symbols: F<"warn-unresolved-symbols">,
|
||||
def whole_archive: F<"whole-archive">,
|
||||
HelpText<"Force load of all members in a static library">;
|
||||
|
||||
def wrap: S<"wrap">, MetaVarName<"<symbol>">,
|
||||
HelpText<"Use wrapper functions for symbol">;
|
||||
defm wrap: Eq<"wrap">, HelpText<"Use wrapper functions for symbol">,
|
||||
MetaVarName<"<symbol>">;
|
||||
|
||||
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
|
||||
HelpText<"Linker option extensions">;
|
||||
@ -293,60 +339,36 @@ def alias_Bdynamic_dy: F<"dy">, Alias<Bdynamic>;
|
||||
def alias_Bstatic_dn: F<"dn">, Alias<Bstatic>;
|
||||
def alias_Bstatic_non_shared: F<"non_shared">, Alias<Bstatic>;
|
||||
def alias_Bstatic_static: F<"static">, Alias<Bstatic>;
|
||||
def alias_L__library_path: J<"library-path=">, Alias<L>;
|
||||
def alias_define_common_d: Flag<["-"], "d">, Alias<define_common>;
|
||||
def alias_define_common_dc: F<"dc">, Alias<define_common>;
|
||||
def alias_define_common_dp: F<"dp">, Alias<define_common>;
|
||||
def alias_defsym: S<"defsym">, Alias<defsym>;
|
||||
def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
|
||||
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
|
||||
def alias_dynamic_list: J<"dynamic-list=">, Alias<dynamic_list>;
|
||||
def alias_emit_relocs: Flag<["-"], "q">, Alias<emit_relocs>;
|
||||
def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
|
||||
def alias_entry_entry: J<"entry=">, Alias<entry>;
|
||||
def alias_error_limit: J<"error-limit=">, Alias<error_limit>;
|
||||
def alias_exclude_libs: J<"exclude-libs=">, Alias<exclude_libs>;
|
||||
def alias_export_dynamic_E: Flag<["-"], "E">, Alias<export_dynamic>;
|
||||
def alias_export_dynamic_symbol: J<"export-dynamic-symbol=">,
|
||||
Alias<export_dynamic_symbol>;
|
||||
def alias_filter: Separate<["-"], "F">, Alias<filter>;
|
||||
def alias_fini_fini: J<"fini=">, Alias<fini>;
|
||||
def alias_format_b: S<"b">, Alias<format>;
|
||||
def alias_hash_style_hash_style: J<"hash-style=">, Alias<hash_style>;
|
||||
def alias_init_init: J<"init=">, Alias<init>;
|
||||
def alias_l__library: J<"library=">, Alias<l>;
|
||||
def alias_Map_eq: J<"Map=">, Alias<Map>;
|
||||
def alias_library: JoinedOrSeparate<["-"], "l">, Alias<library>;
|
||||
def alias_library_path: JoinedOrSeparate<["-"], "L">, Alias<library_path>;
|
||||
def alias_omagic: Flag<["-"], "N">, Alias<omagic>;
|
||||
def alias_o_output: Joined<["--"], "output=">, Alias<o>;
|
||||
def alias_o_output2 : Separate<["--"], "output">, Alias<o>;
|
||||
def alias_pie_pic_executable: F<"pic-executable">, Alias<pie>;
|
||||
def alias_print_map_M: Flag<["-"], "M">, Alias<print_map>;
|
||||
def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
|
||||
def alias_reproduce_eq: J<"reproduce=">, Alias<reproduce>;
|
||||
def alias_retain_symbols_file: S<"retain-symbols-file">, Alias<retain_symbols_file>;
|
||||
def alias_rpath_R: JoinedOrSeparate<["-"], "R">, Alias<rpath>;
|
||||
def alias_rpath_rpath: J<"rpath=">, Alias<rpath>;
|
||||
def alias_script_T: JoinedOrSeparate<["-"], "T">, Alias<script>;
|
||||
def alias_shared_Bshareable: F<"Bshareable">, Alias<shared>;
|
||||
def alias_soname_h: JoinedOrSeparate<["-"], "h">, Alias<soname>;
|
||||
def alias_soname_soname: S<"soname">, Alias<soname>;
|
||||
def alias_sort_section: J<"sort-section=">, Alias<sort_section>;
|
||||
def alias_script: J<"script=">, Alias<script>;
|
||||
def alias_strip_all: Flag<["-"], "s">, Alias<strip_all>;
|
||||
def alias_strip_debug_S: Flag<["-"], "S">, Alias<strip_debug>;
|
||||
def alias_Tbss: J<"Tbss=">, Alias<Tbss>;
|
||||
def alias_Tdata: J<"Tdata=">, Alias<Tdata>;
|
||||
def alias_trace: Flag<["-"], "t">, Alias<trace>;
|
||||
def trace_trace_symbol_eq : J<"trace-symbol=">, Alias<trace_symbol>;
|
||||
def alias_trace_symbol_y : JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>;
|
||||
def alias_Ttext: J<"Ttext=">, Alias<Ttext>;
|
||||
def alias_Ttext_segment: S<"Ttext-segment">, Alias<Ttext>;
|
||||
def alias_Ttext_segment_eq: J<"Ttext-segment=">, Alias<Ttext>;
|
||||
def alias_undefined_eq: J<"undefined=">, Alias<undefined>;
|
||||
def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
|
||||
def alias_version_script_eq: J<"version-script=">, Alias<version_script>;
|
||||
def alias_version_V: Flag<["-"], "V">, Alias<version>;
|
||||
def alias_wrap_wrap: J<"wrap=">, Alias<wrap>;
|
||||
|
||||
// Our symbol resolution algorithm handles symbols in archive files differently
|
||||
// than traditional linkers, so we don't need --start-group and --end-group.
|
||||
@ -369,6 +391,8 @@ def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
|
||||
HelpText<"YAML output file for optimization remarks">;
|
||||
def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
|
||||
HelpText<"Include hotness informations in the optimization remarks file">;
|
||||
defm plugin_opt: Eq<"plugin-opt">,
|
||||
HelpText<"specifies LTO options for compatibility with GNU linkers">;
|
||||
def save_temps: F<"save-temps">;
|
||||
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
|
||||
HelpText<"Path to ThinLTO cached object file directory">;
|
||||
@ -385,18 +409,17 @@ def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
|
||||
// --version output.
|
||||
def plugin: S<"plugin">;
|
||||
def plugin_eq: J<"plugin=">;
|
||||
def plugin_opt: S<"plugin-opt">;
|
||||
def plugin_opt_eq: J<"plugin-opt=">;
|
||||
|
||||
// Options listed below are silently ignored for now for compatibility.
|
||||
def allow_shlib_undefined: F<"allow-shlib-undefined">;
|
||||
def cref: Flag<["--"], "cref">;
|
||||
def cref: F<"cref">;
|
||||
def detect_odr_violations: F<"detect-odr-violations">;
|
||||
def g: Flag<["-"], "g">;
|
||||
def long_plt: F<"long-plt">;
|
||||
def no_add_needed: F<"no-add-needed">;
|
||||
def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">;
|
||||
def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">,
|
||||
Alias<no_add_needed>;
|
||||
def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">;
|
||||
def no_ctors_in_init_array: F<"no-ctors-in-init-array">;
|
||||
def no_keep_memory: F<"no-keep-memory">;
|
||||
def no_mmap_output_file: F<"no-mmap-output-file">;
|
||||
def no_warn_common: F<"no-warn-common">;
|
||||
@ -406,9 +429,9 @@ def rpath_link_eq: J<"rpath-link=">;
|
||||
def sort_common: F<"sort-common">;
|
||||
def stats: F<"stats">;
|
||||
def warn_execstack: F<"warn-execstack">;
|
||||
def warn_once: F<"warn-once">;
|
||||
def warn_shared_textrel: F<"warn-shared-textrel">;
|
||||
def EB : F<"EB">;
|
||||
def EL : F<"EL">;
|
||||
def G: JoinedOrSeparate<["-"], "G">;
|
||||
def Qy : F<"Qy">;
|
||||
|
||||
|
@ -10,13 +10,14 @@
|
||||
#include "OutputSections.h"
|
||||
#include "Config.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Threads.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "llvm/BinaryFormat/Dwarf.h"
|
||||
#include "llvm/Support/Compression.h"
|
||||
#include "llvm/Support/MD5.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Support/SHA1.h"
|
||||
@ -42,7 +43,6 @@ OutputSection *Out::InitArray;
|
||||
OutputSection *Out::FiniArray;
|
||||
|
||||
std::vector<OutputSection *> elf::OutputSections;
|
||||
std::vector<OutputSectionCommand *> elf::OutputSectionCommands;
|
||||
|
||||
uint32_t OutputSection::getPhdrFlags() const {
|
||||
uint32_t Ret = PF_R;
|
||||
@ -68,97 +68,12 @@ void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) {
|
||||
}
|
||||
|
||||
OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
|
||||
: SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
|
||||
: BaseCommand(OutputSectionKind),
|
||||
SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
|
||||
/*Info*/ 0,
|
||||
/*Link*/ 0),
|
||||
SectionIndex(INT_MAX) {}
|
||||
|
||||
static uint64_t updateOffset(uint64_t Off, InputSection *S) {
|
||||
Off = alignTo(Off, S->Alignment);
|
||||
S->OutSecOff = Off;
|
||||
return Off + S->getSize();
|
||||
}
|
||||
|
||||
void OutputSection::addSection(InputSection *S) {
|
||||
assert(S->Live);
|
||||
Sections.push_back(S);
|
||||
S->Parent = this;
|
||||
this->updateAlignment(S->Alignment);
|
||||
|
||||
// The actual offsets will be computed by assignAddresses. For now, use
|
||||
// crude approximation so that it is at least easy for other code to know the
|
||||
// section order. It is also used to calculate the output section size early
|
||||
// for compressed debug sections.
|
||||
this->Size = updateOffset(Size, S);
|
||||
|
||||
// If this section contains a table of fixed-size entries, sh_entsize
|
||||
// holds the element size. Consequently, if this contains two or more
|
||||
// input sections, all of them must have the same sh_entsize. However,
|
||||
// you can put different types of input sections into one output
|
||||
// sectin by using linker scripts. I don't know what to do here.
|
||||
// Probably we sholuld handle that as an error. But for now we just
|
||||
// pick the largest sh_entsize.
|
||||
this->Entsize = std::max(this->Entsize, S->Entsize);
|
||||
}
|
||||
|
||||
static SectionKey createKey(InputSectionBase *C, StringRef OutsecName) {
|
||||
// The ELF spec just says
|
||||
// ----------------------------------------------------------------
|
||||
// In the first phase, input sections that match in name, type and
|
||||
// attribute flags should be concatenated into single sections.
|
||||
// ----------------------------------------------------------------
|
||||
//
|
||||
// However, it is clear that at least some flags have to be ignored for
|
||||
// section merging. At the very least SHF_GROUP and SHF_COMPRESSED have to be
|
||||
// ignored. We should not have two output .text sections just because one was
|
||||
// in a group and another was not for example.
|
||||
//
|
||||
// It also seems that that wording was a late addition and didn't get the
|
||||
// necessary scrutiny.
|
||||
//
|
||||
// Merging sections with different flags is expected by some users. One
|
||||
// reason is that if one file has
|
||||
//
|
||||
// int *const bar __attribute__((section(".foo"))) = (int *)0;
|
||||
//
|
||||
// gcc with -fPIC will produce a read only .foo section. But if another
|
||||
// file has
|
||||
//
|
||||
// int zed;
|
||||
// int *const bar __attribute__((section(".foo"))) = (int *)&zed;
|
||||
//
|
||||
// gcc with -fPIC will produce a read write section.
|
||||
//
|
||||
// Last but not least, when using linker script the merge rules are forced by
|
||||
// the script. Unfortunately, linker scripts are name based. This means that
|
||||
// expressions like *(.foo*) can refer to multiple input sections with
|
||||
// different flags. We cannot put them in different output sections or we
|
||||
// would produce wrong results for
|
||||
//
|
||||
// start = .; *(.foo.*) end = .; *(.bar)
|
||||
//
|
||||
// and a mapping of .foo1 and .bar1 to one section and .foo2 and .bar2 to
|
||||
// another. The problem is that there is no way to layout those output
|
||||
// sections such that the .foo sections are the only thing between the start
|
||||
// and end symbols.
|
||||
//
|
||||
// Given the above issues, we instead merge sections by name and error on
|
||||
// incompatible types and flags.
|
||||
|
||||
uint32_t Alignment = 0;
|
||||
uint64_t Flags = 0;
|
||||
if (Config->Relocatable && (C->Flags & SHF_MERGE)) {
|
||||
Alignment = std::max<uint64_t>(C->Alignment, C->Entsize);
|
||||
Flags = C->Flags & (SHF_MERGE | SHF_STRINGS);
|
||||
}
|
||||
|
||||
return SectionKey{OutsecName, Flags, Alignment};
|
||||
}
|
||||
|
||||
OutputSectionFactory::OutputSectionFactory() {}
|
||||
|
||||
static uint64_t getIncompatibleFlags(uint64_t Flags) {
|
||||
return Flags & (SHF_ALLOC | SHF_TLS);
|
||||
SectionIndex(INT_MAX) {
|
||||
Live = false;
|
||||
}
|
||||
|
||||
// We allow sections of types listed below to merged into a
|
||||
@ -173,96 +88,64 @@ static bool canMergeToProgbits(unsigned Type) {
|
||||
Type == SHT_NOTE;
|
||||
}
|
||||
|
||||
void elf::reportDiscarded(InputSectionBase *IS) {
|
||||
if (!Config->PrintGcSections)
|
||||
return;
|
||||
message("removing unused section from '" + IS->Name + "' in file '" +
|
||||
IS->File->getName() + "'");
|
||||
}
|
||||
void OutputSection::addSection(InputSection *IS) {
|
||||
if (!Live) {
|
||||
// If IS is the first section to be added to this section,
|
||||
// initialize Type and Entsize from IS.
|
||||
Live = true;
|
||||
Type = IS->Type;
|
||||
Entsize = IS->Entsize;
|
||||
} else {
|
||||
// Otherwise, check if new type or flags are compatible with existing ones.
|
||||
if ((Flags & (SHF_ALLOC | SHF_TLS)) != (IS->Flags & (SHF_ALLOC | SHF_TLS)))
|
||||
error("incompatible section flags for " + Name + "\n>>> " + toString(IS) +
|
||||
": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name +
|
||||
": 0x" + utohexstr(Flags));
|
||||
|
||||
void OutputSectionFactory::addInputSec(InputSectionBase *IS,
|
||||
StringRef OutsecName) {
|
||||
// Sections with the SHT_GROUP attribute reach here only when the - r option
|
||||
// is given. Such sections define "section groups", and InputFiles.cpp has
|
||||
// dedup'ed section groups by their signatures. For the -r, we want to pass
|
||||
// through all SHT_GROUP sections without merging them because merging them
|
||||
// creates broken section contents.
|
||||
if (IS->Type == SHT_GROUP) {
|
||||
OutputSection *Out = nullptr;
|
||||
addInputSec(IS, OutsecName, Out);
|
||||
return;
|
||||
}
|
||||
|
||||
// Imagine .zed : { *(.foo) *(.bar) } script. Both foo and bar may have
|
||||
// relocation sections .rela.foo and .rela.bar for example. Most tools do
|
||||
// not allow multiple REL[A] sections for output section. Hence we
|
||||
// should combine these relocation sections into single output.
|
||||
// We skip synthetic sections because it can be .rela.dyn/.rela.plt or any
|
||||
// other REL[A] sections created by linker itself.
|
||||
if (!isa<SyntheticSection>(IS) &&
|
||||
(IS->Type == SHT_REL || IS->Type == SHT_RELA)) {
|
||||
auto *Sec = cast<InputSection>(IS);
|
||||
OutputSection *Out = Sec->getRelocatedSection()->getOutputSection();
|
||||
addInputSec(IS, OutsecName, Out->RelocationSection);
|
||||
return;
|
||||
}
|
||||
|
||||
SectionKey Key = createKey(IS, OutsecName);
|
||||
OutputSection *&Sec = Map[Key];
|
||||
addInputSec(IS, OutsecName, Sec);
|
||||
}
|
||||
|
||||
void OutputSectionFactory::addInputSec(InputSectionBase *IS,
|
||||
StringRef OutsecName,
|
||||
OutputSection *&Sec) {
|
||||
if (!IS->Live) {
|
||||
reportDiscarded(IS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Sec) {
|
||||
if (getIncompatibleFlags(Sec->Flags) != getIncompatibleFlags(IS->Flags))
|
||||
error("incompatible section flags for " + Sec->Name + "\n>>> " +
|
||||
toString(IS) + ": 0x" + utohexstr(IS->Flags) +
|
||||
"\n>>> output section " + Sec->Name + ": 0x" +
|
||||
utohexstr(Sec->Flags));
|
||||
if (Sec->Type != IS->Type) {
|
||||
if (canMergeToProgbits(Sec->Type) && canMergeToProgbits(IS->Type))
|
||||
Sec->Type = SHT_PROGBITS;
|
||||
else
|
||||
if (Type != IS->Type) {
|
||||
if (!canMergeToProgbits(Type) || !canMergeToProgbits(IS->Type))
|
||||
error("section type mismatch for " + IS->Name + "\n>>> " +
|
||||
toString(IS) + ": " +
|
||||
getELFSectionTypeName(Config->EMachine, IS->Type) +
|
||||
"\n>>> output section " + Sec->Name + ": " +
|
||||
getELFSectionTypeName(Config->EMachine, Sec->Type));
|
||||
"\n>>> output section " + Name + ": " +
|
||||
getELFSectionTypeName(Config->EMachine, Type));
|
||||
Type = SHT_PROGBITS;
|
||||
}
|
||||
Sec->Flags |= IS->Flags;
|
||||
} else {
|
||||
Sec = make<OutputSection>(OutsecName, IS->Type, IS->Flags);
|
||||
OutputSections.push_back(Sec);
|
||||
}
|
||||
|
||||
Sec->addSection(cast<InputSection>(IS));
|
||||
IS->Parent = this;
|
||||
Flags |= IS->Flags;
|
||||
Alignment = std::max(Alignment, IS->Alignment);
|
||||
IS->OutSecOff = Size++;
|
||||
|
||||
// If this section contains a table of fixed-size entries, sh_entsize
|
||||
// holds the element size. If it contains elements of different size we
|
||||
// set sh_entsize to 0.
|
||||
if (Entsize != IS->Entsize)
|
||||
Entsize = 0;
|
||||
|
||||
if (!IS->Assigned) {
|
||||
IS->Assigned = true;
|
||||
if (SectionCommands.empty() ||
|
||||
!isa<InputSectionDescription>(SectionCommands.back()))
|
||||
SectionCommands.push_back(make<InputSectionDescription>(""));
|
||||
auto *ISD = cast<InputSectionDescription>(SectionCommands.back());
|
||||
ISD->Sections.push_back(IS);
|
||||
}
|
||||
}
|
||||
|
||||
OutputSectionFactory::~OutputSectionFactory() {}
|
||||
void elf::sortByOrder(MutableArrayRef<InputSection *> In,
|
||||
std::function<int(InputSectionBase *S)> Order) {
|
||||
typedef std::pair<int, InputSection *> Pair;
|
||||
auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; };
|
||||
|
||||
SectionKey DenseMapInfo<SectionKey>::getEmptyKey() {
|
||||
return SectionKey{DenseMapInfo<StringRef>::getEmptyKey(), 0, 0};
|
||||
}
|
||||
std::vector<Pair> V;
|
||||
for (InputSection *S : In)
|
||||
V.push_back({Order(S), S});
|
||||
std::stable_sort(V.begin(), V.end(), Comp);
|
||||
|
||||
SectionKey DenseMapInfo<SectionKey>::getTombstoneKey() {
|
||||
return SectionKey{DenseMapInfo<StringRef>::getTombstoneKey(), 0, 0};
|
||||
}
|
||||
|
||||
unsigned DenseMapInfo<SectionKey>::getHashValue(const SectionKey &Val) {
|
||||
return hash_combine(Val.Name, Val.Flags, Val.Alignment);
|
||||
}
|
||||
|
||||
bool DenseMapInfo<SectionKey>::isEqual(const SectionKey &LHS,
|
||||
const SectionKey &RHS) {
|
||||
return DenseMapInfo<StringRef>::isEqual(LHS.Name, RHS.Name) &&
|
||||
LHS.Flags == RHS.Flags && LHS.Alignment == RHS.Alignment;
|
||||
for (size_t I = 0; I < V.size(); ++I)
|
||||
In[I] = V[I].second;
|
||||
}
|
||||
|
||||
uint64_t elf::getHeaderSize() {
|
||||
@ -271,7 +154,290 @@ uint64_t elf::getHeaderSize() {
|
||||
return Out::ElfHeader->Size + Out::ProgramHeaders->Size;
|
||||
}
|
||||
|
||||
bool OutputSection::classof(const BaseCommand *C) {
|
||||
return C->Kind == OutputSectionKind;
|
||||
}
|
||||
|
||||
void OutputSection::sort(std::function<int(InputSectionBase *S)> Order) {
|
||||
assert(Live);
|
||||
assert(SectionCommands.size() == 1);
|
||||
sortByOrder(cast<InputSectionDescription>(SectionCommands[0])->Sections,
|
||||
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) {
|
||||
size_t I = 0;
|
||||
for (; I + 4 < Size; I += 4)
|
||||
memcpy(Buf + I, &Filler, 4);
|
||||
memcpy(Buf + I, &Filler, Size - I);
|
||||
}
|
||||
|
||||
// Compress section contents if this section contains debug info.
|
||||
template <class ELFT> void OutputSection::maybeCompress() {
|
||||
typedef typename ELFT::Chdr Elf_Chdr;
|
||||
|
||||
// Compress only DWARF debug sections.
|
||||
if (!Config->CompressDebugSections || (Flags & SHF_ALLOC) ||
|
||||
!Name.startswith(".debug_"))
|
||||
return;
|
||||
|
||||
// Calculate the section offsets and size pre-compression.
|
||||
Size = 0;
|
||||
for (BaseCommand *Cmd : SectionCommands)
|
||||
if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
|
||||
for (InputSection *IS : ISD->Sections) {
|
||||
IS->OutSecOff = alignTo(Size, IS->Alignment);
|
||||
this->Size = IS->OutSecOff + IS->getSize();
|
||||
}
|
||||
|
||||
// Create a section header.
|
||||
ZDebugHeader.resize(sizeof(Elf_Chdr));
|
||||
auto *Hdr = reinterpret_cast<Elf_Chdr *>(ZDebugHeader.data());
|
||||
Hdr->ch_type = ELFCOMPRESS_ZLIB;
|
||||
Hdr->ch_size = Size;
|
||||
Hdr->ch_addralign = Alignment;
|
||||
|
||||
// Write section contents to a temporary buffer and compress it.
|
||||
std::vector<uint8_t> Buf(Size);
|
||||
writeTo<ELFT>(Buf.data());
|
||||
if (Error E = zlib::compress(toStringRef(Buf), CompressedData))
|
||||
fatal("compress failed: " + llvm::toString(std::move(E)));
|
||||
|
||||
// Update section headers.
|
||||
Size = sizeof(Elf_Chdr) + CompressedData.size();
|
||||
Flags |= SHF_COMPRESSED;
|
||||
}
|
||||
|
||||
static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) {
|
||||
if (Size == 1)
|
||||
*Buf = Data;
|
||||
else if (Size == 2)
|
||||
write16(Buf, Data, Config->Endianness);
|
||||
else if (Size == 4)
|
||||
write32(Buf, Data, Config->Endianness);
|
||||
else if (Size == 8)
|
||||
write64(Buf, Data, Config->Endianness);
|
||||
else
|
||||
llvm_unreachable("unsupported Size argument");
|
||||
}
|
||||
|
||||
template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
|
||||
if (Type == SHT_NOBITS)
|
||||
return;
|
||||
|
||||
Loc = Buf;
|
||||
|
||||
// If -compress-debug-section is specified and if this is a debug seciton,
|
||||
// we've already compressed section contents. If that's the case,
|
||||
// just write it down.
|
||||
if (!CompressedData.empty()) {
|
||||
memcpy(Buf, ZDebugHeader.data(), ZDebugHeader.size());
|
||||
memcpy(Buf + ZDebugHeader.size(), CompressedData.data(),
|
||||
CompressedData.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// Write leading padding.
|
||||
std::vector<InputSection *> Sections;
|
||||
for (BaseCommand *Cmd : SectionCommands)
|
||||
if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
|
||||
for (InputSection *IS : ISD->Sections)
|
||||
if (IS->Live)
|
||||
Sections.push_back(IS);
|
||||
uint32_t Filler = getFiller();
|
||||
if (Filler)
|
||||
fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler);
|
||||
|
||||
parallelForEachN(0, Sections.size(), [&](size_t I) {
|
||||
InputSection *IS = Sections[I];
|
||||
IS->writeTo<ELFT>(Buf);
|
||||
|
||||
// Fill gaps between sections.
|
||||
if (Filler) {
|
||||
uint8_t *Start = Buf + IS->OutSecOff + IS->getSize();
|
||||
uint8_t *End;
|
||||
if (I + 1 == Sections.size())
|
||||
End = Buf + Size;
|
||||
else
|
||||
End = Buf + Sections[I + 1]->OutSecOff;
|
||||
fill(Start, End - Start, Filler);
|
||||
}
|
||||
});
|
||||
|
||||
// Linker scripts may have BYTE()-family commands with which you
|
||||
// can write arbitrary bytes to the output. Process them if any.
|
||||
for (BaseCommand *Base : SectionCommands)
|
||||
if (auto *Data = dyn_cast<ByteCommand>(Base))
|
||||
writeInt(Buf + Data->Offset, Data->Expression().getValue(), Data->Size);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void finalizeShtGroup(OutputSection *OS,
|
||||
InputSection *Section) {
|
||||
assert(Config->Relocatable);
|
||||
|
||||
// sh_link field for SHT_GROUP sections should contain the section index of
|
||||
// the symbol table.
|
||||
OS->Link = InX::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]);
|
||||
}
|
||||
|
||||
template <class ELFT> void OutputSection::finalize() {
|
||||
InputSection *First = nullptr;
|
||||
for (BaseCommand *Base : SectionCommands) {
|
||||
if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
|
||||
if (ISD->Sections.empty())
|
||||
continue;
|
||||
if (First == nullptr)
|
||||
First = ISD->Sections.front();
|
||||
}
|
||||
if (isa<ByteCommand>(Base) && Type == SHT_NOBITS)
|
||||
Type = SHT_PROGBITS;
|
||||
}
|
||||
|
||||
if (Flags & SHF_LINK_ORDER) {
|
||||
// We must preserve the link order dependency of sections with the
|
||||
// SHF_LINK_ORDER flag. The dependency is indicated by the sh_link field. We
|
||||
// need to translate the InputSection sh_link to the OutputSection sh_link,
|
||||
// all InputSections in the OutputSection have the same dependency.
|
||||
if (auto *D = First->getLinkOrderDep())
|
||||
Link = D->getParent()->SectionIndex;
|
||||
}
|
||||
|
||||
if (Type == SHT_GROUP) {
|
||||
finalizeShtGroup<ELFT>(this, First);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Config->CopyRelocs || (Type != SHT_RELA && Type != SHT_REL))
|
||||
return;
|
||||
|
||||
if (isa<SyntheticSection>(First))
|
||||
return;
|
||||
|
||||
Link = InX::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();
|
||||
Info = S->getOutputSection()->SectionIndex;
|
||||
Flags |= SHF_INFO_LINK;
|
||||
}
|
||||
|
||||
// Returns true if S matches /Filename.?\.o$/.
|
||||
static bool isCrtBeginEnd(StringRef S, StringRef Filename) {
|
||||
if (!S.endswith(".o"))
|
||||
return false;
|
||||
S = S.drop_back(2);
|
||||
if (S.endswith(Filename))
|
||||
return true;
|
||||
return !S.empty() && S.drop_back().endswith(Filename);
|
||||
}
|
||||
|
||||
static bool isCrtbegin(StringRef S) { return isCrtBeginEnd(S, "crtbegin"); }
|
||||
static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); }
|
||||
|
||||
// .ctors and .dtors are sorted by this priority from highest to lowest.
|
||||
//
|
||||
// 1. The section was contained in crtbegin (crtbegin contains
|
||||
// some sentinel value in its .ctors and .dtors so that the runtime
|
||||
// can find the beginning of the sections.)
|
||||
//
|
||||
// 2. The section has an optional priority value in the form of ".ctors.N"
|
||||
// or ".dtors.N" where N is a number. Unlike .{init,fini}_array,
|
||||
// they are compared as string rather than number.
|
||||
//
|
||||
// 3. The section is just ".ctors" or ".dtors".
|
||||
//
|
||||
// 4. The section was contained in crtend, which contains an end marker.
|
||||
//
|
||||
// In an ideal world, we don't need this function because .init_array and
|
||||
// .ctors are duplicate features (and .init_array is newer.) However, there
|
||||
// are too many real-world use cases of .ctors, so we had no choice to
|
||||
// support that with this rather ad-hoc semantics.
|
||||
static bool compCtors(const InputSection *A, const InputSection *B) {
|
||||
bool BeginA = isCrtbegin(A->File->getName());
|
||||
bool BeginB = isCrtbegin(B->File->getName());
|
||||
if (BeginA != BeginB)
|
||||
return BeginA;
|
||||
bool EndA = isCrtend(A->File->getName());
|
||||
bool EndB = isCrtend(B->File->getName());
|
||||
if (EndA != EndB)
|
||||
return EndB;
|
||||
StringRef X = A->Name;
|
||||
StringRef Y = B->Name;
|
||||
assert(X.startswith(".ctors") || X.startswith(".dtors"));
|
||||
assert(Y.startswith(".ctors") || Y.startswith(".dtors"));
|
||||
X = X.substr(6);
|
||||
Y = Y.substr(6);
|
||||
if (X.empty() && Y.empty())
|
||||
return false;
|
||||
return X < Y;
|
||||
}
|
||||
|
||||
// Sorts input sections by the special rules for .ctors and .dtors.
|
||||
// Unfortunately, the rules are different from the one for .{init,fini}_array.
|
||||
// Read the comment above.
|
||||
void OutputSection::sortCtorsDtors() {
|
||||
assert(SectionCommands.size() == 1);
|
||||
auto *ISD = cast<InputSectionDescription>(SectionCommands[0]);
|
||||
std::stable_sort(ISD->Sections.begin(), ISD->Sections.end(), compCtors);
|
||||
}
|
||||
|
||||
// If an input string is in the form of "foo.N" where N is a number,
|
||||
// return N. Otherwise, returns 65536, which is one greater than the
|
||||
// lowest priority.
|
||||
int elf::getPriority(StringRef S) {
|
||||
size_t Pos = S.rfind('.');
|
||||
if (Pos == StringRef::npos)
|
||||
return 65536;
|
||||
int V;
|
||||
if (!to_integer(S.substr(Pos + 1), V, 10))
|
||||
return 65536;
|
||||
return V;
|
||||
}
|
||||
|
||||
// Sorts input sections by section name suffixes, so that .foo.N comes
|
||||
// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections.
|
||||
// We want to keep the original order if the priorities are the same
|
||||
// because the compiler keeps the original initialization order in a
|
||||
// translation unit and we need to respect that.
|
||||
// For more detail, read the section of the GCC's manual about init_priority.
|
||||
void OutputSection::sortInitFini() {
|
||||
// Sort sections by priority.
|
||||
sort([](InputSectionBase *S) { return getPriority(S->Name); });
|
||||
}
|
||||
|
||||
uint32_t OutputSection::getFiller() {
|
||||
if (Filler)
|
||||
return *Filler;
|
||||
if (Flags & SHF_EXECINSTR)
|
||||
return Target->TrapInstr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template void OutputSection::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
|
||||
template void OutputSection::writeHeaderTo<ELF32BE>(ELF32BE::Shdr *Shdr);
|
||||
template void OutputSection::writeHeaderTo<ELF64LE>(ELF64LE::Shdr *Shdr);
|
||||
template void OutputSection::writeHeaderTo<ELF64BE>(ELF64BE::Shdr *Shdr);
|
||||
|
||||
template void OutputSection::writeTo<ELF32LE>(uint8_t *Buf);
|
||||
template void OutputSection::writeTo<ELF32BE>(uint8_t *Buf);
|
||||
template void OutputSection::writeTo<ELF64LE>(uint8_t *Buf);
|
||||
template void OutputSection::writeTo<ELF64BE>(uint8_t *Buf);
|
||||
|
||||
template void OutputSection::maybeCompress<ELF32LE>();
|
||||
template void OutputSection::maybeCompress<ELF32BE>();
|
||||
template void OutputSection::maybeCompress<ELF64LE>();
|
||||
template void OutputSection::maybeCompress<ELF64BE>();
|
||||
|
||||
template void OutputSection::finalize<ELF32LE>();
|
||||
template void OutputSection::finalize<ELF32BE>();
|
||||
template void OutputSection::finalize<ELF64LE>();
|
||||
template void OutputSection::finalize<ELF64BE>();
|
||||
|
@ -12,9 +12,10 @@
|
||||
|
||||
#include "Config.h"
|
||||
#include "InputSection.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Relocations.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
@ -22,23 +23,23 @@ namespace lld {
|
||||
namespace elf {
|
||||
|
||||
struct PhdrEntry;
|
||||
class SymbolBody;
|
||||
class Symbol;
|
||||
struct EhSectionPiece;
|
||||
class EhInputSection;
|
||||
class InputSection;
|
||||
class InputSectionBase;
|
||||
class MergeInputSection;
|
||||
class OutputSection;
|
||||
template <class ELFT> class ObjectFile;
|
||||
template <class ELFT> class ObjFile;
|
||||
template <class ELFT> class SharedFile;
|
||||
class SharedSymbol;
|
||||
class DefinedRegular;
|
||||
class Defined;
|
||||
|
||||
// This represents a section in an output file.
|
||||
// It is composed of multiple InputSections.
|
||||
// The writer creates multiple OutputSections and assign them unique,
|
||||
// non-overlapping file offsets and VAs.
|
||||
class OutputSection final : public SectionBase {
|
||||
class OutputSection final : public BaseCommand, public SectionBase {
|
||||
public:
|
||||
OutputSection(StringRef Name, uint32_t Type, uint64_t Flags);
|
||||
|
||||
@ -46,6 +47,8 @@ public:
|
||||
return S->kind() == SectionBase::Output;
|
||||
}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
|
||||
uint64_t getLMA() const { return Addr + LMAOffset; }
|
||||
template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
|
||||
|
||||
@ -54,42 +57,68 @@ public:
|
||||
|
||||
uint32_t getPhdrFlags() const;
|
||||
|
||||
void updateAlignment(uint32_t Val) {
|
||||
if (Val > Alignment)
|
||||
Alignment = Val;
|
||||
}
|
||||
|
||||
// Pointer to the first section in PT_LOAD segment, which this section
|
||||
// also resides in. This field is used to correctly compute file offset
|
||||
// of a section. When two sections share the same load segment, difference
|
||||
// between their file offsets should be equal to difference between their
|
||||
// virtual addresses. To compute some section offset we use the following
|
||||
// formula: Off = Off_first + VA - VA_first.
|
||||
OutputSection *FirstInPtLoad = nullptr;
|
||||
// Pointer to the PT_LOAD segment, which this section resides in. This field
|
||||
// is used to correctly compute file offset of a section. When two sections
|
||||
// share the same load segment, difference between their file offsets should
|
||||
// be equal to difference between their virtual addresses. To compute some
|
||||
// section offset we use the following formula: Off = Off_first + VA -
|
||||
// VA_first, where Off_first and VA_first is file offset and VA of first
|
||||
// section in PT_LOAD.
|
||||
PhdrEntry *PtLoad = nullptr;
|
||||
|
||||
// Pointer to a relocation section for this section. Usually nullptr because
|
||||
// we consume relocations, but if --emit-relocs is specified (which is rare),
|
||||
// it may have a non-null value.
|
||||
OutputSection *RelocationSection = nullptr;
|
||||
|
||||
// The following fields correspond to Elf_Shdr members.
|
||||
// Initially this field is the number of InputSections that have been added to
|
||||
// the OutputSection so far. Later on, after a call to assignAddresses, it
|
||||
// corresponds to the Elf_Shdr member.
|
||||
uint64_t Size = 0;
|
||||
|
||||
// The following fields correspond to Elf_Shdr members.
|
||||
uint64_t Offset = 0;
|
||||
uint64_t LMAOffset = 0;
|
||||
uint64_t Addr = 0;
|
||||
uint32_t ShName = 0;
|
||||
|
||||
void addSection(InputSection *S);
|
||||
std::vector<InputSection *> Sections;
|
||||
void addSection(InputSection *IS);
|
||||
|
||||
// Location in the output buffer.
|
||||
uint8_t *Loc = nullptr;
|
||||
|
||||
// The following members are normally only used in linker scripts.
|
||||
MemoryRegion *MemRegion = nullptr;
|
||||
Expr AddrExpr;
|
||||
Expr AlignExpr;
|
||||
Expr LMAExpr;
|
||||
Expr SubalignExpr;
|
||||
std::vector<BaseCommand *> SectionCommands;
|
||||
std::vector<StringRef> Phdrs;
|
||||
llvm::Optional<uint32_t> Filler;
|
||||
ConstraintKind Constraint = ConstraintKind::NoConstraint;
|
||||
std::string Location;
|
||||
std::string MemoryRegionName;
|
||||
bool Noload = false;
|
||||
|
||||
template <class ELFT> void finalize();
|
||||
template <class ELFT> void writeTo(uint8_t *Buf);
|
||||
template <class ELFT> void maybeCompress();
|
||||
|
||||
void sort(std::function<int(InputSectionBase *S)> Order);
|
||||
void sortInitFini();
|
||||
void sortCtorsDtors();
|
||||
|
||||
private:
|
||||
// Used for implementation of --compress-debug-sections option.
|
||||
std::vector<uint8_t> ZDebugHeader;
|
||||
llvm::SmallVector<char, 1> CompressedData;
|
||||
|
||||
// Location in the output buffer.
|
||||
uint8_t *Loc = nullptr;
|
||||
uint32_t getFiller();
|
||||
};
|
||||
|
||||
int getPriority(StringRef S);
|
||||
|
||||
// All output sections that are handled by the linker specially are
|
||||
// globally accessible. Writer initializes them, so don't use them
|
||||
// until Writer is initialized.
|
||||
@ -106,47 +135,17 @@ struct Out {
|
||||
static OutputSection *FiniArray;
|
||||
};
|
||||
|
||||
struct SectionKey {
|
||||
StringRef Name;
|
||||
uint64_t Flags;
|
||||
uint32_t Alignment;
|
||||
};
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
namespace llvm {
|
||||
template <> struct DenseMapInfo<lld::elf::SectionKey> {
|
||||
static lld::elf::SectionKey getEmptyKey();
|
||||
static lld::elf::SectionKey getTombstoneKey();
|
||||
static unsigned getHashValue(const lld::elf::SectionKey &Val);
|
||||
static bool isEqual(const lld::elf::SectionKey &LHS,
|
||||
const lld::elf::SectionKey &RHS);
|
||||
};
|
||||
} // namespace llvm
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
// This class knows how to create an output section for a given
|
||||
// input section. Output section type is determined by various
|
||||
// factors, including input section's sh_flags, sh_type and
|
||||
// linker scripts.
|
||||
class OutputSectionFactory {
|
||||
public:
|
||||
OutputSectionFactory();
|
||||
~OutputSectionFactory();
|
||||
|
||||
void addInputSec(InputSectionBase *IS, StringRef OutsecName);
|
||||
void addInputSec(InputSectionBase *IS, StringRef OutsecName,
|
||||
OutputSection *&Sec);
|
||||
|
||||
private:
|
||||
llvm::SmallDenseMap<SectionKey, OutputSection *> Map;
|
||||
};
|
||||
|
||||
uint64_t getHeaderSize();
|
||||
void reportDiscarded(InputSectionBase *IS);
|
||||
void sortByOrder(llvm::MutableArrayRef<InputSection *> In,
|
||||
std::function<int(InputSectionBase *S)> Order);
|
||||
|
||||
extern std::vector<OutputSection *> OutputSections;
|
||||
extern std::vector<OutputSectionCommand *> OutputSectionCommands;
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
1076
ELF/Relocations.cpp
1076
ELF/Relocations.cpp
File diff suppressed because it is too large
Load Diff
@ -10,23 +10,27 @@
|
||||
#ifndef LLD_ELF_RELOCATIONS_H
|
||||
#define LLD_ELF_RELOCATIONS_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class SymbolBody;
|
||||
class Symbol;
|
||||
class InputSection;
|
||||
class InputSectionBase;
|
||||
class OutputSection;
|
||||
struct OutputSectionCommand;
|
||||
class OutputSection;
|
||||
|
||||
// Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL.
|
||||
typedef uint32_t RelType;
|
||||
|
||||
// List of target-independent relocation types. Relocations read
|
||||
// from files are converted to these types so that the main code
|
||||
// doesn't have to know about architecture-specific details.
|
||||
enum RelExpr {
|
||||
R_INVALID,
|
||||
R_ABS,
|
||||
R_ARM_SBREL,
|
||||
R_GOT,
|
||||
@ -111,21 +115,22 @@ template <RelExpr... Exprs> bool isRelExprOneOf(RelExpr Expr) {
|
||||
// Architecture-neutral representation of relocation.
|
||||
struct Relocation {
|
||||
RelExpr Expr;
|
||||
uint32_t Type;
|
||||
RelType Type;
|
||||
uint64_t Offset;
|
||||
int64_t Addend;
|
||||
SymbolBody *Sym;
|
||||
Symbol *Sym;
|
||||
};
|
||||
|
||||
template <class ELFT> void scanRelocations(InputSectionBase &);
|
||||
|
||||
class ThunkSection;
|
||||
class Thunk;
|
||||
struct InputSectionDescription;
|
||||
|
||||
class ThunkCreator {
|
||||
public:
|
||||
// Return true if Thunks have been added to OutputSections
|
||||
bool createThunks(ArrayRef<OutputSectionCommand *> OutputSections);
|
||||
bool createThunks(ArrayRef<OutputSection *> OutputSections);
|
||||
|
||||
// The number of completed passes of createThunks this permits us
|
||||
// to do one time initialization on Pass 0 and put a limit on the
|
||||
@ -133,40 +138,39 @@ public:
|
||||
uint32_t Pass = 0;
|
||||
|
||||
private:
|
||||
void mergeThunks();
|
||||
ThunkSection *getOSThunkSec(OutputSectionCommand *Cmd,
|
||||
std::vector<InputSection *> *ISR);
|
||||
ThunkSection *getISThunkSec(InputSection *IS, OutputSection *OS);
|
||||
void forEachExecInputSection(
|
||||
ArrayRef<OutputSectionCommand *> OutputSections,
|
||||
std::function<void(OutputSectionCommand *, std::vector<InputSection *> *,
|
||||
InputSection *)>
|
||||
Fn);
|
||||
std::pair<Thunk *, bool> getThunk(SymbolBody &Body, uint32_t Type);
|
||||
ThunkSection *addThunkSection(OutputSection *OS,
|
||||
std::vector<InputSection *> *, uint64_t Off);
|
||||
void mergeThunks(ArrayRef<OutputSection *> OutputSections);
|
||||
|
||||
ThunkSection *getISDThunkSec(OutputSection *OS, InputSection *IS,
|
||||
InputSectionDescription *ISD, uint32_t Type,
|
||||
uint64_t Src);
|
||||
|
||||
ThunkSection *getISThunkSec(InputSection *IS);
|
||||
|
||||
void createInitialThunkSections(ArrayRef<OutputSection *> OutputSections);
|
||||
|
||||
void forEachInputSectionDescription(
|
||||
ArrayRef<OutputSection *> OutputSections,
|
||||
std::function<void(OutputSection *, InputSectionDescription *)> Fn);
|
||||
|
||||
std::pair<Thunk *, bool> getThunk(Symbol &Sym, RelType Type, uint64_t Src);
|
||||
|
||||
ThunkSection *addThunkSection(OutputSection *OS, InputSectionDescription *,
|
||||
uint64_t Off);
|
||||
|
||||
bool normalizeExistingThunk(Relocation &Rel, uint64_t Src);
|
||||
|
||||
// Record all the available Thunks for a Symbol
|
||||
llvm::DenseMap<SymbolBody *, std::vector<Thunk *>> ThunkedSymbols;
|
||||
llvm::DenseMap<Symbol *, std::vector<Thunk *>> ThunkedSymbols;
|
||||
|
||||
// Find a Thunk from the Thunks symbol definition, we can use this to find
|
||||
// the Thunk from a relocation to the Thunks symbol definition.
|
||||
llvm::DenseMap<SymbolBody *, Thunk *> Thunks;
|
||||
llvm::DenseMap<Symbol *, Thunk *> Thunks;
|
||||
|
||||
// Track InputSections that have an inline ThunkSection placed in front
|
||||
// an inline ThunkSection may have control fall through to the section below
|
||||
// so we need to make sure that there is only one of them.
|
||||
// The Mips LA25 Thunk is an example of an inline ThunkSection.
|
||||
llvm::DenseMap<InputSection *, ThunkSection *> ThunkedSections;
|
||||
|
||||
// All the ThunkSections that we have created, organised by OutputSection
|
||||
// will contain a mix of ThunkSections that have been created this pass, and
|
||||
// ThunkSections that have been merged into the OutputSection on previous
|
||||
// passes
|
||||
std::map<std::vector<InputSection *> *, std::vector<ThunkSection *>>
|
||||
ThunkSections;
|
||||
|
||||
// The ThunkSection for this vector of InputSections
|
||||
ThunkSection *CurTS;
|
||||
};
|
||||
|
||||
// Return a int64_t to make sure we get the sign extension out of the way as
|
||||
|
@ -33,7 +33,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ScriptLexer.h"
|
||||
#include "Error.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -75,19 +75,14 @@ ScriptLexer::ScriptLexer(MemoryBufferRef MB) { tokenize(MB); }
|
||||
|
||||
// We don't want to record cascading errors. Keep only the first one.
|
||||
void ScriptLexer::setError(const Twine &Msg) {
|
||||
if (Error)
|
||||
if (errorCount())
|
||||
return;
|
||||
Error = true;
|
||||
|
||||
if (!Pos) {
|
||||
error(getCurrentLocation() + ": " + Msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string S = getCurrentLocation() + ": ";
|
||||
error(S + Msg);
|
||||
error(S + getLine());
|
||||
error(S + std::string(getColumnNumber(), ' ') + "^");
|
||||
std::string S = (getCurrentLocation() + ": " + Msg).str();
|
||||
if (Pos)
|
||||
S += "\n>>> " + getLine().str() + "\n>>> " +
|
||||
std::string(getColumnNumber(), ' ') + "^";
|
||||
error(S);
|
||||
}
|
||||
|
||||
// Split S into linker script tokens.
|
||||
@ -164,18 +159,18 @@ StringRef ScriptLexer::skipSpace(StringRef S) {
|
||||
}
|
||||
|
||||
// An erroneous token is handled as if it were the last token before EOF.
|
||||
bool ScriptLexer::atEOF() { return Error || Tokens.size() == Pos; }
|
||||
bool ScriptLexer::atEOF() { return errorCount() || Tokens.size() == Pos; }
|
||||
|
||||
// Split a given string as an expression.
|
||||
// This function returns "3", "*" and "5" for "3*5" for example.
|
||||
static std::vector<StringRef> tokenizeExpr(StringRef S) {
|
||||
StringRef Ops = "+-*/:"; // List of operators
|
||||
StringRef Ops = "+-*/:!~"; // List of operators
|
||||
|
||||
// Quoted strings are literal strings, so we don't want to split it.
|
||||
if (S.startswith("\""))
|
||||
return {S};
|
||||
|
||||
// Split S with +-*/ as separators.
|
||||
// Split S with operators as separators.
|
||||
std::vector<StringRef> Ret;
|
||||
while (!S.empty()) {
|
||||
size_t E = S.find_first_of(Ops);
|
||||
@ -190,9 +185,14 @@ static std::vector<StringRef> tokenizeExpr(StringRef S) {
|
||||
if (E != 0)
|
||||
Ret.push_back(S.substr(0, E));
|
||||
|
||||
// Get the operator as a token.
|
||||
Ret.push_back(S.substr(E, 1));
|
||||
S = S.substr(E + 1);
|
||||
// Get the operator as a token. Keep != as one token.
|
||||
if (S.substr(E).startswith("!=")) {
|
||||
Ret.push_back(S.substr(E, 2));
|
||||
S = S.substr(E + 2);
|
||||
} else {
|
||||
Ret.push_back(S.substr(E, 1));
|
||||
S = S.substr(E + 1);
|
||||
}
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
@ -207,7 +207,7 @@ static std::vector<StringRef> tokenizeExpr(StringRef S) {
|
||||
//
|
||||
// This function may split the current token into multiple tokens.
|
||||
void ScriptLexer::maybeSplitExpr() {
|
||||
if (!InExpr || Error || atEOF())
|
||||
if (!InExpr || errorCount() || atEOF())
|
||||
return;
|
||||
|
||||
std::vector<StringRef> V = tokenizeExpr(Tokens[Pos]);
|
||||
@ -220,7 +220,7 @@ void ScriptLexer::maybeSplitExpr() {
|
||||
StringRef ScriptLexer::next() {
|
||||
maybeSplitExpr();
|
||||
|
||||
if (Error)
|
||||
if (errorCount())
|
||||
return "";
|
||||
if (atEOF()) {
|
||||
setError("unexpected EOF");
|
||||
@ -231,7 +231,7 @@ StringRef ScriptLexer::next() {
|
||||
|
||||
StringRef ScriptLexer::peek() {
|
||||
StringRef Tok = next();
|
||||
if (Error)
|
||||
if (errorCount())
|
||||
return "";
|
||||
Pos = Pos - 1;
|
||||
return Tok;
|
||||
@ -260,7 +260,7 @@ bool ScriptLexer::consumeLabel(StringRef Tok) {
|
||||
void ScriptLexer::skip() { (void)next(); }
|
||||
|
||||
void ScriptLexer::expect(StringRef Expect) {
|
||||
if (Error)
|
||||
if (errorCount())
|
||||
return;
|
||||
StringRef Tok = next();
|
||||
if (Tok != Expect)
|
||||
|
@ -10,7 +10,7 @@
|
||||
#ifndef LLD_ELF_SCRIPT_LEXER_H
|
||||
#define LLD_ELF_SCRIPT_LEXER_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <utility>
|
||||
@ -39,7 +39,6 @@ public:
|
||||
std::vector<StringRef> Tokens;
|
||||
bool InExpr = false;
|
||||
size_t Pos = 0;
|
||||
bool Error = false;
|
||||
|
||||
private:
|
||||
void maybeSplitExpr();
|
||||
|
@ -17,13 +17,14 @@
|
||||
#include "Driver.h"
|
||||
#include "InputSection.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "OutputSections.h"
|
||||
#include "ScriptLexer.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
@ -52,10 +53,10 @@ public:
|
||||
void readLinkerScript();
|
||||
void readVersionScript();
|
||||
void readDynamicList();
|
||||
void readDefsym(StringRef Name);
|
||||
|
||||
private:
|
||||
void addFile(StringRef Path);
|
||||
OutputSection *checkSection(OutputSectionCommand *Cmd, StringRef Loccation);
|
||||
|
||||
void readAsNeeded();
|
||||
void readEntry();
|
||||
@ -67,17 +68,18 @@ private:
|
||||
void readOutputArch();
|
||||
void readOutputFormat();
|
||||
void readPhdrs();
|
||||
void readRegionAlias();
|
||||
void readSearchDir();
|
||||
void readSections();
|
||||
void readVersion();
|
||||
void readVersionScriptCommand();
|
||||
|
||||
SymbolAssignment *readAssignment(StringRef Name);
|
||||
BytesDataCommand *readBytesDataCommand(StringRef Tok);
|
||||
ByteCommand *readByteCommand(StringRef Tok);
|
||||
uint32_t readFill();
|
||||
uint32_t parseFill(StringRef Tok);
|
||||
void readSectionAddressType(OutputSectionCommand *Cmd);
|
||||
OutputSectionCommand *readOutputSectionDescription(StringRef OutSec);
|
||||
void readSectionAddressType(OutputSection *Cmd);
|
||||
OutputSection *readOutputSectionDescription(StringRef OutSec);
|
||||
std::vector<StringRef> readOutputSectionPhdrs();
|
||||
InputSectionDescription *readInputSectionDescription(StringRef Tok);
|
||||
StringMatcher readFilePatterns();
|
||||
@ -90,6 +92,8 @@ private:
|
||||
void readSort();
|
||||
AssertCommand *readAssert();
|
||||
Expr readAssertExpr();
|
||||
Expr readConstant();
|
||||
Expr getPageSize();
|
||||
|
||||
uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
|
||||
std::pair<uint32_t, uint32_t> readMemoryAttributes();
|
||||
@ -109,7 +113,11 @@ private:
|
||||
std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
|
||||
readSymbols();
|
||||
|
||||
// True if a script being read is in a subdirectory specified by -sysroot.
|
||||
bool IsUnderSysroot;
|
||||
|
||||
// A set to detect an INCLUDE() cycle.
|
||||
StringSet<> Seen;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -131,7 +139,7 @@ static bool isUnderSysroot(StringRef Path) {
|
||||
// Some operations only support one non absolute value. Move the
|
||||
// absolute one to the right hand side for convenience.
|
||||
static void moveAbsRight(ExprValue &A, ExprValue &B) {
|
||||
if (A.isAbsolute())
|
||||
if (A.Sec == nullptr || (A.ForceAbsolute && !B.isAbsolute()))
|
||||
std::swap(A, B);
|
||||
if (!B.isAbsolute())
|
||||
error(A.Loc + ": at least one side of the expression must be absolute");
|
||||
@ -139,11 +147,11 @@ static void moveAbsRight(ExprValue &A, ExprValue &B) {
|
||||
|
||||
static ExprValue add(ExprValue A, ExprValue B) {
|
||||
moveAbsRight(A, B);
|
||||
return {A.Sec, A.ForceAbsolute, A.Val + B.getValue(), A.Loc};
|
||||
return {A.Sec, A.ForceAbsolute, A.getSectionOffset() + B.getValue(), A.Loc};
|
||||
}
|
||||
|
||||
static ExprValue sub(ExprValue A, ExprValue B) {
|
||||
return {A.Sec, A.Val - B.getValue(), A.Loc};
|
||||
return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc};
|
||||
}
|
||||
|
||||
static ExprValue mul(ExprValue A, ExprValue B) {
|
||||
@ -170,10 +178,24 @@ static ExprValue bitOr(ExprValue A, ExprValue B) {
|
||||
}
|
||||
|
||||
void ScriptParser::readDynamicList() {
|
||||
Config->HasDynamicList = true;
|
||||
expect("{");
|
||||
readAnonymousDeclaration();
|
||||
if (!atEOF())
|
||||
std::vector<SymbolVersion> Locals;
|
||||
std::vector<SymbolVersion> Globals;
|
||||
std::tie(Locals, Globals) = readSymbols();
|
||||
expect(";");
|
||||
|
||||
if (!atEOF()) {
|
||||
setError("EOF expected, but got " + next());
|
||||
return;
|
||||
}
|
||||
if (!Locals.empty()) {
|
||||
setError("\"local:\" scope not supported in --dynamic-list");
|
||||
return;
|
||||
}
|
||||
|
||||
for (SymbolVersion V : Globals)
|
||||
Config->DynamicList.push_back(V);
|
||||
}
|
||||
|
||||
void ScriptParser::readVersionScript() {
|
||||
@ -188,7 +210,7 @@ void ScriptParser::readVersionScriptCommand() {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!atEOF() && !Error && peek() != "}") {
|
||||
while (!atEOF() && !errorCount() && peek() != "}") {
|
||||
StringRef VerStr = next();
|
||||
if (VerStr == "{") {
|
||||
setError("anonymous version definition is used in "
|
||||
@ -213,7 +235,7 @@ void ScriptParser::readLinkerScript() {
|
||||
continue;
|
||||
|
||||
if (Tok == "ASSERT") {
|
||||
Script->Opt.Commands.push_back(readAssert());
|
||||
Script->SectionCommands.push_back(readAssert());
|
||||
} else if (Tok == "ENTRY") {
|
||||
readEntry();
|
||||
} else if (Tok == "EXTERN") {
|
||||
@ -232,6 +254,8 @@ void ScriptParser::readLinkerScript() {
|
||||
readOutputFormat();
|
||||
} else if (Tok == "PHDRS") {
|
||||
readPhdrs();
|
||||
} else if (Tok == "REGION_ALIAS") {
|
||||
readRegionAlias();
|
||||
} else if (Tok == "SEARCH_DIR") {
|
||||
readSearchDir();
|
||||
} else if (Tok == "SECTIONS") {
|
||||
@ -239,13 +263,21 @@ void ScriptParser::readLinkerScript() {
|
||||
} else if (Tok == "VERSION") {
|
||||
readVersion();
|
||||
} else if (SymbolAssignment *Cmd = readProvideOrAssignment(Tok)) {
|
||||
Script->Opt.Commands.push_back(Cmd);
|
||||
Script->SectionCommands.push_back(Cmd);
|
||||
} else {
|
||||
setError("unknown directive: " + Tok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptParser::readDefsym(StringRef Name) {
|
||||
Expr E = readExpr();
|
||||
if (!atEOF())
|
||||
setError("EOF expected, but got " + next());
|
||||
SymbolAssignment *Cmd = make<SymbolAssignment>(Name, E, getCurrentLocation());
|
||||
Script->SectionCommands.push_back(Cmd);
|
||||
}
|
||||
|
||||
void ScriptParser::addFile(StringRef S) {
|
||||
if (IsUnderSysroot && S.startswith("/")) {
|
||||
SmallString<128> PathData;
|
||||
@ -256,7 +288,7 @@ void ScriptParser::addFile(StringRef S) {
|
||||
}
|
||||
}
|
||||
|
||||
if (sys::path::is_absolute(S)) {
|
||||
if (S.startswith("/")) {
|
||||
Driver->addFile(S, /*WithLOption=*/false);
|
||||
} else if (S.startswith("=")) {
|
||||
if (Config->Sysroot.empty())
|
||||
@ -280,7 +312,7 @@ void ScriptParser::readAsNeeded() {
|
||||
expect("(");
|
||||
bool Orig = Config->AsNeeded;
|
||||
Config->AsNeeded = true;
|
||||
while (!Error && !consume(")"))
|
||||
while (!errorCount() && !consume(")"))
|
||||
addFile(unquote(next()));
|
||||
Config->AsNeeded = Orig;
|
||||
}
|
||||
@ -296,13 +328,13 @@ void ScriptParser::readEntry() {
|
||||
|
||||
void ScriptParser::readExtern() {
|
||||
expect("(");
|
||||
while (!Error && !consume(")"))
|
||||
while (!errorCount() && !consume(")"))
|
||||
Config->Undefined.push_back(next());
|
||||
}
|
||||
|
||||
void ScriptParser::readGroup() {
|
||||
expect("(");
|
||||
while (!Error && !consume(")")) {
|
||||
while (!errorCount() && !consume(")")) {
|
||||
if (consume("AS_NEEDED"))
|
||||
readAsNeeded();
|
||||
else
|
||||
@ -313,20 +345,17 @@ void ScriptParser::readGroup() {
|
||||
void ScriptParser::readInclude() {
|
||||
StringRef Tok = unquote(next());
|
||||
|
||||
// https://sourceware.org/binutils/docs/ld/File-Commands.html:
|
||||
// The file will be searched for in the current directory, and in any
|
||||
// directory specified with the -L option.
|
||||
if (sys::fs::exists(Tok)) {
|
||||
if (Optional<MemoryBufferRef> MB = readFile(Tok))
|
||||
tokenize(*MB);
|
||||
if (!Seen.insert(Tok).second) {
|
||||
setError("there is a cycle in linker script INCLUDEs");
|
||||
return;
|
||||
}
|
||||
if (Optional<std::string> Path = findFromSearchPaths(Tok)) {
|
||||
|
||||
if (Optional<std::string> Path = searchLinkerScript(Tok)) {
|
||||
if (Optional<MemoryBufferRef> MB = readFile(*Path))
|
||||
tokenize(*MB);
|
||||
return;
|
||||
}
|
||||
setError("cannot open " + Tok);
|
||||
setError("cannot find linker script " + Tok);
|
||||
}
|
||||
|
||||
void ScriptParser::readOutput() {
|
||||
@ -341,7 +370,7 @@ void ScriptParser::readOutput() {
|
||||
void ScriptParser::readOutputArch() {
|
||||
// OUTPUT_ARCH is ignored for now.
|
||||
expect("(");
|
||||
while (!Error && !consume(")"))
|
||||
while (!errorCount() && !consume(")"))
|
||||
skip();
|
||||
}
|
||||
|
||||
@ -360,28 +389,43 @@ void ScriptParser::readOutputFormat() {
|
||||
|
||||
void ScriptParser::readPhdrs() {
|
||||
expect("{");
|
||||
while (!Error && !consume("}")) {
|
||||
Script->Opt.PhdrsCommands.push_back(
|
||||
{next(), PT_NULL, false, false, UINT_MAX, nullptr});
|
||||
|
||||
PhdrsCommand &PhdrCmd = Script->Opt.PhdrsCommands.back();
|
||||
PhdrCmd.Type = readPhdrType();
|
||||
while (!errorCount() && !consume("}")) {
|
||||
PhdrsCommand Cmd;
|
||||
Cmd.Name = next();
|
||||
Cmd.Type = readPhdrType();
|
||||
|
||||
while (!Error && !consume(";")) {
|
||||
while (!errorCount() && !consume(";")) {
|
||||
if (consume("FILEHDR"))
|
||||
PhdrCmd.HasFilehdr = true;
|
||||
Cmd.HasFilehdr = true;
|
||||
else if (consume("PHDRS"))
|
||||
PhdrCmd.HasPhdrs = true;
|
||||
Cmd.HasPhdrs = true;
|
||||
else if (consume("AT"))
|
||||
PhdrCmd.LMAExpr = readParenExpr();
|
||||
Cmd.LMAExpr = readParenExpr();
|
||||
else if (consume("FLAGS"))
|
||||
PhdrCmd.Flags = readParenExpr()().getValue();
|
||||
Cmd.Flags = readParenExpr()().getValue();
|
||||
else
|
||||
setError("unexpected header attribute: " + next());
|
||||
}
|
||||
|
||||
Script->PhdrsCommands.push_back(Cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptParser::readRegionAlias() {
|
||||
expect("(");
|
||||
StringRef Alias = unquote(next());
|
||||
expect(",");
|
||||
StringRef Name = next();
|
||||
expect(")");
|
||||
|
||||
if (Script->MemoryRegions.count(Alias))
|
||||
setError("redefinition of memory region '" + Alias + "'");
|
||||
if (!Script->MemoryRegions.count(Name))
|
||||
setError("memory region '" + Name + "' is not defined");
|
||||
Script->MemoryRegions.insert({Alias, Script->MemoryRegions[Name]});
|
||||
}
|
||||
|
||||
void ScriptParser::readSearchDir() {
|
||||
expect("(");
|
||||
StringRef Tok = next();
|
||||
@ -391,7 +435,7 @@ void ScriptParser::readSearchDir() {
|
||||
}
|
||||
|
||||
void ScriptParser::readSections() {
|
||||
Script->Opt.HasSections = true;
|
||||
Script->HasSectionsCommand = true;
|
||||
|
||||
// -no-rosegment is used to avoid placing read only non-executable sections in
|
||||
// their own segment. We do the same if SECTIONS command is present in linker
|
||||
@ -399,7 +443,7 @@ void ScriptParser::readSections() {
|
||||
Config->SingleRoRx = true;
|
||||
|
||||
expect("{");
|
||||
while (!Error && !consume("}")) {
|
||||
while (!errorCount() && !consume("}")) {
|
||||
StringRef Tok = next();
|
||||
BaseCommand *Cmd = readProvideOrAssignment(Tok);
|
||||
if (!Cmd) {
|
||||
@ -408,7 +452,7 @@ void ScriptParser::readSections() {
|
||||
else
|
||||
Cmd = readOutputSectionDescription(Tok);
|
||||
}
|
||||
Script->Opt.Commands.push_back(Cmd);
|
||||
Script->SectionCommands.push_back(Cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -424,7 +468,7 @@ static int precedence(StringRef Op) {
|
||||
|
||||
StringMatcher ScriptParser::readFilePatterns() {
|
||||
std::vector<StringRef> V;
|
||||
while (!Error && !consume(")"))
|
||||
while (!errorCount() && !consume(")"))
|
||||
V.push_back(next());
|
||||
return StringMatcher(V);
|
||||
}
|
||||
@ -456,7 +500,7 @@ SortSectionPolicy ScriptParser::readSortKind() {
|
||||
// any file but a.o, and section .baz in any file but b.o.
|
||||
std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
|
||||
std::vector<SectionPattern> Ret;
|
||||
while (!Error && peek() != ")") {
|
||||
while (!errorCount() && peek() != ")") {
|
||||
StringMatcher ExcludeFilePat;
|
||||
if (consume("EXCLUDE_FILE")) {
|
||||
expect("(");
|
||||
@ -464,7 +508,7 @@ std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
|
||||
}
|
||||
|
||||
std::vector<StringRef> V;
|
||||
while (!Error && peek() != ")" && peek() != "EXCLUDE_FILE")
|
||||
while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE")
|
||||
V.push_back(next());
|
||||
|
||||
if (!V.empty())
|
||||
@ -491,7 +535,7 @@ ScriptParser::readInputSectionRules(StringRef FilePattern) {
|
||||
auto *Cmd = make<InputSectionDescription>(FilePattern);
|
||||
expect("(");
|
||||
|
||||
while (!Error && !consume(")")) {
|
||||
while (!errorCount() && !consume(")")) {
|
||||
SortSectionPolicy Outer = readSortKind();
|
||||
SortSectionPolicy Inner = SortSectionPolicy::Default;
|
||||
std::vector<SectionPattern> V;
|
||||
@ -529,7 +573,7 @@ ScriptParser::readInputSectionDescription(StringRef Tok) {
|
||||
StringRef FilePattern = next();
|
||||
InputSectionDescription *Cmd = readInputSectionRules(FilePattern);
|
||||
expect(")");
|
||||
Script->Opt.KeptSections.push_back(Cmd);
|
||||
Script->KeptSections.push_back(Cmd);
|
||||
return Cmd;
|
||||
}
|
||||
return readInputSectionRules(Tok);
|
||||
@ -579,7 +623,7 @@ 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(OutputSectionCommand *Cmd) {
|
||||
void ScriptParser::readSectionAddressType(OutputSection *Cmd) {
|
||||
if (consume("(")) {
|
||||
if (consume("NOLOAD")) {
|
||||
expect(")");
|
||||
@ -599,21 +643,32 @@ void ScriptParser::readSectionAddressType(OutputSectionCommand *Cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
OutputSectionCommand *
|
||||
ScriptParser::readOutputSectionDescription(StringRef OutSec) {
|
||||
OutputSectionCommand *Cmd =
|
||||
Script->createOutputSectionCommand(OutSec, getCurrentLocation());
|
||||
static Expr checkAlignment(Expr E, std::string &Loc) {
|
||||
return [=] {
|
||||
uint64_t Alignment = std::max((uint64_t)1, E().getValue());
|
||||
if (!isPowerOf2_64(Alignment)) {
|
||||
error(Loc + ": alignment must be power of 2");
|
||||
return (uint64_t)1; // Return a dummy value.
|
||||
}
|
||||
return Alignment;
|
||||
};
|
||||
}
|
||||
|
||||
OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
|
||||
OutputSection *Cmd =
|
||||
Script->createOutputSection(OutSec, getCurrentLocation());
|
||||
|
||||
if (peek() != ":")
|
||||
readSectionAddressType(Cmd);
|
||||
expect(":");
|
||||
|
||||
std::string Location = getCurrentLocation();
|
||||
if (consume("AT"))
|
||||
Cmd->LMAExpr = readParenExpr();
|
||||
if (consume("ALIGN"))
|
||||
Cmd->AlignExpr = readParenExpr();
|
||||
Cmd->AlignExpr = checkAlignment(readParenExpr(), Location);
|
||||
if (consume("SUBALIGN"))
|
||||
Cmd->SubalignExpr = readParenExpr();
|
||||
Cmd->SubalignExpr = checkAlignment(readParenExpr(), Location);
|
||||
|
||||
// Parse constraints.
|
||||
if (consume("ONLY_IF_RO"))
|
||||
@ -622,16 +677,16 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) {
|
||||
Cmd->Constraint = ConstraintKind::ReadWrite;
|
||||
expect("{");
|
||||
|
||||
while (!Error && !consume("}")) {
|
||||
while (!errorCount() && !consume("}")) {
|
||||
StringRef Tok = next();
|
||||
if (Tok == ";") {
|
||||
// Empty commands are allowed. Do nothing here.
|
||||
} else if (SymbolAssignment *Assign = readProvideOrAssignment(Tok)) {
|
||||
Cmd->Commands.push_back(Assign);
|
||||
} else if (BytesDataCommand *Data = readBytesDataCommand(Tok)) {
|
||||
Cmd->Commands.push_back(Data);
|
||||
Cmd->SectionCommands.push_back(Assign);
|
||||
} else if (ByteCommand *Data = readByteCommand(Tok)) {
|
||||
Cmd->SectionCommands.push_back(Data);
|
||||
} else if (Tok == "ASSERT") {
|
||||
Cmd->Commands.push_back(readAssert());
|
||||
Cmd->SectionCommands.push_back(readAssert());
|
||||
expect(";");
|
||||
} else if (Tok == "CONSTRUCTORS") {
|
||||
// CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors
|
||||
@ -642,7 +697,7 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) {
|
||||
} else if (Tok == "SORT") {
|
||||
readSort();
|
||||
} else if (peek() == "(") {
|
||||
Cmd->Commands.push_back(readInputSectionDescription(Tok));
|
||||
Cmd->SectionCommands.push_back(readInputSectionDescription(Tok));
|
||||
} else {
|
||||
setError("unknown command " + Tok);
|
||||
}
|
||||
@ -650,6 +705,8 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) {
|
||||
|
||||
if (consume(">"))
|
||||
Cmd->MemoryRegionName = next();
|
||||
else if (peek().startswith(">"))
|
||||
Cmd->MemoryRegionName = next().drop_front();
|
||||
|
||||
Cmd->Phdrs = readOutputSectionPhdrs();
|
||||
|
||||
@ -712,7 +769,7 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef Name) {
|
||||
Expr E = readExpr();
|
||||
if (Op == "+=") {
|
||||
std::string Loc = getCurrentLocation();
|
||||
E = [=] { return add(Script->getSymbolValue(Loc, Name), E()); };
|
||||
E = [=] { return add(Script->getSymbolValue(Name, Loc), E()); };
|
||||
}
|
||||
return make<SymbolAssignment>(Name, E, getCurrentLocation());
|
||||
}
|
||||
@ -764,7 +821,7 @@ static Expr combine(StringRef Op, Expr L, Expr R) {
|
||||
// This is a part of the operator-precedence parser. This function
|
||||
// assumes that the remaining token stream starts with an operator.
|
||||
Expr ScriptParser::readExpr1(Expr Lhs, int MinPrec) {
|
||||
while (!atEOF() && !Error) {
|
||||
while (!atEOF() && !errorCount()) {
|
||||
// Read an operator and an expression.
|
||||
if (consume("?"))
|
||||
return readTernary(Lhs);
|
||||
@ -790,13 +847,24 @@ Expr ScriptParser::readExpr1(Expr Lhs, int MinPrec) {
|
||||
return Lhs;
|
||||
}
|
||||
|
||||
uint64_t static getConstant(StringRef S) {
|
||||
Expr ScriptParser::getPageSize() {
|
||||
std::string Location = getCurrentLocation();
|
||||
return [=]() -> uint64_t {
|
||||
if (Target)
|
||||
return Target->PageSize;
|
||||
error(Location + ": unable to calculate page size");
|
||||
return 4096; // Return a dummy value.
|
||||
};
|
||||
}
|
||||
|
||||
Expr ScriptParser::readConstant() {
|
||||
StringRef S = readParenLiteral();
|
||||
if (S == "COMMONPAGESIZE")
|
||||
return Target->PageSize;
|
||||
return getPageSize();
|
||||
if (S == "MAXPAGESIZE")
|
||||
return Config->MaxPageSize;
|
||||
error("unknown constant: " + S);
|
||||
return 0;
|
||||
return [] { return Config->MaxPageSize; };
|
||||
setError("unknown constant: " + S);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Parses Tok as an integer. It recognizes hexadecimal (prefixed with
|
||||
@ -812,10 +880,16 @@ static Optional<uint64_t> parseInt(StringRef Tok) {
|
||||
|
||||
// Hexadecimal
|
||||
uint64_t Val;
|
||||
if (Tok.startswith_lower("0x") && to_integer(Tok.substr(2), Val, 16))
|
||||
if (Tok.startswith_lower("0x")) {
|
||||
if (!to_integer(Tok.substr(2), Val, 16))
|
||||
return None;
|
||||
return Val;
|
||||
if (Tok.endswith_lower("H") && to_integer(Tok.drop_back(), Val, 16))
|
||||
}
|
||||
if (Tok.endswith_lower("H")) {
|
||||
if (!to_integer(Tok.drop_back(), Val, 16))
|
||||
return None;
|
||||
return Val;
|
||||
}
|
||||
|
||||
// Decimal
|
||||
if (Tok.endswith_lower("K")) {
|
||||
@ -833,7 +907,7 @@ static Optional<uint64_t> parseInt(StringRef Tok) {
|
||||
return Val;
|
||||
}
|
||||
|
||||
BytesDataCommand *ScriptParser::readBytesDataCommand(StringRef Tok) {
|
||||
ByteCommand *ScriptParser::readByteCommand(StringRef Tok) {
|
||||
int Size = StringSwitch<int>(Tok)
|
||||
.Case("BYTE", 1)
|
||||
.Case("SHORT", 2)
|
||||
@ -842,8 +916,7 @@ BytesDataCommand *ScriptParser::readBytesDataCommand(StringRef Tok) {
|
||||
.Default(-1);
|
||||
if (Size == -1)
|
||||
return nullptr;
|
||||
|
||||
return make<BytesDataCommand>(readParenExpr(), Size);
|
||||
return make<ByteCommand>(readParenExpr(), Size);
|
||||
}
|
||||
|
||||
StringRef ScriptParser::readParenLiteral() {
|
||||
@ -853,14 +926,9 @@ StringRef ScriptParser::readParenLiteral() {
|
||||
return Tok;
|
||||
}
|
||||
|
||||
OutputSection *ScriptParser::checkSection(OutputSectionCommand *Cmd,
|
||||
StringRef Location) {
|
||||
static void checkIfExists(OutputSection *Cmd, StringRef Location) {
|
||||
if (Cmd->Location.empty() && Script->ErrorOnMissingSection)
|
||||
error(Location + ": undefined section " + Cmd->Name);
|
||||
if (Cmd->Sec)
|
||||
return Cmd->Sec;
|
||||
static OutputSection Dummy("", 0, 0);
|
||||
return &Dummy;
|
||||
}
|
||||
|
||||
Expr ScriptParser::readPrimary() {
|
||||
@ -871,6 +939,10 @@ Expr ScriptParser::readPrimary() {
|
||||
Expr E = readPrimary();
|
||||
return [=] { return ~E().getValue(); };
|
||||
}
|
||||
if (consume("!")) {
|
||||
Expr E = readPrimary();
|
||||
return [=] { return !E().getValue(); };
|
||||
}
|
||||
if (consume("-")) {
|
||||
Expr E = readPrimary();
|
||||
return [=] { return -E().getValue(); };
|
||||
@ -891,18 +963,21 @@ Expr ScriptParser::readPrimary() {
|
||||
}
|
||||
if (Tok == "ADDR") {
|
||||
StringRef Name = readParenLiteral();
|
||||
OutputSectionCommand *Cmd = Script->getOrCreateOutputSectionCommand(Name);
|
||||
OutputSection *Sec = Script->getOrCreateOutputSection(Name);
|
||||
return [=]() -> ExprValue {
|
||||
return {checkSection(Cmd, Location), 0, Location};
|
||||
checkIfExists(Sec, Location);
|
||||
return {Sec, false, 0, Location};
|
||||
};
|
||||
}
|
||||
if (Tok == "ALIGN") {
|
||||
expect("(");
|
||||
Expr E = readExpr();
|
||||
if (consume(")"))
|
||||
if (consume(")")) {
|
||||
E = checkAlignment(E, Location);
|
||||
return [=] { return alignTo(Script->getDot(), E().getValue()); };
|
||||
}
|
||||
expect(",");
|
||||
Expr E2 = readExpr();
|
||||
Expr E2 = checkAlignment(readExpr(), Location);
|
||||
expect(")");
|
||||
return [=] {
|
||||
ExprValue V = E();
|
||||
@ -912,22 +987,25 @@ Expr ScriptParser::readPrimary() {
|
||||
}
|
||||
if (Tok == "ALIGNOF") {
|
||||
StringRef Name = readParenLiteral();
|
||||
OutputSectionCommand *Cmd = Script->getOrCreateOutputSectionCommand(Name);
|
||||
return [=] { return checkSection(Cmd, Location)->Alignment; };
|
||||
OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
|
||||
return [=] {
|
||||
checkIfExists(Cmd, Location);
|
||||
return Cmd->Alignment;
|
||||
};
|
||||
}
|
||||
if (Tok == "ASSERT")
|
||||
return readAssertExpr();
|
||||
if (Tok == "CONSTANT") {
|
||||
StringRef Name = readParenLiteral();
|
||||
return [=] { return getConstant(Name); };
|
||||
}
|
||||
if (Tok == "CONSTANT")
|
||||
return readConstant();
|
||||
if (Tok == "DATA_SEGMENT_ALIGN") {
|
||||
expect("(");
|
||||
Expr E = readExpr();
|
||||
expect(",");
|
||||
readExpr();
|
||||
expect(")");
|
||||
return [=] { return alignTo(Script->getDot(), E().getValue()); };
|
||||
return [=] {
|
||||
return alignTo(Script->getDot(), std::max((uint64_t)1, E().getValue()));
|
||||
};
|
||||
}
|
||||
if (Tok == "DATA_SEGMENT_END") {
|
||||
expect("(");
|
||||
@ -944,28 +1022,32 @@ Expr ScriptParser::readPrimary() {
|
||||
expect(",");
|
||||
readExpr();
|
||||
expect(")");
|
||||
return [] { return alignTo(Script->getDot(), Target->PageSize); };
|
||||
Expr E = getPageSize();
|
||||
return [=] { return alignTo(Script->getDot(), E().getValue()); };
|
||||
}
|
||||
if (Tok == "DEFINED") {
|
||||
StringRef Name = readParenLiteral();
|
||||
return [=] { return Script->isDefined(Name) ? 1 : 0; };
|
||||
return [=] { return Symtab->find(Name) ? 1 : 0; };
|
||||
}
|
||||
if (Tok == "LENGTH") {
|
||||
StringRef Name = readParenLiteral();
|
||||
if (Script->Opt.MemoryRegions.count(Name) == 0)
|
||||
if (Script->MemoryRegions.count(Name) == 0)
|
||||
setError("memory region not defined: " + Name);
|
||||
return [=] { return Script->Opt.MemoryRegions[Name].Length; };
|
||||
return [=] { return Script->MemoryRegions[Name]->Length; };
|
||||
}
|
||||
if (Tok == "LOADADDR") {
|
||||
StringRef Name = readParenLiteral();
|
||||
OutputSectionCommand *Cmd = Script->getOrCreateOutputSectionCommand(Name);
|
||||
return [=] { return checkSection(Cmd, Location)->getLMA(); };
|
||||
OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
|
||||
return [=] {
|
||||
checkIfExists(Cmd, Location);
|
||||
return Cmd->getLMA();
|
||||
};
|
||||
}
|
||||
if (Tok == "ORIGIN") {
|
||||
StringRef Name = readParenLiteral();
|
||||
if (Script->Opt.MemoryRegions.count(Name) == 0)
|
||||
if (Script->MemoryRegions.count(Name) == 0)
|
||||
setError("memory region not defined: " + Name);
|
||||
return [=] { return Script->Opt.MemoryRegions[Name].Origin; };
|
||||
return [=] { return Script->MemoryRegions[Name]->Origin; };
|
||||
}
|
||||
if (Tok == "SEGMENT_START") {
|
||||
expect("(");
|
||||
@ -977,18 +1059,18 @@ Expr ScriptParser::readPrimary() {
|
||||
}
|
||||
if (Tok == "SIZEOF") {
|
||||
StringRef Name = readParenLiteral();
|
||||
OutputSectionCommand *Cmd = Script->getOrCreateOutputSectionCommand(Name);
|
||||
OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
|
||||
// Linker script does not create an output section if its content is empty.
|
||||
// We want to allow SIZEOF(.foo) where .foo is a section which happened to
|
||||
// be empty.
|
||||
return [=] { return Cmd->Sec ? Cmd->Sec->Size : 0; };
|
||||
return [=] { return Cmd->Size; };
|
||||
}
|
||||
if (Tok == "SIZEOF_HEADERS")
|
||||
return [=] { return elf::getHeaderSize(); };
|
||||
|
||||
// Tok is the dot.
|
||||
if (Tok == ".")
|
||||
return [=] { return Script->getSymbolValue(Location, Tok); };
|
||||
return [=] { return Script->getSymbolValue(Tok, Location); };
|
||||
|
||||
// Tok is a literal number.
|
||||
if (Optional<uint64_t> Val = parseInt(Tok))
|
||||
@ -997,8 +1079,8 @@ Expr ScriptParser::readPrimary() {
|
||||
// Tok is a symbol name.
|
||||
if (!isValidCIdentifier(Tok))
|
||||
setError("malformed number: " + Tok);
|
||||
Script->Opt.ReferencedSymbols.push_back(Tok);
|
||||
return [=] { return Script->getSymbolValue(Location, Tok); };
|
||||
Script->ReferencedSymbols.push_back(Tok);
|
||||
return [=] { return Script->getSymbolValue(Tok, Location); };
|
||||
}
|
||||
|
||||
Expr ScriptParser::readTernary(Expr Cond) {
|
||||
@ -1017,7 +1099,7 @@ Expr ScriptParser::readParenExpr() {
|
||||
|
||||
std::vector<StringRef> ScriptParser::readOutputSectionPhdrs() {
|
||||
std::vector<StringRef> Phdrs;
|
||||
while (!Error && peek().startswith(":")) {
|
||||
while (!errorCount() && peek().startswith(":")) {
|
||||
StringRef Tok = next();
|
||||
Phdrs.push_back((Tok.size() == 1) ? next() : Tok.substr(1));
|
||||
}
|
||||
@ -1120,7 +1202,7 @@ ScriptParser::readSymbols() {
|
||||
std::vector<SymbolVersion> Globals;
|
||||
std::vector<SymbolVersion> *V = &Globals;
|
||||
|
||||
while (!Error) {
|
||||
while (!errorCount()) {
|
||||
if (consume("}"))
|
||||
break;
|
||||
if (consumeLabel("local")) {
|
||||
@ -1154,7 +1236,7 @@ std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
|
||||
expect("{");
|
||||
|
||||
std::vector<SymbolVersion> Ret;
|
||||
while (!Error && peek() != "}") {
|
||||
while (!errorCount() && peek() != "}") {
|
||||
StringRef Tok = next();
|
||||
bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok);
|
||||
Ret.push_back({unquote(Tok), IsCXX, HasWildcard});
|
||||
@ -1181,7 +1263,7 @@ uint64_t ScriptParser::readMemoryAssignment(StringRef S1, StringRef S2,
|
||||
// MEMORY { name [(attr)] : ORIGIN = origin, LENGTH = len ... }
|
||||
void ScriptParser::readMemory() {
|
||||
expect("{");
|
||||
while (!Error && !consume("}")) {
|
||||
while (!errorCount() && !consume("}")) {
|
||||
StringRef Name = next();
|
||||
|
||||
uint32_t Flags = 0;
|
||||
@ -1196,12 +1278,12 @@ void ScriptParser::readMemory() {
|
||||
expect(",");
|
||||
uint64_t Length = readMemoryAssignment("LENGTH", "len", "l");
|
||||
|
||||
// Add the memory region to the region map (if it doesn't already exist).
|
||||
auto It = Script->Opt.MemoryRegions.find(Name);
|
||||
if (It != Script->Opt.MemoryRegions.end())
|
||||
// Add the memory region to the region map.
|
||||
if (Script->MemoryRegions.count(Name))
|
||||
setError("region '" + Name + "' already defined");
|
||||
else
|
||||
Script->Opt.MemoryRegions[Name] = {Name, Origin, Length, Flags, NegFlags};
|
||||
MemoryRegion *MR = make<MemoryRegion>();
|
||||
*MR = {Name, Origin, Length, Flags, NegFlags};
|
||||
Script->MemoryRegions[Name] = MR;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1245,3 +1327,7 @@ void elf::readVersionScript(MemoryBufferRef MB) {
|
||||
void elf::readDynamicList(MemoryBufferRef MB) {
|
||||
ScriptParser(MB).readDynamicList();
|
||||
}
|
||||
|
||||
void elf::readDefsym(StringRef Name, MemoryBufferRef MB) {
|
||||
ScriptParser(MB).readDefsym(Name);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#ifndef LLD_ELF_SCRIPT_PARSER_H
|
||||
#define LLD_ELF_SCRIPT_PARSER_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
namespace lld {
|
||||
@ -25,6 +25,9 @@ void readVersionScript(MemoryBufferRef MB);
|
||||
|
||||
void readDynamicList(MemoryBufferRef MB);
|
||||
|
||||
// Parses the defsym expression.
|
||||
void readDefsym(StringRef Name, MemoryBufferRef MB);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "Strings.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
@ -54,32 +54,9 @@ std::vector<uint8_t> elf::parseHex(StringRef S) {
|
||||
return Hex;
|
||||
}
|
||||
|
||||
static bool isAlpha(char C) {
|
||||
return ('a' <= C && C <= 'z') || ('A' <= C && C <= 'Z') || C == '_';
|
||||
}
|
||||
|
||||
static bool isAlnum(char C) { return isAlpha(C) || ('0' <= C && C <= '9'); }
|
||||
|
||||
// Returns true if S is valid as a C language identifier.
|
||||
bool elf::isValidCIdentifier(StringRef S) {
|
||||
return !S.empty() && isAlpha(S[0]) &&
|
||||
std::all_of(S.begin() + 1, S.end(), isAlnum);
|
||||
}
|
||||
|
||||
// Returns the demangled C++ symbol name for Name.
|
||||
Optional<std::string> elf::demangle(StringRef Name) {
|
||||
// itaniumDemangle can be used to demangle strings other than symbol
|
||||
// names which do not necessarily start with "_Z". Name can be
|
||||
// either a C or C++ symbol. Don't call itaniumDemangle if the name
|
||||
// does not look like a C++ symbol name to avoid getting unexpected
|
||||
// result for a C symbol that happens to match a mangled type name.
|
||||
if (!Name.startswith("_Z"))
|
||||
return None;
|
||||
|
||||
char *Buf = itaniumDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
|
||||
if (!Buf)
|
||||
return None;
|
||||
std::string S(Buf);
|
||||
free(Buf);
|
||||
return S;
|
||||
return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
|
||||
std::all_of(S.begin() + 1, S.end(),
|
||||
[](char C) { return C == '_' || isAlnum(C); });
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#ifndef LLD_ELF_STRINGS_H
|
||||
#define LLD_ELF_STRINGS_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/BitVector.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
@ -66,10 +66,6 @@ private:
|
||||
std::vector<llvm::GlobPattern> Patterns;
|
||||
};
|
||||
|
||||
// Returns a demangled C++ symbol name. If Name is not a mangled
|
||||
// name, it returns Optional::None.
|
||||
llvm::Optional<std::string> demangle(StringRef Name);
|
||||
|
||||
inline ArrayRef<uint8_t> toArrayRef(StringRef S) {
|
||||
return {(const uint8_t *)S.data(), S.size()};
|
||||
}
|
||||
|
@ -16,10 +16,12 @@
|
||||
|
||||
#include "SymbolTable.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -29,6 +31,16 @@ using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
SymbolTable *elf::Symtab;
|
||||
|
||||
static InputFile *getFirstElf() {
|
||||
if (!ObjectFiles.empty())
|
||||
return ObjectFiles[0];
|
||||
if (!SharedFiles.empty())
|
||||
return SharedFiles[0];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// All input object files must be for the same architecture
|
||||
// (e.g. it does not make sense to link x86 object files with
|
||||
// MIPS object files.) This function checks for that error.
|
||||
@ -46,15 +58,12 @@ template <class ELFT> static bool isCompatible(InputFile *F) {
|
||||
if (!Config->Emulation.empty())
|
||||
error(toString(F) + " is incompatible with " + Config->Emulation);
|
||||
else
|
||||
error(toString(F) + " is incompatible with " + toString(Config->FirstElf));
|
||||
error(toString(F) + " is incompatible with " + toString(getFirstElf()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add symbols in File to the symbol table.
|
||||
template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
|
||||
if (!Config->FirstElf && isa<ELFFileBase<ELFT>>(File))
|
||||
Config->FirstElf = File;
|
||||
|
||||
template <class ELFT> void SymbolTable::addFile(InputFile *File) {
|
||||
if (!isCompatible<ELFT>(File))
|
||||
return;
|
||||
|
||||
@ -72,7 +81,7 @@ template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
|
||||
}
|
||||
|
||||
// Lazy object file
|
||||
if (auto *F = dyn_cast<LazyObjectFile>(File)) {
|
||||
if (auto *F = dyn_cast<LazyObjFile>(File)) {
|
||||
F->parse<ELFT>();
|
||||
return;
|
||||
}
|
||||
@ -84,7 +93,7 @@ template <class ELFT> void SymbolTable<ELFT>::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() || !SoNames.insert(F->SoName).second)
|
||||
return;
|
||||
SharedFiles.push_back(F);
|
||||
F->parseRest();
|
||||
@ -99,9 +108,8 @@ template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
|
||||
}
|
||||
|
||||
// Regular object file
|
||||
auto *F = cast<ObjectFile<ELFT>>(File);
|
||||
ObjectFiles.push_back(F);
|
||||
F->parse(ComdatGroups);
|
||||
ObjectFiles.push_back(File);
|
||||
cast<ObjFile<ELFT>>(File)->parse(ComdatGroups);
|
||||
}
|
||||
|
||||
// This function is where all the optimizations of link-time
|
||||
@ -111,7 +119,7 @@ template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
|
||||
// using LLVM functions and replaces bitcode symbols with the results.
|
||||
// Because all bitcode files that consist of a program are passed
|
||||
// to the compiler at once, it can do whole-program optimization.
|
||||
template <class ELFT> void SymbolTable<ELFT>::addCombinedLTOObject() {
|
||||
template <class ELFT> void SymbolTable::addCombinedLTOObject() {
|
||||
if (BitcodeFiles.empty())
|
||||
return;
|
||||
|
||||
@ -121,82 +129,77 @@ template <class ELFT> void SymbolTable<ELFT>::addCombinedLTOObject() {
|
||||
LTO->add(*F);
|
||||
|
||||
for (InputFile *File : LTO->compile()) {
|
||||
ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(File);
|
||||
DenseSet<CachedHashStringRef> DummyGroups;
|
||||
Obj->parse(DummyGroups);
|
||||
ObjectFiles.push_back(Obj);
|
||||
cast<ObjFile<ELFT>>(File)->parse(DummyGroups);
|
||||
ObjectFiles.push_back(File);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
DefinedRegular *SymbolTable<ELFT>::addAbsolute(StringRef Name,
|
||||
uint8_t Visibility,
|
||||
uint8_t Binding) {
|
||||
Symbol *Sym =
|
||||
addRegular(Name, Visibility, STT_NOTYPE, 0, 0, Binding, nullptr, nullptr);
|
||||
return cast<DefinedRegular>(Sym->body());
|
||||
}
|
||||
|
||||
// Add Name as an "ignored" symbol. An ignored symbol is a regular
|
||||
// linker-synthesized defined symbol, but is only defined if needed.
|
||||
template <class ELFT>
|
||||
DefinedRegular *SymbolTable<ELFT>::addIgnored(StringRef Name,
|
||||
uint8_t Visibility) {
|
||||
SymbolBody *S = find(Name);
|
||||
if (!S || S->isInCurrentDSO())
|
||||
return nullptr;
|
||||
return addAbsolute(Name, Visibility);
|
||||
Defined *SymbolTable::addAbsolute(StringRef Name, uint8_t Visibility,
|
||||
uint8_t Binding) {
|
||||
Symbol *Sym = addRegular<ELFT>(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.
|
||||
template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) {
|
||||
Symtab.insert({CachedHashStringRef(Name), {-1, true}});
|
||||
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<ELFT>::addSymbolWrap(StringRef Name) {
|
||||
SymbolBody *B = find(Name);
|
||||
if (!B)
|
||||
template <class ELFT> void SymbolTable::addSymbolWrap(StringRef Name) {
|
||||
Symbol *Sym = find(Name);
|
||||
if (!Sym)
|
||||
return;
|
||||
Symbol *Sym = B->symbol();
|
||||
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
|
||||
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
|
||||
Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name));
|
||||
Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name));
|
||||
WrappedSymbols.push_back({Sym, Real, Wrap});
|
||||
|
||||
// Tell LTO not to eliminate this symbol
|
||||
Wrap->IsUsedInRegularObj = true;
|
||||
// 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;
|
||||
|
||||
Config->RenamedSymbols[Real] = {Sym, Real->Binding};
|
||||
Config->RenamedSymbols[Sym] = {Wrap, Sym->Binding};
|
||||
}
|
||||
|
||||
// Creates alias for symbol. Used to implement --defsym=ALIAS=SYM.
|
||||
template <class ELFT>
|
||||
void SymbolTable<ELFT>::addSymbolAlias(StringRef Alias, StringRef Name) {
|
||||
SymbolBody *B = find(Name);
|
||||
if (!B) {
|
||||
error("-defsym: undefined symbol: " + Name);
|
||||
return;
|
||||
}
|
||||
Symbol *Sym = B->symbol();
|
||||
Symbol *AliasSym = addUndefined(Alias);
|
||||
|
||||
// Tell LTO not to eliminate this symbol
|
||||
// Tell LTO not to eliminate these symbols.
|
||||
Sym->IsUsedInRegularObj = true;
|
||||
Config->RenamedSymbols[AliasSym] = {Sym, AliasSym->Binding};
|
||||
Wrap->IsUsedInRegularObj = true;
|
||||
}
|
||||
|
||||
// Apply symbol renames created by -wrap and -defsym. The renames are created
|
||||
// before LTO in addSymbolWrap() and addSymbolAlias() to have a chance to inform
|
||||
// LTO (if LTO is running) not to include these symbols in IPO. Now that the
|
||||
// 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.
|
||||
template <class ELFT> void SymbolTable<ELFT>::applySymbolRenames() {
|
||||
for (auto &KV : Config->RenamedSymbols) {
|
||||
Symbol *Dst = KV.first;
|
||||
Symbol *Src = KV.second.Target;
|
||||
Dst->body()->copy(Src->body());
|
||||
Dst->Binding = KV.second.OriginalBinding;
|
||||
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 = (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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,48 +212,50 @@ static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
|
||||
}
|
||||
|
||||
// Find an existing symbol or create and insert a new one.
|
||||
template <class ELFT>
|
||||
std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
|
||||
// <name>@@<version> means the symbol is the default version. In that
|
||||
// case <name>@@<version> will be used to resolve references to <name>.
|
||||
size_t Pos = Name.find("@@");
|
||||
if (Pos != StringRef::npos)
|
||||
//
|
||||
// Since this is a hot path, the following string search code is
|
||||
// optimized for speed. StringRef::find(char) is much faster than
|
||||
// StringRef::find(StringRef).
|
||||
size_t Pos = Name.find('@');
|
||||
if (Pos != StringRef::npos && Pos + 1 < Name.size() && Name[Pos + 1] == '@')
|
||||
Name = Name.take_front(Pos);
|
||||
|
||||
auto P = Symtab.insert(
|
||||
{CachedHashStringRef(Name), SymIndex((int)SymVector.size(), false)});
|
||||
SymIndex &V = P.first->second;
|
||||
auto P = SymMap.insert({CachedHashStringRef(Name), (int)SymVector.size()});
|
||||
int &SymIndex = P.first->second;
|
||||
bool IsNew = P.second;
|
||||
bool Traced = false;
|
||||
|
||||
if (V.Idx == -1) {
|
||||
IsNew = true;
|
||||
V = SymIndex((int)SymVector.size(), true);
|
||||
if (SymIndex == -1) {
|
||||
SymIndex = SymVector.size();
|
||||
IsNew = Traced = true;
|
||||
}
|
||||
|
||||
Symbol *Sym;
|
||||
if (IsNew) {
|
||||
Sym = make<Symbol>();
|
||||
Sym = (Symbol *)make<SymbolUnion>();
|
||||
Sym->InVersionScript = false;
|
||||
Sym->Binding = STB_WEAK;
|
||||
Sym->Visibility = STV_DEFAULT;
|
||||
Sym->IsUsedInRegularObj = false;
|
||||
Sym->ExportDynamic = false;
|
||||
Sym->Traced = V.Traced;
|
||||
Sym->CanInline = true;
|
||||
Sym->Traced = Traced;
|
||||
Sym->VersionId = Config->DefaultSymbolVersion;
|
||||
SymVector.push_back(Sym);
|
||||
} else {
|
||||
Sym = SymVector[V.Idx];
|
||||
Sym = SymVector[SymIndex];
|
||||
}
|
||||
return {Sym, IsNew};
|
||||
}
|
||||
|
||||
// Find an existing symbol or create and insert a new one, then apply the given
|
||||
// attributes.
|
||||
template <class ELFT>
|
||||
std::pair<Symbol *, bool>
|
||||
SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility,
|
||||
bool CanOmitFromDynSym, InputFile *File) {
|
||||
bool IsUsedInRegularObj = !File || File->kind() == InputFile::ObjectKind;
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, uint8_t Type,
|
||||
uint8_t Visibility,
|
||||
bool CanOmitFromDynSym,
|
||||
InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
@ -261,32 +266,30 @@ SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility,
|
||||
if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic))
|
||||
S->ExportDynamic = true;
|
||||
|
||||
if (IsUsedInRegularObj)
|
||||
if (!File || File->kind() == InputFile::ObjKind)
|
||||
S->IsUsedInRegularObj = true;
|
||||
|
||||
if (!WasInserted && S->body()->Type != SymbolBody::UnknownType &&
|
||||
((Type == STT_TLS) != S->body()->isTls())) {
|
||||
error("TLS attribute mismatch: " + toString(*S->body()) +
|
||||
"\n>>> defined in " + toString(S->body()->File) +
|
||||
"\n>>> defined in " + toString(File));
|
||||
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<ELFT>::addUndefined(StringRef Name) {
|
||||
return addUndefined(Name, /*IsLocal=*/false, STB_GLOBAL, STV_DEFAULT,
|
||||
/*Type*/ 0,
|
||||
/*CanOmitFromDynSym*/ false, /*File*/ nullptr);
|
||||
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>
|
||||
Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
|
||||
uint8_t Binding, uint8_t StOther,
|
||||
uint8_t Type, bool CanOmitFromDynSym,
|
||||
InputFile *File) {
|
||||
Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type,
|
||||
bool CanOmitFromDynSym, InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
uint8_t Visibility = getVisibility(StOther);
|
||||
@ -294,26 +297,24 @@ Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
|
||||
insert(Name, Type, Visibility, CanOmitFromDynSym, File);
|
||||
// An undefined symbol with non default visibility must be satisfied
|
||||
// in the same DSO.
|
||||
if (WasInserted ||
|
||||
(isa<SharedSymbol>(S->body()) && Visibility != STV_DEFAULT)) {
|
||||
S->Binding = Binding;
|
||||
replaceBody<Undefined>(S, Name, IsLocal, StOther, Type, File);
|
||||
if (WasInserted || (isa<SharedSymbol>(S) && Visibility != STV_DEFAULT)) {
|
||||
replaceSymbol<Undefined>(S, File, Name, Binding, StOther, Type);
|
||||
return S;
|
||||
}
|
||||
if (S->isShared() || S->isLazy() || (S->isUndefined() && Binding != STB_WEAK))
|
||||
S->Binding = Binding;
|
||||
if (Binding != STB_WEAK) {
|
||||
SymbolBody *B = S->body();
|
||||
if (B->isShared() || B->isLazy() || B->isUndefined())
|
||||
S->Binding = Binding;
|
||||
if (auto *SS = dyn_cast<SharedSymbol>(B))
|
||||
cast<SharedFile<ELFT>>(SS->File)->IsUsed = true;
|
||||
if (auto *SS = dyn_cast<SharedSymbol>(S))
|
||||
if (!Config->GcSections)
|
||||
SS->getFile<ELFT>()->IsNeeded = true;
|
||||
}
|
||||
if (auto *L = dyn_cast<Lazy>(S->body())) {
|
||||
// An undefined weak will not fetch archive members, but we have to remember
|
||||
// its type. See also comment in addLazyArchive.
|
||||
if (S->isWeak())
|
||||
if (auto *L = dyn_cast<Lazy>(S)) {
|
||||
// An undefined weak will not fetch archive members. See comment on Lazy in
|
||||
// Symbols.h for the details.
|
||||
if (Binding == STB_WEAK)
|
||||
L->Type = Type;
|
||||
else if (InputFile *F = L->fetch())
|
||||
addFile(F);
|
||||
addFile<ELFT>(F);
|
||||
}
|
||||
return S;
|
||||
}
|
||||
@ -325,11 +326,11 @@ Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
|
||||
// .symver foo,foo@@@VER
|
||||
// we can delete this hack.
|
||||
static int compareVersion(Symbol *S, StringRef Name) {
|
||||
if (Name.find("@@") != StringRef::npos &&
|
||||
S->body()->getName().find("@@") == StringRef::npos)
|
||||
bool A = Name.contains("@@");
|
||||
bool B = S->getName().contains("@@");
|
||||
if (A && !B)
|
||||
return 1;
|
||||
if (Name.find("@@") == StringRef::npos &&
|
||||
S->body()->getName().find("@@") != StringRef::npos)
|
||||
if (!A && B)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
@ -341,13 +342,10 @@ static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding,
|
||||
StringRef Name) {
|
||||
if (WasInserted)
|
||||
return 1;
|
||||
SymbolBody *Body = S->body();
|
||||
if (!Body->isInCurrentDSO())
|
||||
if (!S->isDefined())
|
||||
return 1;
|
||||
|
||||
if (int R = compareVersion(S, Name))
|
||||
return R;
|
||||
|
||||
if (Binding == STB_WEAK)
|
||||
return -1;
|
||||
if (S->isWeak())
|
||||
@ -358,22 +356,18 @@ static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding,
|
||||
// We have a new non-common defined symbol with the specified binding. Return 1
|
||||
// if the new symbol should win, -1 if the new symbol should lose, or 0 if there
|
||||
// is a conflict. If the new symbol wins, also update the binding.
|
||||
template <typename ELFT>
|
||||
static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
|
||||
bool IsAbsolute, typename ELFT::uint Value,
|
||||
bool IsAbsolute, uint64_t Value,
|
||||
StringRef Name) {
|
||||
if (int Cmp = compareDefined(S, WasInserted, Binding, Name)) {
|
||||
if (Cmp > 0)
|
||||
S->Binding = Binding;
|
||||
if (int Cmp = compareDefined(S, WasInserted, Binding, Name))
|
||||
return Cmp;
|
||||
}
|
||||
SymbolBody *B = S->body();
|
||||
if (isa<DefinedCommon>(B)) {
|
||||
// Non-common symbols take precedence over common symbols.
|
||||
if (Config->WarnCommon)
|
||||
warn("common " + S->body()->getName() + " is overridden");
|
||||
return 1;
|
||||
} else if (auto *R = dyn_cast<DefinedRegular>(B)) {
|
||||
if (auto *R = dyn_cast<Defined>(S)) {
|
||||
if (R->Section && isa<BssSection>(R->Section)) {
|
||||
// Non-common symbols take precedence over common symbols.
|
||||
if (Config->WarnCommon)
|
||||
warn("common " + S->getName() + " is overridden");
|
||||
return 1;
|
||||
}
|
||||
if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute &&
|
||||
R->Value == Value)
|
||||
return -1;
|
||||
@ -381,34 +375,39 @@ static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
|
||||
uint32_t Alignment, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type,
|
||||
InputFile *File) {
|
||||
Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
|
||||
uint8_t Binding, uint8_t StOther, uint8_t Type,
|
||||
InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N, Type, getVisibility(StOther),
|
||||
/*CanOmitFromDynSym*/ false, File);
|
||||
int Cmp = compareDefined(S, WasInserted, Binding, N);
|
||||
if (Cmp > 0) {
|
||||
S->Binding = Binding;
|
||||
replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
|
||||
auto *Bss = make<BssSection>("COMMON", Size, Alignment);
|
||||
Bss->File = File;
|
||||
Bss->Live = !Config->GcSections;
|
||||
InputSections.push_back(Bss);
|
||||
|
||||
replaceSymbol<Defined>(S, File, N, Binding, StOther, Type, 0, Size, Bss);
|
||||
} else if (Cmp == 0) {
|
||||
auto *C = dyn_cast<DefinedCommon>(S->body());
|
||||
if (!C) {
|
||||
auto *D = cast<Defined>(S);
|
||||
auto *Bss = dyn_cast_or_null<BssSection>(D->Section);
|
||||
if (!Bss) {
|
||||
// Non-common symbols take precedence over common symbols.
|
||||
if (Config->WarnCommon)
|
||||
warn("common " + S->body()->getName() + " is overridden");
|
||||
warn("common " + S->getName() + " is overridden");
|
||||
return S;
|
||||
}
|
||||
|
||||
if (Config->WarnCommon)
|
||||
warn("multiple common of " + S->body()->getName());
|
||||
warn("multiple common of " + D->getName());
|
||||
|
||||
Alignment = C->Alignment = std::max(C->Alignment, Alignment);
|
||||
if (Size > C->Size)
|
||||
replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
|
||||
Bss->Alignment = std::max(Bss->Alignment, Alignment);
|
||||
if (Size > Bss->Size) {
|
||||
D->File = Bss->File = File;
|
||||
D->Size = Bss->Size = Size;
|
||||
}
|
||||
}
|
||||
return S;
|
||||
}
|
||||
@ -420,17 +419,17 @@ static void warnOrError(const Twine &Msg) {
|
||||
error(Msg);
|
||||
}
|
||||
|
||||
static void reportDuplicate(SymbolBody *Sym, InputFile *NewFile) {
|
||||
static void reportDuplicate(Symbol *Sym, InputFile *NewFile) {
|
||||
warnOrError("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
|
||||
toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
|
||||
static void reportDuplicate(Symbol *Sym, InputSectionBase *ErrSec,
|
||||
typename ELFT::uint ErrOffset) {
|
||||
DefinedRegular *D = dyn_cast<DefinedRegular>(Sym);
|
||||
if (!D || !D->Section || !ErrSec) {
|
||||
reportDuplicate(Sym, ErrSec ? ErrSec->getFile<ELFT>() : nullptr);
|
||||
Defined *D = cast<Defined>(Sym);
|
||||
if (!D->Section || !ErrSec) {
|
||||
reportDuplicate(Sym, ErrSec ? ErrSec->File : nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -442,10 +441,10 @@ static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
|
||||
// >>> defined at baz.c:563
|
||||
// >>> baz.o in archive libbaz.a
|
||||
auto *Sec1 = cast<InputSectionBase>(D->Section);
|
||||
std::string Src1 = Sec1->getSrcMsg<ELFT>(D->Value);
|
||||
std::string Obj1 = Sec1->getObjMsg<ELFT>(D->Value);
|
||||
std::string Src2 = ErrSec->getSrcMsg<ELFT>(ErrOffset);
|
||||
std::string Obj2 = ErrSec->getObjMsg<ELFT>(ErrOffset);
|
||||
std::string Src1 = Sec1->getSrcMsg<ELFT>(*Sym, D->Value);
|
||||
std::string Obj1 = Sec1->getObjMsg(D->Value);
|
||||
std::string Src2 = ErrSec->getSrcMsg<ELFT>(*Sym, ErrOffset);
|
||||
std::string Obj2 = ErrSec->getObjMsg(ErrOffset);
|
||||
|
||||
std::string Msg = "duplicate symbol: " + toString(*Sym) + "\n>>> defined at ";
|
||||
if (!Src1.empty())
|
||||
@ -458,29 +457,28 @@ static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t StOther,
|
||||
uint8_t Type, uint64_t Value,
|
||||
uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File) {
|
||||
Symbol *SymbolTable::addRegular(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),
|
||||
/*CanOmitFromDynSym*/ false, File);
|
||||
int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
|
||||
Section == nullptr, Value, Name);
|
||||
int Cmp = compareDefinedNonCommon(S, WasInserted, Binding, Section == nullptr,
|
||||
Value, Name);
|
||||
if (Cmp > 0)
|
||||
replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type,
|
||||
Value, Size, Section, File);
|
||||
replaceSymbol<Defined>(S, File, Name, Binding, StOther, Type, Value, Size,
|
||||
Section);
|
||||
else if (Cmp == 0)
|
||||
reportDuplicate<ELFT>(S->body(),
|
||||
dyn_cast_or_null<InputSectionBase>(Section), Value);
|
||||
reportDuplicate<ELFT>(S, dyn_cast_or_null<InputSectionBase>(Section),
|
||||
Value);
|
||||
return S;
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *File, StringRef Name,
|
||||
const Elf_Sym &Sym,
|
||||
const typename ELFT::Verdef *Verdef) {
|
||||
void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> *File,
|
||||
const typename ELFT::Sym &Sym, uint32_t Alignment,
|
||||
uint32_t VerdefIndex) {
|
||||
// DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT
|
||||
// as the visibility, which will leave the visibility in the symbol table
|
||||
// unchanged.
|
||||
@ -492,110 +490,102 @@ void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *File, StringRef Name,
|
||||
if (Sym.getVisibility() == STV_DEFAULT)
|
||||
S->ExportDynamic = true;
|
||||
|
||||
SymbolBody *Body = S->body();
|
||||
// An undefined symbol with non default visibility must be satisfied
|
||||
// in the same DSO.
|
||||
if (WasInserted ||
|
||||
(isa<Undefined>(Body) && Body->getVisibility() == STV_DEFAULT)) {
|
||||
replaceBody<SharedSymbol>(S, File, Name, Sym.st_other, Sym.getType(), &Sym,
|
||||
Verdef);
|
||||
if (!S->isWeak())
|
||||
File->IsUsed = true;
|
||||
if (WasInserted || ((S->isUndefined() || S->isLazy()) &&
|
||||
S->getVisibility() == STV_DEFAULT)) {
|
||||
uint8_t Binding = S->Binding;
|
||||
replaceSymbol<SharedSymbol>(S, File, Name, Sym.getBinding(), Sym.st_other,
|
||||
Sym.getType(), Sym.st_value, Sym.st_size,
|
||||
Alignment, VerdefIndex);
|
||||
if (!WasInserted) {
|
||||
S->Binding = Binding;
|
||||
if (!S->isWeak() && !Config->GcSections)
|
||||
File->IsNeeded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type,
|
||||
bool CanOmitFromDynSym, BitcodeFile *F) {
|
||||
Symbol *SymbolTable::addBitcode(StringRef Name, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type,
|
||||
bool CanOmitFromDynSym, BitcodeFile *F) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) =
|
||||
insert(Name, Type, getVisibility(StOther), CanOmitFromDynSym, F);
|
||||
int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
|
||||
/*IsAbs*/ false, /*Value*/ 0, Name);
|
||||
int Cmp = compareDefinedNonCommon(S, WasInserted, Binding,
|
||||
/*IsAbs*/ false, /*Value*/ 0, Name);
|
||||
if (Cmp > 0)
|
||||
replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type, 0, 0,
|
||||
nullptr, F);
|
||||
replaceSymbol<Defined>(S, F, Name, Binding, StOther, Type, 0, 0, nullptr);
|
||||
else if (Cmp == 0)
|
||||
reportDuplicate(S->body(), F);
|
||||
reportDuplicate(S, F);
|
||||
return S;
|
||||
}
|
||||
|
||||
template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
|
||||
auto It = Symtab.find(CachedHashStringRef(Name));
|
||||
if (It == Symtab.end())
|
||||
Symbol *SymbolTable::find(StringRef Name) {
|
||||
auto It = SymMap.find(CachedHashStringRef(Name));
|
||||
if (It == SymMap.end())
|
||||
return nullptr;
|
||||
SymIndex V = It->second;
|
||||
if (V.Idx == -1)
|
||||
if (It->second == -1)
|
||||
return nullptr;
|
||||
return SymVector[V.Idx]->body();
|
||||
return SymVector[It->second];
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
SymbolBody *SymbolTable<ELFT>::findInCurrentDSO(StringRef Name) {
|
||||
if (SymbolBody *S = find(Name))
|
||||
if (S->isInCurrentDSO())
|
||||
return S;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addLazyArchive(ArchiveFile *F,
|
||||
const object::Archive::Symbol Sym) {
|
||||
Symbol *SymbolTable::addLazyArchive(StringRef Name, ArchiveFile *F,
|
||||
const object::Archive::Symbol Sym) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
StringRef Name = Sym.getName();
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
if (WasInserted) {
|
||||
replaceBody<LazyArchive>(S, *F, Sym, SymbolBody::UnknownType);
|
||||
replaceSymbol<LazyArchive>(S, F, Sym, Symbol::UnknownType);
|
||||
return S;
|
||||
}
|
||||
if (!S->body()->isUndefined())
|
||||
if (!S->isUndefined())
|
||||
return S;
|
||||
|
||||
// Weak undefined symbols should not fetch members from archives. If we were
|
||||
// to keep old symbol we would not know that an archive member was available
|
||||
// if a strong undefined symbol shows up afterwards in the link. If a strong
|
||||
// undefined symbol never shows up, this lazy symbol will get to the end of
|
||||
// the link and must be treated as the weak undefined one. We already marked
|
||||
// this symbol as used when we added it to the symbol table, but we also need
|
||||
// to preserve its type. FIXME: Move the Type field to Symbol.
|
||||
// An undefined weak will not fetch archive members. See comment on Lazy in
|
||||
// Symbols.h for the details.
|
||||
if (S->isWeak()) {
|
||||
replaceBody<LazyArchive>(S, *F, Sym, S->body()->Type);
|
||||
replaceSymbol<LazyArchive>(S, F, Sym, S->Type);
|
||||
S->Binding = STB_WEAK;
|
||||
return S;
|
||||
}
|
||||
std::pair<MemoryBufferRef, uint64_t> MBInfo = F->getMember(&Sym);
|
||||
if (!MBInfo.first.getBuffer().empty())
|
||||
addFile(createObjectFile(MBInfo.first, F->getName(), MBInfo.second));
|
||||
addFile<ELFT>(createObjectFile(MBInfo.first, F->getName(), MBInfo.second));
|
||||
return S;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void SymbolTable<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) {
|
||||
void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &Obj) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
if (WasInserted) {
|
||||
replaceBody<LazyObject>(S, Name, Obj, SymbolBody::UnknownType);
|
||||
replaceSymbol<LazyObject>(S, &Obj, Name, Symbol::UnknownType);
|
||||
return;
|
||||
}
|
||||
if (!S->body()->isUndefined())
|
||||
if (!S->isUndefined())
|
||||
return;
|
||||
|
||||
// See comment for addLazyArchive above.
|
||||
if (S->isWeak())
|
||||
replaceBody<LazyObject>(S, Name, Obj, S->body()->Type);
|
||||
replaceSymbol<LazyObject>(S, &Obj, Name, S->Type);
|
||||
else if (InputFile *F = Obj.fetch())
|
||||
addFile(F);
|
||||
addFile<ELFT>(F);
|
||||
}
|
||||
|
||||
// Process undefined (-u) flags by loading lazy symbols named by those flags.
|
||||
template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
|
||||
for (StringRef S : Config->Undefined)
|
||||
if (auto *L = dyn_cast_or_null<Lazy>(find(S)))
|
||||
// If we already saw this symbol, force loading its file.
|
||||
template <class ELFT> void SymbolTable::fetchIfLazy(StringRef Name) {
|
||||
if (Symbol *B = find(Name)) {
|
||||
// Mark the symbol not to be eliminated by LTO
|
||||
// even if it is a bitcode symbol.
|
||||
B->IsUsedInRegularObj = true;
|
||||
if (auto *L = dyn_cast_or_null<Lazy>(B))
|
||||
if (InputFile *File = L->fetch())
|
||||
addFile(File);
|
||||
addFile<ELFT>(File);
|
||||
}
|
||||
}
|
||||
|
||||
// This function takes care of the case in which shared libraries depend on
|
||||
@ -605,19 +595,19 @@ template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
|
||||
// We need to put such symbols to the main program's .dynsym so that
|
||||
// shared libraries can find them.
|
||||
// Except this, we ignore undefined symbols in DSOs.
|
||||
template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
|
||||
for (SharedFile<ELFT> *File : SharedFiles) {
|
||||
for (StringRef U : File->getUndefinedSymbols()) {
|
||||
SymbolBody *Sym = find(U);
|
||||
template <class ELFT> void SymbolTable::scanShlibUndefined() {
|
||||
for (InputFile *F : SharedFiles) {
|
||||
for (StringRef U : cast<SharedFile<ELFT>>(F)->getUndefinedSymbols()) {
|
||||
Symbol *Sym = find(U);
|
||||
if (!Sym || !Sym->isDefined())
|
||||
continue;
|
||||
Sym->symbol()->ExportDynamic = true;
|
||||
Sym->ExportDynamic = true;
|
||||
|
||||
// If -dynamic-list is given, the default version is set to
|
||||
// VER_NDX_LOCAL, which prevents a symbol to be exported via .dynsym.
|
||||
// Set to VER_NDX_GLOBAL so the symbol will be handled as if it were
|
||||
// specified by -dynamic-list.
|
||||
Sym->symbol()->VersionId = VER_NDX_GLOBAL;
|
||||
Sym->VersionId = VER_NDX_GLOBAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -635,37 +625,32 @@ template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
|
||||
// other than trying to match a pattern against all demangled symbols.
|
||||
// So, if "extern C++" feature is used, we need to demangle all known
|
||||
// symbols.
|
||||
template <class ELFT>
|
||||
StringMap<std::vector<SymbolBody *>> &SymbolTable<ELFT>::getDemangledSyms() {
|
||||
StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
|
||||
if (!DemangledSyms) {
|
||||
DemangledSyms.emplace();
|
||||
for (Symbol *Sym : SymVector) {
|
||||
SymbolBody *B = Sym->body();
|
||||
if (B->isUndefined())
|
||||
if (!Sym->isDefined())
|
||||
continue;
|
||||
if (Optional<std::string> S = demangle(B->getName()))
|
||||
(*DemangledSyms)[*S].push_back(B);
|
||||
if (Optional<std::string> S = demangleItanium(Sym->getName()))
|
||||
(*DemangledSyms)[*S].push_back(Sym);
|
||||
else
|
||||
(*DemangledSyms)[B->getName()].push_back(B);
|
||||
(*DemangledSyms)[Sym->getName()].push_back(Sym);
|
||||
}
|
||||
}
|
||||
return *DemangledSyms;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::vector<SymbolBody *> SymbolTable<ELFT>::findByVersion(SymbolVersion Ver) {
|
||||
std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion Ver) {
|
||||
if (Ver.IsExternCpp)
|
||||
return getDemangledSyms().lookup(Ver.Name);
|
||||
if (SymbolBody *B = find(Ver.Name))
|
||||
if (!B->isUndefined())
|
||||
if (Symbol *B = find(Ver.Name))
|
||||
if (B->isDefined())
|
||||
return {B};
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::vector<SymbolBody *>
|
||||
SymbolTable<ELFT>::findAllByVersion(SymbolVersion Ver) {
|
||||
std::vector<SymbolBody *> Res;
|
||||
std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion Ver) {
|
||||
std::vector<Symbol *> Res;
|
||||
StringMatcher M(Ver.Name);
|
||||
|
||||
if (Ver.IsExternCpp) {
|
||||
@ -675,18 +660,16 @@ SymbolTable<ELFT>::findAllByVersion(SymbolVersion Ver) {
|
||||
return Res;
|
||||
}
|
||||
|
||||
for (Symbol *Sym : SymVector) {
|
||||
SymbolBody *B = Sym->body();
|
||||
if (!B->isUndefined() && M.match(B->getName()))
|
||||
Res.push_back(B);
|
||||
}
|
||||
for (Symbol *Sym : SymVector)
|
||||
if (Sym->isDefined() && M.match(Sym->getName()))
|
||||
Res.push_back(Sym);
|
||||
return Res;
|
||||
}
|
||||
|
||||
// If there's only one anonymous version definition in a version
|
||||
// script file, the script does not actually define any symbol version,
|
||||
// but just specifies symbols visibilities.
|
||||
template <class ELFT> void SymbolTable<ELFT>::handleAnonymousVersion() {
|
||||
void SymbolTable::handleAnonymousVersion() {
|
||||
for (SymbolVersion &Ver : Config->VersionScriptGlobals)
|
||||
assignExactVersion(Ver, VER_NDX_GLOBAL, "global");
|
||||
for (SymbolVersion &Ver : Config->VersionScriptGlobals)
|
||||
@ -697,17 +680,33 @@ template <class ELFT> void SymbolTable<ELFT>::handleAnonymousVersion() {
|
||||
assignWildcardVersion(Ver, VER_NDX_LOCAL);
|
||||
}
|
||||
|
||||
// Handles -dynamic-list.
|
||||
void SymbolTable::handleDynamicList() {
|
||||
for (SymbolVersion &Ver : Config->DynamicList) {
|
||||
std::vector<Symbol *> Syms;
|
||||
if (Ver.HasWildcard)
|
||||
Syms = findAllByVersion(Ver);
|
||||
else
|
||||
Syms = findByVersion(Ver);
|
||||
|
||||
for (Symbol *B : Syms) {
|
||||
if (!Config->Shared)
|
||||
B->ExportDynamic = true;
|
||||
else if (B->includeInDynsym())
|
||||
B->IsPreemptible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set symbol versions to symbols. This function handles patterns
|
||||
// containing no wildcard characters.
|
||||
template <class ELFT>
|
||||
void SymbolTable<ELFT>::assignExactVersion(SymbolVersion Ver,
|
||||
uint16_t VersionId,
|
||||
StringRef VersionName) {
|
||||
void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
|
||||
StringRef VersionName) {
|
||||
if (Ver.HasWildcard)
|
||||
return;
|
||||
|
||||
// Get a list of symbols which we need to assign the version to.
|
||||
std::vector<SymbolBody *> Syms = findByVersion(Ver);
|
||||
std::vector<Symbol *> Syms = findByVersion(Ver);
|
||||
if (Syms.empty()) {
|
||||
if (Config->NoUndefinedVersion)
|
||||
error("version script assignment of '" + VersionName + "' to symbol '" +
|
||||
@ -716,14 +715,13 @@ void SymbolTable<ELFT>::assignExactVersion(SymbolVersion Ver,
|
||||
}
|
||||
|
||||
// Assign the version.
|
||||
for (SymbolBody *B : Syms) {
|
||||
for (Symbol *Sym : Syms) {
|
||||
// Skip symbols containing version info because symbol versions
|
||||
// specified by symbol names take precedence over version scripts.
|
||||
// See parseSymbolVersion().
|
||||
if (B->getName().find('@') != StringRef::npos)
|
||||
if (Sym->getName().contains('@'))
|
||||
continue;
|
||||
|
||||
Symbol *Sym = B->symbol();
|
||||
if (Sym->InVersionScript)
|
||||
warn("duplicate symbol '" + Ver.Name + "' in version script");
|
||||
Sym->VersionId = VersionId;
|
||||
@ -731,25 +729,24 @@ void SymbolTable<ELFT>::assignExactVersion(SymbolVersion Ver,
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void SymbolTable<ELFT>::assignWildcardVersion(SymbolVersion Ver,
|
||||
uint16_t VersionId) {
|
||||
void SymbolTable::assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId) {
|
||||
if (!Ver.HasWildcard)
|
||||
return;
|
||||
|
||||
// Exact matching takes precendence over fuzzy matching,
|
||||
// so we set a version to a symbol only if no version has been assigned
|
||||
// to the symbol. This behavior is compatible with GNU.
|
||||
for (SymbolBody *B : findAllByVersion(Ver))
|
||||
if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
|
||||
B->symbol()->VersionId = VersionId;
|
||||
for (Symbol *B : findAllByVersion(Ver))
|
||||
if (B->VersionId == Config->DefaultSymbolVersion)
|
||||
B->VersionId = VersionId;
|
||||
}
|
||||
|
||||
// This function processes version scripts by updating VersionId
|
||||
// member of symbols.
|
||||
template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
|
||||
void SymbolTable::scanVersionScript() {
|
||||
// Handle edge cases first.
|
||||
handleAnonymousVersion();
|
||||
handleDynamicList();
|
||||
|
||||
// Now we have version definitions, so we need to set version ids to symbols.
|
||||
// Each version definition has a glob pattern, and all symbols that match
|
||||
@ -773,10 +770,92 @@ template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
|
||||
// can contain versions in the form of <name>@<version>.
|
||||
// Let them parse and update their names to exclude version suffix.
|
||||
for (Symbol *Sym : SymVector)
|
||||
Sym->body()->parseSymbolVersion();
|
||||
Sym->parseSymbolVersion();
|
||||
}
|
||||
|
||||
template class elf::SymbolTable<ELF32LE>;
|
||||
template class elf::SymbolTable<ELF32BE>;
|
||||
template class elf::SymbolTable<ELF64LE>;
|
||||
template class elf::SymbolTable<ELF64BE>;
|
||||
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,
|
||||
uint8_t, bool, InputFile *);
|
||||
template Symbol *SymbolTable::addUndefined<ELF64LE>(StringRef, uint8_t, uint8_t,
|
||||
uint8_t, bool, InputFile *);
|
||||
template Symbol *SymbolTable::addUndefined<ELF64BE>(StringRef, uint8_t, uint8_t,
|
||||
uint8_t, bool, InputFile *);
|
||||
|
||||
template void SymbolTable::addCombinedLTOObject<ELF32LE>();
|
||||
template void SymbolTable::addCombinedLTOObject<ELF32BE>();
|
||||
template void SymbolTable::addCombinedLTOObject<ELF64LE>();
|
||||
template void SymbolTable::addCombinedLTOObject<ELF64BE>();
|
||||
|
||||
template Symbol *SymbolTable::addRegular<ELF32LE>(StringRef, uint8_t, uint8_t,
|
||||
uint64_t, uint64_t, uint8_t,
|
||||
SectionBase *, InputFile *);
|
||||
template Symbol *SymbolTable::addRegular<ELF32BE>(StringRef, uint8_t, uint8_t,
|
||||
uint64_t, uint64_t, uint8_t,
|
||||
SectionBase *, InputFile *);
|
||||
template Symbol *SymbolTable::addRegular<ELF64LE>(StringRef, uint8_t, uint8_t,
|
||||
uint64_t, uint64_t, uint8_t,
|
||||
SectionBase *, InputFile *);
|
||||
template Symbol *SymbolTable::addRegular<ELF64BE>(StringRef, uint8_t, uint8_t,
|
||||
uint64_t, uint64_t, uint8_t,
|
||||
SectionBase *, InputFile *);
|
||||
|
||||
template Defined *SymbolTable::addAbsolute<ELF32LE>(StringRef, uint8_t,
|
||||
uint8_t);
|
||||
template Defined *SymbolTable::addAbsolute<ELF32BE>(StringRef, uint8_t,
|
||||
uint8_t);
|
||||
template Defined *SymbolTable::addAbsolute<ELF64LE>(StringRef, uint8_t,
|
||||
uint8_t);
|
||||
template Defined *SymbolTable::addAbsolute<ELF64BE>(StringRef, uint8_t,
|
||||
uint8_t);
|
||||
|
||||
template Symbol *
|
||||
SymbolTable::addLazyArchive<ELF32LE>(StringRef, ArchiveFile *,
|
||||
const object::Archive::Symbol);
|
||||
template Symbol *
|
||||
SymbolTable::addLazyArchive<ELF32BE>(StringRef, ArchiveFile *,
|
||||
const object::Archive::Symbol);
|
||||
template Symbol *
|
||||
SymbolTable::addLazyArchive<ELF64LE>(StringRef, ArchiveFile *,
|
||||
const object::Archive::Symbol);
|
||||
template Symbol *
|
||||
SymbolTable::addLazyArchive<ELF64BE>(StringRef, ArchiveFile *,
|
||||
const object::Archive::Symbol);
|
||||
|
||||
template void SymbolTable::addLazyObject<ELF32LE>(StringRef, LazyObjFile &);
|
||||
template void SymbolTable::addLazyObject<ELF32BE>(StringRef, LazyObjFile &);
|
||||
template void SymbolTable::addLazyObject<ELF64LE>(StringRef, LazyObjFile &);
|
||||
template void SymbolTable::addLazyObject<ELF64BE>(StringRef, LazyObjFile &);
|
||||
|
||||
template void SymbolTable::addShared<ELF32LE>(StringRef, SharedFile<ELF32LE> *,
|
||||
const typename ELF32LE::Sym &,
|
||||
uint32_t Alignment, uint32_t);
|
||||
template void SymbolTable::addShared<ELF32BE>(StringRef, SharedFile<ELF32BE> *,
|
||||
const typename ELF32BE::Sym &,
|
||||
uint32_t Alignment, uint32_t);
|
||||
template void SymbolTable::addShared<ELF64LE>(StringRef, SharedFile<ELF64LE> *,
|
||||
const typename ELF64LE::Sym &,
|
||||
uint32_t Alignment, uint32_t);
|
||||
template void SymbolTable::addShared<ELF64BE>(StringRef, SharedFile<ELF64BE> *,
|
||||
const typename ELF64BE::Sym &,
|
||||
uint32_t Alignment, uint32_t);
|
||||
|
||||
template void SymbolTable::fetchIfLazy<ELF32LE>(StringRef);
|
||||
template void SymbolTable::fetchIfLazy<ELF32BE>(StringRef);
|
||||
template void SymbolTable::fetchIfLazy<ELF64LE>(StringRef);
|
||||
template void SymbolTable::fetchIfLazy<ELF64BE>(StringRef);
|
||||
|
||||
template void SymbolTable::scanShlibUndefined<ELF32LE>();
|
||||
template void SymbolTable::scanShlibUndefined<ELF32BE>();
|
||||
template void SymbolTable::scanShlibUndefined<ELF64LE>();
|
||||
template void SymbolTable::scanShlibUndefined<ELF64BE>();
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
struct Symbol;
|
||||
class Defined;
|
||||
class SectionBase;
|
||||
|
||||
// SymbolTable is a bucket of all known symbols, including defined,
|
||||
// undefined, or lazy symbols (the last one is symbols in archive
|
||||
@ -33,45 +33,44 @@ struct Symbol;
|
||||
// to replace the lazy symbol. The logic is implemented in the
|
||||
// add*() functions, which are called by input files as they are parsed. There
|
||||
// is one add* function per symbol type.
|
||||
template <class ELFT> class SymbolTable {
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
|
||||
class SymbolTable {
|
||||
public:
|
||||
void addFile(InputFile *File);
|
||||
void addCombinedLTOObject();
|
||||
void addSymbolAlias(StringRef Alias, StringRef Name);
|
||||
void addSymbolWrap(StringRef Name);
|
||||
void applySymbolRenames();
|
||||
template <class ELFT> void addFile(InputFile *File);
|
||||
template <class ELFT> void addCombinedLTOObject();
|
||||
template <class ELFT> void addSymbolWrap(StringRef Name);
|
||||
void applySymbolWrap();
|
||||
|
||||
ArrayRef<Symbol *> getSymbols() const { return SymVector; }
|
||||
ArrayRef<ObjectFile<ELFT> *> getObjectFiles() const { return ObjectFiles; }
|
||||
ArrayRef<BinaryFile *> getBinaryFiles() const { return BinaryFiles; }
|
||||
ArrayRef<SharedFile<ELFT> *> getSharedFiles() const { return SharedFiles; }
|
||||
|
||||
DefinedRegular *addAbsolute(StringRef Name,
|
||||
uint8_t Visibility = llvm::ELF::STV_HIDDEN,
|
||||
uint8_t Binding = llvm::ELF::STB_GLOBAL);
|
||||
DefinedRegular *addIgnored(StringRef Name,
|
||||
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
|
||||
|
||||
Symbol *addUndefined(StringRef Name);
|
||||
Symbol *addUndefined(StringRef Name, bool IsLocal, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type, bool CanOmitFromDynSym,
|
||||
InputFile *File);
|
||||
template <class ELFT>
|
||||
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);
|
||||
template <class ELFT>
|
||||
Symbol *addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
uint64_t Value, uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File);
|
||||
|
||||
void addShared(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
|
||||
const typename ELFT::Verdef *Verdef);
|
||||
template <class ELFT>
|
||||
void addShared(StringRef Name, SharedFile<ELFT> *F,
|
||||
const typename ELFT::Sym &Sym, uint32_t Alignment,
|
||||
uint32_t VerdefIndex);
|
||||
|
||||
template <class ELFT>
|
||||
Symbol *addLazyArchive(StringRef Name, ArchiveFile *F,
|
||||
const llvm::object::Archive::Symbol S);
|
||||
|
||||
template <class ELFT> void addLazyObject(StringRef Name, LazyObjFile &Obj);
|
||||
|
||||
Symbol *addLazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S);
|
||||
void addLazyObject(StringRef Name, LazyObjectFile &Obj);
|
||||
Symbol *addBitcode(StringRef Name, uint8_t Binding, uint8_t StOther,
|
||||
uint8_t Type, bool CanOmitFromDynSym, BitcodeFile *File);
|
||||
|
||||
Symbol *addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
|
||||
Symbol *addCommon(StringRef Name, uint64_t Size, uint32_t Alignment,
|
||||
uint8_t Binding, uint8_t StOther, uint8_t Type,
|
||||
InputFile *File);
|
||||
|
||||
@ -80,31 +79,27 @@ public:
|
||||
uint8_t Visibility, bool CanOmitFromDynSym,
|
||||
InputFile *File);
|
||||
|
||||
void scanUndefinedFlags();
|
||||
void scanShlibUndefined();
|
||||
template <class ELFT> void fetchIfLazy(StringRef Name);
|
||||
template <class ELFT> void scanShlibUndefined();
|
||||
void scanVersionScript();
|
||||
|
||||
SymbolBody *find(StringRef Name);
|
||||
SymbolBody *findInCurrentDSO(StringRef Name);
|
||||
Symbol *find(StringRef Name);
|
||||
|
||||
void trace(StringRef Name);
|
||||
|
||||
private:
|
||||
std::vector<SymbolBody *> findByVersion(SymbolVersion Ver);
|
||||
std::vector<SymbolBody *> findAllByVersion(SymbolVersion Ver);
|
||||
void handleDynamicList();
|
||||
|
||||
llvm::StringMap<std::vector<SymbolBody *>> &getDemangledSyms();
|
||||
private:
|
||||
std::vector<Symbol *> findByVersion(SymbolVersion Ver);
|
||||
std::vector<Symbol *> findAllByVersion(SymbolVersion Ver);
|
||||
void defsym(Symbol *Dst, Symbol *Src);
|
||||
|
||||
llvm::StringMap<std::vector<Symbol *>> &getDemangledSyms();
|
||||
void handleAnonymousVersion();
|
||||
void assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
|
||||
StringRef VersionName);
|
||||
void assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId);
|
||||
|
||||
struct SymIndex {
|
||||
SymIndex(int Idx, bool Traced) : Idx(Idx), Traced(Traced) {}
|
||||
int Idx : 31;
|
||||
unsigned Traced : 1;
|
||||
};
|
||||
|
||||
// The order the global symbols are in is not defined. We can use an arbitrary
|
||||
// order, but it has to be reproducible. That is true even when cross linking.
|
||||
// The default hashing of StringRef produces different results on 32 and 64
|
||||
@ -112,7 +107,7 @@ private:
|
||||
// but a bit inefficient.
|
||||
// FIXME: Experiment with passing in a custom hashing or sorting the symbols
|
||||
// once symbol resolution is finished.
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, SymIndex> Symtab;
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, int> SymMap;
|
||||
std::vector<Symbol *> SymVector;
|
||||
|
||||
// Comdat groups define "link once" sections. If two comdat groups have the
|
||||
@ -120,11 +115,6 @@ private:
|
||||
// is used to uniquify them.
|
||||
llvm::DenseSet<llvm::CachedHashStringRef> ComdatGroups;
|
||||
|
||||
std::vector<ObjectFile<ELFT> *> ObjectFiles;
|
||||
std::vector<SharedFile<ELFT> *> SharedFiles;
|
||||
std::vector<BitcodeFile *> BitcodeFiles;
|
||||
std::vector<BinaryFile *> BinaryFiles;
|
||||
|
||||
// Set of .so files to not link the same shared object file more than once.
|
||||
llvm::DenseSet<StringRef> SoNames;
|
||||
|
||||
@ -132,15 +122,22 @@ private:
|
||||
// This mapping is 1:N because two symbols with different versions
|
||||
// can have the same name. We use this map to handle "extern C++ {}"
|
||||
// directive in version scripts.
|
||||
llvm::Optional<llvm::StringMap<std::vector<SymbolBody *>>> DemangledSyms;
|
||||
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;
|
||||
};
|
||||
|
||||
template <class ELFT> struct Symtab { static SymbolTable<ELFT> *X; };
|
||||
template <class ELFT> SymbolTable<ELFT> *Symtab<ELFT>::X;
|
||||
|
||||
extern SymbolTable *Symtab;
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
251
ELF/Symbols.cpp
251
ELF/Symbols.cpp
@ -8,15 +8,15 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Symbols.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "InputSection.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Strings.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include <cstring>
|
||||
@ -28,25 +28,23 @@ using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
DefinedRegular *ElfSym::Bss;
|
||||
DefinedRegular *ElfSym::Etext1;
|
||||
DefinedRegular *ElfSym::Etext2;
|
||||
DefinedRegular *ElfSym::Edata1;
|
||||
DefinedRegular *ElfSym::Edata2;
|
||||
DefinedRegular *ElfSym::End1;
|
||||
DefinedRegular *ElfSym::End2;
|
||||
DefinedRegular *ElfSym::GlobalOffsetTable;
|
||||
DefinedRegular *ElfSym::MipsGp;
|
||||
DefinedRegular *ElfSym::MipsGpDisp;
|
||||
DefinedRegular *ElfSym::MipsLocalGp;
|
||||
Defined *ElfSym::Bss;
|
||||
Defined *ElfSym::Etext1;
|
||||
Defined *ElfSym::Etext2;
|
||||
Defined *ElfSym::Edata1;
|
||||
Defined *ElfSym::Edata2;
|
||||
Defined *ElfSym::End1;
|
||||
Defined *ElfSym::End2;
|
||||
Defined *ElfSym::GlobalOffsetTable;
|
||||
Defined *ElfSym::MipsGp;
|
||||
Defined *ElfSym::MipsGpDisp;
|
||||
Defined *ElfSym::MipsLocalGp;
|
||||
|
||||
static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
|
||||
switch (Body.kind()) {
|
||||
case SymbolBody::DefinedRegularKind: {
|
||||
auto &D = cast<DefinedRegular>(Body);
|
||||
static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
|
||||
switch (Sym.kind()) {
|
||||
case Symbol::DefinedKind: {
|
||||
auto &D = cast<Defined>(Sym);
|
||||
SectionBase *IS = D.Section;
|
||||
if (auto *ISB = dyn_cast_or_null<InputSectionBase>(IS))
|
||||
IS = ISB->Repl;
|
||||
|
||||
// According to the ELF spec reference to a local symbol from outside
|
||||
// the group are not allowed. Unfortunately .eh_frame breaks that rule
|
||||
@ -59,6 +57,7 @@ static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
|
||||
if (!IS)
|
||||
return D.Value;
|
||||
|
||||
IS = IS->Repl;
|
||||
uint64_t Offset = D.Value;
|
||||
|
||||
// An object in an SHF_MERGE section might be referenced via a
|
||||
@ -99,139 +98,85 @@ static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
|
||||
}
|
||||
return VA;
|
||||
}
|
||||
case SymbolBody::DefinedCommonKind:
|
||||
if (!Config->DefineCommon)
|
||||
return 0;
|
||||
return InX::Common->getParent()->Addr + InX::Common->OutSecOff +
|
||||
cast<DefinedCommon>(Body).Offset;
|
||||
case SymbolBody::SharedKind: {
|
||||
auto &SS = cast<SharedSymbol>(Body);
|
||||
if (SS.NeedsCopy)
|
||||
return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff +
|
||||
SS.CopyRelSecOff;
|
||||
case Symbol::SharedKind: {
|
||||
auto &SS = cast<SharedSymbol>(Sym);
|
||||
if (SS.CopyRelSec)
|
||||
return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff;
|
||||
if (SS.NeedsPltAddr)
|
||||
return Body.getPltVA();
|
||||
return Sym.getPltVA();
|
||||
return 0;
|
||||
}
|
||||
case SymbolBody::UndefinedKind:
|
||||
case Symbol::UndefinedKind:
|
||||
return 0;
|
||||
case SymbolBody::LazyArchiveKind:
|
||||
case SymbolBody::LazyObjectKind:
|
||||
assert(Body.symbol()->IsUsedInRegularObj && "lazy symbol reached writer");
|
||||
case Symbol::LazyArchiveKind:
|
||||
case Symbol::LazyObjectKind:
|
||||
assert(Sym.IsUsedInRegularObj && "lazy symbol reached writer");
|
||||
return 0;
|
||||
}
|
||||
llvm_unreachable("invalid symbol kind");
|
||||
}
|
||||
|
||||
SymbolBody::SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type)
|
||||
: SymbolKind(K), NeedsCopy(false), NeedsPltAddr(false), IsLocal(IsLocal),
|
||||
IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
|
||||
IsInIgot(false), Type(Type), StOther(StOther), Name(Name) {}
|
||||
|
||||
// Returns true if a symbol can be replaced at load-time by a symbol
|
||||
// with the same name defined in other ELF executable or DSO.
|
||||
bool SymbolBody::isPreemptible() const {
|
||||
if (isLocal())
|
||||
return false;
|
||||
|
||||
// Shared symbols resolve to the definition in the DSO. The exceptions are
|
||||
// symbols with copy relocations (which resolve to .bss) or preempt plt
|
||||
// entries (which resolve to that plt entry).
|
||||
if (isShared())
|
||||
return !NeedsCopy && !NeedsPltAddr;
|
||||
|
||||
// That's all that can be preempted in a non-DSO.
|
||||
if (!Config->Shared)
|
||||
return false;
|
||||
|
||||
// Only symbols that appear in dynsym can be preempted.
|
||||
if (!symbol()->includeInDynsym())
|
||||
return false;
|
||||
|
||||
// Only default visibility symbols can be preempted.
|
||||
if (symbol()->Visibility != STV_DEFAULT)
|
||||
return false;
|
||||
|
||||
// -Bsymbolic means that definitions are not preempted.
|
||||
if (Config->Bsymbolic || (Config->BsymbolicFunctions && isFunc()))
|
||||
return !isDefined();
|
||||
return true;
|
||||
// Returns true if this is a weak undefined symbol.
|
||||
bool Symbol::isUndefWeak() const {
|
||||
// See comment on Lazy in Symbols.h for the details.
|
||||
return isWeak() && (isUndefined() || isLazy());
|
||||
}
|
||||
|
||||
// Overwrites all attributes with Other's so that this symbol becomes
|
||||
// an alias to Other. This is useful for handling some options such as
|
||||
// --wrap.
|
||||
void SymbolBody::copy(SymbolBody *Other) {
|
||||
memcpy(symbol()->Body.buffer, Other->symbol()->Body.buffer,
|
||||
sizeof(Symbol::Body));
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getVA(int64_t Addend) const {
|
||||
uint64_t Symbol::getVA(int64_t Addend) const {
|
||||
uint64_t OutVA = getSymVA(*this, Addend);
|
||||
return OutVA + Addend;
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getGotVA() const {
|
||||
return InX::Got->getVA() + getGotOffset();
|
||||
}
|
||||
uint64_t Symbol::getGotVA() const { return InX::Got->getVA() + getGotOffset(); }
|
||||
|
||||
uint64_t SymbolBody::getGotOffset() const {
|
||||
uint64_t Symbol::getGotOffset() const {
|
||||
return GotIndex * Target->GotEntrySize;
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getGotPltVA() const {
|
||||
uint64_t Symbol::getGotPltVA() const {
|
||||
if (this->IsInIgot)
|
||||
return InX::IgotPlt->getVA() + getGotPltOffset();
|
||||
return InX::GotPlt->getVA() + getGotPltOffset();
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getGotPltOffset() const {
|
||||
uint64_t Symbol::getGotPltOffset() const {
|
||||
return GotPltIndex * Target->GotPltEntrySize;
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getPltVA() const {
|
||||
uint64_t Symbol::getPltVA() const {
|
||||
if (this->IsInIplt)
|
||||
return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize;
|
||||
return InX::Plt->getVA() + Target->PltHeaderSize +
|
||||
PltIndex * Target->PltEntrySize;
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
|
||||
if (const auto *C = dyn_cast<DefinedCommon>(this))
|
||||
return C->Size;
|
||||
if (const auto *DR = dyn_cast<DefinedRegular>(this))
|
||||
uint64_t Symbol::getSize() const {
|
||||
if (const auto *DR = dyn_cast<Defined>(this))
|
||||
return DR->Size;
|
||||
if (const auto *S = dyn_cast<SharedSymbol>(this))
|
||||
return S->getSize<ELFT>();
|
||||
return S->Size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
OutputSection *SymbolBody::getOutputSection() const {
|
||||
if (auto *S = dyn_cast<DefinedRegular>(this)) {
|
||||
if (S->Section)
|
||||
return S->Section->getOutputSection();
|
||||
OutputSection *Symbol::getOutputSection() const {
|
||||
if (auto *S = dyn_cast<Defined>(this)) {
|
||||
if (auto *Sec = S->Section)
|
||||
return Sec->Repl->getOutputSection();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto *S = dyn_cast<SharedSymbol>(this)) {
|
||||
if (S->NeedsCopy)
|
||||
if (S->CopyRelSec)
|
||||
return S->CopyRelSec->getParent();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (isa<DefinedCommon>(this)) {
|
||||
if (Config->DefineCommon)
|
||||
return InX::Common->getParent();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If a symbol name contains '@', the characters after that is
|
||||
// a symbol version name. This function parses that.
|
||||
void SymbolBody::parseSymbolVersion() {
|
||||
void Symbol::parseSymbolVersion() {
|
||||
StringRef S = getName();
|
||||
size_t Pos = S.find('@');
|
||||
if (Pos == 0 || Pos == StringRef::npos)
|
||||
@ -244,7 +189,7 @@ void SymbolBody::parseSymbolVersion() {
|
||||
Name = {S.data(), Pos};
|
||||
|
||||
// If this is not in this DSO, it is not a definition.
|
||||
if (!isInCurrentDSO())
|
||||
if (!isDefined())
|
||||
return;
|
||||
|
||||
// '@@' in a symbol name means the default version.
|
||||
@ -258,9 +203,9 @@ void SymbolBody::parseSymbolVersion() {
|
||||
continue;
|
||||
|
||||
if (IsDefault)
|
||||
symbol()->VersionId = Ver.Id;
|
||||
VersionId = Ver.Id;
|
||||
else
|
||||
symbol()->VersionId = Ver.Id | VERSYM_HIDDEN;
|
||||
VersionId = Ver.Id | VERSYM_HIDDEN;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -273,81 +218,34 @@ void SymbolBody::parseSymbolVersion() {
|
||||
Verstr);
|
||||
}
|
||||
|
||||
Defined::Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type)
|
||||
: SymbolBody(K, Name, IsLocal, StOther, Type) {}
|
||||
|
||||
template <class ELFT> bool DefinedRegular::isMipsPIC() const {
|
||||
typedef typename ELFT::Ehdr Elf_Ehdr;
|
||||
if (!Section || !isFunc())
|
||||
return false;
|
||||
|
||||
auto *Sec = cast<InputSectionBase>(Section);
|
||||
const Elf_Ehdr *Hdr = Sec->template getFile<ELFT>()->getObj().getHeader();
|
||||
return (this->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC ||
|
||||
(Hdr->e_flags & EF_MIPS_PIC);
|
||||
}
|
||||
|
||||
Undefined::Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type, InputFile *File)
|
||||
: SymbolBody(SymbolBody::UndefinedKind, Name, IsLocal, StOther, Type) {
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint32_t Alignment,
|
||||
uint8_t StOther, uint8_t Type, InputFile *File)
|
||||
: Defined(SymbolBody::DefinedCommonKind, Name, /*IsLocal=*/false, StOther,
|
||||
Type),
|
||||
Alignment(Alignment), Size(Size) {
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
// If a shared symbol is referred via a copy relocation, its alignment
|
||||
// becomes part of the ABI. This function returns a symbol alignment.
|
||||
// Because symbols don't have alignment attributes, we need to infer that.
|
||||
template <class ELFT> uint32_t SharedSymbol::getAlignment() const {
|
||||
auto *File = cast<SharedFile<ELFT>>(this->File);
|
||||
uint32_t SecAlign = File->getSection(getSym<ELFT>())->sh_addralign;
|
||||
uint64_t SymValue = getSym<ELFT>().st_value;
|
||||
uint32_t SymAlign = uint32_t(1) << countTrailingZeros(SymValue);
|
||||
return std::min(SecAlign, SymAlign);
|
||||
}
|
||||
|
||||
InputFile *Lazy::fetch() {
|
||||
if (auto *S = dyn_cast<LazyArchive>(this))
|
||||
return S->fetch();
|
||||
return cast<LazyObject>(this)->fetch();
|
||||
}
|
||||
|
||||
LazyArchive::LazyArchive(ArchiveFile &File,
|
||||
const llvm::object::Archive::Symbol S, uint8_t Type)
|
||||
: Lazy(LazyArchiveKind, S.getName(), Type), Sym(S) {
|
||||
this->File = &File;
|
||||
}
|
||||
|
||||
LazyObject::LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type)
|
||||
: Lazy(LazyObjectKind, Name, Type) {
|
||||
this->File = &File;
|
||||
}
|
||||
ArchiveFile *LazyArchive::getFile() { return cast<ArchiveFile>(File); }
|
||||
|
||||
InputFile *LazyArchive::fetch() {
|
||||
std::pair<MemoryBufferRef, uint64_t> MBInfo = file()->getMember(&Sym);
|
||||
std::pair<MemoryBufferRef, uint64_t> MBInfo = getFile()->getMember(&Sym);
|
||||
|
||||
// getMember returns an empty buffer if the member was already
|
||||
// read from the library.
|
||||
if (MBInfo.first.getBuffer().empty())
|
||||
return nullptr;
|
||||
return createObjectFile(MBInfo.first, file()->getName(), MBInfo.second);
|
||||
return createObjectFile(MBInfo.first, getFile()->getName(), MBInfo.second);
|
||||
}
|
||||
|
||||
InputFile *LazyObject::fetch() { return file()->fetch(); }
|
||||
LazyObjFile *LazyObject::getFile() { return cast<LazyObjFile>(File); }
|
||||
|
||||
InputFile *LazyObject::fetch() { return getFile()->fetch(); }
|
||||
|
||||
uint8_t Symbol::computeBinding() const {
|
||||
if (Config->Relocatable)
|
||||
return Binding;
|
||||
if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
|
||||
return STB_LOCAL;
|
||||
if (VersionId == VER_NDX_LOCAL && body()->isInCurrentDSO())
|
||||
if (VersionId == VER_NDX_LOCAL && isDefined())
|
||||
return STB_LOCAL;
|
||||
if (Config->NoGnuUnique && Binding == STB_GNU_UNIQUE)
|
||||
return STB_GLOBAL;
|
||||
@ -355,45 +253,36 @@ uint8_t Symbol::computeBinding() const {
|
||||
}
|
||||
|
||||
bool Symbol::includeInDynsym() const {
|
||||
if (!Config->HasDynSymTab)
|
||||
return false;
|
||||
if (computeBinding() == STB_LOCAL)
|
||||
return false;
|
||||
return ExportDynamic || body()->isShared() ||
|
||||
(body()->isUndefined() && Config->Shared);
|
||||
if (!isDefined())
|
||||
return true;
|
||||
return ExportDynamic;
|
||||
}
|
||||
|
||||
// Print out a log message for --trace-symbol.
|
||||
void elf::printTraceSymbol(Symbol *Sym) {
|
||||
SymbolBody *B = Sym->body();
|
||||
std::string S;
|
||||
if (B->isUndefined())
|
||||
if (Sym->isUndefined())
|
||||
S = ": reference to ";
|
||||
else if (B->isCommon())
|
||||
else if (Sym->isLazy())
|
||||
S = ": lazy definition of ";
|
||||
else if (Sym->isShared())
|
||||
S = ": shared definition of ";
|
||||
else if (dyn_cast_or_null<BssSection>(cast<Defined>(Sym)->Section))
|
||||
S = ": common definition of ";
|
||||
else
|
||||
S = ": definition of ";
|
||||
|
||||
message(toString(B->File) + S + B->getName());
|
||||
message(toString(Sym->File) + S + Sym->getName());
|
||||
}
|
||||
|
||||
// Returns a symbol for an error message.
|
||||
std::string lld::toString(const SymbolBody &B) {
|
||||
std::string lld::toString(const Symbol &B) {
|
||||
if (Config->Demangle)
|
||||
if (Optional<std::string> S = demangle(B.getName()))
|
||||
if (Optional<std::string> S = demangleItanium(B.getName()))
|
||||
return *S;
|
||||
return B.getName();
|
||||
}
|
||||
|
||||
template uint32_t SymbolBody::template getSize<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getSize<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getSize<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getSize<ELF64BE>() const;
|
||||
|
||||
template bool DefinedRegular::template isMipsPIC<ELF32LE>() const;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF32BE>() const;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF64LE>() const;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF64BE>() const;
|
||||
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF32LE>() const;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF32BE>() const;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF64LE>() const;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF64BE>() const;
|
||||
|
399
ELF/Symbols.h
399
ELF/Symbols.h
@ -18,7 +18,7 @@
|
||||
#include "InputSection.h"
|
||||
#include "Strings.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
@ -27,53 +27,86 @@ namespace elf {
|
||||
|
||||
class ArchiveFile;
|
||||
class BitcodeFile;
|
||||
class BssSection;
|
||||
class InputFile;
|
||||
class LazyObjectFile;
|
||||
template <class ELFT> class ObjectFile;
|
||||
class LazyObjFile;
|
||||
template <class ELFT> class ObjFile;
|
||||
class OutputSection;
|
||||
template <class ELFT> class SharedFile;
|
||||
|
||||
struct Symbol;
|
||||
|
||||
// The base class for real symbol classes.
|
||||
class SymbolBody {
|
||||
class Symbol {
|
||||
public:
|
||||
enum Kind {
|
||||
DefinedFirst,
|
||||
DefinedRegularKind = DefinedFirst,
|
||||
DefinedKind,
|
||||
SharedKind,
|
||||
DefinedCommonKind,
|
||||
DefinedLast = DefinedCommonKind,
|
||||
UndefinedKind,
|
||||
LazyArchiveKind,
|
||||
LazyObjectKind,
|
||||
};
|
||||
|
||||
SymbolBody(Kind K) : SymbolKind(K) {}
|
||||
|
||||
Symbol *symbol();
|
||||
const Symbol *symbol() const {
|
||||
return const_cast<SymbolBody *>(this)->symbol();
|
||||
}
|
||||
|
||||
Kind kind() const { return static_cast<Kind>(SymbolKind); }
|
||||
|
||||
// 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.
|
||||
// - An undefined weak will not fetch archive members, but we have to
|
||||
// remember it is weak.
|
||||
uint8_t Binding;
|
||||
|
||||
// Version definition index.
|
||||
uint16_t VersionId;
|
||||
|
||||
// Symbol visibility. This is the computed minimum visibility of all
|
||||
// observed non-DSO symbols.
|
||||
unsigned Visibility : 2;
|
||||
|
||||
// True if the symbol was used for linking and thus need to be added to the
|
||||
// output file's symbol table. This is true for all symbols except for
|
||||
// unreferenced DSO symbols and bitcode symbols that are unreferenced except
|
||||
// by other bitcode objects.
|
||||
unsigned IsUsedInRegularObj : 1;
|
||||
|
||||
// If this flag is true and the symbol has protected or default visibility, it
|
||||
// will appear in .dynsym. This flag is set by interposable DSO symbols in
|
||||
// executables, by most symbols in DSOs and executables built with
|
||||
// --export-dynamic, and by dynamic lists.
|
||||
unsigned ExportDynamic : 1;
|
||||
|
||||
// False if LTO shouldn't inline whatever this symbol points to. If a symbol
|
||||
// is overwritten after LTO, LTO shouldn't inline the symbol because it
|
||||
// doesn't know the final contents of the symbol.
|
||||
unsigned CanInline : 1;
|
||||
|
||||
// True if this symbol is specified by --trace-symbol option.
|
||||
unsigned Traced : 1;
|
||||
|
||||
// This symbol version was found in a version script.
|
||||
unsigned InVersionScript : 1;
|
||||
|
||||
// The file from which this symbol was created.
|
||||
InputFile *File;
|
||||
|
||||
bool includeInDynsym() const;
|
||||
uint8_t computeBinding() const;
|
||||
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
|
||||
|
||||
bool isUndefined() const { return SymbolKind == UndefinedKind; }
|
||||
bool isDefined() const { return SymbolKind <= DefinedLast; }
|
||||
bool isCommon() const { return SymbolKind == DefinedCommonKind; }
|
||||
bool isDefined() const { return SymbolKind == DefinedKind; }
|
||||
bool isShared() const { return SymbolKind == SharedKind; }
|
||||
bool isLocal() const { return Binding == llvm::ELF::STB_LOCAL; }
|
||||
|
||||
bool isLazy() const {
|
||||
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
|
||||
}
|
||||
bool isShared() const { return SymbolKind == SharedKind; }
|
||||
bool isInCurrentDSO() const {
|
||||
return !isUndefined() && !isShared() && !isLazy();
|
||||
}
|
||||
bool isLocal() const { return IsLocal; }
|
||||
bool isPreemptible() const;
|
||||
|
||||
// True is this is an undefined weak symbol. This only works once
|
||||
// all input files have been added.
|
||||
bool isUndefWeak() const;
|
||||
|
||||
StringRef getName() const { return Name; }
|
||||
uint8_t getVisibility() const { return StOther & 0x3; }
|
||||
void parseSymbolVersion();
|
||||
void copy(SymbolBody *Other);
|
||||
|
||||
bool isInGot() const { return GotIndex != -1U; }
|
||||
bool isInPlt() const { return PltIndex != -1U; }
|
||||
@ -85,12 +118,9 @@ public:
|
||||
uint64_t getGotPltOffset() const;
|
||||
uint64_t getGotPltVA() const;
|
||||
uint64_t getPltVA() const;
|
||||
template <class ELFT> typename ELFT::uint getSize() const;
|
||||
uint64_t getSize() const;
|
||||
OutputSection *getOutputSection() const;
|
||||
|
||||
// The file from which this symbol was created.
|
||||
InputFile *File = nullptr;
|
||||
|
||||
uint32_t DynsymIndex = 0;
|
||||
uint32_t GotIndex = -1;
|
||||
uint32_t GotPltIndex = -1;
|
||||
@ -98,23 +128,19 @@ public:
|
||||
uint32_t GlobalDynIndex = -1;
|
||||
|
||||
protected:
|
||||
SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type);
|
||||
Symbol(Kind K, InputFile *File, StringRefZ Name, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type)
|
||||
: Binding(Binding), File(File), SymbolKind(K), NeedsPltAddr(false),
|
||||
IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
|
||||
IsInIgot(false), IsPreemptible(false), Used(!Config->GcSections),
|
||||
Type(Type), StOther(StOther), Name(Name) {}
|
||||
|
||||
const unsigned SymbolKind : 8;
|
||||
|
||||
public:
|
||||
// True if the linker has to generate a copy relocation.
|
||||
// For SharedSymbol only.
|
||||
unsigned NeedsCopy : 1;
|
||||
|
||||
// True the symbol should point to its PLT entry.
|
||||
// For SharedSymbol only.
|
||||
unsigned NeedsPltAddr : 1;
|
||||
|
||||
// True if this is a local symbol.
|
||||
unsigned IsLocal : 1;
|
||||
|
||||
// True if this symbol has an entry in the global part of MIPS GOT.
|
||||
unsigned IsInGlobalMipsGot : 1;
|
||||
|
||||
@ -127,6 +153,11 @@ public:
|
||||
// True if this symbol is in the Igot sub-section of the .got.plt or .got.
|
||||
unsigned IsInIgot : 1;
|
||||
|
||||
unsigned IsPreemptible : 1;
|
||||
|
||||
// True if an undefined or shared symbol is used from a live section.
|
||||
unsigned Used : 1;
|
||||
|
||||
// The following fields have the same meaning as the ELF symbol attributes.
|
||||
uint8_t Type; // symbol type
|
||||
uint8_t StOther; // st_other field value
|
||||
@ -149,139 +180,111 @@ protected:
|
||||
StringRefZ Name;
|
||||
};
|
||||
|
||||
// The base class for any defined symbols.
|
||||
class Defined : public SymbolBody {
|
||||
// Represents a symbol that is defined in the current output file.
|
||||
class Defined : public Symbol {
|
||||
public:
|
||||
Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type);
|
||||
static bool classof(const SymbolBody *S) { return S->isDefined(); }
|
||||
};
|
||||
Defined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
|
||||
uint8_t Type, uint64_t Value, uint64_t Size, SectionBase *Section)
|
||||
: Symbol(DefinedKind, File, Name, Binding, StOther, Type), Value(Value),
|
||||
Size(Size), Section(Section) {}
|
||||
|
||||
class DefinedCommon : public Defined {
|
||||
public:
|
||||
DefinedCommon(StringRef N, uint64_t Size, uint32_t Alignment, uint8_t StOther,
|
||||
uint8_t Type, InputFile *File);
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == SymbolBody::DefinedCommonKind;
|
||||
}
|
||||
|
||||
// The output offset of this common symbol in the output bss. Computed by the
|
||||
// writer.
|
||||
uint64_t Offset;
|
||||
|
||||
// The maximum alignment we have seen for this symbol.
|
||||
uint32_t Alignment;
|
||||
|
||||
uint64_t Size;
|
||||
};
|
||||
|
||||
// Regular defined symbols read from object file symbol tables.
|
||||
class DefinedRegular : public Defined {
|
||||
public:
|
||||
DefinedRegular(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
|
||||
uint64_t Value, uint64_t Size, SectionBase *Section,
|
||||
InputFile *File)
|
||||
: Defined(SymbolBody::DefinedRegularKind, Name, IsLocal, StOther, Type),
|
||||
Value(Value), Size(Size), Section(Section) {
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
// Return true if the symbol is a PIC function.
|
||||
template <class ELFT> bool isMipsPIC() const;
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == SymbolBody::DefinedRegularKind;
|
||||
}
|
||||
static bool classof(const Symbol *S) { return S->isDefined(); }
|
||||
|
||||
uint64_t Value;
|
||||
uint64_t Size;
|
||||
SectionBase *Section;
|
||||
};
|
||||
|
||||
class Undefined : public SymbolBody {
|
||||
class Undefined : public Symbol {
|
||||
public:
|
||||
Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
|
||||
InputFile *F);
|
||||
Undefined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
|
||||
uint8_t Type)
|
||||
: Symbol(UndefinedKind, File, Name, Binding, StOther, Type) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == UndefinedKind;
|
||||
}
|
||||
static bool classof(const Symbol *S) { return S->kind() == UndefinedKind; }
|
||||
};
|
||||
|
||||
class SharedSymbol : public Defined {
|
||||
class SharedSymbol : public Symbol {
|
||||
public:
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == SymbolBody::SharedKind;
|
||||
}
|
||||
static bool classof(const Symbol *S) { return S->kind() == SharedKind; }
|
||||
|
||||
SharedSymbol(InputFile *File, StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
const void *ElfSym, const void *Verdef)
|
||||
: Defined(SymbolBody::SharedKind, Name, /*IsLocal=*/false, StOther, Type),
|
||||
Verdef(Verdef), ElfSym(ElfSym) {
|
||||
// IFuncs defined in DSOs are treated as functions by the static linker.
|
||||
if (isGnuIFunc())
|
||||
SharedSymbol(InputFile *File, StringRef Name, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type, uint64_t Value, uint64_t Size,
|
||||
uint32_t Alignment, uint32_t VerdefIndex)
|
||||
: Symbol(SharedKind, File, Name, Binding, StOther, Type), Value(Value),
|
||||
Size(Size), VerdefIndex(VerdefIndex), Alignment(Alignment) {
|
||||
// GNU ifunc is a mechanism to allow user-supplied functions to
|
||||
// resolve PLT slot values at load-time. This is contrary to the
|
||||
// regualr symbol resolution scheme in which symbols are resolved just
|
||||
// by name. Using this hook, you can program how symbols are solved
|
||||
// for you program. For example, you can make "memcpy" to be resolved
|
||||
// to a SSE-enabled version of memcpy only when a machine running the
|
||||
// program supports the SSE instruction set.
|
||||
//
|
||||
// Naturally, such symbols should always be called through their PLT
|
||||
// slots. What GNU ifunc symbols point to are resolver functions, and
|
||||
// calling them directly doesn't make sense (unless you are writing a
|
||||
// loader).
|
||||
//
|
||||
// For DSO symbols, we always call them through PLT slots anyway.
|
||||
// So there's no difference between GNU ifunc and regular function
|
||||
// symbols if they are in DSOs. So we can handle GNU_IFUNC as FUNC.
|
||||
if (this->Type == llvm::ELF::STT_GNU_IFUNC)
|
||||
this->Type = llvm::ELF::STT_FUNC;
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
template <class ELFT> uint64_t getShndx() const {
|
||||
return getSym<ELFT>().st_shndx;
|
||||
template <class ELFT> SharedFile<ELFT> *getFile() const {
|
||||
return cast<SharedFile<ELFT>>(File);
|
||||
}
|
||||
|
||||
template <class ELFT> uint64_t getValue() const {
|
||||
return getSym<ELFT>().st_value;
|
||||
}
|
||||
// If not null, there is a copy relocation to this section.
|
||||
InputSection *CopyRelSec = nullptr;
|
||||
|
||||
template <class ELFT> uint64_t getSize() const {
|
||||
return getSym<ELFT>().st_size;
|
||||
}
|
||||
uint64_t Value; // st_value
|
||||
uint64_t Size; // st_size
|
||||
|
||||
template <class ELFT> uint32_t getAlignment() const;
|
||||
// This field is a index to the symbol's version definition.
|
||||
uint32_t VerdefIndex;
|
||||
|
||||
// This field is a pointer to the symbol's version definition.
|
||||
const void *Verdef;
|
||||
|
||||
// CopyRelSec and CopyRelSecOff are significant only when NeedsCopy is true.
|
||||
InputSection *CopyRelSec;
|
||||
uint64_t CopyRelSecOff;
|
||||
|
||||
private:
|
||||
template <class ELFT> const typename ELFT::Sym &getSym() const {
|
||||
return *(const typename ELFT::Sym *)ElfSym;
|
||||
}
|
||||
|
||||
const void *ElfSym;
|
||||
uint32_t Alignment;
|
||||
};
|
||||
|
||||
// This class represents a symbol defined in an archive file. It is
|
||||
// created from an archive file header, and it knows how to load an
|
||||
// object file from an archive to replace itself with a defined
|
||||
// symbol. If the resolver finds both Undefined and Lazy for
|
||||
// the same name, it will ask the Lazy to load a file.
|
||||
class Lazy : public SymbolBody {
|
||||
// This represents a symbol that is not yet in the link, but we know where to
|
||||
// find it if needed. If the resolver finds both Undefined and Lazy for the same
|
||||
// name, it will ask the Lazy to load a file.
|
||||
//
|
||||
// A special complication is the handling of weak undefined symbols. They should
|
||||
// not load a file, but we have to remember we have seen both the weak undefined
|
||||
// and the lazy. We represent that with a lazy symbol with a weak binding. This
|
||||
// means that code looking for undefined symbols normally also has to take lazy
|
||||
// symbols into consideration.
|
||||
class Lazy : public Symbol {
|
||||
public:
|
||||
static bool classof(const SymbolBody *S) { return S->isLazy(); }
|
||||
static bool classof(const Symbol *S) { return S->isLazy(); }
|
||||
|
||||
// Returns an object file for this symbol, or a nullptr if the file
|
||||
// was already returned.
|
||||
InputFile *fetch();
|
||||
|
||||
protected:
|
||||
Lazy(SymbolBody::Kind K, StringRef Name, uint8_t Type)
|
||||
: SymbolBody(K, Name, /*IsLocal=*/false, llvm::ELF::STV_DEFAULT, Type) {}
|
||||
Lazy(Kind K, InputFile *File, StringRef Name, uint8_t Type)
|
||||
: Symbol(K, File, Name, llvm::ELF::STB_GLOBAL, llvm::ELF::STV_DEFAULT,
|
||||
Type) {}
|
||||
};
|
||||
|
||||
// LazyArchive symbols represents symbols in archive files.
|
||||
// This class represents a symbol defined in an archive file. It is
|
||||
// created from an archive file header, and it knows how to load an
|
||||
// object file from an archive to replace itself with a defined
|
||||
// symbol.
|
||||
class LazyArchive : public Lazy {
|
||||
public:
|
||||
LazyArchive(ArchiveFile &File, const llvm::object::Archive::Symbol S,
|
||||
uint8_t Type);
|
||||
LazyArchive(InputFile *File, const llvm::object::Archive::Symbol S,
|
||||
uint8_t Type)
|
||||
: Lazy(LazyArchiveKind, File, S.getName(), Type), Sym(S) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == LazyArchiveKind;
|
||||
}
|
||||
static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; }
|
||||
|
||||
ArchiveFile *file() { return (ArchiveFile *)this->File; }
|
||||
ArchiveFile *getFile();
|
||||
InputFile *fetch();
|
||||
|
||||
private:
|
||||
@ -292,123 +295,85 @@ private:
|
||||
// --start-lib and --end-lib options.
|
||||
class LazyObject : public Lazy {
|
||||
public:
|
||||
LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type);
|
||||
LazyObject(InputFile *File, StringRef Name, uint8_t Type)
|
||||
: Lazy(LazyObjectKind, File, Name, Type) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == LazyObjectKind;
|
||||
}
|
||||
static bool classof(const Symbol *S) { return S->kind() == LazyObjectKind; }
|
||||
|
||||
LazyObjectFile *file() { return (LazyObjectFile *)this->File; }
|
||||
LazyObjFile *getFile();
|
||||
InputFile *fetch();
|
||||
};
|
||||
|
||||
// Some linker-generated symbols need to be created as
|
||||
// DefinedRegular symbols.
|
||||
// Defined symbols.
|
||||
struct ElfSym {
|
||||
// __bss_start
|
||||
static DefinedRegular *Bss;
|
||||
static Defined *Bss;
|
||||
|
||||
// etext and _etext
|
||||
static DefinedRegular *Etext1;
|
||||
static DefinedRegular *Etext2;
|
||||
static Defined *Etext1;
|
||||
static Defined *Etext2;
|
||||
|
||||
// edata and _edata
|
||||
static DefinedRegular *Edata1;
|
||||
static DefinedRegular *Edata2;
|
||||
static Defined *Edata1;
|
||||
static Defined *Edata2;
|
||||
|
||||
// end and _end
|
||||
static DefinedRegular *End1;
|
||||
static DefinedRegular *End2;
|
||||
static Defined *End1;
|
||||
static Defined *End2;
|
||||
|
||||
// The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to
|
||||
// be at some offset from the base of the .got section, usually 0 or
|
||||
// the end of the .got.
|
||||
static DefinedRegular *GlobalOffsetTable;
|
||||
static Defined *GlobalOffsetTable;
|
||||
|
||||
// _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS.
|
||||
static DefinedRegular *MipsGp;
|
||||
static DefinedRegular *MipsGpDisp;
|
||||
static DefinedRegular *MipsLocalGp;
|
||||
static Defined *MipsGp;
|
||||
static Defined *MipsGpDisp;
|
||||
static Defined *MipsLocalGp;
|
||||
};
|
||||
|
||||
// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
|
||||
// always one Symbol for each symbol name. The resolver updates the SymbolBody
|
||||
// stored in the Body field of this object as it resolves symbols. Symbol also
|
||||
// holds computed properties of symbol names.
|
||||
struct Symbol {
|
||||
// Symbol binding. This is on the Symbol to track changes during resolution.
|
||||
// In particular:
|
||||
// An undefined weak is still weak when it resolves to a shared library.
|
||||
// An undefined weak will not fetch archive members, but we have to remember
|
||||
// it is weak.
|
||||
uint8_t Binding;
|
||||
|
||||
// Version definition index.
|
||||
uint16_t VersionId;
|
||||
|
||||
// Symbol visibility. This is the computed minimum visibility of all
|
||||
// observed non-DSO symbols.
|
||||
unsigned Visibility : 2;
|
||||
|
||||
// True if the symbol was used for linking and thus need to be added to the
|
||||
// output file's symbol table. This is true for all symbols except for
|
||||
// unreferenced DSO symbols and bitcode symbols that are unreferenced except
|
||||
// by other bitcode objects.
|
||||
unsigned IsUsedInRegularObj : 1;
|
||||
|
||||
// If this flag is true and the symbol has protected or default visibility, it
|
||||
// will appear in .dynsym. This flag is set by interposable DSO symbols in
|
||||
// executables, by most symbols in DSOs and executables built with
|
||||
// --export-dynamic, and by dynamic lists.
|
||||
unsigned ExportDynamic : 1;
|
||||
|
||||
// True if this symbol is specified by --trace-symbol option.
|
||||
unsigned Traced : 1;
|
||||
|
||||
// This symbol version was found in a version script.
|
||||
unsigned InVersionScript : 1;
|
||||
|
||||
bool includeInDynsym() const;
|
||||
uint8_t computeBinding() const;
|
||||
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
|
||||
|
||||
// This field is used to store the Symbol's SymbolBody. This instantiation of
|
||||
// AlignedCharArrayUnion gives us a struct with a char array field that is
|
||||
// large and aligned enough to store any derived class of SymbolBody.
|
||||
llvm::AlignedCharArrayUnion<DefinedCommon, DefinedRegular, Undefined,
|
||||
SharedSymbol, LazyArchive, LazyObject>
|
||||
Body;
|
||||
|
||||
SymbolBody *body() { return reinterpret_cast<SymbolBody *>(Body.buffer); }
|
||||
const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
|
||||
// A buffer class that is large enough to hold any Symbol-derived
|
||||
// object. We allocate memory using this class and instantiate a symbol
|
||||
// using the placement new.
|
||||
union SymbolUnion {
|
||||
alignas(Defined) char A[sizeof(Defined)];
|
||||
alignas(Undefined) char C[sizeof(Undefined)];
|
||||
alignas(SharedSymbol) char D[sizeof(SharedSymbol)];
|
||||
alignas(LazyArchive) char E[sizeof(LazyArchive)];
|
||||
alignas(LazyObject) char F[sizeof(LazyObject)];
|
||||
};
|
||||
|
||||
void printTraceSymbol(Symbol *Sym);
|
||||
|
||||
template <typename T, typename... ArgT>
|
||||
void replaceBody(Symbol *S, ArgT &&... Arg) {
|
||||
static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
|
||||
static_assert(alignof(T) <= alignof(decltype(S->Body)),
|
||||
"Body not aligned enough");
|
||||
assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
|
||||
"Not a SymbolBody");
|
||||
void replaceSymbol(Symbol *S, ArgT &&... Arg) {
|
||||
static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
|
||||
static_assert(alignof(T) <= alignof(SymbolUnion),
|
||||
"SymbolUnion not aligned enough");
|
||||
assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
|
||||
"Not a Symbol");
|
||||
|
||||
new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
|
||||
Symbol Sym = *S;
|
||||
|
||||
new (S) T(std::forward<ArgT>(Arg)...);
|
||||
|
||||
S->VersionId = Sym.VersionId;
|
||||
S->Visibility = Sym.Visibility;
|
||||
S->IsUsedInRegularObj = Sym.IsUsedInRegularObj;
|
||||
S->ExportDynamic = Sym.ExportDynamic;
|
||||
S->CanInline = Sym.CanInline;
|
||||
S->Traced = Sym.Traced;
|
||||
S->InVersionScript = Sym.InVersionScript;
|
||||
|
||||
// Print out a log message if --trace-symbol was specified.
|
||||
// This is for debugging.
|
||||
if (S->Traced)
|
||||
printTraceSymbol(S);
|
||||
}
|
||||
|
||||
inline Symbol *SymbolBody::symbol() {
|
||||
assert(!isLocal());
|
||||
return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
|
||||
offsetof(Symbol, Body));
|
||||
}
|
||||
} // namespace elf
|
||||
|
||||
std::string toString(const elf::SymbolBody &B);
|
||||
std::string toString(const elf::Symbol &B);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -26,11 +26,11 @@
|
||||
#include "InputSection.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
|
||||
#include <set>
|
||||
#include <functional>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class SharedSymbol;
|
||||
|
||||
class SyntheticSection : public InputSection {
|
||||
public:
|
||||
@ -47,7 +47,7 @@ public:
|
||||
virtual void finalizeContents() {}
|
||||
// If the section has the SHF_ALLOC flag and the size may be changed if
|
||||
// thunks are added, update the section size.
|
||||
virtual void updateAllocSize() {}
|
||||
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; }
|
||||
@ -59,21 +59,12 @@ public:
|
||||
};
|
||||
|
||||
struct CieRecord {
|
||||
EhSectionPiece *Piece = nullptr;
|
||||
std::vector<EhSectionPiece *> FdePieces;
|
||||
EhSectionPiece *Cie = nullptr;
|
||||
std::vector<EhSectionPiece *> Fdes;
|
||||
};
|
||||
|
||||
// Section for .eh_frame.
|
||||
template <class ELFT> class EhFrameSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
|
||||
void updateAlignment(uint64_t Val) {
|
||||
if (Val > this->Alignment)
|
||||
this->Alignment = Val;
|
||||
}
|
||||
|
||||
class EhFrameSection final : public SyntheticSection {
|
||||
public:
|
||||
EhFrameSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
@ -81,30 +72,36 @@ public:
|
||||
bool empty() const override { return Sections.empty(); }
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
void addSection(InputSectionBase *S);
|
||||
|
||||
size_t NumFdes = 0;
|
||||
template <class ELFT> void addSection(InputSectionBase *S);
|
||||
|
||||
std::vector<EhInputSection *> Sections;
|
||||
size_t NumFdes = 0;
|
||||
|
||||
struct FdeData {
|
||||
uint32_t Pc;
|
||||
uint32_t FdeVA;
|
||||
};
|
||||
|
||||
std::vector<FdeData> getFdeData() const;
|
||||
|
||||
private:
|
||||
uint64_t Size = 0;
|
||||
template <class RelTy>
|
||||
|
||||
template <class ELFT, class RelTy>
|
||||
void addSectionAux(EhInputSection *S, llvm::ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
template <class ELFT, class RelTy>
|
||||
CieRecord *addCie(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
template <class ELFT, class RelTy>
|
||||
bool isFdeLive(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
|
||||
|
||||
uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc);
|
||||
uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc) const;
|
||||
|
||||
std::vector<CieRecord *> Cies;
|
||||
std::vector<CieRecord *> CieRecords;
|
||||
|
||||
// CIE records are uniquified by their contents and personality functions.
|
||||
llvm::DenseMap<std::pair<ArrayRef<uint8_t>, SymbolBody *>, CieRecord *>
|
||||
CieMap;
|
||||
llvm::DenseMap<std::pair<ArrayRef<uint8_t>, Symbol *>, CieRecord *> CieMap;
|
||||
};
|
||||
|
||||
class GotSection : public SyntheticSection {
|
||||
@ -115,11 +112,11 @@ public:
|
||||
bool empty() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
||||
void addEntry(SymbolBody &Sym);
|
||||
bool addDynTlsEntry(SymbolBody &Sym);
|
||||
void addEntry(Symbol &Sym);
|
||||
bool addDynTlsEntry(Symbol &Sym);
|
||||
bool addTlsIndex();
|
||||
uint64_t getGlobalDynAddr(const SymbolBody &B) const;
|
||||
uint64_t getGlobalDynOffset(const SymbolBody &B) const;
|
||||
uint64_t getGlobalDynAddr(const Symbol &B) const;
|
||||
uint64_t getGlobalDynOffset(const Symbol &B) const;
|
||||
|
||||
uint64_t getTlsIndexVA() { return this->getVA() + TlsIndexOff; }
|
||||
uint32_t getTlsIndexOff() const { return TlsIndexOff; }
|
||||
@ -159,14 +156,13 @@ private:
|
||||
// respectively.
|
||||
class BssSection final : public SyntheticSection {
|
||||
public:
|
||||
BssSection(StringRef Name);
|
||||
BssSection(StringRef Name, uint64_t Size, uint32_t Alignment);
|
||||
void writeTo(uint8_t *) override {}
|
||||
bool empty() const override { return getSize() == 0; }
|
||||
size_t reserveSpace(uint64_t Size, uint32_t Alignment);
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
private:
|
||||
uint64_t Size = 0;
|
||||
static bool classof(const SectionBase *S) { return S->Bss; }
|
||||
uint64_t Size;
|
||||
};
|
||||
|
||||
class MipsGotSection final : public SyntheticSection {
|
||||
@ -174,21 +170,21 @@ public:
|
||||
MipsGotSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return Size; }
|
||||
void updateAllocSize() override;
|
||||
bool updateAllocSize() override;
|
||||
void finalizeContents() override;
|
||||
bool empty() const override;
|
||||
void addEntry(SymbolBody &Sym, int64_t Addend, RelExpr Expr);
|
||||
bool addDynTlsEntry(SymbolBody &Sym);
|
||||
void addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr);
|
||||
bool addDynTlsEntry(Symbol &Sym);
|
||||
bool addTlsIndex();
|
||||
uint64_t getPageEntryOffset(const SymbolBody &B, int64_t Addend) const;
|
||||
uint64_t getBodyEntryOffset(const SymbolBody &B, int64_t Addend) const;
|
||||
uint64_t getGlobalDynOffset(const SymbolBody &B) const;
|
||||
uint64_t getPageEntryOffset(const Symbol &B, int64_t Addend) const;
|
||||
uint64_t getSymEntryOffset(const Symbol &B, int64_t Addend) const;
|
||||
uint64_t getGlobalDynOffset(const Symbol &B) const;
|
||||
|
||||
// Returns the symbol which corresponds to the first entry of the global part
|
||||
// of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic
|
||||
// table properties.
|
||||
// Returns nullptr if the global part is empty.
|
||||
const SymbolBody *getFirstGlobalEntry() const;
|
||||
const Symbol *getFirstGlobalEntry() const;
|
||||
|
||||
// Returns the number of entries in the local part of GOT including
|
||||
// the number of reserved entries.
|
||||
@ -248,7 +244,7 @@ private:
|
||||
// to the first index of "Page" entries allocated for this section.
|
||||
llvm::SmallMapVector<const OutputSection *, size_t, 16> PageIndexMap;
|
||||
|
||||
typedef std::pair<const SymbolBody *, uint64_t> GotEntry;
|
||||
typedef std::pair<const Symbol *, uint64_t> GotEntry;
|
||||
typedef std::vector<GotEntry> GotEntries;
|
||||
// Map from Symbol-Addend pair to the GOT index.
|
||||
llvm::DenseMap<GotEntry, size_t> EntryIndexMap;
|
||||
@ -261,7 +257,7 @@ private:
|
||||
GotEntries GlobalEntries;
|
||||
|
||||
// TLS entries.
|
||||
std::vector<const SymbolBody *> TlsEntries;
|
||||
std::vector<const Symbol *> TlsEntries;
|
||||
|
||||
uint32_t TlsIndexOff = -1;
|
||||
uint64_t Size = 0;
|
||||
@ -270,13 +266,13 @@ private:
|
||||
class GotPltSection final : public SyntheticSection {
|
||||
public:
|
||||
GotPltSection();
|
||||
void addEntry(SymbolBody &Sym);
|
||||
void addEntry(Symbol &Sym);
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override { return Entries.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<const SymbolBody *> Entries;
|
||||
std::vector<const Symbol *> Entries;
|
||||
};
|
||||
|
||||
// The IgotPltSection is a Got associated with the PltSection for GNU Ifunc
|
||||
@ -286,13 +282,13 @@ private:
|
||||
class IgotPltSection final : public SyntheticSection {
|
||||
public:
|
||||
IgotPltSection();
|
||||
void addEntry(SymbolBody &Sym);
|
||||
void addEntry(Symbol &Sym);
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override { return Entries.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<const SymbolBody *> Entries;
|
||||
std::vector<const Symbol *> Entries;
|
||||
};
|
||||
|
||||
class StringTableSection final : public SyntheticSection {
|
||||
@ -315,8 +311,7 @@ private:
|
||||
class DynamicReloc {
|
||||
public:
|
||||
DynamicReloc(uint32_t Type, const InputSectionBase *InputSec,
|
||||
uint64_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
|
||||
int64_t Addend)
|
||||
uint64_t OffsetInSec, bool UseSymVA, Symbol *Sym, int64_t Addend)
|
||||
: Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec),
|
||||
UseSymVA(UseSymVA), Addend(Addend) {}
|
||||
|
||||
@ -328,7 +323,7 @@ public:
|
||||
uint32_t Type;
|
||||
|
||||
private:
|
||||
SymbolBody *Sym;
|
||||
Symbol *Sym;
|
||||
const InputSectionBase *InputSec = nullptr;
|
||||
uint64_t OffsetInSec;
|
||||
bool UseSymVA;
|
||||
@ -342,30 +337,8 @@ template <class ELFT> class DynamicSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
|
||||
// The .dynamic section contains information for the dynamic linker.
|
||||
// The section consists of fixed size entries, which consist of
|
||||
// type and value fields. Value are one of plain integers, symbol
|
||||
// addresses, or section addresses. This struct represents the entry.
|
||||
struct Entry {
|
||||
int32_t Tag;
|
||||
union {
|
||||
OutputSection *OutSec;
|
||||
InputSection *InSec;
|
||||
uint64_t Val;
|
||||
const SymbolBody *Sym;
|
||||
};
|
||||
enum KindT { SecAddr, SecSize, SymAddr, PlainInt, InSecAddr } Kind;
|
||||
Entry(int32_t Tag, OutputSection *OutSec, KindT Kind = SecAddr)
|
||||
: Tag(Tag), OutSec(OutSec), Kind(Kind) {}
|
||||
Entry(int32_t Tag, InputSection *Sec)
|
||||
: Tag(Tag), InSec(Sec), Kind(InSecAddr) {}
|
||||
Entry(int32_t Tag, uint64_t Val) : Tag(Tag), Val(Val), Kind(PlainInt) {}
|
||||
Entry(int32_t Tag, const SymbolBody *Sym)
|
||||
: Tag(Tag), Sym(Sym), Kind(SymAddr) {}
|
||||
};
|
||||
|
||||
// finalizeContents() fills this vector with the section contents.
|
||||
std::vector<Entry> Entries;
|
||||
std::vector<std::pair<int32_t, std::function<uint64_t()>>> Entries;
|
||||
|
||||
public:
|
||||
DynamicSection();
|
||||
@ -374,33 +347,66 @@ public:
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
private:
|
||||
void addEntries();
|
||||
void add(Entry E) { Entries.push_back(E); }
|
||||
void add(int32_t Tag, std::function<uint64_t()> Fn);
|
||||
void addInt(int32_t Tag, uint64_t Val);
|
||||
void addInSec(int32_t Tag, InputSection *Sec);
|
||||
void addOutSec(int32_t Tag, OutputSection *Sec);
|
||||
void addSize(int32_t Tag, OutputSection *Sec);
|
||||
void addSym(int32_t Tag, Symbol *Sym);
|
||||
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT> class RelocationSection final : public SyntheticSection {
|
||||
class RelocationBaseSection : public SyntheticSection {
|
||||
public:
|
||||
RelocationBaseSection(StringRef Name, uint32_t Type, int32_t DynamicTag,
|
||||
int32_t SizeDynamicTag);
|
||||
void addReloc(const DynamicReloc &Reloc);
|
||||
bool empty() const override { return Relocs.empty(); }
|
||||
size_t getSize() const override { return Relocs.size() * this->Entsize; }
|
||||
size_t getRelativeRelocCount() const { return NumRelativeRelocs; }
|
||||
void finalizeContents() override;
|
||||
int32_t DynamicTag, SizeDynamicTag;
|
||||
|
||||
protected:
|
||||
std::vector<DynamicReloc> Relocs;
|
||||
size_t NumRelativeRelocs = 0;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class RelocationSection final : public RelocationBaseSection {
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
|
||||
public:
|
||||
RelocationSection(StringRef Name, bool Sort);
|
||||
void addReloc(const DynamicReloc &Reloc);
|
||||
unsigned getRelocOffset();
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override { return Relocs.empty(); }
|
||||
size_t getSize() const override { return Relocs.size() * this->Entsize; }
|
||||
size_t getRelativeRelocCount() const { return NumRelativeRelocs; }
|
||||
|
||||
private:
|
||||
bool Sort;
|
||||
size_t NumRelativeRelocs = 0;
|
||||
std::vector<DynamicReloc> Relocs;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class AndroidPackedRelocationSection final : public RelocationBaseSection {
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
|
||||
public:
|
||||
AndroidPackedRelocationSection(StringRef Name);
|
||||
|
||||
bool updateAllocSize() override;
|
||||
size_t getSize() const override { return RelocData.size(); }
|
||||
void writeTo(uint8_t *Buf) override {
|
||||
memcpy(Buf, RelocData.data(), RelocData.size());
|
||||
}
|
||||
|
||||
private:
|
||||
SmallVector<char, 0> RelocData;
|
||||
};
|
||||
|
||||
struct SymbolTableEntry {
|
||||
SymbolBody *Symbol;
|
||||
Symbol *Sym;
|
||||
size_t StrTabOffset;
|
||||
};
|
||||
|
||||
@ -410,9 +416,9 @@ public:
|
||||
void finalizeContents() override;
|
||||
void postThunkContents() override;
|
||||
size_t getSize() const override { return getNumSymbols() * Entsize; }
|
||||
void addSymbol(SymbolBody *Body);
|
||||
void addSymbol(Symbol *Sym);
|
||||
unsigned getNumSymbols() const { return Symbols.size() + 1; }
|
||||
size_t getSymbolIndex(SymbolBody *Body);
|
||||
size_t getSymbolIndex(Symbol *Sym);
|
||||
ArrayRef<SymbolTableEntry> getSymbols() const { return Symbols; }
|
||||
|
||||
protected:
|
||||
@ -420,6 +426,10 @@ protected:
|
||||
std::vector<SymbolTableEntry> Symbols;
|
||||
|
||||
StringTableSection &StrTabSec;
|
||||
|
||||
llvm::once_flag OnceFlag;
|
||||
llvm::DenseMap<Symbol *, size_t> SymbolIndexMap;
|
||||
llvm::DenseMap<OutputSection *, size_t> SectionIndexMap;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
@ -451,9 +461,10 @@ private:
|
||||
void writeHashTable(uint8_t *Buf);
|
||||
|
||||
struct Entry {
|
||||
SymbolBody *Body;
|
||||
Symbol *Sym;
|
||||
size_t StrTabOffset;
|
||||
uint32_t Hash;
|
||||
uint32_t BucketIdx;
|
||||
};
|
||||
|
||||
std::vector<Entry> Symbols;
|
||||
@ -462,7 +473,7 @@ private:
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT> class HashTableSection final : public SyntheticSection {
|
||||
class HashTableSection final : public SyntheticSection {
|
||||
public:
|
||||
HashTableSection();
|
||||
void finalizeContents() override;
|
||||
@ -485,57 +496,81 @@ public:
|
||||
bool empty() const override { return Entries.empty(); }
|
||||
void addSymbols();
|
||||
|
||||
template <class ELFT> void addEntry(SymbolBody &Sym);
|
||||
template <class ELFT> void addEntry(Symbol &Sym);
|
||||
|
||||
private:
|
||||
void writeHeader(uint8_t *Buf){};
|
||||
void addHeaderSymbols(){};
|
||||
unsigned getPltRelocOff() const;
|
||||
std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
|
||||
std::vector<std::pair<const Symbol *, unsigned>> Entries;
|
||||
// Iplt always has HeaderSize of 0, the Plt HeaderSize is always non-zero
|
||||
size_t HeaderSize;
|
||||
};
|
||||
|
||||
class GdbIndexSection final : public SyntheticSection {
|
||||
const unsigned OffsetTypeSize = 4;
|
||||
const unsigned CuListOffset = 6 * OffsetTypeSize;
|
||||
const unsigned CompilationUnitSize = 16;
|
||||
const unsigned AddressEntrySize = 16 + OffsetTypeSize;
|
||||
const unsigned SymTabEntrySize = 2 * OffsetTypeSize;
|
||||
// GdbIndexChunk is created for each .debug_info section and contains
|
||||
// information to create a part of .gdb_index for a given input section.
|
||||
struct GdbIndexChunk {
|
||||
struct AddressEntry {
|
||||
InputSection *Section;
|
||||
uint64_t LowAddress;
|
||||
uint64_t HighAddress;
|
||||
uint32_t CuIndex;
|
||||
};
|
||||
|
||||
struct CuEntry {
|
||||
uint64_t CuOffset;
|
||||
uint64_t CuLength;
|
||||
};
|
||||
|
||||
struct NameTypeEntry {
|
||||
llvm::CachedHashStringRef Name;
|
||||
uint8_t Type;
|
||||
};
|
||||
|
||||
InputSection *DebugInfoSec;
|
||||
std::vector<AddressEntry> AddressAreas;
|
||||
std::vector<CuEntry> CompilationUnits;
|
||||
std::vector<NameTypeEntry> NamesAndTypes;
|
||||
};
|
||||
|
||||
// The symbol type for the .gdb_index section.
|
||||
struct GdbSymbol {
|
||||
uint32_t NameHash;
|
||||
size_t NameOffset;
|
||||
size_t CuVectorIndex;
|
||||
};
|
||||
|
||||
class GdbIndexSection final : public SyntheticSection {
|
||||
public:
|
||||
GdbIndexSection(std::vector<GdbIndexChunk> &&Chunks);
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
bool empty() const override;
|
||||
|
||||
// Symbol table is a hash table for types and names.
|
||||
// It is the area of gdb index.
|
||||
GdbHashTab SymbolTable;
|
||||
private:
|
||||
void fixCuIndex();
|
||||
std::vector<std::vector<uint32_t>> createCuVectors();
|
||||
std::vector<GdbSymbol *> createGdbSymtab();
|
||||
|
||||
// A symbol table for this .gdb_index section.
|
||||
std::vector<GdbSymbol *> GdbSymtab;
|
||||
|
||||
// CU vector is a part of constant pool area of section.
|
||||
std::vector<std::set<uint32_t>> CuVectors;
|
||||
std::vector<std::vector<uint32_t>> CuVectors;
|
||||
|
||||
// String pool is also a part of constant pool, it follows CU vectors.
|
||||
llvm::StringTableBuilder StringPool;
|
||||
// Symbol table contents.
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, GdbSymbol *> Symbols;
|
||||
|
||||
// Each chunk contains information gathered from a debug sections of single
|
||||
// object and used to build different areas of gdb index.
|
||||
std::vector<GdbIndexChunk> Chunks;
|
||||
|
||||
private:
|
||||
void buildIndex();
|
||||
|
||||
static constexpr uint32_t CuListOffset = 24;
|
||||
uint32_t CuTypesOffset;
|
||||
uint32_t SymTabOffset;
|
||||
uint32_t SymtabOffset;
|
||||
uint32_t ConstantPoolOffset;
|
||||
uint32_t StringPoolOffset;
|
||||
uint32_t StringPoolSize;
|
||||
|
||||
size_t CuVectorsSize = 0;
|
||||
std::vector<size_t> CuVectorsOffset;
|
||||
|
||||
bool Finalized = false;
|
||||
std::vector<size_t> CuVectorOffsets;
|
||||
};
|
||||
|
||||
template <class ELFT> GdbIndexSection *createGdbIndex();
|
||||
@ -549,21 +584,12 @@ template <class ELFT> GdbIndexSection *createGdbIndex();
|
||||
// Detailed info about internals can be found in Ian Lance Taylor's blog:
|
||||
// http://www.airs.com/blog/archives/460 (".eh_frame")
|
||||
// http://www.airs.com/blog/archives/462 (".eh_frame_hdr")
|
||||
template <class ELFT> class EhFrameHeader final : public SyntheticSection {
|
||||
class EhFrameHeader final : public SyntheticSection {
|
||||
public:
|
||||
EhFrameHeader();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
void addFde(uint32_t Pc, uint32_t FdeVA);
|
||||
bool empty() const override;
|
||||
|
||||
private:
|
||||
struct FdeData {
|
||||
uint32_t Pc;
|
||||
uint32_t FdeVA;
|
||||
};
|
||||
|
||||
std::vector<FdeData> Fdes;
|
||||
};
|
||||
|
||||
// For more information about .gnu.version and .gnu.version_r see:
|
||||
@ -639,22 +665,58 @@ public:
|
||||
// with different attributes in a single output sections. To do that
|
||||
// we put them into MergeSyntheticSection synthetic input sections which are
|
||||
// attached to regular output sections.
|
||||
class MergeSyntheticSection final : public SyntheticSection {
|
||||
class MergeSyntheticSection : public SyntheticSection {
|
||||
public:
|
||||
MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
|
||||
uint32_t Alignment);
|
||||
void addSection(MergeInputSection *MS);
|
||||
|
||||
protected:
|
||||
MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
|
||||
uint32_t Alignment)
|
||||
: SyntheticSection(Flags, Type, Alignment, Name) {}
|
||||
|
||||
std::vector<MergeInputSection *> Sections;
|
||||
};
|
||||
|
||||
class MergeTailSection final : public MergeSyntheticSection {
|
||||
public:
|
||||
MergeTailSection(StringRef Name, uint32_t Type, uint64_t Flags,
|
||||
uint32_t Alignment);
|
||||
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void finalizeContents() override;
|
||||
bool shouldTailMerge() const;
|
||||
size_t getSize() const override;
|
||||
|
||||
private:
|
||||
void finalizeTailMerge();
|
||||
void finalizeNoTailMerge();
|
||||
|
||||
llvm::StringTableBuilder Builder;
|
||||
std::vector<MergeInputSection *> Sections;
|
||||
};
|
||||
|
||||
class MergeNoTailSection final : public MergeSyntheticSection {
|
||||
public:
|
||||
MergeNoTailSection(StringRef Name, uint32_t Type, uint64_t Flags,
|
||||
uint32_t Alignment)
|
||||
: MergeSyntheticSection(Name, Type, Flags, Alignment) {}
|
||||
|
||||
size_t getSize() const override { return Size; }
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void finalizeContents() override;
|
||||
|
||||
private:
|
||||
// We use the most significant bits of a hash as a shard ID.
|
||||
// The reason why we don't want to use the least significant bits is
|
||||
// because DenseMap also uses lower bits to determine a bucket ID.
|
||||
// If we use lower bits, it significantly increases the probability of
|
||||
// hash collisons.
|
||||
size_t getShardId(uint32_t Hash) {
|
||||
return Hash >> (32 - llvm::countTrailingZeros(NumShards));
|
||||
}
|
||||
|
||||
// Section size
|
||||
size_t Size;
|
||||
|
||||
// String table contents
|
||||
constexpr static size_t NumShards = 32;
|
||||
std::vector<llvm::StringTableBuilder> Shards;
|
||||
size_t ShardOffsets[NumShards];
|
||||
};
|
||||
|
||||
// .MIPS.abiflags section.
|
||||
@ -746,13 +808,13 @@ private:
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT> InputSection *createCommonSection();
|
||||
InputSection *createInterpSection();
|
||||
template <class ELFT> MergeInputSection *createCommentSection();
|
||||
void decompressAndMergeSections();
|
||||
void decompressSections();
|
||||
void mergeSections();
|
||||
|
||||
SymbolBody *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
|
||||
uint64_t Size, InputSectionBase *Section);
|
||||
Symbol *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 {
|
||||
@ -760,11 +822,13 @@ struct InX {
|
||||
static BssSection *Bss;
|
||||
static BssSection *BssRelRo;
|
||||
static BuildIdSection *BuildId;
|
||||
static InputSection *Common;
|
||||
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;
|
||||
@ -774,29 +838,20 @@ struct InX {
|
||||
static MipsRldMapSection *MipsRldMap;
|
||||
static PltSection *Plt;
|
||||
static PltSection *Iplt;
|
||||
static RelocationBaseSection *RelaDyn;
|
||||
static RelocationBaseSection *RelaPlt;
|
||||
static RelocationBaseSection *RelaIplt;
|
||||
static StringTableSection *ShStrTab;
|
||||
static StringTableSection *StrTab;
|
||||
static SymbolTableBaseSection *SymTab;
|
||||
};
|
||||
|
||||
template <class ELFT> struct In : public InX {
|
||||
static EhFrameHeader<ELFT> *EhFrameHdr;
|
||||
static EhFrameSection<ELFT> *EhFrame;
|
||||
static HashTableSection<ELFT> *HashTab;
|
||||
static RelocationSection<ELFT> *RelaDyn;
|
||||
static RelocationSection<ELFT> *RelaPlt;
|
||||
static RelocationSection<ELFT> *RelaIplt;
|
||||
template <class ELFT> struct In {
|
||||
static VersionDefinitionSection<ELFT> *VerDef;
|
||||
static VersionTableSection<ELFT> *VerSym;
|
||||
static VersionNeedSection<ELFT> *VerNeed;
|
||||
};
|
||||
|
||||
template <class ELFT> EhFrameHeader<ELFT> *In<ELFT>::EhFrameHdr;
|
||||
template <class ELFT> EhFrameSection<ELFT> *In<ELFT>::EhFrame;
|
||||
template <class ELFT> HashTableSection<ELFT> *In<ELFT>::HashTab;
|
||||
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaDyn;
|
||||
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaPlt;
|
||||
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaIplt;
|
||||
template <class ELFT> VersionDefinitionSection<ELFT> *In<ELFT>::VerDef;
|
||||
template <class ELFT> VersionTableSection<ELFT> *In<ELFT>::VerSym;
|
||||
template <class ELFT> VersionNeedSection<ELFT> *In<ELFT>::VerNeed;
|
||||
|
@ -25,11 +25,11 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Target.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "OutputSections.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -40,7 +40,7 @@ using namespace lld::elf;
|
||||
|
||||
TargetInfo *elf::Target;
|
||||
|
||||
std::string lld::toString(uint32_t Type) {
|
||||
std::string lld::toString(RelType Type) {
|
||||
StringRef S = getELFRelocationTypeName(elf::Config->EMachine, Type);
|
||||
if (S == "Unknown")
|
||||
return ("Unknown (" + Twine(Type) + ")").str();
|
||||
@ -117,27 +117,26 @@ std::string elf::getErrorLocation(const uint8_t *Loc) {
|
||||
|
||||
TargetInfo::~TargetInfo() {}
|
||||
|
||||
int64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
int64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TargetInfo::usesOnlyLowPageBits(uint32_t Type) const { return false; }
|
||||
bool TargetInfo::usesOnlyLowPageBits(RelType Type) const { return false; }
|
||||
|
||||
bool TargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile *File, const SymbolBody &S) const {
|
||||
bool TargetInfo::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
|
||||
uint64_t BranchAddr, const Symbol &S) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TargetInfo::inBranchRange(uint32_t RelocType, uint64_t Src,
|
||||
uint64_t Dst) const {
|
||||
bool TargetInfo::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
void TargetInfo::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
|
||||
writeGotPlt(Buf, S);
|
||||
}
|
||||
|
||||
RelExpr TargetInfo::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr TargetInfo::adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
RelExpr Expr) const {
|
||||
return Expr;
|
||||
}
|
||||
@ -146,22 +145,29 @@ void TargetInfo::relaxGot(uint8_t *Loc, uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
|
||||
void TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
|
||||
void TargetInfo::relaxTlsGdToLe(uint8_t *Loc, RelType Type,
|
||||
uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
|
||||
void TargetInfo::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
|
||||
void TargetInfo::relaxTlsGdToIe(uint8_t *Loc, RelType Type,
|
||||
uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
|
||||
void TargetInfo::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
|
||||
void TargetInfo::relaxTlsIeToLe(uint8_t *Loc, RelType Type,
|
||||
uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
|
||||
void TargetInfo::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
|
||||
void TargetInfo::relaxTlsLdToLe(uint8_t *Loc, RelType Type,
|
||||
uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
|
||||
uint64_t TargetInfo::getImageBase() {
|
||||
// Use -image-base if set. Fall back to the target default if not.
|
||||
if (Config->ImageBase)
|
||||
return *Config->ImageBase;
|
||||
return Config->Pic ? 0 : DefaultImageBase;
|
||||
}
|
||||
|
111
ELF/Target.h
111
ELF/Target.h
@ -10,25 +10,27 @@
|
||||
#ifndef LLD_ELF_TARGET_H
|
||||
#define LLD_ELF_TARGET_H
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputSection.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
std::string toString(uint32_t RelType);
|
||||
std::string toString(elf::RelType Type);
|
||||
|
||||
namespace elf {
|
||||
class Defined;
|
||||
class InputFile;
|
||||
class SymbolBody;
|
||||
class Symbol;
|
||||
|
||||
class TargetInfo {
|
||||
public:
|
||||
virtual bool isPicRel(uint32_t Type) const { return true; }
|
||||
virtual uint32_t getDynRel(uint32_t Type) const { return Type; }
|
||||
virtual uint32_t calcEFlags() const { return 0; }
|
||||
virtual bool isPicRel(RelType Type) const { return true; }
|
||||
virtual RelType getDynRel(RelType Type) const { return Type; }
|
||||
virtual void writeGotPltHeader(uint8_t *Buf) const {}
|
||||
virtual void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {};
|
||||
virtual void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const;
|
||||
virtual int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const;
|
||||
virtual void writeGotPlt(uint8_t *Buf, const Symbol &S) const {};
|
||||
virtual void writeIgotPlt(uint8_t *Buf, const Symbol &S) const;
|
||||
virtual int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const;
|
||||
|
||||
// If lazy binding is supported, the first entry of the PLT has code
|
||||
// to call the dynamic linker to resolve PLT entries the first time
|
||||
@ -40,48 +42,52 @@ public:
|
||||
unsigned RelOff) const {}
|
||||
virtual void addPltHeaderSymbols(InputSectionBase *IS) const {}
|
||||
virtual void addPltSymbols(InputSectionBase *IS, uint64_t Off) const {}
|
||||
|
||||
// Returns true if a relocation only uses the low bits of a value such that
|
||||
// all those bits are in in the same page. For example, if the relocation
|
||||
// only uses the low 12 bits in a system with 4k pages. If this is true, the
|
||||
// bits will always have the same value at runtime and we don't have to emit
|
||||
// a dynamic relocation.
|
||||
virtual bool usesOnlyLowPageBits(uint32_t Type) const;
|
||||
virtual bool usesOnlyLowPageBits(RelType Type) const;
|
||||
|
||||
// Decide whether a Thunk is needed for the relocation from File
|
||||
// targeting S.
|
||||
virtual bool needsThunk(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile *File, const SymbolBody &S) const;
|
||||
virtual bool needsThunk(RelExpr Expr, RelType RelocType,
|
||||
const InputFile *File, uint64_t BranchAddr,
|
||||
const Symbol &S) const;
|
||||
// Return true if we can reach Dst from Src with Relocation RelocType
|
||||
virtual bool inBranchRange(uint32_t RelocType, uint64_t Src,
|
||||
virtual bool inBranchRange(RelType Type, uint64_t Src,
|
||||
uint64_t Dst) const;
|
||||
virtual RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
virtual RelExpr getRelExpr(RelType Type, const Symbol &S,
|
||||
const uint8_t *Loc) const = 0;
|
||||
virtual void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const = 0;
|
||||
|
||||
virtual void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const = 0;
|
||||
|
||||
virtual ~TargetInfo();
|
||||
|
||||
unsigned TlsGdRelaxSkip = 1;
|
||||
unsigned PageSize = 4096;
|
||||
unsigned DefaultMaxPageSize = 4096;
|
||||
|
||||
// On FreeBSD x86_64 the first page cannot be mmaped.
|
||||
// On Linux that is controled by vm.mmap_min_addr. At least on some x86_64
|
||||
// installs that is 65536, so the first 15 pages cannot be used.
|
||||
// Given that, the smallest value that can be used in here is 0x10000.
|
||||
uint64_t DefaultImageBase = 0x10000;
|
||||
uint64_t getImageBase();
|
||||
|
||||
// Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
|
||||
// end of .got
|
||||
uint64_t GotBaseSymOff = 0;
|
||||
|
||||
uint32_t CopyRel;
|
||||
uint32_t GotRel;
|
||||
uint32_t PltRel;
|
||||
uint32_t RelativeRel;
|
||||
uint32_t IRelativeRel;
|
||||
uint32_t TlsDescRel;
|
||||
uint32_t TlsGotRel;
|
||||
uint32_t TlsModuleIndexRel;
|
||||
uint32_t TlsOffsetRel;
|
||||
// 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 PltRel;
|
||||
RelType RelativeRel;
|
||||
RelType IRelativeRel;
|
||||
RelType TlsDescRel;
|
||||
RelType TlsGotRel;
|
||||
RelType TlsModuleIndexRel;
|
||||
RelType TlsOffsetRel;
|
||||
unsigned GotEntrySize = 0;
|
||||
unsigned GotPltEntrySize = 0;
|
||||
unsigned PltEntrySize;
|
||||
@ -100,13 +106,20 @@ public:
|
||||
// executable OutputSections.
|
||||
uint32_t TrapInstr = 0;
|
||||
|
||||
virtual RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
virtual RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
|
||||
RelExpr Expr) const;
|
||||
virtual void relaxGot(uint8_t *Loc, uint64_t Val) const;
|
||||
virtual void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
|
||||
virtual void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
|
||||
virtual void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
|
||||
virtual void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
|
||||
virtual void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const;
|
||||
virtual void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const;
|
||||
virtual void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const;
|
||||
virtual void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const;
|
||||
|
||||
protected:
|
||||
// On FreeBSD x86_64 the first page cannot be mmaped.
|
||||
// On Linux that is controled by vm.mmap_min_addr. At least on some x86_64
|
||||
// installs that is 65536, so the first 15 pages cannot be used.
|
||||
// Given that, the smallest value that can be used in here is 0x10000.
|
||||
uint64_t DefaultImageBase = 0x10000;
|
||||
};
|
||||
|
||||
TargetInfo *getAArch64TargetInfo();
|
||||
@ -129,32 +142,42 @@ uint64_t getAArch64Page(uint64_t Expr);
|
||||
extern TargetInfo *Target;
|
||||
TargetInfo *getTarget();
|
||||
|
||||
template <class ELFT> bool isMipsPIC(const Defined *Sym);
|
||||
|
||||
static inline void reportRangeError(uint8_t *Loc, RelType Type, const Twine &V,
|
||||
int64_t Min, uint64_t Max) {
|
||||
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
|
||||
" out of range: " + V + " is not in [" + Twine(Min) + ", " +
|
||||
Twine(Max) + "]");
|
||||
}
|
||||
|
||||
template <unsigned N>
|
||||
static void checkInt(uint8_t *Loc, int64_t V, uint32_t Type) {
|
||||
static void checkInt(uint8_t *Loc, int64_t V, RelType Type) {
|
||||
if (!llvm::isInt<N>(V))
|
||||
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
|
||||
" out of range");
|
||||
reportRangeError(Loc, Type, Twine(V), llvm::minIntN(N), llvm::maxIntN(N));
|
||||
}
|
||||
|
||||
template <unsigned N>
|
||||
static void checkUInt(uint8_t *Loc, uint64_t V, uint32_t Type) {
|
||||
static void checkUInt(uint8_t *Loc, uint64_t V, RelType Type) {
|
||||
if (!llvm::isUInt<N>(V))
|
||||
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
|
||||
" out of range");
|
||||
reportRangeError(Loc, Type, Twine(V), 0, llvm::maxUIntN(N));
|
||||
}
|
||||
|
||||
template <unsigned N>
|
||||
static void checkIntUInt(uint8_t *Loc, uint64_t V, uint32_t Type) {
|
||||
static void checkIntUInt(uint8_t *Loc, uint64_t V, RelType Type) {
|
||||
if (!llvm::isInt<N>(V) && !llvm::isUInt<N>(V))
|
||||
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
|
||||
" out of range");
|
||||
// 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
|
||||
reportRangeError(Loc, Type, Twine((int64_t)V), llvm::minIntN(N),
|
||||
llvm::maxUIntN(N));
|
||||
}
|
||||
|
||||
template <unsigned N>
|
||||
static void checkAlignment(uint8_t *Loc, uint64_t V, uint32_t Type) {
|
||||
static void checkAlignment(uint8_t *Loc, uint64_t V, RelType Type) {
|
||||
if ((V & (N - 1)) != 0)
|
||||
error(getErrorLocation(Loc) + "improper alignment for relocation " +
|
||||
lld::toString(Type));
|
||||
lld::toString(Type) + ": 0x" + llvm::utohexstr(V) +
|
||||
" is not aligned to " + Twine(N) + " bytes");
|
||||
}
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
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